Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Ring Menu ! For everywhere!

Ring Menu
Version: 2.0.0 "updated on 20/09/2010"
By: Trebor777

Introduction

A nice customizable Ring Menu :) that you can where you want! It's not a scene! :)
but an actual "object" like a window.

Features
  • Specify the radius for the Vertical and Horizontal dimension. :) If Horizontal is 0, you have a vertical riing menu.
  • Pass a block of code to each options! :)
  • Icons can be from the icon set or an independant file.
  • "Perspective effect" if "zoom" activated.
  • Smooth "scrolling" between options :) you can even specify how smooth you want it!
  • Easy access to the "selected" option, and possibilty to disable options as well :)( they will be skipped when scrolling )
  • Non-static => you can move the menu around and change its attributes after creation!
  • Brings a few useful methods to the Array class :)

1.8.1 -> 2.0.0
.Demos Updated
.Better and Simpler code structure, Menu_Options now Inherits from Sprite, less redondant code
.More comments, explaning what does what
.Move Command: @menu.move(x,y)
.Doesn't block other graphics update
.Introduction of Refresh Delay: Menu is only update every "delay" frame, by default delay is 1, so it's updated every frame.
.For options, name is now independant, and doesn't not setup the icon->
in VX: if the icon parameter is a number, it'll look in the iconset, but if it's a string, it'll look in the icon folder, as it would happen in XP
in XP: the icon parameter is a string, to look for a file in the icon folder.

1.8->1.8.1
Fixed: You can now used Up/Down Keys when it's a vertical ring menu


Screenshots
-> From Castlevania :
ring_menu_sc0.png

ring_menu_sc1.png

Demo

Basic demo for VX 400KB Updated to 2.0.0
Basic demo for XP 262KB Updated to 2.0.0

Look at the Scene_Demo script :)

Script


SCRIPT FOR VX:

[rgss]#===============================================================================
<span style="color:#000080; font-style:italic;">=begin
<span style="color:#000080; font-style:italic;">RING MENU advanced.
<span style="color:#000080; font-style:italic;">by Trebor777
<span style="color:#000080; font-style:italic;">v2.0.0
<span style="color:#000080; font-style:italic;">20/09/2010
<span style="color:#000080; font-style:italic;">An independant, class free, Ring menu, easily usable, with a lot of options.
<span style="color:#000080; font-style:italic;">=end
#===============================================================================
class Array
  # rotate  n item(s) to the left ( first becomes last) as a new array
  def rotate_l(n=1)
    a = self.clone
    n.times{a.push(a.shift)}
    a
  end
  # rotate  n item(s) to the right ( last becomes first) as a new array
  def rotate_r(n=1)
    a = self.clone
    n.times{a.unshift(a.pop)}
    a
  end
  # rotate n item(s) to the left
  def rotate_l!(n=1)
    n.times{self.push(self.shift)}
    self
  end
  # rotate n item(s) to the right
  def rotate_r!(n=1)
    n.times{self.unshift(self.pop)}
    self
  end
  # Do the sum of all the items. (they need to be all of the same type)
  def sum
    inject( nil ) { |sum,x| sum ? sum+x : x if x.respond_to? "+" }
  end
end
#===============================================================================
module Cache
  def self.icon(filename)
    load_bitmap("Graphics/Icons/",filename)
  end
