虽然这个游戏的功能比较简单,但是对于新接触游戏编程的初学者来说,仍然显得有些复杂,为了降低学习难度,原书分5个阶段实现这个游戏,每个阶段将建立一个版本,逐步添加功能,最终完成“公主迎圣诞”游戏。
先建立项目目录(sdgz-圣诞公主的拼音首字母),然后根据GameZero的固定规则,建立字体、图像、声音等素材文件子目录,将相关文件放在指定目录下,整个项目文件结构如下图:
按照前面介绍的编程思路和分阶段、多版本的思想编写这个游戏程序。在VSCode中,打开sdgz目录,准备编写游戏代码。
原书的小标题是“加载游戏资源和搭建游戏框架”,因为python的pyglet库有专门资源管理类pyglet.resource,通过该类的image()、media()方法将资源加载到内存,再通过bit()方法显示到窗口上。不过GameZero没有资源管理的概念,因此不必进行资源管理。
在第1个阶段,将配置游戏中使用的各种图像和声音资源,并搭建游戏的基本框架,能够响应按下回车键的链盘事件和根据游戏状态变化显示不同的背景图像。
1)配置游戏中使用的图像和声音
原书新建了一个game_res.py文件用以加载资源文件,GameZero不需要加载资源文件,但是本着资源与逻辑分离的理念,我们还是建立一个文件用来配置相关资源文件。
在根目录下新建game_res.jl文件,将资源配置写入该文件:
#图像资源 welcome_img = "game_welcome.png" end_img = "game_end.png" bg_img = "game_play.png" heart1_img = "heart1.png" heart2_img = "heart2.png" heart3_img = "heart3.png" clipper_img = "剪刀.png" snowflake_img = "雪花.png" gift_img = "礼物.png" princess_img = "公主.png" #声音资源 music = "铃儿响叮当.mp3" pop_sound = "pop.wav"
2) 搭建游戏的基本框
(1) 加载资源配置文件
在julia中,加载另一个代码文件的方法为include("source.jl"),这样文件 source.jl
的内容将在出现 include
调用的模块的全局作用域内执行。括号中是source.jl
文件的相对路径。
不过,由于GameZero是通过rungame(“xx.jl”)方法运行游戏代码的,相当于游戏源码托管给GameZero了,所以不能直接用include("source.jl")加载其他代码文件,否则GameZero无法识别。好在GameZero给我们提供了加载的方法。
game_include("game_res.jl")
(2) 创建一个记录游戏状态的变量 game_state,并设置初始值为 0
game_state = 0
说明: 游戏的 3 个状态用数字表示: 0 表示等待状态,1 表示进行状态,2 表示结束状态。
(3) 将窗口尺寸设定为800*600
HEIGHT = 600
WIDTH = 800
(4) 绘制游戏画面。根据游戏状态 game_state 绘制不同的图像作为游戏背景画面。现在问题来了,在GameZero里,窗口背景是由常量BACKGROUND设置的,而常量是不能在程序运行中修改的。当然我们也可以定义一个图片Actor来充当背景,但是背景的意义就失去了。好在GameZero提供fill函数来更改窗口背景颜色或图像。
function fill(s::Screen, c::Colorant)
function fill(s::Screen, sf::Ptr{SDL_Surface})
第一个fill函数的参数大家都能理解,一个Screen对象,一个Colorant类型的颜色值。但是第二个fill函数的第二个参数是什么?其实你如果学过c或C++,应该知道那是一个指针。指向的是一个SDL_Surface类型的对象。不过关于指针的知识已经超过本文的范围,这里不再赘述。要吐槽的是,GameZero这里对调用很不友好,如果直接将图片路径作为参数就比较好,而不需要调用者去将图片对象转换成Ptr指针。好在GameZero是开源的,通过对其源码的研究,我把相关处理代码提取出来,放到了game_res.jl文件里,大家只需知道怎么调用就行。另外还需要用到SimpleDirectMediaLayer.LibSDL2库,它是SimpleDirectMediaLaye.jl(绑定C++跨平台库Simple DirectMedia Layer的Julia库)的一部分,所以我们还需要安装SimpleDirectMediaLayer库(在第二章第一节的扩展阅读里,我介绍过julia库的安装方法)。
fill方法须在draw()事件里调用,具体代码如下:
function draw(g::Game) global game_state screen=g.screen clear() if game_state == 0 fill(screen,image_surface(welcome_img)) elseif game_state == 1 fill(screen,image_surface(bg_img)) else game_state == 2 fill(screen,image_surface(end_img)) end end
注意welcome_img,bg_img,end_img在game_res.jl文件里已定义。另外,因为要使用全局变量 game_state,所以在函数或方法中要用 global 关键字进行声明,这一点和python是一致的。
(5) 使用窗口的 on_key_down()事件方法去响应键盘按下事件。在游戏处于等待状态和结束状态时,才会响应用户按下回车键的行为,将游戏设定为进行状态。 由于游戏程序没有完成,为了方便测试,当用户按下空格链时,游戏将进入结束状态:
function on_key_down(g, k) global game_state if k ==Keys.RETURN #13 if game_state != 1 game_state = 1 end end #用于测试游戏结束的情况 if k == Keys.SPACE game_state = 2 end end
至此,这个程序的第一个版本完成。运行程序,然后按以下流程进行测试。
①游戏程序启动后,显示游戏欢迎画面,游戏处于等待状态。②按下回车键,显示游戏进行画面,游戏处于进行状态。③扭安下空格键,显示游戏结束画面,游戏处于结束状态。④再按下回车键,又显示游戏进行画面。如此反复。
如果程序没有按上述流程运行,请认真检查自己编写的代码,或者对照本文资源包中提供的源代码进行检查。
源码下载:https://files.cnblogs.com/files/zjzkiss/sdgz_v1.rar