作者简介:

打盹的消防车——活跃于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,让万物互联更简单

Luat Inside | 致敬经典,使用Air724UG制作简易贪吃蛇的更多相关文章

  1. C - 简易贪吃蛇的编写

    不多废话,直接进入正题——用C编写简易贪吃蛇.附上拙劣的源码 * c-snake * 首先说明使画面动起来的原理:通过 system("cls"); 清除当前控制台的显示,再pri ...

  2. Python制作AI贪吃蛇,很多很多细节、思路都写下来了!

    前提:本文实现AI贪吃蛇自行对战,加上人机对战,读者可再次基础上自行添加电脑VS电脑和玩家VS玩家(其实把人机对战写完,这2个都没什么了,思路都一样) 实现效果: 很多人学习python,不知道从何学 ...

  3. Python制作AI贪吃蛇

    前提:本文实现AI贪吃蛇自行对战,加上人机对战,文章末尾附上源代码以及各位大佬的链接,还有一些实现步骤,读者可再次基础上自行添加电脑VS电脑和玩家VS玩家(其实把人机对战写完,这2个都没什么了,思路都 ...

  4. <Android 应用 之路> 简易贪吃蛇

    最简单的贪吃蛇 最近想着忙里偷闲写点简单的Android应用,增加一些生活乐趣,由于平时工作主要精力并不是集中在书写apk上,更多的是解决代码问题和维护模块稳定,但是写代码本身是一件比较有趣的事情,因 ...

  5. Unity经典游戏教程之:贪吃蛇

    版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...

  6. 如何用python制作贪吃蛇以及AI版贪吃蛇

    用python制作普通贪吃蛇 哈喽,大家不知道是上午好还是中午好还是下午好还是晚上好! 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很 ...

  7. C#制作简易屏保(转)

    C#制作简易屏保[原创] 原始网址: http://www.cnblogs.com/drizzlecrj/archive/2006/10/06/522182.html 2006-10-06 16:25 ...

  8. 【百度地图API】——如何用label制作简易的房产标签

    原文:[百度地图API]--如何用label制作简易的房产标签 摘要: 最近,API爱好者们纷纷说,自定义marker太复杂了!不仅定义复杂,连所有的dom事件都要自己重新定义.有没有快速简易创建房产 ...

  9. 免费IP代理池定时维护,封装通用爬虫工具类每次随机更新IP代理池跟UserAgent池,并制作简易流量爬虫

    前言 我们之前的爬虫都是模拟成浏览器后直接爬取,并没有动态设置IP代理以及UserAgent标识,本文记录免费IP代理池定时维护,封装通用爬虫工具类每次随机更新IP代理池跟UserAgent池,并制作 ...

随机推荐

  1. 浅谈持续集成(CI)、持续交付(CD)、持续部署(CD)

    CI/CD是实现敏捷和Devops理念的一种方法,具体而言,CI/CD 可让持续自动化和持续监控贯穿于应用的 整个生命周期(从集成和测试阶段,到交付和部署).这些关联的事务通常被统称为"CI ...

  2. Python内置函数(Built-in Function)

    直接查看编码以及示例: 1 """ 2 内置函数 Built-in Function 3 """ 4 5 # abs() 取绝对值 6 pr ...

  3. 逆向工程初步160个crackme-------6

    工具:1. 按钮事件地址转换器E2A 2. PEID 3. Ollydbg 同样我们先来运行一下这个程序, ok按钮是被禁用的,有一个help按钮点击后弹出一个消息框:消息框显示提示信息为.本程序需要 ...

  4. Spring 实现策略模式--自定义注解方式解耦if...else

    策略模式 定义 定义一簇算法类,将每个算法分别封装起来,让他们可以互相替换,策略模式可以使算法的变化独立于使用它们的客户端 场景 使用策略模式,可以避免冗长的if-else 或 switch分支判断 ...

  5. EventSource的自定义实现

    前言: 前面两篇文章都介绍了.NET Core 性能诊断工具,其中诊断工具都用到了EventCounters来实时的收集服务器性能指标. 那么收集指标能否自己定义呢? 一.What's EventCo ...

  6. Java容器 | 基于源码分析Map集合体系

    一.容器之Map集合 集合体系的源码中,Map中的HashMap的设计堪称最经典,涉及数据结构.编程思想.哈希计算等等,在日常开发中对于一些源码的思想进行参考借鉴还是很有必要的. 基础:元素增查删.容 ...

  7. JAVA的基本介绍和JDK的安装

    JAVA帝国 JAVA特性和优势 简单 面向对象 可复制性 高性能 分布式 动态性 多线性 安全性 健壮性 JAVA三大版本 javaSE:标准版(桌面程序.控制台开发) javaME(嵌入式开发) ...

  8. Bash技巧:使用 set 内置命令帮助调试 shell 脚本

    Bash技巧:使用 set 内置命令帮助调试 shell 脚本 霜鱼片发布于 2020-02-03   在 bash 中,可以使用 set 内置命令设置和查看 shell 的属性.这些属性会影响 sh ...

  9. Linux后台执行命令:&和nohup nohup和&后台运行,进程查看及终止

    nohup和&后台运行,进程查看及终止   阅读目录 nohup和&后台运行,进程查看及终止 1.nohup 2.& 3.nohup和&的区别 &:是指在后台运 ...

  10. CentOS7配置kdump

    CentOS7配置kdump 简单生活,简单爱 2020-10-27 16:29:56  56  收藏 1 分类专栏: Linux实际开发总结 版权 文章目录 1.kdump简介 2.配置kdump ...