end
#===============================================================================
class Ring_Menu
  RESO = 4 # Default Number of Frames between 2 position
  PLAY_SE = true # Play Sound Effect when scrolling ?
  SELECTED_ANGLE= Math::PI/2.0 # 90°CW to set the "0" at the bottom
  #---------------------------------
  attr_reader :eek:ptions, :h_radius, :v_radius, :center, :zoom, :visible, :eek:pacity, :zoom_max, :resolution
  attr_accessor :active, :index, :refresh_delay
  alias :reso :resolution
  #---------------------------------
  def initialize(h_rad=10,v_rad=10,center=[0,0],options=[])
    @options=options                                          # List of Commands
    @v_radius=v_rad                                            # Vertical Radius, how wide is the menu on the Y-axis
    @h_radius=h_rad                                           # Horizontal Radius, how wide is the menu on the X-axis
    @center = center                                             # Position of the center, in the screen
    @index=0                                                          # Cursor Position, used to get the current selected option
    @resolution = RESO                                      # Number of frames betwen 2 commands, the larger the smoother, but more time needed
    @refresh_delay = 1                                        # By default, refresh the menu every frame (1).
    @zoom_max = 1.4                                          # Max Scale factor, for the perspective Effect (only for Horizontal Menu)
    @opacity = 255                                                # General Opacity, gets applied to every command
    @visible = true                                                 # Show or Hide the Menu.
    @active = true                                                  # Enable / Disable the Menu.
    @zoom = true                                                  # Activate the Perspective Effect
    @angle = vertical? ? 0 : SELECTED_ANGLE # Where is the first command on the circle. Not recommended to change
    process
  end
  # Attributes
#---------------------------------
  def x
    @center[0]
  end
#---------------------------------  
  def y
    @center[1]
  end
  #---------------------------------
  def center=(value) # Placing is moving at a specific distance
    delta_x = value[0] - @center[0]
    delta_y = value[1] - @center[1]
    move(delta_x, delta_y)
  end
#---------------------------------  
  def x=(*args)
    self.center=[ args.first, @center[1] ]
  end
#---------------------------------  
  def y=(*args)
    self.center=[ @center[0], args.first ]
  end
#---------------------------------  
  def options=(a=[])
    @options=a
    @index=0 # reset the Index position
    process
  end
#---------------------------------  
  def visible=(value)
    @visible = value
    @options.each{|opt| opt.visible=value unless opt.nil?}
  end
#---------------------------------
  def opacity=(value)
    value = [[255,value].min, 0].max # Set value Range between 0..255
    @opacity = value
    @options.each{|opt| opt.opacity=value unless opt.nil?}
  end
#---------------------------------
  def h_radius=(value)
    @h_radius = value
    @angle = vertical? ? 0 : SELECTED_ANGLE
    process
  end
#---------------------------------
  def v_radius=(value)
    @v_radius = value
    process
  end
#---------------------------------
  def zoom=(value)
    @zoom = value
    update_draw
  end
#---------------------------------
  def resolution=(v)
    @resolution = v
    process
  end
  alias :reso= :resolution=
#---------------------------------
  def zoom_max=(value)
    @zoom_max= value
    process
  end
# HELPERS Method ==============
  def vertical? # Is the menu vertical (meaning that the horizontal radius is zero)
    @h_radius == 0
  end
#---------------------------------  
  def check_z_selected
    self.selected.z =1000 if vertical? # Set the selected option to the top
  end
#--------------------------------
  def move(d_x, d_y) # Moves center from d_x , d_y, every option will be moved accordingly
      @center[0] += d_x
      @center[1] += d_y
  end  
  # ======================
  #---------------------------------
  # Update, Called Every Frame
  #---------------------------------
  def update
    return if !self.active or !self.visible
    return unless Graphics.frame_count % @refresh_delay == 0
    unless @rotating
      start_rotate if Input.repeat?(vertical? ? Input::UP : Input::LEFT)
      start_rotate(1) if Input.repeat?(vertical? ? Input::DOWN : Input::RIGHT)
    end
   
    if @rotating
      if @counter+1==@resolution
        unless self.selected.disabled
          @rotating = false
          self.selected.selected = true
          check_z_selected
        else#if self.selected.disabled
          @index += @way*2-1
          @index %= @options.size
        end
      end
      if @way.zero?
        @coord_set.rotate_l!
        @zoom_set.rotate_l!
      elsif @way==1
        @coord_set.rotate_r!
        @zoom_set.rotate_r!
      end
      @counter+=1
      @counter%=@resolution
    end
    update_draw
  end
 #---------------------------------
