预览游戏

love2d游戏引擎重要函数

详情:

  • love.load:当游戏开始时被调用且仅调用一次

  • love.draw:回调函数,每帧更新一次游戏画面

  • love.update:回调函数,每帧更新一次游戏状态

  • love.keypressed:回调函数,当有按键被按下时触发

  • love.filesystem.load:加载一个lua脚本文件但不执行

!其他的函数在用到时再做解释

版本区别以及初始化资源

!首先要注意的是,本次使用的游戏引擎时love 0.9版本,与最新的love 11.x版本稍有区别。在0.9版本中颜色使用0~255来表示,而在11.x版本中是0~1来表示。

因为需要制作的游戏非常小,所以我们将所用到的资源在第一时间将其初始化并加载到内存中,以便使用。使用到的资源主要有:

  • 字体

  • 颜色

  • 声音

  • 窗口大小与块大小

  • 标题

  • 边框

所用到的函数:

  • love.window.setMode:设置窗口大小,以及样式

  • love.window.setTitle:设置标题

  • love.graphics.newFont:加载字体文件,大小自定义,返回Font类型

  • love.audio.newSource:加载音效文件

代码如下:

function love.load ()-- 块大小,窗口宽高,标题cellSize = 20width = 20 * 40height = 20 * 25title = 'SNAKE !'-- 设置窗口大小和标题love.window.setMode (width, height)love.window.setTitle (title)-- 加载不同大小字体fonts = {pixies100 = love.graphics.newFont ('Fonts/Pixies.TTF', 100),pixies30 = love.graphics.newFont ('Fonts/Pixies.TTF', 30),pixies10 = love.graphics.newFont ('Fonts/Pixies.TTF', 10)}-- 加载音效资源sounds = {showMenu = love.audio.newSource ('Sounds/showMenu.wav', 'stream'),switchOption = love.audio.newSource ('Sounds/switchOption.wav', 'stream'),eatFood = love.audio.newSource ('Sounds/eatFood.wav', 'stream'),collided = love.audio.newSource ('Sounds/collided.wav', 'stream'),gameOver = love.audio.newSource ('Sounds/gameOver.wav', 'stream')}-- 边框数据border = {1, 1,width-1, 1,width-1, height-1,1, height-1,1, 1}-- 颜色数据colors = {darkGray = { 0.3, 0.3, 0.3, 1 },beiga = { 0.98, 0.91, 0.76, 1 },white = { 1, 1, 1, 1 },paleTurquoise = { 0.7, 1, 1, 1 },}SwitchScence ('Menu')
end

场景与其切换

!首先我们需要实现一个简单的场景切换函数,因为一个游戏总是有多个场景

  1. 先将love2d引擎的主要回调函数赋值nil以免之后出现错误
  2. 加载新场景的lua脚本
  3. 执行新场景的lua脚本

代码如下:

function SwitchScence (scence)-- 将重要的函数赋予空值,以免冲突love.update = nillove.draw = nillove.keypressed = nil-- 将需要的场景加载进来,并执行load函数love.filesystem.load ('Scences/'..scence..'.lua') ()love.load ()
end-- 切换到初始化场景
SwitchScence ('Init')

绘制开始界面

在这里我们需要认识一些游戏买卖函数:

  • love.graphics.setFont:设置当期字体

  • love.graphics.setColor:设置当前颜色

  • love.graphics.rectangle:绘制矩形

  • love.graphics.line:绘制直线

  • love.graphics.print:在窗口上输出

!绘制比较简单,其他详情都在代码里有详细注释,要注意的是我绘制选项的方法。options的有效长度并不是#options,而是options.count记录的选项数量

代码如下:

