Angry Birds
物理世界
love.physics.newWorld(0, 300)- 世界是唯一推进物理的容器
- 重力单位是像素每二次方秒
- 默认 30 像素 = 1 米
300由9.8 × 30近似而来- 零重力做俯视或太空玩法
world:update(dt)驱动整个模拟- 固定步长
1/60加累加器 - 可变
dt过大会穿墙 velocityIterations默认 8positionIterations默认 3body:setBullet(true)做连续检测world:getGravity()供轨迹预览- 每关建一个新世界
- 渲染与物理世界分离
刚体
love.physics.newBody(world, x, y, type)- body 只存位置、角度与速度
- 原点在质心,不在左上角
static固定,不受重力dynamic受重力与碰撞kinematic主动移动,不受外力- Pong 的球拍适合
kinematic body:getPosition()返回两个值body:getAngle()返回弧度setLinearVelocity直接改速度applyLinearImpulse施加瞬时冲量setLinearDamping与setAngularDampingsetFixedRotation(true)锁定旋转getMass()由 fixture 密度决定body:destroy()与isDestroyed()- 一个 body 可挂多个 fixture
Shape 与 Fixture
- body 没有形状,fixture 才有
love.physics.newFixture(body, shape)newRectangleShape(w, h)原点居中newCircleShape(r)参数是半径newEdgeShape(x1, y1, x2, y2)做地面newPolygonShape(vertices)要求凸多边形- 多边形顶点上限 8 个
setRestitution(0.4)控制弹性setFriction(0.5)控制滑动setDensity决定质量setSensor(true)只触发回调setUserData('Player')打类型标签fixture:getBody()在回调里反查body:getWorldPoints(shape:getPoints())love.graphics.polygon画碰撞盒- 精灵用 body 位置与角度绘制
- 同一 shape 可挂多个 fixture
拖拽与发射
love.mousepressed与love.mousereleasedlove.mouse.isDown(1)检测按住- 课程自建
love.mouse.wasPressed(1) - 事件表在每帧末清空
push.toGame把窗口坐标转虚拟坐标baseX与baseY是弹弓锚点shiftedX与shiftedY是拖动位置- 拖动范围钳制在 ±30 像素
- 松手之前不创建 body
- 初速度 = 锚点减拖动位置乘 10
- 拖动方向与飞行方向相反
setRestitution让鸟弹跳setAngularDamping(1)抑制旋转aiming与launched两个布尔量- 轨迹预测 90 帧,每 5 帧画点
- 轨迹 Y 轴叠加重力项
碰撞回调
world:setCallbacks(begin, end, pre, post)- 回调参数是 fixture 与 contact
beginContact刚开始接触endContact接触结束preSolve求解前,可禁用接触postSolve拿到法向与切向冲量fixture:getUserData()判断类型- 两个 fixture 的顺序不固定
- 只用
beginContact,其余留空 - 回调内禁止增删 body
- 世界在回调期间被锁定
- 延迟销毁表在
update后清空 isDestroyed()防止重复销毁- 速度阈值决定是否破坏
- 数组用倒序
table.remove contact:isTouching()查询状态
相机与坐标
- 物理坐标与屏幕坐标分离
love.graphics.translate只改绘制- 平移量
levelTranslateX - 相机范围钳制在
±VIRTUAL_WIDTH math.floor避免画面发虚- 每帧开始变换自动重置
- 背景单独绘制并做视差
- 背景速度是地图的一半
xOffset % width实现无限平铺- 鼠标坐标要减去相机偏移
love.graphics.scale做缩放- hump
camera提供attach与detach - 跟随用插值逼近目标
- 世界宽度决定相机边界
关卡与计分
Level持有 world 与实体表aliens与obstacles两个列表- 地面用
newEdgeShape拉长 - 障碍按中心坐标摆放
- 玻璃 1 击、木头 2 击、金属 3 击
- 命中次数存在实体字段
- 每次命中换裂纹贴图
- 血量归零才进销毁队列
- 清空敌人即胜利
- 障碍砸中敌人也算消灭
- 剩余鸟数可作失败条件
- 鸟停下或飞出左边界后重置
- 状态机只有
start与play - 重开关卡要重建 world
- 分数来自摧毁物与剩余鸟
- 累加器放在
Level:update - 关卡数据写成表便于扩展
关节
- joint 连接两个同世界的 body
- 锚点使用世界坐标
collideConnected决定两端是否碰撞newWeldJoint刚性连接newRevoluteJoint铰链旋转newRopeJoint限制最大距离newDistanceJoint固定距离newPulleyJoint滑轮联动newPrismaticJoint单轴滑动newMouseJoint把 body 拖向鼠标joint:destroy()解除约束- 铰链可配马达与限位
- 关节把力传给两端
- 可搭吊桥、摆锤与杠杆
容易混淆的问题
wasPressed是内建还是自己写的- 重力该传
9.8还是300 - body 原点是左上角还是中心
beginContact里能不能destroystatic与kinematic差在哪- 相机偏移要不要加到 body 上
- 速度阈值 20 的单位是什么
- fixture 与 body 谁持有形状
- 拖得越远是否一定飞得越远
world:update与love.update的关系