# Process: Create/recreate according to the menu attributes, the position/zoom values of the options, and store them into arrays.
 #---------------------------------
  def process
    # return if the menu is empty.
    return if @options.empty?
    # Get the maximum size for the arrays.
    max_data=@options.size*@resolution
    # Divide a circle(2*Pi) by the numbers of options.
    @base_angle = Math::PI*2.0/max_data
    @zoom_step = (@zoom_max-0.5)/(max_data/2.0)
    # set an array having precalculated pair of coordinates so we don't have to
    # get process them for each rotation(we simply switch the options coords)
    @coord_set= Array.new(max_data,0)
    # and for zoom values as well
    @zoom_set = Array.new(max_data,0)
    max_data.times do |i|
      angle = i*@base_angle+@angle
      x = (Math::cos(angle)*@h_radius)            # Calculates relative position, as if center was in 0,0
      y = (Math::sin(angle)*@v_radius)
      @coord_set= [x,y]
      @zoom_set= i<max_data/2.0 ? @zoom_max-@zoom_step*i : @zoom_set[max_data/2.0-i-max_data/2.0-1]
    end
 
    val = (@index % max_data)+1+((@resolution-4)/2.0).round
   
    @coord_set.rotate_r!(val)
    @zoom_set.rotate_r!(val)
    @old_center = @center
    @old_hr = @h_radius
    @old_vr = @v_radius
    update_draw
    self.selected.selected = true
    check_z_selected
  end
  #--------------------------------------
  # Draw the options according to the current index
  #--------------------------------------
  def update_draw
    @coord_set.each_index do |index|
      i = (index / @resolution.to_f).round
      # next iteration if the current option is not a RMenu_Option
      next if !@options.is_a?(RMenu_Option)
      # Set the Option coords
      @options.coord = @coord_set[index]
      @options.x += @center[0] # Place options relatively to the center.
      @options.y += @center[1]
      # Set the option's Z-index to its y.
      @options.z = @coord_set[index][1]
      @options.zoom = @zoom ? @zoom_set[index] : 1.0
    end
    @old_zoom = @zoom
    check_z_selected unless @rotating
  end
  #---------------------------------
  # Rotate the menu options
  #---------------------------------
  def start_rotate(way=0)# 0 = left, 1 = right
    self.selected.selected = false
    @rotating = true
    @counter = 0
    @way = way
    @index += @way*2-1
    @index %= @options.size
    Sound.play_cursor if PLAY_SE
  end
  #---------------------------------
  # Return the current selected option
  #---------------------------------
  def selected
    @options[@index % @options.size]
  end
  #---------------------------------
  # Dispose the menu (and all the options)
  #---------------------------------
  def dispose
    @options.each{|opt| opt.dispose}
  end
  #--------------------------------------------------------------------------
  # * Hide -> disable and hides the menu
  #--------------------------------------------------------------------------
  def hide
    self.visible = false
    self.active = false
  end
  #--------------------------------------------------------------------------
  # * Show -> enable and show the menu
  #--------------------------------------------------------------------------
  def show
    self.visible = true
    self.active = true
  end
end
#===============================================================================
# Menu option class, Inherits from the Sprite class, to ease management.
#===============================================================================
class RMenu_Option < Sprite
  attr_reader :name, :icon, :selected, :disabled
#--------------------------------------------------------------------------
  def initialize(name="",icon=0,viewport=nil,&block)
    super(viewport)
    @name=name
    @icon=icon
    @block=block
    @disabled = false
    self.selected = false
    if icon > 0 and !icon.is_a? String
      self.bitmap = Bitmap.new(24,24)
      self.draw_icon(@icon,0,0,true)
    elsif icon.is_a? String
      self.bitmap = Cache.icon(icon)
    end
    self.ox = self.width/2
    self.oy = self.height/2
  end
  #--------------------------------------------------------------------------
  # * Draw Icon
  #     icon_index : Icon number
  #     x     : draw spot x-coordinate
  #     y     : draw spot y-coordinate
  #     enabled    : Enabled flag. When false, draw semi-transparently.
  #--------------------------------------------------------------------------
  def draw_icon(icon_index, x, y, enabled = true)
    bitmap = Cache.system("Iconset")
    rect = Rect.new(icon_index % 16 * 24, icon_index / 16 * 24, 24, 24)
    self.bitmap.blt(x, y, bitmap, rect, enabled ? 255 : 128)
  end
