返回

使用 Love2D 引擎开发贪吃蛇游戏

今天来介绍博主最近捣腾的一个小游戏“贪吃蛇”。“贪吃蛇”这个游戏相信大家都不会感到陌生吧。今天博主将通过Love2D这款游戏引擎来为大家实现一个简单的贪吃蛇游戏,在本篇文章当中我们将会涉及到“贪吃蛇”的基本算法、Lua 语言编程等基本的内容,希望能够对大家开发类似的游戏提供借鉴和思考,文章中如有不足之处,还希望大家能够谅解,因为博主的游戏开发基本就是这样慢慢摸索着学习,所以难免会有不足的地方。

游戏算法

我们首先来看看贪吃蛇是怎么移动的?

贪吃蛇游戏算法演示1
贪吃蛇游戏算法演示1
贪吃蛇游戏算法演示2
贪吃蛇游戏算法演示2
贪吃蛇游戏算法演示3
贪吃蛇游戏算法演示3
贪吃蛇游戏算法演示4
贪吃蛇游戏算法演示4
通过这四张图的演示,我们可以发现这样一个规律:

蛇的移动其实是将蛇身体的最后一个元素移动到第一个元素的位置

那么完成这样一个工作需要两个步骤:

1、将在蛇头位置插入一个新的元素 2、移除蛇尾位置的最后一个元素

好了,了解了蛇的移动后我们再来考虑一个问题,怎样判断蛇吃到了食物?思路和蛇的移动类似,主要考虑在蛇头插入的这个元素和食物的关系,如果这个元素的坐标和食物的坐标是相同的,那么就可以认为蛇吃到了食物,此时蛇的身体应该是变长的,所以只要在蛇头位置插入一个元素就可以了。反之,如果蛇没有吃到食物,那么蛇应该是移动的,所以就可以按照移动的方法来处理了。那么在蛇头位置插入的这个元素该如何确定呢?我们来看下面这段程序:

 1--计算下一个目标点
 2function getNextPoint()
 3  --计算下一个目标点
 4  snake = {}
 5  if (dir == 0) then
 6    snake.x = snakes[1].x
 7    snake.y = snakes[1].y - 20
 8  end
 9  if (dir == 1) then
10    snake.x = snakes[1].x
11    snake.y = snakes[1].y + 20
12  end
13  if (dir == 2) then
14    snake.x = snakes[1].x - 20
15    snake.y = snakes[1].y
16  end
17  if (dir == 3) then
18    snake.x = snakes[1].x + 20
19    snake.y = snakes[1].y
20  end
21
22  return snake
23end

这里定义了 getNextPoint()的方法,目的是计算在蛇头位置添加的下一个元素,这里我们注意到根据蛇的移动方向(dir)的不同,其中 0 表示上、1 表示下、2 表示左、3 表示右,计算出下一个元素的位置,因为在这个游戏中网格大小是 20,所以这里可以直接根据坐标来计算一个元素的位置。snakes 是一个 table,保存的是当前的蛇的全部元素的坐标。通过维护这个 table,我们就可以利用绘图的函数绘制出蛇的身体,这样蛇就可以移动起来了。我们来看看蛇是怎样移动的:

 1--核心算法——蛇的移动
 2function SnakeUpdate()
 3  --获取元素个数
 4  local n = table.maxn(snakes)
 5  if (table.maxn(snakes) > 0) then
 6    if (getNextPoint().x == foodX and getNextPoint().y == foodY) then
 7      --将下一个目标点的位置插入表中
 8      table.insert(snakes, 1, getNextPoint())
 9      --将食物状态设置为BeEated
10      foodState="BeEated"
11    else
12      --将下一个目标点的位置插入表中
13      table.insert(snakes, 1, getNextPoint())
14      --移除最后一个元素
15      table.remove(snakes,n+1)
16    end 
17  end
18end

在这里我们定义了一个 foodState 变量以保存食物的状态,当食物的状态为 BeEated 的时候表示食物被蛇吃掉了,此时应该重新生成一个食物的坐标,此时事物的状态将变成 WaitToEat。食物的坐标保存在 foodX 和 foodY 这两个变量中,大家可以到完整的代码中去查看。

游戏状态

我们知道蛇碰到四周墙壁的时候就会死亡,此时游戏结束。这个比较简单,只要判断蛇头的坐标和屏幕的关系就可以了。因为在这个游戏中屏幕的尺寸为 640X640,所以判断游戏是否结束的代码可以这样写:

1--判断游戏状态
2  if(snakes[1].x <= 0 or snakes[1].x >= 640 or snakes[1].y <= 0 or snakes[1].y >= 640) then
3    gameState = 0
4  else
5    gameState = 1
6  end

这里 gameState 为 0 表示游戏结束,gameState 为 1 表示游戏正常进行。

完整代码

