经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Lua » 查看文章
[Lua][Love Engine] 打砖块游戏实现过程与知识点
来源:cnblogs  作者:小能日记  时间:2023/8/21 8:56:41  对本文有异议

本文旨在根据LOVE2D官方文档和教程实现打砖块的游戏,记录部分实现过程和重要知识点

  • 目标摧毁所有砖块
  • 玩家控制球拍左右滑动反弹小球
  • 小球摧毁砖块
  • 小球保持在屏幕内
  • 小球碰到屏幕底部,GAME OVER
image-20230821054659181

引擎配置

  1. -- conf.lua
  2. love.conf = function(t)
  3. t.console = true
  4. t.window.width = 800
  5. t.window.height = 600
  6. end

在加载引擎的时候回调该函数修改引擎基本参数,默认参数可看Config Files - LOVE (love2d.org)

物理世界

  1. -- world.lua
  2. local begin_contact_callback = function(fixture_a, fixture_b, contact)
  3. end
  4. local end_contact_callback = function(fixture_a, fixture_b, contact)
  5. end
  6. local pre_solve_callback = function(fixture_a, fixture_b, contact)
  7. end
  8. local post_solve_callback = function(fixture_a, fixture_b, contact)
  9. end
  10. local world = love.physics.newWorld(0, 0)
  11. world:setCallbacks(begin_contact_callback, end_contact_callback, pre_solve_callback, post_solve_callback)
  12. return world

建立了一个无重力的物理世界,为世界的物理碰撞绑定了四个回调函数,这四个回调函数依次的作用是

  • 两个物理实体开始接触
  • 接触未解除,每帧碰撞处理计算前
  • 接触未解除,每帧碰撞处理计算后
  • 两个物理实体结束接触

单一职责

按单一职责角度,main.lua 只负责创建游戏运行所必须的回调函数,而以下行为不属于这个职责范围

  • 加载和保存所有实体
  • 解决各类实体的绘制
  • 保存按键映射

举个例子:main.lualove.draw的回调函数的函数体负责了解释各类实体如何绘制的操作,如

  1. love.draw = function()
  2. local ball_x, ball_y = ball.body:getWorldCenter()
  3. love.graphics.circle('fill', ball_x, ball_y, ball.shape:getRadius())
  4. end

可以通过修改实体结构,让每个实体有对自身绘制行为的描述,将love.draw的一部分职责分担到各个实体上。如下述代码为ball实体添加了draw方法实现对自身绘图行为的描述。

  1. -- entities/ball.lua
  2. local world = require 'world'
  3. local entity = {}
  4. -- 设置body位置,形状将以该位置为中心,设置动态还是静态,即会不会受到其他物理实体的影响
  5. entity.body = love.physics.newBody(world, 200, 200, 'dynamic')
  6. entity.body:setMass(32) -- 设置质量kg
  7. entity.body:setLinearVelocity(300, 300) -- 右下角匀速加速度
  8. entity.shape = love.physics.newCircleShape(10) -- 创建一个圆形形状
  9. entity.fixture = love.physics.newFixture(entity.body, entity.shape) -- body和形状进行绑定
  10. entity.fixture:setRestitution(1) -- 设置弹性系数
  11. entity.fixture:setUserData(entity) -- 设置用户数据,用于在碰撞回调时获取用户自定义信息来判断操作
  12. -- 实体对自身绘图行为的描述
  13. function entity:draw()
  14. pos_x, pos_y = self.body:getWorldCenter() -- 获取body的位置(也是圆心的位置)
  15. love.graphics.circle('fill', pos_x, pos_y, self.shape:getRadius()) -- 绘制这个圆,还要从形状那获取半径
  16. end
  17. return entity

实体列表

考虑到实体创建和实体管理较难维护,可以用一个实体列表来进行统一管理。

修改示例 ball.lua

