Class: Chingu::Animation

Inherits:
Object
  • Object
show all
Defined in:
lib/chingu/animation.rb

Overview

The Animation-class helps you load and manage a tileanimation. A Tileanimation is a file where all the frames are put after eachother.

An easy to use program to create tileanimations is tilestudio.sourceforge.net/ or www.humanbalance.net/gale/us/

TODO: Support frames in invidual image-files? Is autodetection of width / height possible?

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Animation

Create a new Animation. Must use :file OR :frames OR :image to create it.

- loop: [true|false]. After the last frame is used, start from the beginning.
- bounce: [true|false]. After the last frame is used, play it backwards untill the first frame is used again, then start playing forwards again.
- file:   Tile-file to cut up animation frames from. Could be a full path or just a name -- then it will look for media_path(file)
- frames: Creates the animation from existing images which are the same size (Array<Gosu::Image>)
- image: Image containing a strip of frames for the animation (Gosu::Image)
- width:  width of each frame in the tileanimation
- height:  width of each frame in the tileanimation
- size: [width, height]-Array or just one fixnum which will spez both height and width
- delay: milliseconds between each frame
- step: [steps] move animation forward [steps] frames each time we call #next

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/chingu/animation.rb', line 30

def initialize(options)
  options = {:step => 1, :loop => true, :bounce => false, :index => 0, :delay => 100}.merge!(options)

  @loop = options[:loop]
  @bounce = options[:bounce]
  file = options[:file]
  image = options[:image]
  @frames = options[:frames]
  @index = options[:index]
  @delay = options[:delay]
  @step = options[:step] || 1
  @dt = 0

  @sub_animations = {}
  @frame_actions = []
  
  raise ArgumentError, "Must provide one of :frames, :image OR :file parameter" unless [@frames, file, image].compact.size == 1

  if @frames        
    raise ArgumentError, "Must provide at least one frame image with :frames" if @frames.empty?
    raise ArgumentError, ":frames must consist of images only" unless @frames.all? {|i| i.is_a? Gosu::Image }
    
    @width, @height = @frames[0].width, @frames[0].height
    
    raise ArgumentError, ":frames must be of identical size" unless @frames[1..-1].all? {|i| i.width == @width and i.height == @height }
    
  else    
    if file and not File.exists?(file)
      Gosu::Image.autoload_dirs.each do |autoload_dir|
        full_path = File.join(autoload_dir, file)
        if File.exists?(full_path)
          file = full_path
          break
        end
      end      
    end

    #
    # Various ways of determening the framesize
    #
    if options[:height] && options[:width]
      @height = options[:height]
      @width = options[:width]
    elsif options[:size] && options[:size].is_a?(Array)
      @width = options[:size][0]
      @height = options[:size][1]
    elsif options[:size]
      @width = options[:size]
      @height = options[:size]
    elsif file
      if file =~ /_(\d+)x(\d+)/
        # Auto-detect width/height from filename
        # Tilefile foo_10x25.png would mean frame width 10px and height 25px
        @width = $1.to_i
        @height = $2.to_i
      else
        # Assume the shortest side of the actual file is the width/height for each frame
        image = Gosu::Image.new($window, file)
        @width = @height = (image.width < image.height) ? image.width : image.height
       end
    else
      @width = @height = (image.width < image.height) ? image.width : image.height
    end
    
    @frames = Gosu::Image.load_tiles($window, image || file, @width, @height, true)
  end
end

Instance Attribute Details

#bounceObject

Returns the value of attribute bounce



13
14
15
# File 'lib/chingu/animation.rb', line 13

def bounce
  @bounce
end

#delayObject

Returns the value of attribute delay



13
14
15
# File 'lib/chingu/animation.rb', line 13

def delay
  @delay
end

#framesObject

Returns the value of attribute frames



13
14
15
# File 'lib/chingu/animation.rb', line 13

def frames
  @frames
end

#indexObject

Returns the value of attribute index



13
14
15
# File 'lib/chingu/animation.rb', line 13

def index
  @index
end

#loopObject

Returns the value of attribute loop



13
14
15
# File 'lib/chingu/animation.rb', line 13

def loop
  @loop
end

#stepObject

Returns the value of attribute step



13
14
15
# File 'lib/chingu/animation.rb', line 13

def step
  @step
end

Instance Method Details

#[](index) ⇒ Object

Fetch a frame or frames:

@animation[0]         # returns first frame
@animation[0..2]      # returns a new Animation-instance with first, second and third frame
@animation[:explode]  # returns a cached Animation-instance with frames earlier set with @animation.frame_names = { ... }


175
176
177
178
179
# File 'lib/chingu/animation.rb', line 175

def [](index)
  return @frames[index]               if  index.is_a?(Fixnum)
  return self.new_from_frames(index)  if  index.respond_to?(:each)
  return @sub_animations[index]       if  index.is_a?(Symbol)
