打盹的消防车——活跃于Luat社群的新生代全能开发者,东北小伙儿爽朗幽默、好学敏思,更是实力行动派。幼年曾手握火红炽铁而后全然无恙,堪称魔幻经历;如今热衷于各类嵌入式软硬件研究,快意物联江湖。
PS:因作者超强动手能力,样机外壳摔裂后已被强胶封印,本文无样机分解图示;
大家好,今天我们使用合宙的Air724UG开发板做一个简单的贪吃蛇小游戏。
致敬经典|使用Air724UG 简易贪吃蛇,源码开放@合宙Luat #物联网#嵌入式开发
本项目使用合宙Luat开发方式,贪吃蛇采用对象创建,多对象可以多个贪吃蛇,几个人一起玩。本文示例受控键限制,仅演示单个贪吃蛇。感兴趣的朋友可以自己加,直接多创建就行。
- 前期主要准备工作 -
硬件准备:
● Air724UG开发板
● 矩阵键盘
● LCD
注:我使用的是ST7899的LCD,1.54寸屏幕上分辨率 240*240,画面细腻有弹性。
软件准备:
● LuaTools环境设置,不了解Luat开发的朋友,可参考:
稀饭放姜大神《LuaTools上手教程》
http://doc.openluat.com/article/1719/0
晨旭大神《Luat入门教程》
http://doc.openluat.com/wiki/3?wiki_page_id=606
● LCD驱动
https://gitee.com/Dozingfiretruck/luat-snake_game
图片素材:
准备几个需要用到的图片,包括贪吃蛇的身体、头部、墙体、食物、开始按键等;或使用抽象化图形简单展示。
基础准备就绪,我们可以进行相关开发程序的编写了。首先是键盘控制:通过消息机制得到按键事件,以及长按关机功能。
- 矩阵键盘控制 -
\`\`\`lua module(..., package.seeall) --[[sta:按键状态,IDLE表示空闲状态,PRESSED表示已按下状态,LONGPRESSED表示已经长按下状态 longprd:长按键判断时长,默认3秒;按下大于等于3秒再弹起判定为长按键;按下后,在3秒内弹起,判定为短按键 longcb:长按键处理函数 shortcb:短按键处理函数]] local sta,longprd = "IDLE",1500 local function longtimercb() log.info("keypad.longtimercb") sta = "LONGPRESSED" end local lcd_out = pins.setup(pio.P0_7,1)--GPIO7配置为输出 local function keyMsg(msg) --msg.key_matrix_row:行 --msg.key_matrix_col:列 --msg.pressed:true表示按下,false表示弹起 --log.info("keyMsg",msg.key_matrix_row,msg.key_matrix_col,msg.pressed) if msg.pressed then disp.sleep(0) lcd_out(1) if msg.key_matrix_row == 2 then if msg.key_matrix_col == 1 then sys.publish("key","key_up") elseif msg.key_matrix_col == 2 then sys.publish("key","key_back") elseif msg.key_matrix_col == 3 then end elseif msg.key_matrix_row == 3 then if msg.key_matrix_col == 1 then sys.publish("key","key_down") elseif msg.key_matrix_col == 2 then end elseif msg.key_matrix_row == 4 then if msg.key_matrix_col == 1 then sys.publish("key","key_left") elseif msg.key_matrix_col == 2 then sys.publish("key","key_right") end elseif msg.key_matrix_row == 255 then if msg.key_matrix_col == 255 then sta = "PRESSED" sys.timerStart(longtimercb,longprd) end end else sys.timerStop(longtimercb) if sta=="PRESSED" then sys.publish("key","key_ok") elseif sta=="LONGPRESSED" then rtos.poweroff() end sta = "IDLE" end end --注册按键消息处理函数 rtos.on(rtos.MSG_KEYPAD,keyMsg) --初始化键盘阵列 --之一个参数:固定为rtos.MOD_KEYPAD,表示键盘 --第二个参数:目前无意义,固定为0 --第三个参数:表示键盘阵列keyin标记,例如使用了keyin0、keyin1、keyin2、keyin3,则第三个参数为1<<0|1<<1|1<<2|1<<3 = 0x0F --第四个参数:表示键盘阵列keyout标记,例如使用了keyout0、keyout1、keyout2、keyout3,则第四个参数为1<<0|1<<1|1<<2|1<<3 = 0x0F rtos.init_module(rtos.MOD_KEYPAD,0,0x1C,0x0E) '''
按键有了,屏幕有了,接下来开始愉快的掉头发吧~
首先创建一个协程作为入口,然后加个游戏图标:
- 创建入口+游戏图标 -
```lua disp.setbkcolor(0x0000)` disp.clear() lcd.setcolor(0x00FF) disp.setfontheight(24) disp.puttext(common.utf8ToGb2312("贪吃蛇"),(lcd.HEIGHT-string.utf8Len("贪吃蛇")*24)/2,170) disp.putimage("/lua/snake.png",90,90) disp.update() disp.setfontheight(16) sys.wait(2000)
好,接下来开始做蛇:
我们知道,对象由属性和 组成。Lua中最基本的结构是table,所以需要用table来描述对象的属性。
Lua中的function可以用来表示 。那么Lua中的类可以通过 table + function 模拟出来。
至于继承,可以通过metetable模拟出来,所以有了我们的蛇。
- 蛇的基本属性 -
'''lua local sk = {} sk.__index = sklocal function snake() return setmetatable({ x = 20+2*20, y = 40+0, body = {{20+2*20,40+0},{40,40},{20,40}}, body_len = 3, direction = "right", food = {}, run = false, }, sk) end
可以看到属性有蛇头的初始坐标、蛇身坐标、长度、蛇头方向、食物坐标、蛇的状态,接下来我们使用snake1=snake()即可创建一条蛇的对象。
然后放蛇!!!!不对,界面是不是很丑?没有围墙跑出去咬人咋整,哈哈哈~ 随后初始化游戏环境:
- 初始化游戏环境 -
\`\`\`lua local function snake_init() disp.clear() disp.puttext(common.utf8ToGb2312("得分:"),10,2) disp.puttext(common.utf8ToGb2312(score),55,2) for i = 0, LCD_WIDTH-20, 20 do disp.putimage("/lua/wall.png",i,20) end for i = 0, LCD_WIDTH-20, 20 do disp.putimage("/lua/wall.png",i,LCD_WIDTH-20) end for i = 20, LCD_HEIGHT-20, 20 do disp.putimage("/lua/wall.png",0,i) end for i = 20, LCD_HEIGHT-20, 20 do disp.putimage("/lua/wall.png",LCD_HEIGHT-20,i) end if snake1.direction=="left" then disp.putimage("/lua/snake_l.png",snake1.x,snake1.y) elseif snake1.direction=="right" then disp.putimage("/lua/snake_r.png",snake1.x,snake1.y) elseif snake1.direction=="up" then disp.putimage("/lua/snake_u.png",snake1.x,snake1.y) elseif snake1.direction=="down" then disp.putimage("/lua/snake_d.png",snake1.x,snake1.y) end for k,v in pairs(snake1.body) do if k==1 then -- body else disp.putimage("/lua/snake_body.png",v[1],v[2]) end end disp.putimage("/lua/start.png",40,100) disp.update() end
可以看到我们放了墙和蛇,之后呢?按开始进入游戏入口。
- 游戏入口设置 -
```lua `while true do` `local result, data = sys.waitUntil("key",20) if result == true then if data == "key_ok" then game_thread() end end` `end`
接着在游戏里做一个协程吧,死了或者退出就break出来,nice!
- 退出机制 -
\`\`\`lua local function game_thread() snake1.run = true snake1:putfood() while true do snake1:draw() disp.putimage("/lua/food.png",snake1.food[1],snake1.food[2]) disp.update() local result, data = sys.waitUntil("key",500 - score*4) if result == true then if data == "key_left" then snake1.direction="left" elseif data == "key_right" then snake1.direction="right" elseif data == "key_up" then snake1.direction="up" elseif data == "key_down" then snake1.direction="down" elseif data == "key_back" then snake1.run = false elseif data == "key_ok" then disp.putimage("/lua/start.png",40,100) disp.update() while true do local result, data = sys.waitUntil("key",500 - score*4) if result == true then if data == "key_ok" then break end end end end end if snake1.run == false then snake1:kill() snake_init() break end end end
可以看到:进去之后让蛇的状态为存活;投食,之后循环画蛇画食物;通过按键改变蛇方向状态,如果死了或者退出就break出去;速度越快,得分越高。
那我们还缺什么呢?对哦,投食和画蛇。
- 投食操作 -
```lua function sk:putfood() local state local x local y repeat x = math.random(1,10)*20 y = math.random(2,10)*20 state = 0 for k,v in pairs(self.body) do if x == v[1] and y == v[2] then state = 1 break end end until( state == 0 ) self.food[1]=x self.food[2]=y end
投食——很简单,做两个墙以内的,并且不在蛇身上的随机数来投放食物,之后画蛇。
画蛇——就是不断地加蛇身坐标,删除蛇尾坐标,如果吃到食物就不删除蛇尾,吃自己或者撞墙就死掉。
- 投食+画蛇 -
\`\`\`lua function sk:draw() disp.drawrect(20,40,220,220,0x0000) if self.direction=="left" then self.x = self.x - 20 disp.putimage("/lua/snake_l.png",self.x,self.y) elseif self.direction=="right" then self.x = self.x + 20 disp.putimage("/lua/snake_r.png",self.x,self.y) elseif self.direction=="up" then self.y = self.y - 20 disp.putimage("/lua/snake_u.png",self.x,self.y) elseif self.direction=="down" then self.y = self.y + 20 disp.putimage("/lua/snake_d.png",self.x,self.y) end for k,v in pairs(self.body) do if self.x == v[1]and self.y == v[2]then self.run = false break end end if self.x>LCD_WIDTH-20-20 or self.x<20 or self.y>LCD_HEIGHT-20-20 or self.y<20+20 then self.run = false table.insert(self.body,1,{self.x,self.y}) table.remove (self.body) elseif self.x ==self.food[1] and self.y==self.food[2] then disp.drawrect(0,0,240,19,0x0000) score=score+1 disp.puttext(common.utf8ToGb2312("得分:"),10,2) disp.puttext(common.utf8ToGb2312(score),55,2) table.insert(self.body,1,{self.x,self.y}) self:putfood() else table.insert(self.body,1,{self.x,self.y}) table.remove (self.body) end for k,v in pairs(self.body) do if k == 1 then -- body else disp.putimage("/lua/snake_body.png",v[1],v[2]) end end end
好了,一起来玩贪吃蛇吧
上海合宙通信模块 - 合宙Luat,让万物互联更简单