From 632e007e711ba8e4e7d9b3a39cd64e32ca58afd1 Mon Sep 17 00:00:00 2001 From: Tom Black Date: Tue, 4 Sep 2018 00:45:00 -0700 Subject: Sprite enhancements Add ability to flip the sprite, run a block after an animation, properly change the width and height, and other fixes. --- ext/ruby2d/ruby2d.c | 20 ++++---- lib/ruby2d/sprite.rb | 131 +++++++++++++++++++++++++++++++++++++++++---------- test/sprite.rb | 62 +++++++++++++++++++++--- test/testcard.rb | 17 ++++--- 4 files changed, 180 insertions(+), 50 deletions(-) diff --git a/ext/ruby2d/ruby2d.c b/ext/ruby2d/ruby2d.c index b44ba58..74c7823 100644 --- a/ext/ruby2d/ruby2d.c +++ b/ext/ruby2d/ruby2d.c @@ -368,14 +368,10 @@ static R_VAL ruby2d_sprite_ext_init(R_VAL self, R_VAL path) { S2D_Log(S2D_INFO, "Init sprite: %s", RSTRING_PTR(path)); S2D_Sprite *spr = S2D_CreateSprite(RSTRING_PTR(path)); - // Get width and height from Ruby class. If set, use it, else choose the - // native dimensions of the sprite image. - R_VAL w = r_iv_get(self, "@width"); - R_VAL h = r_iv_get(self, "@height"); - r_iv_set(self, "@width" , r_test(w) ? w : INT2NUM(spr->width)); - r_iv_set(self, "@height", r_test(h) ? h : INT2NUM(spr->height)); - + r_iv_set(self, "@img_width" , INT2NUM(spr->width)); + r_iv_set(self, "@img_height", INT2NUM(spr->height)); r_iv_set(self, "@data", r_data_wrap_struct(sprite, spr)); + return R_NIL; } @@ -393,8 +389,14 @@ static R_VAL ruby2d_sprite_ext_render(R_VAL self) { S2D_Sprite *spr; r_data_get_struct(self, "@data", &sprite_data_type, S2D_Sprite, spr); - spr->x = NUM2DBL(r_iv_get(self, "@x")); - spr->y = NUM2DBL(r_iv_get(self, "@y")); + spr->x = NUM2DBL(r_iv_get(self, "@flip_x")); + spr->y = NUM2DBL(r_iv_get(self, "@flip_y")); + + R_VAL w = r_iv_get(self, "@flip_width"); + if (r_test(w)) spr->width = NUM2DBL(w); + + R_VAL h = r_iv_get(self, "@flip_height"); + if (r_test(h)) spr->height = NUM2DBL(h); S2D_ClipSprite( spr, diff --git a/lib/ruby2d/sprite.rb b/lib/ruby2d/sprite.rb index a9d9e98..f2d7075 100644 --- a/lib/ruby2d/sprite.rb +++ b/lib/ruby2d/sprite.rb @@ -15,26 +15,34 @@ module Ruby2D end end - @path = path - @x = opts[:x] || 0 - @y = opts[:y] || 0 - @z = opts[:z] || 0 - @width = opts[:width] || nil - @height = opts[:height] || nil - @start_time = 0.0 - @loop = opts[:loop] || false - @frame_time = opts[:time] || 300 - @animations = opts[:animations] || {} - @playing = false + @path = path + @x = opts[:x] || 0 + @y = opts[:y] || 0 + @z = opts[:z] || 0 + @flip_x = @x + @flip_y = @y + @width = opts[:width] || nil + @height = opts[:height] || nil + @flip_width = @width + @flip_height = @height + @clip_x = opts[:clip_x] || 0 + @clip_y = opts[:clip_y] || 0 + @rotate = 0 + @start_time = 0.0 + @loop = opts[:loop] || false + @frame_time = opts[:time] || 300 + @animations = opts[:animations] || {} + @playing = false @current_frame = opts[:default] || 0 - @last_frame = 0 + @last_frame = 0 + @flip = nil + @done_proc = nil + @img_width = nil; @img_height = nil # set by `ext_init` ext_init(@path) - @clip_x = opts[:clip_x] || 0 - @clip_y = opts[:clip_y] || 0 - @clip_width = opts[:clip_width] || @width - @clip_height = opts[:clip_height] || @height - @animations[:default] = 0..(@width / @clip_width) - 1 # set default animation + @clip_width = opts[:clip_width] || @img_width + @clip_height = opts[:clip_height] || @img_height + @animations[:default] = 0..(@img_width / @clip_width) - 1 # set default animation @defaults = { animation: @animations.first[0], @@ -50,12 +58,47 @@ module Ruby2D add end - def play(animation = nil, loop = nil) - if !@playing || (animation != @playing_animation && animation != nil) + # Set the x coordinate + def x=(x) + @x = @flip_x = x + if @flip == :flip_h || @flip == :flip_hv + @flip_x = x + @width + end + end + + # Set the y coordinate + def y=(y) + @y = @flip_y = y + if @flip == :flip_v || @flip == :flip_hv + @flip_y = y + @height + end + end + + # Set the width + def width=(width) + @width = @flip_width = width + if @flip == :flip_h || @flip == :flip_hv + @flip_width = -width + end + end + + # Set the height + def height=(height) + @height = @flip_height = height + if @flip == :flip_v || @flip == :flip_hv + @flip_height = -height + end + end + + # Play an animation + def play(animation = nil, loop = nil, flip = nil, &done_proc) + if !@playing || (animation != @playing_animation && animation != nil) || flip != @flip @playing = true @playing_animation = animation || :default frames = @animations[@playing_animation] + flip_sprite(flip) + @done_proc = done_proc case frames # When animation is a range, play through frames horizontally @@ -79,11 +122,42 @@ module Ruby2D end # Stop the current animation and set to the default frame - def stop - @playing = false - @playing_animation = @defaults[:animation] - @current_frame = @defaults[:frame] - set_frame + def stop(animation = nil) + if !animation || animation == @playing_animation + @playing = false + @playing_animation = @defaults[:animation] + @current_frame = @defaults[:frame] + set_frame + end + end + + # Flip the sprite + def flip_sprite(flip) + + # A width and height must be set for the sprite for this to work + unless @width && @height then return end + + @flip = flip + + # Reset flip values + @flip_x = @x + @flip_y = @y + @flip_width = @width + @flip_height = @height + + case flip + when :flip_h # horizontal + @flip_x = @x + @width + @flip_width = -@width + when :flip_v # vertical + @flip_y = @y + @height + @flip_height = -@height + when :flip_hv # horizontal and vertical + @flip_x = @x + @width + @flip_width = -@width + @flip_y = @y + @height + @flip_height = -@height + end end # Reset frame to defaults @@ -107,7 +181,7 @@ module Ruby2D @clip_y = f[:y] || @defaults[:clip_y] @clip_width = f[:width] || @defaults[:clip_width] @clip_height = f[:height] || @defaults[:clip_height] - @frame_time = f[:time] || @defaults[:frame_time] + @frame_time = f[:time] || @defaults[:frame_time] end end @@ -134,7 +208,12 @@ module Ruby2D # Reset to the starting frame if all frames played if @current_frame > @last_frame @current_frame = @first_frame - unless @loop then stop end + unless @loop + # Stop animation and play block, if provided + stop + if @done_proc then @done_proc.call end + @done_proc = nil + end end set_frame diff --git a/test/sprite.rb b/test/sprite.rb index 713375a..e62488b 100644 --- a/test/sprite.rb +++ b/test/sprite.rb @@ -6,17 +6,28 @@ else media = "media" end -set title: "Ruby 2D — Sprite", width: 350, height: 150 +set title: "Ruby 2D — Sprite", width: 400, height: 300 +coin1 = Sprite.new( + "#{media}/coin.png", + clip_width: 84, + time: 300, + loop: true +) + +coin1.play -coin = Sprite.new( +coin2 = Sprite.new( "#{media}/coin.png", + y: 90, + width: 42, + height: 42, clip_width: 84, time: 300, loop: true ) -coin.play +coin2.play boom = Sprite.new( "#{media}/boom.png", @@ -28,6 +39,8 @@ boom = Sprite.new( hero = Sprite.new( "#{media}/hero.png", x: 261, + width: 78, + height: 99, clip_width: 78, time: 250, animations: { @@ -39,7 +52,7 @@ hero = Sprite.new( atlas = Sprite.new( "#{media}/texture_atlas.png", - x: 10, y: 100, + x: 50, y: 90, animations: { count: [ { @@ -79,21 +92,58 @@ on :key_down do |e| case e.key when 'p' - coin.play + coin1.play + coin2.play boom.play atlas.play :count + when 'b' + boom.play nil, nil, nil do + puts "Boom animation finished!" + end when 's' - coin.stop + coin1.stop + coin2.stop hero.stop atlas.stop + when 'left' + hero.play :walk, :loop, :flip_h when 'right' hero.play :walk, :loop when 'up' hero.play :climb, :loop when 'down' + hero.play :climb, :loop, :flip_v + when 'h' + hero.play :climb, :loop, :flip_hv + when 'c' hero.play :cheer end end +on :key_held do |e| + case e.key + when 'a' + hero.play :walk, :loop, :flip_h + hero.x -= 1 + when 'd' + hero.play :walk, :loop + hero.x += 1 + when 'w' + hero.play :climb, :loop + hero.y -= 1 + when 's' + hero.play :climb, :loop, :flip_v + hero.y += 1 + when 'z' + hero.width = get(:mouse_x) + hero.height = get(:mouse_y) + end +end + +on :key_up do |e| + if ['w', 'a', 's', 'd'].include? e.key + hero.stop + end +end show diff --git a/test/testcard.rb b/test/testcard.rb index 4e629bc..7ae153f 100644 --- a/test/testcard.rb +++ b/test/testcard.rb @@ -221,13 +221,14 @@ Text.new(x: 144, y: 202, text: "B", font: font, color: [0.0, 0.0, 1.0, 1.0]) fps = Text.new(x: 10, y: 470, text: "", font: font) # Sprites -s1 = Sprite.new(450, 200, "#{media}/sprite_sheet.png") -s1.add(forwards: [ - [ 0, 0, 50, 50, 30], - [ 50, 0, 50, 50, 40], - [100, 0, 50, 50, 50], - [150, 0, 50, 50, 60] -]) +spr = Sprite.new( + "#{media}/sprite_sheet.png", + x: 450, y: 200, + clip_width: 50, + time: 500, + loop: true +) +spr.play # Pointer for mouse pointer = Square.new(size: 10) @@ -293,8 +294,6 @@ update do pointer_outline.color = [0, 1, 0, 0] end - s1.animate(:forwards) - if (get :frames) % 20 == 0 fps.text = "FPS: #{(get :fps).round(3)}" end -- cgit v1.2.3