#--------------------------------------------------------------------------
  def call # Execute the Code passed as an argument when the option was created
    @block.call unless @block.nil?
  end
#--------------------------------------------------------------------------
  def selected=(v)
    if v != @selected
      self.tone.gray = (v==true ? 0 : 255)
      @selected = v==true
    end
  end
#--------------------------------------------------------------------------
  def disabled=(v)
    @disabled= v==true # v == true makes sure that v is a boolean, meaning that if v isn't a boolean, @disabled will be set to false
  end
#--------------------------------------------------------------------------
  def coord=(c = [0,0])
    self.x = c[0]
    self.y = c[1]
  end
#--------------------------------------------------------------------------
  def zoom=value
    self.zoom_x = value
    self.zoom_y = value
  end
#--------------------------------------------------------------------------
  def dispose
    @coord=nil
    @block=nil
    super
  end
end
[/rgss]

SCRIPT FOR XP:

[rgss]#===============================================================================
<span style="color:#000080; font-style:italic;">=begin
<span style="color:#000080; font-style:italic;">RING MENU advanced. XP VERSION
<span style="color:#000080; font-style:italic;">by Trebor777
<span style="color:#000080; font-style:italic;">v2.0.0
<span style="color:#000080; font-style:italic;">20/09/2010
<span style="color:#000080; font-style:italic;">An independant, class free, Ring menu, easily usable, with a lot of options.
<span style="color:#000080; font-style:italic;">=end
#===============================================================================
class Array
  # rotate  n item(s) to the left ( first becomes last) as a new array
  def rotate_l(n=1)
    a = self.clone
    n.times{a.push(a.shift)}
    a
  end
  # rotate  n item(s) to the right ( last becomes first) as a new array
  def rotate_r(n=1)
    a = self.clone
    n.times{a.unshift(a.pop)}
    a
  end
  # rotate n item(s) to the left
  def rotate_l!(n=1)
    n.times{self.push(self.shift)}
    self
  end
  # rotate n item(s) to the right
  def rotate_r!(n=1)
    n.times{self.unshift(self.pop)}
    self
  end
  # Do the sum of all the items. (they need to be all of the same type)
  def sum
    inject( nil ) { |sum,x| sum ? sum+x : x if x.respond_to? "+" }
  end
end
#===============================================================================
module Sound
  #--------------------------------------------------------------------------
  # * Play Sound Effect
  #     se : sound effect to be played
  #--------------------------------------------------------------------------
  def self.se_play(se)
    if se != nil and se.name != ""
      Audio.se_play("Audio/SE/" + se.name, se.volume, se.pitch)
    end
  end
 
  def self.play_cursor
    se_play($data_system.decision_se) unless $data_system.nil?
  end
end
#===============================================================================
class Ring_Menu
  RESO = 4 # Default Number of Frames between 2 position
  PLAY_SE = true # Play Sound Effect when scrolling ?
  SELECTED_ANGLE= Math::PI/2.0 # 90°CW to set the "0" at the bottom
  #---------------------------------
  attr_reader :eek:ptions, :h_radius, :v_radius, :center, :zoom, :visible, :eek:pacity, :zoom_max, :resolution
  attr_accessor :active, :index, :refresh_delay
  alias :reso :resolution
  #---------------------------------
  def initialize(h_rad=10,v_rad=10,center=[0,0],options=[])
    @options=options                                          # List of Commands
    @v_radius=v_rad                                            # Vertical Radius, how wide is the menu on the Y-axis
    @h_radius=h_rad                                           # Horizontal Radius, how wide is the menu on the X-axis
    @center = center                                             # Position of the center, in the screen
    @index=0                                                          # Cursor Position, used to get the current selected option
    @resolution = RESO                                      # Number of frames betwen 2 commands, the larger the smoother, but more time needed
    @refresh_delay = 1                                        # By default, refresh the menu every frame (1).
    @zoom_max = 1.4                                          # Max Scale factor, for the perspective Effect (only for Horizontal Menu)
    @opacity = 255                                                # General Opacity, gets applied to every command
    @visible = true                                                 # Show or Hide the Menu.
    @active = true                                                  # Enable / Disable the Menu.
    @zoom = true                                                  # Activate the Perspective Effect
    @angle = vertical? ? 0 : SELECTED_ANGLE # Where is the first command on the circle. Not recommended to change
    process
  end
  # Attributes