end

#[]=(name, animation) ⇒ Object

Manually initialize a frame-range with an Animation-instance

@animation[:scan] = Animation.new(...)


186
187
188
189
# File 'lib/chingu/animation.rb', line 186

def []=(name, animation)
  @sub_animations[name] = animation
  return @sub_animations[name]
end

#animationsObject



136
137
138
# File 'lib/chingu/animation.rb', line 136

def animations
  @sub_animations.keys
end

#firstObject

Returns the first frame (Gosu::Image) from animation



143
144
145
# File 'lib/chingu/animation.rb', line 143

def first
  @frames.first
end

#frame_namesObject

Return frame names in { :name => range } -form



132
133
134
# File 'lib/chingu/animation.rb', line 132

def frame_names
  @frame_names
end

#frame_names=(names) ⇒ Object

Put name on specific ranges of frames. Eg. name_frames(:default => 0..3, :explode => 3..8)

Can then be accessed with @animation



118
119
120
121
122
123
124
125
126
127
# File 'lib/chingu/animation.rb', line 118

def frame_names=(names)
  names.each do |key, value|
    @sub_animations[key] = self.new_from_frames(value)  if value.is_a? Range
    @sub_animations[key] = @frames[value]               if value.is_a? Fixnum
    #
    # TODO: Add support for [1,4,5] array frame selection
    #
    # @frame_names[key] = self.new_from_frames(value) if value.is_a? Array
  end
end

#imageObject

Get the current frame (a Gosu::Image)



194
195
196
# File 'lib/chingu/animation.rb', line 194

def image
  @frames[@index]
end

#lastObject

Returns the last frame (Gosu::Image) from animation



150
151
152
# File 'lib/chingu/animation.rb', line 150

def last
  @frames.last
end

#last_frame?Boolean

Returns true if the current frame is the last

Returns:

  • (Boolean)


164
165
166
# File 'lib/chingu/animation.rb', line 164

def last_frame?
	@previous_index == @index
end

#new_from_frames(range) ⇒ Object

Returns a new animation with the frames from the original animation. Specify which frames you want with “range”, for example “0..3” for the 4 first frames.



212
213
214
215
216
217
218
219
# File 'lib/chingu/animation.rb', line 212

def new_from_frames(range)				
  new_animation = self.dup
  new_animation.frames = []
  range.each do |nr|
    new_animation.frames << self.frames[nr]
  end
  return new_animation
end

#next(recursion = true) ⇒ Object Also known as: next!

Propelles the animation forward. Usually called in #update within the class which holds the animation. Animation#next() will look at bounce and loop flags to always return a correct frame (a Gosu#Image)



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/chingu/animation.rb', line 225

def next(recursion = true)
    
  if (@dt += $window.milliseconds_since_last_tick) >= @delay
    @dt = 0
    @previous_index = @index
    @index += @step
    
    # Has the animation hit end or beginning... time for bounce or loop?
    if (@index >= @frames.size || @index < 0)
      if @bounce
        @step *= -1   # invert number
        @index += @step
        @index += @step
      elsif @loop
        @index = 0	
      else
        @index = @previous_index # no bounce or loop, use previous frame
      end
    end
    @frame_actions[@index].call	if	@frame_actions[@index]
  end
  @frames[@index]
end

#on_frame(frames, &block) ⇒ Object

Execute a certain block of code when a certain frame in the animation is active. This could be used for pixel perfect animation/movement.



262
263
264
265
266
267
268
# File 'lib/chingu/animation.rb', line 262

def on_frame(frames, &block)
  if frames.kind_of? Array
    frames.each { |frame| @frame_actions[frame] = block }
  else
    @frame_actions[frames] = block
  end
end

#resetObject Also known as: reset!

Resets the animation, re-starts it at frame 0 returns itself.



202
203
204
205
# File 'lib/chingu/animation.rb', line 202

def reset
  @index = 0
  self
end

#retrofyObject

Initialize non-blurry zoom on frames in animation



253
254
255
256
# File 'lib/chingu/animation.rb', line 253

def retrofy
  frames.each { |frame| frame.retrofy }
  self
end

#sizeObject

width, height

for each frame in the animation



157
158
159
# File 'lib/chingu/animation.rb', line 157

def size
  [@width, @height]
end

#trimObject

Remove transparent space from each frame so the actual sprite is touching the border of the image. This requires TexPlay



102
103
104
105
106
107
108
109
110
# File 'lib/chingu/animation.rb', line 102

def trim
  #@frames.each do |frame|
  #  y = 0
  #  x2, y2, color = frame.line 0,y,frame.width,y, :trace => { :while_color => :alpha }
  #  puts "final y: #{y}"
  #  #frame.image.trace 0,0,image
  #  #frame.splice(frame,0,0, :crop => [10, 10, 20, 20])
  #end
end