ruby游戏框架:gosu与chingu(一)

本文介绍了在Ubuntu环境下安装Gosu游戏框架,并详细讲解了如何覆写窗口回调、处理图像应用(包括人物移动)、实现简单动画以及文字与声音的运用。在Gosu中,draw方法的灵活性使得它只负责渲染,而update方法用于处理状态更新。文章还探讨了如何在游戏开发中合理使用这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

"If you program and want any longevity to your work, make a game. 
All else recycles, but people rewrite architectures to keep games alive.", _why

如果想让你的代码长寿 ,那就做一个游戏。

所有其他的代码都在回收站了,但是人们总会花时间更新代码让游戏还活着。

----- _why


gosu的安装(ubuntu环境)

sudo apt-get install build-essential freeglut3-dev libfreeimage-dev libgl1-mesa-dev libopenal-dev libpango1.0-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsndfile-dev libxinerama-dev
sudo gem install gosu 

gosu的用法

1 覆写window的回调
创建一个完整的Gosu应用,最简单的写法是写一个Gosu::Window的子类,下面是一个最小的GameWindow类写法:
require 'gosu'

class GameWindow < Gosu::Window
  def initialize
    super 640, 480, false
    self.caption = "Gosu Tutorial Game"
  end
  
  def update
  end
  
  def draw
  end
end

window = GameWindow.new
window.show
以上代码初始化了 Gosu::Window基类,创建了640x480的窗口,改变了窗口的标题。
update() 和 draw() 覆写了Gosu::Window的两个成员方法。
update()缺省下每秒调用60次,包含了主游戏逻辑:移动对象,碰撞等等
随后调用的draw()用于重绘,如果FPS(每秒帧数)过低则跳过。重绘包括整个屏幕和所有无逻辑代码
再往下,一个window对象被创建,调用其show方法,如果不关闭窗口,不会有返回值
现在,你拥有了一个小黑window和一个自定义的title!
更详细的方法描述
window_loop

  • draw是十分灵活的。通常的流程如上图所示,但是draw通常在系统想要重绘widnow的时候才被调用,它可能发生在直接调用show方法时,或调用两次update方法之间,或在window被一个frame遮挡时。
  • 由此可见,你无法知道哪一个回调被首先调用,initialize必须建立一个有效的状态,如果update中包含if player == nil then respawn(),那么initialize应该包含respawn()你可以在初始化时把update()作为最后一个调用。
  • 由于draw如此的灵活,它只渲染当前状态而不改变任何东西,包括动画,如果你用draw实现一个功能,用 read-only模式也许更安全。
  • 在update中,你可能想要用button_down?察看一个按钮的状态,因为在button_down?update前,输入事件应该被触发,它必须在update前发生,遗憾的是你无法直接用draw直接反应这一最新的按钮事件;一个update调用将在中间发生。
  • 因为TextInput可以回调到游戏中,所以它不是一个缓存并且能发生在循环的任意位置,你不能指望text()在update和draw中是相同的。
  • 但不要害怕,Gosu不是多线程的,没有一个回调是可以被打断的
2 图片应用
class GameWindow < Gosu::Window
  def initialize
    super(640, 480, false)
    self.caption = "Gosu Tutorial Game"
    
    @background_image = Gosu::Image.new(self, "media/Space.png", true)
  end
  
  def update
  end
  
  def draw
    @background_image.draw(0, 0, 0)
  end
end
Gosu::Image#initialize有三个参数,第一个:绑定的窗口(self),所有的gosu资源初始化都需要一个内部引用的窗口;第二个:给定的图片文件名。第三个:是否创建粗边框,具体见基本概念的解释。
window的draw()成员方法是画图的地方,所以在这里绘制我们的背景图。
参数配置很明显,从(0,0)开始,第三个参数是z轴,详见 基本概念
2.1 人物与移动
简单人物类
class Player
  def initialize(window)
    @image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
    @x = @y = @vel_x = @vel_y = @angle = 0.0
    @score = 0
  end

  def warp(x, y)
    @x, @y = x, y
  end
  
  def turn_left
    @angle -= 4.5
  end
  
  def turn_right
    @angle += 4.5
  end
  
  def accelerate
    @vel_x += Gosu::offset_x(@angle, 0.5)
    @vel_y += Gosu::offset_y(@angle, 0.5)
  end
  
  def move
    @x += @vel_x
    @y += @vel_y
    @x %= 640
    @y %= 480
    
    @vel_x *= 0.95
    @vel_y *= 0.95
  end

  def draw
    @image.draw_rot(@x, @y, 1, @angle)
  end
end
有几件事情要说明:


  • Player#accelerate 用了offset_x/offset_y方法,它们类似于sin/cos,比如:如果一个东西沿30度角移动100像素,它其实是通过水平移动offset_x(30, 100) pixels 和垂直移动offset_y(30, 100) pixels 得到的
  • 当加载pmp文件时,gosu将会把0xff00ff樱红色/品红;非常丑陋的一种粉红色变为透明色
  • 注意:draw_rot将把图片中心放到(x,y)上-不是左上角。可以通过center_x/center_y 参数控制
  • draw的所有方法和参数详见这里