在完成了这些核心的算法以后,剩下的事情就交给 Love2D 引擎来绘制吧,最后给出完整的程序代码:

  1--定义窗口宽度和高度
  2local w	= 640
  3local h	= 640
  4--定义网格单元大小
  5local unitSize = 20;
  6
  7--方块的初始位置
  8local initX	= 320
  9local initY	= 320
 10
 11--移动方向
 12local dir = 1
 13
 14--贪吃蛇集合
 15local snakes = {}
 16
 17--食物状态
 18--WaitToEat:绘制食物
 19--BeEated:随机生成食物
 20local foodState = "WaitToEat"
 21
 22--游戏状态
 23--0:游戏结束
 24--1:游戏正常
 25local gameState = 1
 26
 27--食物的位置
 28local foodX = 0
 29local foodY = 0
 30
 31--Love2D加载事件
 32function love.load()
 33  --设置窗口标题
 34  love.window.setTitle("Love2D-贪吃蛇游戏")
 35  --设置窗口大小
 36  love.window.setMode(w,h)
 37  --定义字体
 38  local myFont = love.graphics.newFont(30)
 39  --设置字体
 40  love.graphics.setFont(myFont)
 41  --设置背景色
 42  love.graphics.setBackgroundColor(255,255,255,255)
 43  --设置线条类型为平滑
 44  love.graphics.setLineStyle("smooth")
 45  --设置线宽
 46  love.graphics.setLineWidth(0.1)
 47
 48  --蛇的初始化(蛇的长度为5)
 49  for i=1,5 do
 50    local snake = { }
 51    snake.x = initX + (i-1) * 20
 52    snake.y = initY
 53    snakes[i] = snake
 54  end
 55
 56  --食物初始化
 57  foodX = love.math.random(32-1)*20
 58  foodY = love.math.random(32-1)*20
 59end
 60
 61
 62--Love2D绘制事件
 63function love.draw()
 64  --绘制竖线
 65  love.graphics.setColor(0,0,0,255)
 66  for i = 0, w, unitSize do
 67    love.graphics.line(0,i,h,i)
 68  end
 69  --绘制横线
 70  for j = 0, h, unitSize do
 71    love.graphics.line(j,0,j,w)
 72  end
 73
 74  --绘制蛇
 75  for i = 1,table.maxn(snakes) do
 76    love.graphics.setColor(0,0,255,255)
 77    love.graphics.rectangle("fill",snakes[i].x,snakes[i].y,20,20)
 78  end
 79
 80  --绘制食物
 81  if(foodState == "WaitToEat") then
 82    love.graphics.setColor(255,0,0,255)
 83    love.graphics.rectangle("fill",foodX,foodY,20,20)
 84  end
 85
 86  --如果游戏结束则显示GameOver
 87  if(gameState == 0) then
 88    love.graphics.setColor(255,0,0,255)
 89    love.graphics.print("Game Over",250,300)
 90  end
 91end 
 92
 93--
 94function love.update(dt)
 95  --判断游戏状态
 96  if (snakes[1].x <= 0 or snakes[1].x >= 640 or snakes[1].y <= 0 or snakes[1].y >= 640) then
 97    gameState = 0
 98  else
 99    gameState = 1
100  end
101
102  --如果游戏状态为正常
103  if (gameState == 1) then
104    SnakeUpdate()
105    FoodUpdate()
106  end
107end
108
109--核心算法——蛇的移动
110function SnakeUpdate(dt)
111  --获取元素个数
112  local n = table.maxn(snakes)
113  if(table.maxn(snakes) > 0) then
114    if(getNextPoint().x == foodX and getNextPoint().y == foodY) then
115      --将下一个目标点的位置插入表中
116      table.insert(snakes, 1, getNextPoint())
117      --将食物状态设置为BeEated
118      foodState="BeEated"
119    else
120      --将下一个目标点的位置插入表中
121      table.insert(snakes, 1, getNextPoint())
122      --移除最后一个元素
123      table.remove(snakes,n+1)
124    end 
125  end
126end
127
128--随机生成食物
129function FoodUpdate()
130  --如果食物被蛇吃掉则重新生成食物
131  if(foodState == "BeEated") then
132    foodX=love.math.random(32-1)*20
133    foodY=love.math.random(32-1)*20
134    foodState = "WaitToEat"
135   end
136end
137
138--根据玩家按下的键位定义不同的方向
139function love.keypressed(key)
140  if (key == "a") then
141    dir = 2
142  end
143  if (key == "d") then
144    dir = 3
145  end
146  if (key == "w") then
147    dir = 0
148  end
149  if (key=="s") then
150    dir = 1
151  end
152end
153
154--计算下一个目标点
155function getNextPoint()
156  --计算下一个目标点
157  local snake = {}
158  if(dir == 0) then
159    snake.x = snakes[1].x
160    snake.y = snakes[1].y - 20
161  end
162  if(dir == 1) then
163    snake.x = snakes[1].x
164    snake.y = snakes[1].y + 20
165  end
166  if(dir == 2) then
167    snake.x=snakes[1].x - 20
168    snake.y=snakes[1].y
169  end
170  if(dir == 3) then
171    snake.x = snakes[1].x + 20
172    snake.y = snakes[1].y
173  end
174
175  return snake
176end

将代码压缩成.love 文件后就可以运行了,我们来看看最终的效果:

Love2D 贪吃蛇游戏示例1
Love2D 贪吃蛇游戏示例1

Love2D 贪吃蛇游戏示例2
Love2D 贪吃蛇游戏示例2

本文的项目作为开源项目托管在 Github 上,可以通过Github来获取项目源代码。谢谢大家,今天的内容就是这样了。

Built with Hugo
Theme Stack designed by Jimmy