-
Notifications
You must be signed in to change notification settings - Fork 9
Model properties
Models within Metro are able to define properties. Properties allow you to specify numeric values, text, booleans, and arrays. Alongside these simple property types you can define properties for animations, images, samples and songs.
Properties, at first glance, provide a similar functionality that instance variables and getter/setter methods. They have other benefits as well:
- Allow you to define getter and setter methods
Properties will define getter and setter methods that match their name provided. Instead of accessing instead variables, the content is stored in a hash of properties that all models share.
- Properties may define additional dependent properties or expose helpers to make it easier to access values of the original property easier.
Several properties define with them a number of convenience properties which make it easier to get or set portions of the entire property. This is the case with the FontProperty which will define a font_name, and font_size property.
- Allow for the specification of defaults or initial values.
The initial values or default values of the properties can be defined alongside the definition of the property. This is easier to read as opposed to traditionally defining an
attr_accessor
and/or defining your own unique getter with the default as the fallback value.
- Provide an easy system to define multiple type conversion when setting and getting the values.
The position property, for instance, can be set with both a Point unit and a string representation of the point. Properties can define multiple different type conversion.
- Makes it easier to save the state of the model or pass the state of the model if required.
Alongside the ability to accept multiple inputs, a property can generate portable output types which can more easily be serialized to a file or passed as a hash between models to allow them to be handed from one scene to another scene.
- Reduces the use of calls to raw Gosu resources.
Importantly the use of properties reduces the numerous uses of core Gosu assets like
Gosu::Font
,Gosu::Color
,Gosu::Image
, etc. This provides an abstraction layer removing those instances making the code more portable in the future if Metro were to depart from Gosu.
- Allows for global caching of resources like images, fonts, and animations.
Gosu::Image
s andGosu::Font
s particularly are assets in which you will immediately want to cache. Using properties, and the abstraction layer they provide, allows for these assets to be generated from pre-existing versions allowing for better re-use overall throughout the entire game.
The initial game generates a model with several properties defined. Let's examine those property definitions.
class Hero < Metro::Model
property :image, path: "hero.png"
property :position
property :angle
property :move_amount, default: 1.5
property :turn_amount, default: 90.0
end
This model defines five properties with the names: image
; position
; angle
; move_amount
; and turn_amount
.
All properties take the format for of the following:
property NAME_AS_SYMBOL, OPTIONS
The name of the property is actually very important. By default if the name of the property matches the first name of a property class (e.g. ImageProperty or PositionProperty) it is assumed that you want to use that type of property.
This is an ImageProperty:
property :image, path: "hero.png"
This is a PositionProperty:
property :position
When the name does match the first name of a property class it is assumed that you want a NumericProperty. This is the case with the remaining properties.
These are NumericPropery instances:
property :angle
property :move_amount, default: 1.5
property :turn_amount, default: 90.0
You can use different names or be more explicit by specifying the type in the options.
This is an example of explicitly specifying the types:
class Hero < Metro::Model
property :image, type: :image, path: "hero.png"
property :position, type: :position
property :angle, type: :numeric
property :move_amount, type: :numeric, default: 1.5
property :turn_amount, type: :numeric, default: 90.0
end
Explicitly specifying the types allows you to change the names if you desire while maintaining the same property.
class Hero < Metro::Model
property :avatar, type: :image, path: "hero.png"
property :location, type: :position
property :numeric_angle, type: :numeric
property :amount_to_move, type: :numeric, default: 1.5
property :amount_to_turn, type: :numeric, default: 90.0
end
The Hero model defines these five properties and you will notice that two of the numeric fields specify default values:
property :move_amount, default: 1.5
property :turn_amount, default: 90.0
Numeric values allow a default value. If one is not provided then the default for numeric values is 0.0
. The default value will be used when attempting to set the property with an unsupported value. With numeric properties an unsupported value is nil.
class Hero < Metro::Model
property :image, type: :image, path: "hero.png"
property :position, type: :position
property :angle, type: :numeric
property :move_amount, type: :numeric, default: 1.5
property :turn_amount, type: :numeric, default: 90.0
def show
self.move_amount = 4.5 # => 4.5
puts "Hero will move with amount: #{move_amount}" # => 4.5
self.move_amount = nil
puts "Hero will now move with amount: #{move_amount}" # => 1.5
end
end
Notice that the Image Property does not define a default option. It instead defines a path. Though they are different words (default and path), the effect in this case is the same. By default, the image property for the Hero will be hero.png unless the image property is set otherwise.
While the goal is for the properties to provide a consistent interface, each property has variations to hopefully increase the readability of the property and the model as a whole.
Properties define setter and getter methods matching the name in the model that they are defined. Consider it similar to having defined them the attr_accessor
method.
It is important to note that when attempting assignment you will need to preface the use of the property with self, this will ensure ruby knows you are assigning the value and not simply creating a new variable within the scope of the method.
class Hero < Metro::Model
property :image, type: :image, path: "hero.png"
property :position, type: :position
property :angle, type: :numeric
property :move_amount, type: :numeric, default: 1.5
property :turn_amount, type: :numeric, default: 90.0
def show
puts "#{image} #{position} #{angle} #{move_amount} #{turn_amount}"
self.image = "new_hero.png"
self.position = "100,100,0"
self.angle = 90.0
self.move_amount = 4.5
self.turn_amount = 180
end
end
The properties are outlined here with any additional information about how they are configured, used or designed.
Animation API
A property that manages an Animation. An animation is an array of Gosu::Image files.
When defining an animation you specify a path to the image file and the dimensions of an individual image in the series of images, and the time_per_image.
- Path if unspecified will load a default animation
- Dimensions defaults to 16 x 16
- Time per image defaults to 50 ms
class Hero < Metro::Model
property :walking, type: :animation, path: "hero_walking.png",
dimensions: Dimensions.of(25,25)
def draw
walking.image.draw text, x, y, z_order, x_factor, y_factor, color
end
end
Array API
A property that manages an array of symbols, strings, or numbers.
class Hero < Metro::Model
property :characteristics, default: [ 'good', 'bad', 'ugly' ]
end
Boolean API
A property that manages a true or false value.
class Hero < Metro::Model
property :asleep, default: false
end
Color API
A property that manages a color.
A color can be set with a Gosu::Color
, a hexadecimal number 0xffffffff
,
a string in the format '#FFFFFF', a string in the format 'rgb(255,255,255), and
a string in the format 'rgba(255,255,255,1.0)'.
class Hero < Metro::Model
property :color, default: Gosu::Color.new(0xffffffff)
property :first_color, type: :color, default: 0xffffffff
property :second_color, type: :color, default: "#FF0000"
property :third_color, type: :color, default: "rgb(255,0,0)"
property :fourth_color, type: :color, default: "rgba(255,0,0,1.0)"
end
A color property also defines accessor elements for 'alpha', 'red', 'green', and 'blue'. When using the default property name color they have no prefix. When using a non-standard name for the color property the sub-properties are available with a prefix.
class Hero < Metro::Model
property :color, default: Gosu::Color.new(0xffffffff)
property :first_color, type: :color, default: 0xffffffff
property :second_color, type: :color, default: "#FF0000"
property :third_color, type: :color, default: "rgb(255,0,0)"
property :fourth_color, type: :color, default: "rgba(255,0,0,1.0)"
def show
puts "Details about color: #{color} = (#{red},#{green},#{blue},#{alpha})"
puts "Details about color: #{first_color} = (#{first_color_red},#{first_color_green},#{first_color_blue},#{first_color_alpha})"
end
end
Dimensions API
A property that manages a dimensions object.
class Hero < Metro::Model
property :dimensions, default: Dimensions.of(100.0, 100.0)
def show
puts "Dimensions: #{dimensions} - #{width} #{height}"
end
end
A property also defines accessor properties for 'width' and 'height'. When using a non-standard name for the dimensions property the sub-properties are available with a prefix.
class Hero < Metro::Model
property :box, type: dimensions, default: Dimensions.of(100.0, 100.0)
def show
puts "Dimensions: #{box} - #{box_width} #{box_height}"
end
end
Font API
A property that manages a font object. A font object also defines sub-properties are available with the name as a prefix.
class Hero < Metro::Model
property :font, default: { name: 'Comic Sans', size: 80 }
def show
puts "Font: #{font} - #{font_name} #{font_size}"
end
end
Image API
A property that manages an image.
class Player < Metro::Model
property :image, path: "player.png"
end
Numeric API
A property that manages a numeric value.
By default if the type has not been specified and does not map to an existing property the property is a numeric property.
class Player < Metro::Model
property :angle, default: 0.0
end
Options (Menu) API
Position API
A property that manages a point. A position also defines sub-properties 'x', 'y', 'z' and 'z_order'. When using a non-standard name for the properties property the sub-properties are available with a prefix.
class Enemy < Metro::Model
property :position
def draw
image.draw x, y, z_order, x_factor, y_factor, color, :add)
end
end
Sample API
A property that manages a Gosu::Sample.
class Hero < Metro::Model
property :sample, path: 'pickup.wav'
end
Scale API
A property that manages a Scale object.
class Hero < Metro::Model
property :image, path: 'hero.jpg'
property :scale
property :enraged_scale, type: :scale, default: "4.0,4.0"
property :angle
def draw
image.draw_rot x, y, z_order, angle.to_f, 0.5, 0.5,
enraged_scale_factor_x, enraged_scale_factor_y, color
end
end
A property that manages a Gosu::Song.
class Song < Metro::Model
property :song, path: 'happy-song.wav'
end
Text API
A property that manages a String.
class Scoreboard < Metro::Model
property :font
property :position
property :color
property :scale
property :text
def draw
font.draw text, x, y, z_order, x_factor, y_factor, color
end
end
The text property can define text that will be escaped and executed within the context of the property holder.
class ScoreBoard < Metro::Model
property :score
property :text, default: 'Score is #{score}'
end