Skip to content
burtlo edited this page Apr 10, 2013 · 5 revisions

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.

Property Basics

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::Images and Gosu::Fonts 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.

Defining A Property

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

Names and Types

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

Defaults and Starting Values

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.

Using a Property

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

Property Reference

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

Song API

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