可以把每个实体脚本包裹进一个函数中,给定位置参数生成并返回这个实体,上述代码将修改为

  1. -- entities/ball.lua
  2. local world = require 'world'
  3. -- 导出一个函数,这个函数需要xy位置参数,返回一个对象
  4. return function(x, y)
  5. local entity = {}
  6. -- 设置body位置,形状将以该位置为中心,设置动态还是静态,即会不会受到物理系统的影响
  7. entity.body = love.physics.newBody(world, x, y, 'dynamic')
  8. entity.body:setMass(32) -- 设置质量kg
  9. entity.body:setLinearVelocity(300, 300) -- 右下角匀速加速度
  10. entity.shape = love.physics.newCircleShape(10) -- 创建一个圆形形状
  11. entity.fixture = love.physics.newFixture(entity.body, entity.shape) -- body和形状进行绑定
  12. entity.fixture:setRestitution(1) -- 设置弹性系数
  13. entity.fixture:setUserData(entity) -- 设置用户数据,用于在碰撞回调时获取用户自定义信息来判断操作
  14. -- 实体对自身绘图行为的描述
  15. function entity:draw()
  16. pos_x, pos_y = self.body:getWorldCenter() -- 获取body的位置(也是圆心的位置)
  17. love.graphics.circle('fill', pos_x, pos_y, self.shape:getRadius()) -- 绘制这个圆,还要从形状那获取半径
  18. end
  19. return entity -- 返回对象
  20. end

其他修改示例

再修改其他实体代码统一成上述形式

boundary-bottom.lua

  1. -- entities/boundary-bottom.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(800, 10)
  7. -- 形状将以body的位置为中心
  8. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  9. entity.fixture:setUserData(eneity)
  10. function entity:draw()
  11. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  12. end
  13. return entity
  14. end

boundary-vertical.lua

  1. -- entities/boundary-vertical.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(10, 600)
  7. -- 形状将以body的位置为中心
  8. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  9. entity.fixture:setUserData(eneity)
  10. function entity:draw()
  11. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  12. end
  13. return entity
  14. end

boundary-top.lua

  1. -- entities/boundary-top.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(800, 10)
  7. -- 形状将以body的位置为中心
  8. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  9. entity.fixture:setUserData(eneity)
  10. function entity:draw()
  11. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  12. end
  13. return entity
  14. end

brick.lua

  1. -- entities/boundary-top.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(50, 20)
  7. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  8. entity.fixture:setUserData(entity)
  9. function entity:draw()
  10. love.graphics.polygon('fill', self.body:getWorldPoints(self.shape:getPoints()))
  11. end
  12. return entity
  13. end

paddle.lua

  1. -- entities/boundary-paddle.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(180, 20) -- 生成一个长方体多边形
  7. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  8. entity.fixture:setUserData(entity)
  9. function entity:draw()
  10. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  11. end
  12. return entity
  13. end

创建实体列表

  1. -- main.lua
  2. local boundary_bottom = require('entities/boundary-bottom')
  3. local boundary_vertical = require('entities/boundary-vertical')
  4. local boundary_top = require('entities/boundary-top')
  5. local paddle = require 'entities.paddle'
  6. local ball = require 'entities.ball'
  7. local brick = require 'entities.brick'
  8. local world = require 'world'
  9. local entities = {boundary_bottom(400, 606), boundary_vertical(-6, 300), boundary_vertical(806, 300), boundary_top(400, -6),
  10. paddle(300, 500), ball(200, 200), brick(100, 100), brick(200, 100), brick(300, 100)}

直接调用实体创建函数创建各个实体,并放在同一个实体列表内,再修改love.draw遍历这个实体列表调用各个实体的draw行为,代码量大大减少

  1. love.draw = function()
  2. for _, entity in ipairs(entities) do
  3. entity:draw()
  4. end
  5. end

代码拆分 entities.lua

此时创建实体列表的相关代码还在main.lua内,将它独立成entities.lua

  1. -- entities.lua
  2. local boundary_bottom = require 'entities/boundary-bottom'
  3. local boundary_vertical = require 'entities/boundary-vertical'
  4. local boundary_top = require 'entities/boundary-top'
  5. local paddle = require 'entities.paddle'
  6. local ball = require 'entities.ball'
  7. local brick = require 'entities.brick'
  8. return {boundary_bottom(400, 606), boundary_vertical(-6, 300), boundary_vertical(806, 300), boundary_top(400, -6),
  9. paddle(300, 500), ball(200, 200), brick(100, 100), brick(200, 100), brick(300, 100)}