-- 游戏标题,以及绘制位置
local gameName = {text = title,textX = cellSize * 12,textY = cellSize * 6
}-- 选项:开始和退出
local options = {{text = "START",textX = cellSize * 18,textY = cellSize * 15 - 5,border = {cellSize*16, cellSize*14,cellSize*24, cellSize*14,cellSize*24, cellSize*17,cellSize*16, cellSize*17,cellSize*16, cellSize*14}},{text = "QUIT",textX = cellSize * 19 - 10,textY = cellSize * 19 - 5,border = {cellSize*16, cellSize*18,cellSize*24, cellSize*18,cellSize*24, cellSize*21,cellSize*16, cellSize*21,cellSize*16, cellSize*18}},-- 一些其他属性count = 2,selected = 1
}function love.load ()-- 加载并播放背景音乐sounds.showMenu:play ()-- 设置米色和蓝色的透明程度为0,为了之后的动画效果colors.beiga[4] = 0colors.paleTurquoise[4] = 0
endfunction love.draw ()-- 灰色背景love.graphics.setColor (colors.darkGray)love.graphics.rectangle ('fill',0,0,width,height)-- 白色边框love.graphics.setColor (colors.white)love.graphics.line (border)-- 渐显效果if colors.beiga[4] < 1 thencolors.beiga[4] = colors.beiga[4] + 0.01colors.paleTurquoise[4] = colors.paleTurquoise[4] + 0.01end-- 设置字体,在指定位置画出米色标题love.graphics.setFont (fonts.pixies100)love.graphics.setColor (colors.beiga)love.graphics.print (gameName.text, gameName.textX, gameName.textY)-- 设置字体love.graphics.setFont (fonts.pixies30)-- 绘制所有选项for i = 1, options.count doif i == options.selected thenlove.graphics.setColor (colors.paleTurquoise)elselove.graphics.setColor (colors.beiga)end-- 绘制选项边框和字体love.graphics.line (options[i].border)love.graphics.print (options[i].text, options[i].textX, options[i].textY)end
endfunction love.keypressed (key)-- 上下箭头选择选项,回车按键确认选项if key == 'up' then-- 关闭切换选项的声音并重新播放if sounds.switchOption.isPlaying thensounds.switchOption:stop ()endsounds.switchOption:play ()-- 切换当前选项索引options.selected = options.selected - 1if options.selected <= 0 thenoptions.selected = options.countendelseif key == 'down' then-- 同上if sounds.switchOption.isPlaying thensounds.switchOption:stop ()endsounds.switchOption:play ()options.selected = options.selected + 1if options.selected > options.count thenoptions.selected = 1endelseif key == 'return' then-- 关闭显示界面声音if sounds.showMenu.isPlaying thensounds.showMenu:stop ()end-- 对应不同选项作出不同回应if options.selected == 1 thenSwitchScence ('GameStart')elseif options.selected == 2 thenlove.event.quit ()endend
end

实现游戏主体

游戏的实现方法,主要知道两个方面:

  • 蛇的移动方式:根据方向获取下一个头的位置,若没有吃到食物就将蛇尾删除,达到移动效果
-- 下一个蛇头位置
local nextX = snake.body[1].x
local nextY = snake.body[1].y-- 当方向队列中的方向大于1时除去第一个方向(当前方向)
if #directionQueue > 1 thentable.remove (directionQueue, 1)
end-- 根据方向作出改动
if directionQueue[1] == 'right' thennextX = nextX + 1if nextX > limit.x thennextX = 0endelseif directionQueue[1] == 'left' thennextX = nextX - 1if nextX < 0 thennextX = limit.xendelseif directionQueue[1] == 'down' thennextY = nextY + 1if nextY > limit.y thennextY = 0endelseif directionQueue[1] == 'up' thennextY = nextY - 1if nextY < 0 thennextY = limit.yendend-- 蛇是否可以移动(没有与自身相撞)local canMove = truefor index, pair in ipairs (snake.body) doif index ~= #snake.bodyand nextX == pair.xand nextY == pair.y thencanMove = falseendend-- 当蛇可以移动时if canMove then-- 将新位置加在蛇身的头,并检测是否吃到了食物table.insert (snake.body, 1, { x = nextX, y = nextY })if nextX == food.x and nextY == food.y then-- 播放吃到食物的音效(关闭之前的音效)if sounds.eatFood.isPlaying thensounds.eatFood:stop ()endsounds.eatFood:play ()-- 分数加一,并生成新的食物位置currentScore.score = currentScore.score + 1CreateFood ()else-- 没有吃到食物则删去蛇身的尾部,达到移动的目的table.remove (snake.body)endelse-- 蛇死亡,并播放相撞的音效snake.alive = falsesounds.collided:play ()end
end
  • 方向队列的引入:主要是解决键位冲突的问题