#---------------------------------
  def x
    @center[0]
  end
#---------------------------------  
  def y
    @center[1]
  end
  #---------------------------------
  def center=(value) # Placing is moving at a specific distance
    delta_x = value[0] - @center[0]
    delta_y = value[1] - @center[1]
    move(delta_x, delta_y)
  end
#---------------------------------  
  def x=(*args)
    self.center=[ args.first, @center[1] ]
  end
#---------------------------------  
  def y=(*args)
    self.center=[ @center[0], args.first ]
  end
#---------------------------------  
  def options=(a=[])
    @options=a
    @index=0 # reset the Index position
    process
  end
#---------------------------------  
  def visible=(value)
    @visible = value
    @options.each{|opt| opt.visible=value unless opt.nil?}
  end
#---------------------------------
  def opacity=(value)
    value = [[255,value].min, 0].max # Set value Range between 0..255
    @opacity = value
    @options.each{|opt| opt.opacity=value unless opt.nil?}
  end
#---------------------------------
  def h_radius=(value)
    @h_radius = value
    @angle = vertical? ? 0 : SELECTED_ANGLE
    process
  end
#---------------------------------
  def v_radius=(value)
    @v_radius = value
    process
  end
#---------------------------------
  def zoom=(value)
    @zoom = value
    update_draw
  end
#---------------------------------
  def resolution=(v)
    @resolution = v
    process
  end
  alias :reso= :resolution=
#---------------------------------
  def zoom_max=(value)
    @zoom_max= value
    process
  end
# HELPERS Method ==============
  def vertical? # Is the menu vertical (meaning that the horizontal radius is zero)
    @h_radius == 0
  end
#---------------------------------  
  def check_z_selected
    self.selected.z =1000 if vertical? # Set the selected option to the top
  end
#--------------------------------
  def move(d_x, d_y) # Moves center from d_x , d_y, every option will be moved accordingly
      @center[0] += d_x
      @center[1] += d_y
  end  
  # ======================
  #---------------------------------
  # Update, Called Every Frame
  #---------------------------------
  def update
    return if !self.active or !self.visible
    return unless Graphics.frame_count % @refresh_delay == 0
    unless @rotating
      start_rotate if Input.repeat?(vertical? ? Input::UP : Input::LEFT)
      start_rotate(1) if Input.repeat?(vertical? ? Input::DOWN : Input::RIGHT)
    end
   
    if @rotating
      if @counter+1==@resolution
        unless self.selected.disabled
          @rotating = false
          self.selected.selected = true
          check_z_selected
        else#if self.selected.disabled
          @index += @way*2-1
          @index %= @options.size
        end
      end
      if @way.zero?
        @coord_set.rotate_l!
        @zoom_set.rotate_l!
      elsif @way==1
        @coord_set.rotate_r!
        @zoom_set.rotate_r!
      end
      @counter+=1
      @counter%=@resolution
    end
    update_draw
  end
 #---------------------------------