main.lua 只需导入 entities.lua 并使用即可

  1. -- main.lua
  2. local world = require 'world'
  3. local entities = require 'entities'
  4. love.draw = function()
  5. for _, entity in ipairs(entities) do
  6. entity:draw()
  7. end
  8. end
  9. love.update = function(dt)
  10. world:update(dt)
  11. end

输入处理

输入系统

专门新建一个文件input.lua用于输入处理

  1. -- input.lua
  2. -- 存放输入系统一切变量和操作的表
  3. local input = {}
  4. -- 各个按键对应回调函数的映射
  5. local press_functions = {}
  6. local release_functions = {}
  7. -- 初始值
  8. input.left = false
  9. input.right = false
  10. input.paused = false
  11. -- 根据key触发对应的映射函数
  12. input.press = function(key)
  13. if press_functions[key] then
  14. press_functions[key]()
  15. end
  16. end
  17. input.release = function(key)
  18. if release_functions[key] then
  19. release_functions[key]()
  20. end
  21. end
  22. -- 如果离开当前程序窗口则暂停
  23. input.focused = function(f)
  24. if not focused then
  25. input.paused = true
  26. end
  27. end
  28. press_functions.left = function()
  29. input.left = true
  30. end
  31. press_functions.right = function()
  32. input.right = true
  33. end
  34. press_functions.escape = function()
  35. love.event.quit()
  36. end
  37. press_functions.space = function()
  38. input.paused = not input.paused
  39. end
  40. release_functions.left = function()
  41. input.left = false
  42. end
  43. release_functions.right = function()
  44. input.right = false
  45. end
  46. return input

然后在main.lua导入input.lua

  1. -- main.lua
  2. local world = require 'world'
  3. local entities = require 'entities'
  4. local input = require 'input'
  5. love.draw = function()
  6. for _, entity in ipairs(entities) do
  7. entity:draw()
  8. end
  9. end
  10. love.update = function(dt)
  11. if not paused then
  12. world:update(dt)
  13. end
  14. end
  15. love.focus = input.focused
  16. love.keypressed = input.press
  17. love.keyreleased = input.release

更新实体位置

监测输入后需要根据输入系统的变量实时更新实体位置,修改love.update,查询各个实体的update方法,若有则执行

  1. love.update = function(dt)
  2. if not input.paused then
  3. for _, entity in ipairs(entities) do
  4. if entity.update then
  5. entity:update(dt)
  6. end
  7. end
  8. world:update(dt)
  9. end
  10. end

修改paddle.lua的代码

  1. -- entities/boundary-paddle.lua
  2. local world = require 'world'
  3. local input = require 'input'
  4. return function(x, y)
  5. local entity = {}
  6. entity.body = love.physics.newBody(world, x, y, 'static')
  7. entity.shape = love.physics.newRectangleShape(180, 20) -- 生成一个长方体多边形
  8. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  9. entity.fixture:setUserData(entity)
  10. function entity:draw()
  11. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  12. end
  13. function entity:update(dt)
  14. -- 两个按键都按了或都不按是不会动的
  15. if input.left and input.right or not input.left and not input.right then
  16. return
  17. end
  18. local self_x, self_y = self.body:getPosition()
  19. if input.left then
  20. -- 用时间增量去计算位置
  21. self.body:setPosition(self_x - 250 * dt, self_y)
  22. elseif input.right then
  23. self.body:setPosition(self_x + 250 * dt, self_y)
  24. end
  25. end
  26. return entity
  27. end

由于paddle在创建的时候是静态实体,是不受其他物理实体影响的,两边空气墙真如同空气,需要手动代码限制,修改如下

  1. if input.left then
  2. -- 用时间增量去计算位置
  3. local new_x = math.max(self_x - 400 * dt, 90)
  4. self.body:setPosition(new_x, self_y)
  5. elseif input.right then
  6. local new_x = math.min(self_x + 400 * dt, 710)
  7. self.body:setPosition(new_x, self_y)
  8. end