function love.keypressed (key)-- 空格键暂停游戏if key == 'space' thenpaused = not pausedend-- 没有暂停时if not paused then-- 记录方向键的按下顺序,同方向或相反方向的不记录if key == 'right'and directionQueue[#directionQueue] ~= 'right'and directionQueue[#directionQueue] ~= 'left' thentable.insert (directionQueue, 'right')elseif key == 'left'and directionQueue[#directionQueue] ~= 'left'and directionQueue[#directionQueue] ~= 'right' thentable.insert (directionQueue, 'left')elseif key == 'down'and directionQueue[#directionQueue] ~= 'down'and directionQueue[#directionQueue] ~= 'up' thentable.insert (directionQueue, 'down')elseif key == 'up'and directionQueue[#directionQueue] ~= 'up'and directionQueue[#directionQueue] ~= 'down' thentable.insert (directionQueue, 'up')endend
end

代码如下:

-- 游戏窗口与记分窗口的分界线
local boundary = {cellSize*30, 0,cellSize*30, height
}-- 当前分数的信息
local currentScore = {text = 'SCORE',score = 0,-- 文字的绘图位置textX = cellSize * 33,textY = cellSize * 2,-- 分数的绘图位置scoreX = cellSize * 34,scoreY = cellSize * 5
}-- 最高分的信息
local highScore = {text = 'HIGH SCORE',score = 0,-- 同上textX = cellSize * 31,textY = cellSize * 12,scoreX = cellSize * 34,scoreY = cellSize * 15
}-- 提示信息
local notes = {{text = 'ARROW KEY TO MOVE',textX = cellSize * 34,textY = cellSize * 22},{text = 'ENTER KEY TO PAUSE',textX = cellSize * 34,textY = cellSize * 23}
}-- 游戏窗口的限制
local limit = { x = 29, y = 24 }-- 蛇的初始化信息
local snake = {-- 蛇身body = {{ x = 2, y = 0 },{ x = 1, y = 0 },{ x = 0, y = 0 }},-- 速度与状态speed = 0.1,alive = true,
}-- 食物的位置
local food = { x = nil, y = nil }-- 方向队列,用于记录键盘按下的顺序以免产生冲突
local directionQueue = { 'right' }-- 计时器,暂停状态以及最高分文件
local timer = 0
local paused = false
local file = nil-- 用于生成食物的可存在位置
local function CreateFood ()local foodPosition = {}-- 遍历整个窗口,将可生成食物的位置记录在foodPosition表里for i = 0, limit.x dofor j = 0, limit.y dolocal possible = true-- 是否与蛇身冲突for index, pair in ipairs (snake.body) doif i == pair.x and j == pair.y thenpossible = falseendendif possible thentable.insert (foodPosition, { x = i, y = j })endendend-- 生成随机食物位置local index = love.math.random (#foodPosition)food.x, food.y = foodPosition[index].x, foodPosition[index].y
endfunction love.load ()file = love.filesystem.newFile ('HighScore.txt')file:open ('r')highScore.score = file:read ()file:close ()-- 没有透明度colors.beiga[4] = 1colors.paleTurquoise[4] = 1CreateFood ()
endfunction love.draw ()-- 绘制背景love.graphics.setColor (colors.darkGray)love.graphics.rectangle ('fill',0,0,width,height)-- 绘制白色边框和边界线love.graphics.setColor (colors.white)love.graphics.line (border)love.graphics.line (boundary)-- 设置字体和颜色,并在指定位置绘制当前分数信息和最高分信息love.graphics.setFont (fonts.pixies30)love.graphics.setColor (colors.beiga)love.graphics.print (currentScore.text, currentScore.textX, currentScore.textY)love.graphics.print (currentScore.score, currentScore.scoreX, currentScore.scoreY)love.graphics.setColor (colors.paleTurquoise)love.graphics.print (highScore.text, highScore.textX, highScore.textY)love.graphics.print (highScore.score, highScore.scoreX, highScore.scoreY)-- 蛇生存和死亡时使用不同的颜色绘制if snake.alive thenlove.graphics.setColor (colors.paleTurquoise)elselove.graphics.setColor (colors.beiga)end-- 绘制蛇身,蛇头另绘for index, pair in ipairs (snake.body) doif index == 1 thenlove.graphics.rectangle ('fill',cellSize*pair.x,cellSize*pair.y,cellSize,cellSize)endlove.graphics.rectangle ('fill',cellSize*pair.x+1,cellSize*pair.y+1,cellSize-1*2,cellSize-1*2)end-- 绘制食物love.graphics.setColor (colors.beiga)love.graphics.rectangle ('fill',cellSize*food.x+1,cellSize*food.y+1,cellSize-1*2,cellSize-1*2)-- 如果是暂停状态,则绘制暂停字样if paused thenlove.graphics.print ('PAUSED !', cellSize*12, cellSize*11)end-- 设置字体和颜色并绘制提示信息love.graphics.setFont (fonts.pixies10)love.graphics.setColor (colors.beiga)for i = 1, #notes dolove.graphics.print (notes[i].text, notes[i].textX, notes[i].textY)end
endfunction love.update (dt)-- 使用计时器timer = timer + dt-- 当蛇生存时if snake.alive then-- 根据蛇的速度更新游戏if timer > snake.speed thentimer = timer - snake.speed-- 没有暂停时if not paused then-- 下一个蛇头位置local nextX = snake.body[1].xlocal nextY = snake.body[1].y-- 当方向队列中的方向大于1时除去第一个方向(当前方向)if #directionQueue > 1 thentable.remove (directionQueue, 1)end-- 根据方向作出改动if directionQueue[1] == 'right' thennextX = nextX + 1if nextX > limit.x thennextX = 0endelseif directionQueue[1] == 'left' thennextX = nextX - 1if nextX < 0 thennextX = limit.xendelseif directionQueue[1] == 'down' thennextY = nextY + 1if nextY > limit.y thennextY = 0endelseif directionQueue[1] == 'up' thennextY = nextY - 1if nextY < 0 thennextY = limit.yendend-- 蛇是否可以移动(没有与自身相撞)local canMove = truefor index, pair in ipairs (snake.body) doif index ~= #snake.bodyand nextX == pair.xand nextY == pair.y thencanMove = falseendend-- 当蛇可以移动时if canMove then-- 将新位置加在蛇身的头,并检测是否吃到了食物table.insert (snake.body, 1, { x = nextX, y = nextY })if nextX == food.x and nextY == food.y then-- 播放吃到食物的音效(关闭之前的音效)if sounds.eatFood.isPlaying thensounds.eatFood:stop ()endsounds.eatFood:play ()-- 分数加一,并生成新的食物位置currentScore.score = currentScore.score + 1CreateFood ()else-- 没有吃到食物则删去蛇身的尾部,达到移动的目的table.remove (snake.body)endelse-- 蛇死亡,并播放相撞的音效snake.alive = falsesounds.collided:play ()endendend-- 等待一秒elseif timer >= 1 then-- 存储最高分if currentScore.score > tonumber (highScore.score) thenfile:open ('w')file:write (tostring (currentScore.score))file:close ()end-- 切换到游戏结束场景SwitchScence ('GameOver')end
endfunction love.keypressed (key)-- 回车键暂停游戏if key == 'return' thenpaused = not pausedend-- 没有暂停时if not paused then-- 记录方向键的按下顺序,同方向或相反方向的不记录if key == 'right'and directionQueue[#directionQueue] ~= 'right'and directionQueue[#directionQueue] ~= 'left' thentable.insert (directionQueue, 'right')elseif key == 'left'and directionQueue[#directionQueue] ~= 'left'and directionQueue[#directionQueue] ~= 'right' thentable.insert (directionQueue, 'left')elseif key == 'down'and directionQueue[#directionQueue] ~= 'down'and directionQueue[#directionQueue] ~= 'up' thentable.insert (directionQueue, 'down')elseif key == 'up'and directionQueue[#directionQueue] ~= 'up'and directionQueue[#directionQueue] ~= 'down' thentable.insert (directionQueue, 'up')endend
end

实现最高分的保存与读取

游戏存档目录:

  • Windows XP: C:\Documents and Settings\user\Application Data\LOVE\ or %appdata%\LOVE\

  • Windows Vista and 7,8: C:\Users\user\AppData\Roaming\LOVE or %appdata%\LOVE\

  • Linux: $XDG_DATA_HOME/love/ or ~/.local/share/love/

  • Mac: /Users/user/Library/Application Support/LOVE/

!写文件只能在存档目录

最高分读取:

file = love.filesystem.newFile ('HighScore.txt')
file:open ('r')
highScore.score = file:read ()
file:close ()

最高分保存:

file:open ('w')
file:write (tostring (currentScore.score))
file:close ()

绘制游戏结束界面

游戏结束界面的绘制与开始界面大致相同,这里不再赘述

代码如下:

local gameOver = {text = 'GAME OVER !',textX = cellSize * 6,textY = cellSize * 6
}-- 选项:开始和退出
local options = {{text = "BACK",textX = cellSize * 13 - 15,textY = cellSize * 17 - 5,border = {cellSize*10, cellSize*16,cellSize*18, cellSize*16,cellSize*18, cellSize*19,cellSize*10, cellSize*19,cellSize*10, cellSize*16}},{text = "RETRY",textX = cellSize * 24,textY = cellSize * 17 - 5,border = {cellSize*22, cellSize*16,cellSize*30, cellSize*16,cellSize*30, cellSize*19,cellSize*22, cellSize*19,cellSize*22, cellSize*16}},-- 一些其他属性count = 2,selected = 1
}function love.load ()sounds.gameOver:play ()-- 设置米色和蓝色的透明程度为0,为了之后的动画效果colors.beiga[4] = 0colors.paleTurquoise[4] = 0
endfunction love.draw ()-- 灰色背景love.graphics.setColor (colors.darkGray)love.graphics.rectangle ('fill',0,0,width,height)-- 白色边框love.graphics.setColor (colors.white)love.graphics.line (border)-- 渐显效果if colors.beiga[4] < 1 thencolors.beiga[4] = colors.beiga[4] + 0.01colors.paleTurquoise[4] = colors.paleTurquoise[4] + 0.01end-- 设置字体,在指定位置画出米色标题love.graphics.setFont (fonts.pixies100)love.graphics.setColor (colors.beiga)love.graphics.print (gameOver.text, gameOver.textX, gameOver.textY)-- 设置字体love.graphics.setFont (fonts.pixies30)for i = 1, options.count doif i == options.selected thenlove.graphics.setColor (colors.paleTurquoise)elselove.graphics.setColor (colors.beiga)endlove.graphics.line (options[i].border)love.graphics.print (options[i].text, options[i].textX, options[i].textY)end
endfunction love.keypressed (key)-- 上下箭头选择选项,回车按键确认选项if key == 'left' thenif sounds.gameOver.isPlaying thensounds.gameOver:stop ()endif sounds.switchOption.isPlaying thensounds.switchOption:stop ()endsounds.switchOption:play ()options.selected = options.selected - 1if options.selected <= 0 thenoptions.selected = options.countendelseif key == 'right' thenif sounds.gameOver.isPlaying thensounds.gameOver:stop ()endif sounds.switchOption.isPlaying thensounds.switchOption:stop ()endsounds.switchOption:play ()options.selected = options.selected + 1if options.selected > options.count thenoptions.selected = 1endelseif key == 'return' thenif sounds.gameOver.isPlaying thensounds.gameOver:stop ()endif options.selected == 1 thenSwitchScence ('Menu')elseif options.selected == 2 thenSwitchScence ('GameStart')endend
end

项目结构

项目结构图如下

Love2D游戏引擎制作贪吃蛇游戏

Love2D游戏引擎制作贪吃蛇游戏相关推荐

  1. 如何用html做一个贪吃蛇,如何用HTML5制作贪吃蛇游戏

    如何用HTML5制作贪吃蛇游戏 发布时间:2020-07-09 15:09:59 来源:亿速云 阅读:122 作者:Leah 如何用HTML5制作贪吃蛇游戏?很多新手对此不是很清楚,为了帮助大家解决这 ...

  2. 手把手教你使用 Python 制作贪吃蛇游戏

    贪吃蛇游戏是有史以来最受欢迎的街机游戏之一.在这个游戏中,玩家的主要目标是在不撞墙或不撞墙的情况下抓住最大数量的水果.在学习 Python 或 Pygame 时,可以将创建蛇游戏视为一项挑战.这是每个 ...

  3. 单片机8×8点阵显示简单汉字的程序_干货 | 浅析单片机制作贪吃蛇游戏

    为了让大家更深入地了解底层的原理,在讲解时特意选择了51单片机(而非STM系列),另外16*16点阵由译码器和移位缓存器直接驱动(而非MAX系列芯片),摇杆也利用ADC功能判断方向. 那如何让单片机驱 ...

  4. python制作贪吃蛇游戏下载_自动玩贪吃蛇,满屏的蛇影当然由python制作AI贪吃蛇!...

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

  5. 手把手教你使用 Python 制作贪吃蛇游戏,才发现原来制作起来很简单ǃ

    贪吃蛇游戏是有史以来最受欢迎的街机游戏之一.在这个游戏中,玩家的主要目标是在不撞墙或不撞墙的情况下抓住最大数量的水果.在学习 Python 或 Pygame 时,可以将创建蛇游戏视为一项挑战.这是每个 ...

  6. 使用Python语言制作贪吃蛇游戏,并制作成为exe可执行文件

    本项目为贪吃蛇游戏,上下左右控制蛇的行走路径,并将游戏打包为exe文件,可供其他没有python环境和代码的电脑 Play 游戏! 目录 一.项目成果 二.项目架构 三.项目代码 四.环境搭建 五.矢 ...

  7. python制作贪吃蛇游戏_用Python写贪吃蛇游戏的代码实例

    这篇文章主要为大家详细介绍了Python贪吃蛇游戏的编写代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 最近在学Python,想做点什么来练练手,命令行的贪吃蛇一般是C的练手项目,但是一时之间 ...

  8. 用vb.net制作贪吃蛇游戏

    贪吃蛇游戏相信很多朋友都听说或者玩过,特别是以前使用过诺基亚手机的朋友,这在当时就是诺基亚手机的专配游戏. 本篇文章讲述如何在vb.net中编写此游戏代码. 一种方法是可以使用控件数组,在用户界面上使 ...

  9. 回忆经典,九叔教你用Python制作贪吃蛇游戏

    众所周知Python除了不能生孩子啥都行. 咱们今天就说一下如何利用Python写一个简易的小游戏. 所以呀咱们今天就用Python写大家都玩过的小游戏--贪吃蛇. 用Python写游戏就得用到pyg ...

最新文章

  1. 【开源项目】之智能鞋柜(STM32)
  2. 读取 XML 数据时,超出最大字符串内容长度配额 (8192)
  3. Maven引入外部jar的几种方法
  4. 三分钟掌握PHP操作数据库
  5. Ubuntu 14.04.3 LTS 配置 DNS Server
  6. 深度学习之卷积神经网络 GoogleNet
  7. rm linux 复制目录,linux学习(四)复制(cp)移动(mv)删除(rm)查找(find)文件、文件夹操作、软硬链接的区别...
  8. 8 MM配置-主数据-定义行业部门和具体行业部门字段选择
  9. Java多线程包之BlockingQueue
  10. vmware之VMware Remote Console (VMRC) SDK(三)
  11. java如何验证手机号码_Java 手机号码正则表达式验证
  12. vivo手机怎么安装Android10,vivo X20手机怎么装卡 vivo X20安装手机卡步骤介绍
  13. wmp流代理服务器设置为空,03服务器安装wmp10的方法
  14. 计算机笔记--【Java设计模式】
  15. n个水手分椰子问题 (递推)
  16. css之3D旋转讲解
  17. 关于数据中台、数据平台、数据仓库、数据湖等数据概念的对比解析
  18. 怎样进行大数据的入门级学习?
  19. 电子信息 物联网 微电子等专业毕业设计选题表1-20
  20. [Cue]emulator unknown skin name 'WVGA800'

热门文章

  1. python sklearn下载了但是引用失败_关于python:导入sklearn时出错
  2. vue - blog开发学习1
  3. Nexus入门【转】
  4. Docker的网络模式和跨主机通信
  5. MySQL:行锁、表锁、乐观锁、悲观锁、读锁、写锁
  6. Java:清空文件内容
  7. 微信公众号--消息回复
  8. Python Day11
  9. Lr中脚本的迭代次数和场景运行时间的关系
  10. ios开发 json数据文件的存取