# Process: Create/recreate according to the menu attributes, the position/zoom values of the options, and store them into arrays.
 #---------------------------------
  def process
    # return if the menu is empty.
    return if @options.empty?
    # Get the maximum size for the arrays.
    max_data=@options.size*@resolution
    # Divide a circle(2*Pi) by the numbers of options.
    @base_angle = Math::PI*2.0/max_data
    @zoom_step = (@zoom_max-0.5)/(max_data/2.0)
    # set an array having precalculated pair of coordinates so we don't have to
    # get process them for each rotation(we simply switch the options coords)
    @coord_set= Array.new(max_data,0)
    # and for zoom values as well
    @zoom_set = Array.new(max_data,0)
    max_data.times do |i|
      angle = i*@base_angle+@angle
      x = (Math::cos(angle)*@h_radius)            # Calculates relative position, as if center was in 0,0
      y = (Math::sin(angle)*@v_radius)
      @coord_set= [x,y]
      @zoom_set= i<max_data/2.0 ? @zoom_max-@zoom_step*i : @zoom_set[max_data/2.0-i-max_data/2.0-1]
    end
 
    val = (@index % max_data)+1+((@resolution-4)/2.0).round
   
    @coord_set.rotate_r!(val)
    @zoom_set.rotate_r!(val)
    @old_center = @center
    @old_hr = @h_radius
    @old_vr = @v_radius
    update_draw
    self.selected.selected = true
    check_z_selected
  end
  #--------------------------------------
  # Draw the options according to the current index
  #--------------------------------------
  def update_draw
    @coord_set.each_index do |index|
      i = (index / @resolution.to_f).round
      # next iteration if the current option is not a RMenu_Option
      next if !@options.is_a?(RMenu_Option)
      # Set the Option coords
      @options.coord = @coord_set[index]
      @options.x += @center[0] # Place options relatively to the center.
      @options.y += @center[1]
      # Set the option's Z-index to its y.
      @options.z = @coord_set[index][1]
      @options.zoom = @zoom ? @zoom_set[index] : 1.0
    end
    @old_zoom = @zoom
    check_z_selected unless @rotating
  end
  #---------------------------------
  # Rotate the menu options
  #---------------------------------
  def start_rotate(way=0)# 0 = left, 1 = right
    self.selected.selected = false
    @rotating = true
    @counter = 0
    @way = way
    @index += @way*2-1
    @index %= @options.size
    Sound.play_cursor if PLAY_SE
  end
  #---------------------------------
  # Return the current selected option
  #---------------------------------
  def selected
    @options[@index % @options.size]
  end
  #---------------------------------
  # Dispose the menu (and all the options)
  #---------------------------------
  def dispose
    @options.each{|opt| opt.dispose}
  end
  #--------------------------------------------------------------------------
  # * Hide -> disable and hides the menu
  #--------------------------------------------------------------------------
  def hide
    self.visible = false
    self.active = false
  end
  #--------------------------------------------------------------------------
  # * Show -> enable and show the menu
  #--------------------------------------------------------------------------
  def show
    self.visible = true
    self.active = true
  end
end
#===============================================================================
# Menu option class, Inherits from the Sprite class, to ease management.
#===============================================================================
class RMenu_Option < Sprite
  attr_reader :name, :icon_name, :selected, :disabled
#--------------------------------------------------------------------------
  def initialize(name="",icon="",viewport=nil,&block)
    super(viewport)
    @name=name
    @icon_name=icon
    @block=block
    @disabled = false
    self.selected = false
    self.bitmap = RPG::Cache.icon(@icon_name)
    self.ox = self.bitmap.width/2
    self.oy = self.bitmap.height/2
  end
#--------------------------------------------------------------------------
  def call # Execute the Code passed as an argument when the option was created
    @block.call unless @block.nil?
  end
#--------------------------------------------------------------------------
  def selected=(v)
    if v != @selected
      self.tone.gray = (v==true ? 0 : 255)
      @selected = v==true
    end
  end
#--------------------------------------------------------------------------
  def disabled=(v)
    @disabled= v==true # v == true makes sure that v is a boolean, meaning that if v isn't a boolean, @disabled will be set to false
  end
#--------------------------------------------------------------------------
  def coord=(c = [0,0])
    self.x = c[0]
    self.y = c[1]
  end
#--------------------------------------------------------------------------
  def zoom=value
    self.zoom_x = value
    self.zoom_y = value
  end
#--------------------------------------------------------------------------
  def dispose
    @coord=nil
    @block=nil
    super
  end
end
[/rgss]


Instructions

Very easy to use, only a few things to keep in mind!

->Paste the script above main!

->Now to create a ring menu:
Step 1
- you need to create the options like this =>
Code:
 RMenu_Option.new("Name", [icon_id, viewport]) { optional_block }
FOR VX ONLY : If icon_id is a string , it will look for an image named with icon_id in a folder called "Icons" in the Graphics folder. Else, if it's a number >0, it'll take the icon from the icon set