去除摩擦力

虽然小球实体的弹力系数设置为1,但是在碰撞过程中会越来越慢,这是默认有摩擦力的问题

  1. print(entity.fixture:getFriction())
  2. -- 0.20000000298023

摩擦力是 fixture 的属性,可以使用 fixture:setFriction 进行设置,修改ball.lua,在创建实体的过程中添加如下代码

  1. entity.fixture:setFriction(0) -- 小球受到的摩擦力为0

暂停显示文字

把文字当做一个实体 pause-text.lua ,当暂停时绘制这个实体

  1. -- entities/pause-text.lua
  2. local input = require('input')
  3. return function()
  4. -- https://love2d.org/wiki/love.window.getMode
  5. local window_width, window_height = love.window.getMode()
  6. local entity = {}
  7. entity.draw = function(self)
  8. if input.paused then
  9. -- https://love2d.org/wiki/love.graphics.print 用到了coloredtext表
  10. love.graphics.print({{0.2, 1, 0.2, 1}, 'PAUSED'}, math.floor(window_width / 2) - 54,
  11. math.floor(window_height / 2), 0, 2, 2)
  12. end
  13. end
  14. return entity
  15. end

entities.lua创建这个实体并添加至列表中。只有当游戏暂停时会执行打印函数

三大刚体

static

静态刚体,零质量,零速度,即不会受到重力或速度影响,但是可以设置他的位置来进行移动

  • 物理引擎并不认为这种刚体在移动
  • 适用于固定位置的对象,地面、墙壁、任何你不希望角色碰撞的游戏对象

dynamic

动态刚体,有质量,可以设置速度,会受到重力影响

  • 与三种刚体都有物理效果

kinematic

运动刚体,零质量,可以设置速度,不会受到重力的影响,但是可以设置速度来进行移动

  • 这种运动刚体完全由脚本控制

球拍改为运动刚体

将body类型修改为kinematic,删除原先的重新设置位置方式,修改为修改速度来达到移动效果

  1. -- entities/boundary-paddle.lua
  2. local world = require 'world'
  3. local input = require 'input'
  4. return function(x, y)
  5. local window_width = love.window.getMode()
  6. local entity_width = 120
  7. local entity_height = 20
  8. local entity_speed = 600
  9. -- 计算一次边界
  10. local left_boundary = entity_width / 2 + 2
  11. local right_boundary = window_width - entity_width / 2 - 2
  12. local entity = {}
  13. entity.body = love.physics.newBody(world, x, y, 'kinematic')
  14. entity.shape = love.physics.newRectangleShape(entity_width, entity_height)
  15. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  16. entity.fixture:setUserData(entity)
  17. function entity:draw()
  18. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  19. end
  20. function entity:update(dt)
  21. -- 两个按键都按是不会动的
  22. if input.left and input.right then
  23. return
  24. end
  25. local self_x = self.body:getPosition()
  26. if input.left and self_x > left_boundary then
  27. self.body:setLinearVelocity(-entity_speed, 0)
  28. elseif input.right and self_x < right_boundary then
  29. self.body:setLinearVelocity(entity_speed, 0)
  30. else
  31. self.body:setLinearVelocity(0, 0)
  32. end
  33. end
  34. return entity
  35. end

