在游戏应用程序中,通常使用键盘和鼠标作为游戏的操作设备。游戏的窗口都能接收来自键盘和鼠标设备的输人。当用户在键盘上按下按建或释放按键时,会产生相应的键盘事件;当用户移动鼠标、拖动鼠标、按下鼠标按键或者是滚动鼠鼠标滚轮,会产生相应的鼠标事件。在游戏程序中,需要编写相应的事件处理代码来响应这些事件,进行游戏功能的开发。Python游戏库Pyglet和Julia游戏库GameZero都定义了键盘和鼠标事件的处理函数。
在Pyglet里,基本的键盘事件是0n_key_Press(),而在GameZero里,基本的键盘事件是on_key_down(),他们之间有很多相似之处。在键盘按下时触发该事件:
Pyglet:
def 0n_key_Press (symbol,modifiers)
pass
GameZero:
function on_key_down() .... end
function on_key_down(g) .... end
function on_key_down(g, key) .... end
function on_key_down(g, key, keymod) .... end
我们可以注意到,on_key_down有三个可选函数,其中key传入键盘被按键的ASCII码。
以下代码展示了对键盘按下事件on_key_down()的处理方法,当用户按下键盘上的回车键、方向键、字母键、数字键、控制键等按键时,在Shell 窗口中会打印出相关信息。
function on_key_down(g,key) #print(Keys.1) if key == Keys.SPACE println("空格键被按下") elseif key==Keys.RETURN println("回车键被按下") elseif key==Keys.LEFT println("左方向键被按下") elseif key==Keys.A println("字母键A被按下") elseif key==Keys.LCTRL println("左侧Ctrl键被按下") elseif key==49 println("数字键1被按下") end end
on_key_down事件只在键盘被按下时触发一次,针对不断按住键盘按键不放的情况,比如移动某个游戏对象时,GameZero在update事件中提供对键盘按键的跟踪。例如:
1 txt = TextActor("Hello World","chiller",font_size=24, color=Int[0,0,0,255])# 2 txt.pos = (0,0) 3 4 function draw(g::Game) 5 clear() 6 draw(txt) 7 end 8 9 function update(g::Game,dt) 10 if g.keyboard.DOWN 11 txt.position.y+= 400 * dt 12 elseif g.keyboard.UP 13 txt.position.y-= 400 * dt 14 elseif g.keyboard.LEFT 15 txt.position.x -= 400 * dt 16 elseif g.keyboard.RIGHT 17 txt.position.x += 400 * dt 18 end 19 end
上面的代码演示了分别按住上、下、左、右方向键时,txt对象朝相应方向移动的场景。
GameZero提供on_Mouse_move函数来跟踪鼠标移动事件,鼠标在移动过程中,会不断触发这个事件。函数的输入是游戏对象和鼠标位置(一个数字元组)。
function on_mouse_move(g::Game, pos) .... end
需要说明的是,鼠标按钮button是一个枚举值,
LEFT = 1
MIDDLE = 2
RIGHT = 3
WHEEL_UP = 4
WHEEL_DOWN = 5
这个鼠标事件的典型应用是控制窗口中的角色随着鼠标指针一起移动。例如,在原书的示例程序28-9中展示了让玩家用鼠标控制一架飞机在窗口中自由移动。现将其改写为julia代码:
1 plane =Actor("plane.png") 2 plane.pos=0, 0 3 4 function draw(g::Game) 5 clear() 6 draw(plane) 7 end 8 9 function on_mouse_move(g::Game, pos) 10 plane.pos=pos 11 end
在上面的代码中,通过在 on_mouse_move ()方法中设定飞机角色 plane 的坐标为鼠标指针的当前坐标,从而使鼠标指针移动时,飞机精能角色跟着一起移动。在 on_mouse_move ( )方法中 ,pos 参数给出鼠标指针相对于窗口左上角的坐标(python的Pyglet是左下角)。 还记得吗? GameZero窗口左上角的坐标为(0,0).
对于鼠标按键按下和松开,使用on_mouse_down()和on_mouse_up()函数跟踪鼠标点击事件,函数将游戏对象、鼠标位置和鼠标按钮作为输入参数。
function on_mouse_down(g::Game, pos, button) .... end
function on_mouse_up(g::Game, pos, button) .... end
下方代码展示了这几个鼠标事件的基本用法:
function on_mouse_down(g::Game, pos, button) if button==MouseButtons.LEFT println("在窗口'$pos'处按下左键") end end function on_mouse_up(g::Game, pos, button) if button==MouseButtons.LEFT println("在窗口'$pos'处松开左键") end end
要播放音效,可以调用play_sound函数。要在循环中播放音乐,可调用play_music函数。这两种功能都可以加载wav、mp3和ogg文件。注意sound和music文件,必须放在声音(sounds)和音乐(music)两个文件夹中。
在游戏程序中,经常需要按照一定的时间隔去执行某个任务。 例如,让一个角色以指定的速度在屏幕上平滑移动,定时检测某个角色是否披击中,给游戏增加倒计时功能,等等。在 GameZero 的 timer模块中提供实现计划任务的一些方法,常用的有 pyglet' schedule_once()方法和schedule_interval()方法,它们都是按照指定的时间间隔调用一个函数。不过前者只调用一次。例如:
schedule_once(move, 1/10)
schedule_interval(move,1/10,1)
在上面代码中,schedule_once( f::Function, interval)方法时提供了两个参数值: move 是一个已经定义的函数(称为回调函数)的名字; 1/10是时间间隔(单位= S)。GameZero会按照这个时间间隔调用 move()函一次。而schedule_interval(f::Function, interval, first_interval=interval)提供了三个参数,前两个参数与schedule_once()相同,第三个为可选参数,是第一次调用move函数前间隔的时间,即1秒之后开始执行计划。GameZero会按照第二个参数定义的时间间隔反复调用move函数。
原书示例程序 28_11中演示了 Pyglet 计划任务的用法,让一个显示 hello, world 的文本标签在窗口底部水平移动到 x 坐标为 300 的位置。现将其改写为julia代码:
1 txt = TextActor("Hello World","chiller",font_size=24, col-or=Int[0,0,0,255])# 2 txt.pos = (0,0) 3 dt=0.5 4 function draw(g::Game) 5 clear() 6 draw(txt) 7 end 8 9 function move() 10 global dt 11 txt.x += Int(100 * dt) 12 end 13 14 schedule_interval(move,1/10) 15 #schedule_once(move, 1) 16 function update(g::Game,dt) 17 if txt.x > 300 18 unschedule(move) 19 end 20 end
值得注意的是,原书的间隔时间是1/60s,但是在GameZero中会报溢出错误,可见在GameZero中间隔时间不能太小,这也许是GameZero的一个bug吧。另外要从计划任务列表中删除一个已存在的计划任务,可用unschedule()方法,不过在GameZero中,该方法如果写在回调函数内部调用,不会被执行,但也不会报错。所以这里将其改到update方法中执行。
GameZero目前不能象Pyglet那样播放gif动画或视频,只能通过一些技巧模拟动画,比如通过定时或循环更换角色的图片来模拟动画。例如:
function shoot_animation() global shoot_frame if shoot_frame < 16 space_pod.image = "space_pod_shoot" * string(shoot_frame) * ".png" shoot_frame += 1 schedule_once(shoot_animation, 1/16) else space_pod.image = "space_pod.png" end end
具体可去这个地址:https://github.com/SquidSinker/GZExamples查看Spaceship这个示例。
通过以上几节,我们了解了GameZero在游戏编程中的一些技术,并对比了Python游戏库Pyglet的游戏编程,读者朋友可以两相对照,运行一下各自的代码,比较异同。后续会将原书的一些游戏,用GameZero实现,并放出。敬请期待!