2.2 整合以上代码
class GameWindow < Gosu::Window
  def initialize
    super(640, 480, false)
    self.caption = "Gosu Tutorial Game"

    @background_image = Gosu::Image.new(self, "media/Space.png", true)

    @player = Player.new(self)
    @player.warp(320, 240)
  end

  def update
    if button_down? Gosu::KbLeft or button_down? Gosu::GpLeft then
      @player.turn_left
    end
    if button_down? Gosu::KbRight or button_down? Gosu::GpRight then
      @player.turn_right
    end
    if button_down? Gosu::KbUp or button_down? Gosu::GpButton0 then
      @player.accelerate
    end
    @player.move
  end

  def draw
    @player.draw
    @background_image.draw(0, 0, 0);
  end

  def button_down(id)
    if id == Gosu::KbEscape
      close
    end
  end
end
嗯 可以看到 gosu::window提供了两个方法 button_down(id)  和  button_up(id)以供覆盖,缺省下什么都不做。按esc推出窗口,预设的button列表参看rdoc。当按下按钮时,update()方法将开始运作,调用人物行动方法。

3. 简单动画

首先,我们通过替换下列常量来去掉z轴排列
module ZOrder
  Background, Stars, Player, UI = *0..3
end
什么是动画?一组序列的图片。因此我们用一个ruby数组存储他们,在真实游戏中,不可能出现一些满足个人需求的类,但是我们这里为了教学方便简易做一个
让我们做一个动画星星的动画,星星会随机生成在屏幕上知道我们收集它们,星星类的定义十分简单
class Star
  attr_reader :x, :y

  def initialize(animation)
    @animation = animation
    @color = Gosu::Color.new(0xff000000)
    @color.red = rand(256 - 40) + 40
    @color.green = rand(256 - 40) + 40
    @color.blue = rand(256 - 40) + 40
    @x = rand * 640
    @y = rand * 480
  end

  def draw  
    img = @animation[Gosu::milliseconds / 100 % @animation.size];
    img.draw(@x - img.width / 2.0, @y - img.height / 2.0,
        ZOrder::Stars, 1, 1, @color, :add)
  end
end
因为我们不想每一个星星重复加载动画,我们不能再构造函数中做这个,但是可以通过它从其他地方入手(window分三段加载动画)
每一百毫秒展示星星动画的不同的帧,img是由 Gosu::milliseconds  返回的时间被100所除,再模上帧数,这个img是一个在星星中心点的,以构造函数中的随机颜色为参数的叠加绘图
现在在play中实现收集星星的工作
class Player
  ...
  def score
    @score
  end

  def collect_stars(stars)
    if stars.reject! {|star| Gosu::distance(@x, @y, star.x, star.y) < 35 } then
      @score += 1
    end
  end
end
添加新代码
class Window < Gosu::Window
  def initialize
    super(640, 480, false)
    self.caption = "Gosu Tutorial Game"

    @background_image = Gosu::Image.new(self, "media/Space.png", true)

    @player = Player.new(self)
    @player.warp(320, 240)

    @star_anim = Gosu::Image::load_tiles(self, "media/Star.png", 25, 25, false)
    @stars = Array.new
  end

  def update
    ...
    @player.move
    @player.collect_stars(@stars)

    if rand(100) < 4 and @stars.size < 25 then
      @stars.push(Star.new(@star_anim))
    end
  end

  def draw
    @background_image.draw(0, 0, ZOrder::Background)
    @player.draw
    @stars.each { |star| star.draw }
  end
  ...
完成了!你可以收集星星了~

4. 文字与声音

最后,我们想画当前分数使用位图字体,每次玩家收集一个星星发出“哔哔”声。窗口将处理的文本部分,加载字体20像素高:
class Window < Gosu::Window
  def initialize
    ...
    @font = Gosu::Font.new(self, Gosu::default_font_name, 20)
  end

  ...

  def draw
    @background_image.draw(0, 0, ZOrder::Background)
    @player.draw
    @stars.each { |star| star.draw }
    @font.draw("Score: #{@player.score}", 10, 10, ZOrder::UI, 1.0, 1.0, 0xffffff00)
  end
end
分数留给玩家
class Player
  attr_reader :score

  def initialize(window)
    @image = Gosu::Image.new(window, "media/Starfighter.bmp", false)
    @beep = Gosu::Sample.new(window, "media/Beep.wav")
    @x = @y = @vel_x = @vel_y = @angle = 0.0
    @score = 0
  end

  ...

  def collect_stars(stars)
    stars.reject! do |star|
      if Gosu::distance(@x, @y, star.x, star.y) < 35 then
        @score += 10
        @beep.play
        true
      else
        false
      end
    end
  end
end
这个游戏就到这里了~

这就是gosu!一切取决于你的想象力。
如果你想得知gosu究竟能做到什么地步,看一看在Gosu例子展示板


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值