防止小球速度过快

  1. -- entities/ball.lua
  2. local world = require 'world'
  3. -- 导出一个函数,这个函数需要xy位置参数,返回一个对象
  4. return function(x, y)
  5. local entity_max_speed = 880
  6. local entity = {}
  7. -- 设置body位置,形状将以该位置为中心,设置动态还是静态,即会不会受到其他物理实体的影响
  8. entity.body = love.physics.newBody(world, x, y, 'dynamic')
  9. entity.body:setMass(32) -- 设置质量kg
  10. entity.body:setLinearVelocity(300, 300) -- 右下角匀速加速度
  11. entity.shape = love.physics.newCircleShape(10) -- 创建一个圆形形状
  12. entity.fixture = love.physics.newFixture(entity.body, entity.shape) -- body和形状进行绑定
  13. entity.fixture:setRestitution(1) -- 设置弹性系数
  14. entity.fixture:setUserData(entity) -- 设置用户数据,用于在碰撞回调时获取用户自定义信息来判断操作
  15. entity.fixture:setFriction(0) -- 小球受到的摩擦力为0
  16. -- 实体对自身绘图行为的描述
  17. function entity:draw()
  18. pos_x, pos_y = self.body:getWorldCenter() -- 获取body的位置(也是圆心的位置)
  19. love.graphics.circle('fill', pos_x, pos_y, self.shape:getRadius()) -- 绘制这个圆,还要从形状那获取半径
  20. end
  21. function entity:update()
  22. v_x, v_y = self.body:getLinearVelocity()
  23. local speed = math.abs(v_x) + math.abs(v_y)
  24. print(speed)
  25. -- 看看小球反弹之后的速度是否过快
  26. local vel_x_is_critical = math.abs(v_x) > entity_max_speed * 2
  27. local vel_y_is_critical = math.abs(v_y) > entity_max_speed * 2
  28. -- 如果反弹后某一方向移动速度过快则减慢速度
  29. if vel_x_is_critical or vel_y_is_critical then
  30. self.body:setLinearVelocity(v_x * .75, v_y * .75)
  31. end
  32. -- 如果整体速度过大,则设置阻尼
  33. if speed > entity_max_speed then
  34. self.body:setLinearDamping(0.1)
  35. else
  36. self.body:setLinearDamping(0)
  37. end
  38. end
  39. return entity -- 返回对象
  40. end

销毁砖块

需要做以下四件事情

  • 修改world.lua处理碰撞的函数
  • brick.lua实体添加碰撞后的处理函数
  • brick.lua实体添加一个属性用于表示生命值,如entity.health
  • main.lua 检查并删除生命值为0的实体

修改两个物体实体离开接触时的函数 end_contact_callback,检查两个物理实体各自是否有end_contact方法,如果有则执行

  1. local end_contact_callback = function(fixture_a, fixture_b, contact)
  2. local entity_a = fixture_a:getUserData()
  3. local entity_b = fixture_b:getUserData()
  4. if entity_a.end_contact then
  5. entity_a.end_contact()
  6. end
  7. if entity_b.end_contact then
  8. entity_b.end_contact()
  9. end
  10. end

修改brick.lua 添加生命值与end_contact方法,并修改draw行为,使其能在不同生命的时候显示不同颜色

  1. -- entities/boundary-top.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(50, 20)
  7. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  8. entity.fixture:setUserData(entity)
  9. entity.health = 2
  10. local step = 1 / entity.health
  11. function entity:draw()
  12. local r, g, b, a = love.graphics.getColor()
  13. love.graphics.setColor({1 - step * entity.health, step * entity.health, 0, 1})
  14. love.graphics.polygon('fill', self.body:getWorldPoints(self.shape:getPoints()))
  15. love.graphics.setColor(r, g, b, a)
  16. end
  17. function entity:end_contact()
  18. self.health = self.health - 1
  19. end
  20. return entity
  21. end

然后修改 main.lua 添加对各个实体生命值的检查

  1. love.update = function(dt)
  2. if not input.paused then
  3. -- 运行实体的update
  4. for i, entity in ipairs(entities) do
  5. if entity.update then
  6. entity:update(dt)
  7. end
  8. end
  9. -- 检测实体health
  10. local index = 1
  11. while index <= #entities do
  12. if entities[index].health == 0 then
  13. local entity = table.remove(entities, index)
  14. entity.fixture:destroy()
  15. else
  16. index = index + 1
  17. end
  18. end
  19. world:update(dt)
  20. end
  21. end

批量生成砖块

