代碼地址以下:
http://www.demodashi.com/demo/15051.htmlhtml
內附有linux下的makefile,windows下的生成方法請查看:linux
for windowswindows
詳情:app
love2d wikidom
love.load:當遊戲開始時被調用且僅調用一次ide
love.draw:回調函數,每幀更新一次遊戲畫面函數
love.update:回調函數,每幀更新一次遊戲狀態字體
love.keypressed:回調函數,當有按鍵被按下時觸發動畫
love.filesystem.load:加載一個lua腳本文件但不執行ui
!其餘的函數在用到時再作解釋
!首先要注意的是,本次使用的遊戲引擎時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 = 20 width = 20 * 40 height = 20 * 25 title = '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
!首先咱們須要實現一個簡單的場景切換函數,由於一個遊戲老是有多個場景
代碼以下:
function SwitchScence (scence) -- 將重要的函數賦予空值,以避免衝突 love.update = nil love.draw = nil love.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] = 0 colors.paleTurquoise[4] = 0 end function 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 then colors.beiga[4] = colors.beiga[4] + 0.01 colors.paleTurquoise[4] = colors.paleTurquoise[4] + 0.01 end -- 設置字體,在指定位置畫出米色標題 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 do if i == options.selected then love.graphics.setColor (colors.paleTurquoise) else love.graphics.setColor (colors.beiga) end -- 繪製選項邊框和字體 love.graphics.line (options[i].border) love.graphics.print (options[i].text, options[i].textX, options[i].textY) end end function love.keypressed (key) -- 上下箭頭選擇選項,回車按鍵確認選項 if key == 'up' then -- 關閉切換選項的聲音並從新播放 if sounds.switchOption.isPlaying then sounds.switchOption:stop () end sounds.switchOption:play () -- 切換當前選項索引 options.selected = options.selected - 1 if options.selected <= 0 then options.selected = options.count end elseif key == 'down' then -- 同上 if sounds.switchOption.isPlaying then sounds.switchOption:stop () end sounds.switchOption:play () options.selected = options.selected + 1 if options.selected > options.count then options.selected = 1 end elseif key == 'return' then -- 關閉顯示界面聲音 if sounds.showMenu.isPlaying then sounds.showMenu:stop () end -- 對應不一樣選項做出不一樣迴應 if options.selected == 1 then SwitchScence ('GameStart') elseif options.selected == 2 then love.event.quit () end end end
遊戲的實現方法,主要知道兩個方面:
-- 下一個蛇頭位置 local nextX = snake.body[1].x local nextY = snake.body[1].y -- 當方向隊列中的方向大於1時除去第一個方向(當前方向) if #directionQueue > 1 then table.remove (directionQueue, 1) end -- 根據方向做出改動 if directionQueue[1] == 'right' then nextX = nextX + 1 if nextX > limit.x then nextX = 0 end elseif directionQueue[1] == 'left' then nextX = nextX - 1 if nextX < 0 then nextX = limit.x end elseif directionQueue[1] == 'down' then nextY = nextY + 1 if nextY > limit.y then nextY = 0 end elseif directionQueue[1] == 'up' then nextY = nextY - 1 if nextY < 0 then nextY = limit.y end end -- 蛇是否能夠移動(沒有與自身相撞) local canMove = true for index, pair in ipairs (snake.body) do if index ~= #snake.body and nextX == pair.x and nextY == pair.y then canMove = false end end -- 當蛇能夠移動時 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 then sounds.eatFood:stop () end sounds.eatFood:play () -- 分數加一,並生成新的食物位置 currentScore.score = currentScore.score + 1 CreateFood () else -- 沒有吃到食物則刪去蛇身的尾部,達到移動的目的 table.remove (snake.body) end else -- 蛇死亡,並播放相撞的音效 snake.alive = false sounds.collided:play () end end
function love.keypressed (key) -- 空格鍵暫停遊戲 if key == 'space' then paused = not paused end -- 沒有暫停時 if not paused then -- 記錄方向鍵的按下順序,同方向或相反方向的不記錄 if key == 'right' and directionQueue[#directionQueue] ~= 'right' and directionQueue[#directionQueue] ~= 'left' then table.insert (directionQueue, 'right') elseif key == 'left' and directionQueue[#directionQueue] ~= 'left' and directionQueue[#directionQueue] ~= 'right' then table.insert (directionQueue, 'left') elseif key == 'down' and directionQueue[#directionQueue] ~= 'down' and directionQueue[#directionQueue] ~= 'up' then table.insert (directionQueue, 'down') elseif key == 'up' and directionQueue[#directionQueue] ~= 'up' and directionQueue[#directionQueue] ~= 'down' then table.insert (directionQueue, 'up') end end 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 do for j = 0, limit.y do local possible = true -- 是否與蛇身衝突 for index, pair in ipairs (snake.body) do if i == pair.x and j == pair.y then possible = false end end if possible then table.insert (foodPosition, { x = i, y = j }) end end end -- 生成隨機食物位置 local index = love.math.random (#foodPosition) food.x, food.y = foodPosition[index].x, foodPosition[index].y end function love.load () file = love.filesystem.newFile ('HighScore.txt') file:open ('r') highScore.score = file:read () file:close () -- 沒有透明度 colors.beiga[4] = 1 colors.paleTurquoise[4] = 1 CreateFood () end function 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 then love.graphics.setColor (colors.paleTurquoise) else love.graphics.setColor (colors.beiga) end -- 繪製蛇身,蛇頭另繪 for index, pair in ipairs (snake.body) do if index == 1 then love.graphics.rectangle ( 'fill', cellSize*pair.x, cellSize*pair.y, cellSize, cellSize ) end love.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 then love.graphics.print ('PAUSED !', cellSize*12, cellSize*11) end -- 設置字體和顏色並繪製提示信息 love.graphics.setFont (fonts.pixies10) love.graphics.setColor (colors.beiga) for i = 1, #notes do love.graphics.print (notes[i].text, notes[i].textX, notes[i].textY) end end function love.update (dt) -- 使用計時器 timer = timer + dt -- 當蛇生存時 if snake.alive then -- 根據蛇的速度更新遊戲 if timer > snake.speed then timer = timer - snake.speed -- 沒有暫停時 if not paused then -- 下一個蛇頭位置 local nextX = snake.body[1].x local nextY = snake.body[1].y -- 當方向隊列中的方向大於1時除去第一個方向(當前方向) if #directionQueue > 1 then table.remove (directionQueue, 1) end -- 根據方向做出改動 if directionQueue[1] == 'right' then nextX = nextX + 1 if nextX > limit.x then nextX = 0 end elseif directionQueue[1] == 'left' then nextX = nextX - 1 if nextX < 0 then nextX = limit.x end elseif directionQueue[1] == 'down' then nextY = nextY + 1 if nextY > limit.y then nextY = 0 end elseif directionQueue[1] == 'up' then nextY = nextY - 1 if nextY < 0 then nextY = limit.y end end -- 蛇是否能夠移動(沒有與自身相撞) local canMove = true for index, pair in ipairs (snake.body) do if index ~= #snake.body and nextX == pair.x and nextY == pair.y then canMove = false end end -- 當蛇能夠移動時 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 then sounds.eatFood:stop () end sounds.eatFood:play () -- 分數加一,並生成新的食物位置 currentScore.score = currentScore.score + 1 CreateFood () else -- 沒有吃到食物則刪去蛇身的尾部,達到移動的目的 table.remove (snake.body) end else -- 蛇死亡,並播放相撞的音效 snake.alive = false sounds.collided:play () end end end -- 等待一秒 elseif timer >= 1 then -- 存儲最高分 if currentScore.score > tonumber (highScore.score) then file:open ('w') file:write (tostring (currentScore.score)) file:close () end -- 切換到遊戲結束場景 SwitchScence ('GameOver') end end function love.keypressed (key) -- 回車鍵暫停遊戲 if key == 'return' then paused = not paused end -- 沒有暫停時 if not paused then -- 記錄方向鍵的按下順序,同方向或相反方向的不記錄 if key == 'right' and directionQueue[#directionQueue] ~= 'right' and directionQueue[#directionQueue] ~= 'left' then table.insert (directionQueue, 'right') elseif key == 'left' and directionQueue[#directionQueue] ~= 'left' and directionQueue[#directionQueue] ~= 'right' then table.insert (directionQueue, 'left') elseif key == 'down' and directionQueue[#directionQueue] ~= 'down' and directionQueue[#directionQueue] ~= 'up' then table.insert (directionQueue, 'down') elseif key == 'up' and directionQueue[#directionQueue] ~= 'up' and directionQueue[#directionQueue] ~= 'down' then table.insert (directionQueue, 'up') end end end
遊戲存檔目錄:
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] = 0 colors.paleTurquoise[4] = 0 end function 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 then colors.beiga[4] = colors.beiga[4] + 0.01 colors.paleTurquoise[4] = colors.paleTurquoise[4] + 0.01 end -- 設置字體,在指定位置畫出米色標題 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 do if i == options.selected then love.graphics.setColor (colors.paleTurquoise) else love.graphics.setColor (colors.beiga) end love.graphics.line (options[i].border) love.graphics.print (options[i].text, options[i].textX, options[i].textY) end end function love.keypressed (key) -- 上下箭頭選擇選項,回車按鍵確認選項 if key == 'left' then if sounds.gameOver.isPlaying then sounds.gameOver:stop () end if sounds.switchOption.isPlaying then sounds.switchOption:stop () end sounds.switchOption:play () options.selected = options.selected - 1 if options.selected <= 0 then options.selected = options.count end elseif key == 'right' then if sounds.gameOver.isPlaying then sounds.gameOver:stop () end if sounds.switchOption.isPlaying then sounds.switchOption:stop () end sounds.switchOption:play () options.selected = options.selected + 1 if options.selected > options.count then options.selected = 1 end elseif key == 'return' then if sounds.gameOver.isPlaying then sounds.gameOver:stop () end if options.selected == 1 then SwitchScence ('Menu') elseif options.selected == 2 then SwitchScence ('GameStart') end end end
項目結構圖以下
Love2D遊戲引擎製做貪吃蛇遊戲
注:本文著做權歸做者,由demo大師代發,拒絕轉載,轉載須要做者受權