FOR XP only: Because XP doesn't have an icon set, the icon feature only works with the name, the xp script doesn't care about the Icon id, and will only look for a valid icon name

If you don't set up any graphics, it won't display anything. Anyway you can access the option bitmap :) so you can do whatever you want with it.

The block is optional as well :) if not needed, don't write anything after the parenthesis.

Step 2
You place those options inside a temporary array, this array will be used when creating the menu:
Code:
opt  = [

      RMenu_Option.new("SelData"){ $scene = Scene_Data.new },

      RMenu_Option.new("CopData"){ $scene = Scene_Copy_Data.new },

      RMenu_Option.new("DelData"){ $scene = Scene_Del_Data.new }

      ]

 

Step 3
Create the menu!
Code:
Ring_Menu.new( [h_radius, v_radius, [x,y], opt_array] )
-> by default h_radius and v_radius = 10, x & y = 0 and opt_array is an empty array.
Yes that means you can add/remove options later on!

That's for the basic Creation!

-> The menu needs to be updated! Like any normal graphical object in a scene. :) menu.update
No need to use code stuff for arrow keys, it's already done in the update method.

-> AND to be disposed as well :) menu.dispose

To get the current selected option : menu.selected
Therefore: to execute the optional block given at the option creation, -> menu.selected.call


Like a window, the menu is only active or visible when you want it: menu.active/visible = true/false,

The zoom attribute: if true ( menu.zoom = true ), it'll activate the perspective effect, -> basically the options 'in front' of us are bigger than the options in the back.
By default the "max size" is 40% bigger than the original file (zoom_x/y = 1.4)
But you can change that max value if required. For example -> menu.zoom_max = 1.0

If you need to change the fluidity of the menu scrolling: you need to use -> menu.reso= value
by default this value is 4, it means it takes 4 frames to go from one option to another.

If you intend to change the "fluidity", it's important that you do it before anything else.

You can play with the opacity, as well like sprites.

FAQ

"I've noticed some constants in the RingMenu class, what are they? Can I use them?"
3 Constants:
- RESO : the default "fluidity"(number of frames) used to move from one option ot another, you can change it, if you don't want to do it for several menu that use the same value
- PLAY_SE : if false, the menu won't make any noise when being scrolled.
- SELECTED_ANGLE : I suggest you don't touch it. The function -> Because a 0° angle is on the right on the trigonometric circle, I need to "rotate" that circle in order to have the 0 , in front of me, which is at the bottom. It is used when the menu is "horizontal", to have the selected option, in front of us, instead of the right of the ring. The angle is in Radian.

When I press Up/Down on a vertical ring menu, nothing happens, Why ?
-> Check that the horizontal radius is 0, if not, Use the left/right keys!

Compatibility

I've added Methods to the cache module, to the array Class, the sprite Class as well
As long as no other classes are called like mines, it should be fine. ( too lazy to put mine into a module. )

Author's Notes

I did this script for the 1st time in 12/2007 on XP :) and updated for my needs with time.
It's used in Castlevania as well. And I thought I should release it, cause it's a very convenient thing to use!

Terms and Conditions
Everywhere you want, just credit me :)
 
yes you can use it in any script you want:) that's the point of it. It replaces easily any "windows_selectable/command"
I remember using a previous version of this script to do a paid commission for a mario RPG like battle system.
 
My XP version exists already(been created originally on XP), I just need to update it first to have all the features of the VX version. But yeah :) It's possible.
 
OMG this is sexy! Also it so happened that I had made the same kind of methods for the Array class for one of my menus but your array methods totally pwn mine, thanks ;)

BTW, thats really bad ass you decided to post this for us (patiently awaiting your XP version)! Next menu/selection type thing I do I'm gonna purposely make sure to use this with it :biggrin:

:thumb: x 100
 
Cool script, im always coming back here looking for something new scripts and updates as well, keep up the good work. :smile:
 
Thx for your comments people!

I've updated the 1st post with the XP version :)
There is a minor change in the instructions for XP :) be sure to have a look at that.

Funny, took me longer to convert the actual demo scene, than the script itself.
Enjoy!
 