每生成一个砖块实体,需要调用一次brick,可以用函数批量生成,修改entities.lua

  1. -- entities.lua
  2. local boundary_bottom = require 'entities/boundary-bottom'
  3. local boundary_vertical = require 'entities/boundary-vertical'
  4. local boundary_top = require 'entities/boundary-top'
  5. local paddle = require 'entities.paddle'
  6. local ball = require 'entities.ball'
  7. local brick = require 'entities.brick'
  8. local pause_text = require 'entities.pause-text'
  9. local entities = {boundary_bottom(400, 606), boundary_vertical(-6, 300), boundary_vertical(806, 300),
  10. boundary_top(400, -6), paddle(300, 500), ball(200, 200), pause_text()}
  11. local row_width = love.window.getMode() - 20
  12. for number = 0, 38 do
  13. local brick_x = ((number * 60) % row_width) + 40
  14. local brick_y = (math.floor((number * 60) / row_width) * 40) + 80
  15. entities[#entities + 1] = brick(brick_x, brick_y)
  16. end
  17. return entities

状态管理

是否暂停、游戏胜利或失败这种在程序生命期间会动态变化的变量都称为状态。由于状态会越来越多,需要一种方式进行有效管理。需要做到以下几点

  • 很容易就能找到和访问状态。就像在main.lua导入了entities实体列表一样,轻松可得
  • 状态数据只能有一份。如只有一个paused变量,而不是每个文件里都有paused
  • 状态数据只有有明确需求时才能访问。在ball.lua获取paused是无意义的

现在有哪些状态

  • entities.lua
    • 导出的实体列表里的每个实体对各自的状态负责,如每块砖都存储着自己的健康情况
  • input.lua
    • 当前暂停状态,左右按键的状态
  • world.lua
    • 导出的world负责整个物理空间的状态

实现状态管理,新建一个state.lua文件,用于存储游戏的大部分状态,比如将input.lua中的状态迁移到此文件,使input.lua专心于捕获和映射用户输入。world.luaentities.lua没有迁移的必要,避免代码过于臃肿

  1. -- state.lua
  2. return {
  3. left = false,
  4. right = false,
  5. game_over = false,
  6. palette = {{1.0, 0.0, 0.0, 1.0}, -- red
  7. {0.0, 1.0, 0.0, 1.0}, -- green
  8. {0.4, 0.4, 1.0, 1.0}, -- blue
  9. {0.9, 1.0, 0.2, 1.0}, -- yellow
  10. {1.0, 1.0, 1.0, 1.0} -- white
  11. },
  12. paused = false,
  13. stage_cleared = false
  14. }

再修改 input.lua

  1. -- input.lua
  2. local state = require 'state'
  3. -- 各个按键对应回调函数的映射
  4. local press_functions = {}
  5. local release_functions = {}
  6. press_functions.left = function()
  7. state.left = true
  8. end
  9. press_functions.right = function()
  10. state.right = true
  11. end
  12. press_functions.escape = function()
  13. love.event.quit()
  14. end
  15. press_functions.space = function()
  16. state.paused = not state.paused
  17. end
  18. release_functions.left = function()
  19. state.left = false
  20. end
  21. release_functions.right = function()
  22. state.right = false
  23. end
  24. return {
  25. press = function(key)
  26. print(key)
  27. if press_functions[key] then
  28. press_functions[key]()
  29. end
  30. end,
  31. release = function(key)
  32. if release_functions[key] then
  33. release_functions[key]()
  34. end
  35. end,
  36. focused = function(f)
  37. if not f then
  38. state.paused = true
  39. end
  40. end
  41. }

修改main.lua

  1. love.update = function(dt)
  2. if state.game_over or state.paused or state.stage_cleared then
  3. return
  4. end
  5. -- 运行实体的update
  6. for i, entity in ipairs(entities) do
  7. if entity.update then
  8. entity:update(dt)
  9. end
  10. end
  11. -- 检测实体health
  12. local index = 1
  13. while index <= #entities do
  14. if entities[index].health == 0 then
  15. local entity = table.remove(entities, index)
  16. entity.fixture:destroy()
  17. else
  18. index = index + 1
  19. end
  20. end
  21. world:update(dt)
  22. end

修改pause-text.lua

  1. -- entities/pause-text.lua
  2. local input = require 'input'
  3. local state = require 'state'
  4. return function()
  5. -- https://love2d.org/wiki/love.window.getMode
  6. local window_width, window_height = love.window.getMode()
  7. local entity = {}
  8. entity.draw = function(self)
  9. if state.paused then
  10. -- https://love2d.org/wiki/love.graphics.print 用到了coloredtext表
  11. love.graphics.print({{0.2, 1, 0.2, 1}, 'PAUSED'}, math.floor(window_width / 2) - 54,
  12. math.floor(window_height / 2), 0, 2, 2)
  13. end
  14. end
  15. return entity
  16. end

使用调色板为砖块上色

  1. love.graphics.setColor(state.palette[entity.health + 1])
  2. love.graphics.polygon('fill', self.body:getWorldPoints(self.shape:getPoints()))
  3. love.graphics.setColor(state.palette[5])

胜利与失败

复制pause-text.luagame-over-text.lua,修改判断条件为 state.game_over

  1. -- entities/game-over-text.lua
  2. local input = require 'input'
  3. local state = require 'state'
  4. return function()
  5. -- https://love2d.org/wiki/love.window.getMode
  6. local window_width, window_height = love.window.getMode()
  7. local entity = {}
  8. entity.draw = function(self)
  9. if state.game_over then
  10. -- https://love2d.org/wiki/love.graphics.print 用到了coloredtext表
  11. love.graphics.print({{0.2, 1, 0.2, 1}, 'GAME OVER'}, math.floor(window_width / 2) - 54,
  12. math.floor(window_height / 2), 0, 2, 2)
  13. end
  14. end
  15. return entity
  16. end

复制pause-text.luastage-clear-text.lua,修改判断条件为 state.stage_cleared

  1. -- entities/boundary-bottom.lua
  2. local world = require 'world'
  3. return function(x, y)
  4. local entity = {}
  5. entity.body = love.physics.newBody(world, x, y, 'static')
  6. entity.shape = love.physics.newRectangleShape(800, 10)
  7. -- 形状将以body的位置为中心
  8. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  9. entity.fixture:setUserData(entity)
  10. function entity:draw()
  11. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  12. end
  13. return entity
  14. end

将新增的两个实体添加至实体列表

修改boundary-bottom.lua,添加end_contact事件处理函数修改全局状态“是否失败”

  1. -- entities/boundary-bottom.lua
  2. local world = require 'world'
  3. local state = require 'state'
  4. return function(x, y)
  5. local entity = {}
  6. entity.body = love.physics.newBody(world, x, y, 'static')
  7. entity.shape = love.physics.newRectangleShape(800, 10)
  8. -- 形状将以body的位置为中心
  9. entity.fixture = love.physics.newFixture(entity.body, entity.shape)
  10. entity.fixture:setUserData(entity)
  11. function entity:draw()
  12. love.graphics.polygon('line', self.body:getWorldPoints(self.shape:getPoints()))
  13. end
  14. function entity:end_contact()
  15. state.game_over = true
  16. end
  17. return entity
  18. end

修改brick.lua 添加 entity.type = "brick",然后修改main.lua判断当前帧是否还有砖块

  1. love.update = function(dt)
  2. if state.game_over or state.paused or state.stage_cleared then
  3. return
  4. end
  5. -- 运行实体的update
  6. local isBrick = false
  7. for i, entity in ipairs(entities) do
  8. if entity.type == "brick" then
  9. isBrick = true
  10. end
  11. if entity.update then
  12. entity:update(dt)
  13. end
  14. end
  15. -- 检测实体health
  16. local index = 1
  17. while index <= #entities do
  18. if entities[index].health == 0 then
  19. local entity = table.remove(entities, index)
  20. entity.fixture:destroy()
  21. else
  22. index = index + 1
  23. end
  24. end
  25. world:update(dt)
  26. if not isBrick then
  27. state.stage_cleared = true
  28. end
  29. end
image-20230821054718520

原文链接:https://www.cnblogs.com/linxiaoxu/p/17645027.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号