Ails

Member

Help me please, how could i put this inside a battle system? All this seems confusing to me, cuz the demo and your instructions are very different :/

I mean, how to replace this:

s1 = $data_system.words.attack
s2 = $data_system.words.skill
s3 = $data_system.words.guard
s4 = $data_system.words.item
@actor_command_window = Window_Command.new(160, [s1, s2, s3, s4])
@actor_command_window.y = 160
@actor_command_window.back_opacity = 160
@actor_command_window.active = false
@actor_command_window.visible = false

for a ring menu?
 
you'll need icons(pictures) to represent each option, if you want text in those icons, you either do it when you create the icons or do that with the script (though it's a bit tricky) Icons in XP can be any size:)
If you use the name method in VX for the icon, then they can be anysize as well.

Then you create the options, I suggest to do that in an array, so you can access them independently later: (let's go for the xp version )
[rgss] 
options = []
options << RMenu_Option.new("Attack_ico",im_a_number_if_needed, viewport_if_needed)# Im anumber can be anything for XP, but for VX can be 0 (then it's using the name to find the icon) or a the icon_id from the iconset. If you don't need the number and the viewport, you don't have to specify them.
# ETC. for the other icons
 
[/rgss]

then you create the menu
[rgss] 
# Radius of 100, kind of arbitrary, but it's to stick with the 160width of the original window (100 + 2*ico width)
@actor_command_window = Ring_Menu.new(100,10, [x,160], options) # X is created from a constant * the actor_id I think :) doesn't matter, like a window, the x can be specified later. And i guess it's specified somewhere else in the code.
# Sorry no back_opacity :) as there is no back lol.
@actor_command_window.active = false
@actor_command_window.visible = false
 
[/rgss]

Hopefully we don't need more edition :), as you can still use "menu.index" like you would do "window.index" so the rest of the system should work properly. No need to insert code in the options and such, as it would require a major rewrite of the update methods as well.

#-------------------------------------------------------
Completly unrelated:

- For those who explored a bit the script, nothing prevents you to use the ring_menu for esthetic purpose only :), it can turn on it's own if you call the rotate method on it. :)

- If you want to write text on a single "background" for the options, Like all the options have the same look, but different text: To do that without problems of text overwriting each other is:
-> load the bitmap of the original icon in a variable
-> Create the option with the "original_icon" to use.
-> then edit the option with something like this:
option.sprite.bitmap = local_bitmap.clone # Clone will create a new bitmap instead of using the one in the cache :)
-> Write you text on it :) : option.sprite.bitmap.draw_text(all theargs)
-> Create your menu as normal.

That's what I do for the title screen in Castlevania.
 
It's completely plug and play(if you know how to script).

It's not a menu system, it's a system for making your own menus.

By the way Trebor, the key input for it doesn't work(at least not on my computer).
 
Ulqiorra":1k2lvvao said:
It's completely plug and play(if you know how to script).
Absolute big grin at this sentence! :D


@trebor: Good script as in functionality theory. However, practical test allowed me selecting the disabled option quite a few times (resulting in the script sometimes executing the moveme-selection, and sometimes putting me on Scene_Map with completely accessible sapnish menu), so if that wasn't intentional, you got quite a bit of bugfixing ahead of ya (no time to test it any further really, and it's easier for you either way). If it WAS intentional, you have BAD design errors in there as far as guidance goes ^^

Keep up the good work!
 
I'm glad you noticed that Blue.

But yeah, the XP demo is missing the Key Input code(which i already added to my version, by the way), you should fix that.

And why does it give the menu a slanted perspective even when :h_radius and :v_radius are the same? isn't there a formula you can use to calculate the perspective from the horizontal and vertical radius's?
 
It's not a formula that calculates the "perspective", but just a boolean, called zoom.

Yeah sorry about the code input in the demo, I've a tendancy to forget updating my demos...

I think I did a pretty bad demo anyway, made it very quickly, mixing 2 menu on the same scene, which was something i never tried before, but thought it could work.

Blue can you tell me how you've managed to end up on the disabled option ?
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top