概述
台球游戏的核心是2D物理模拟:碰撞检测、反弹计算、摩擦力。App Inventor的Canvas + ImageSprite可以实现。
已有文档:canvas_game.md 介绍了Canvas游戏基础。本文聚焦台球物理。
核心物理概念
| 概念 | 说明 | 实现方式 |
|---|---|---|
| 碰撞检测 | 球与球/球与边界 | 距离计算 |
| 弹性碰撞 | 球碰撞后速度交换 | 向量计算 |
| 反弹 | 球碰到边界反弹 | 速度分量取反 |
| 摩擦力 | 球逐渐减速 | 速度衰减 |
| 入袋检测 | 球进入袋口 | 距离判断 |
界面设计
Screen_Pool
├── Canvas_球桌(球台画布)
│ ├── ImageSprite_白球
│ ├── ImageSprite_球1 ~ 球15
│ └── 球杆线(Canvas画线)
├── 按钮_击球
├── 标签_分数
├── 标签_状态
├── 滑块_力度(控制击球力量)
├── Clock_物理引擎(驱动物理模拟)
└── Clock_绘图(绘制辅助线)
球桌布局
摩擦系数
设 球台宽度 = Canvas_球桌.宽度
设 球台高度 = Canvas_球桌.高度
设 球半径 = 15
设 摩擦系数 = 0.995 ' 每帧速度保留99.5%
设 袋口半径 = 25
设 袋口位置 = [
[0, 0], ' 左上
[球台宽度/2, 0], ' 中上
[球台宽度, 0], ' 右上
[0, 球台高度], ' 左下
[球台宽度/2, 球台高度], ' 中下
[球台宽度, 球台高度] ' 右下
]
物理引擎
球的数据结构
设 球列表 = 创建空列表
定义 创建球(x, y, 颜色, 编号) 返回 球
返回 创建字典(
"x" → x, "y" → y,
"vx" → 0, "vy" → 0, ' 速度
"radius" → 球半径,
"color" → 颜色,
"number" → 编号,
"active" → true
)
物理更新(每帧)
当 Clock_物理.计时
设 i = 1
当 i ≤ 列表长度(球列表)
设 球 = 列表第i项(球列表)
如果 从字典 球 获取 "active"
' 更新位置
设 x = (从字典 球 获取 "x") + (从字典 球 获取 "vx")
设 y = (从字典 球 获取 "y") + (从字典 球 获取 "vy")
' 摩擦力(速度衰减)
设 vx = (从字典 球 获取 "vx") * 摩擦系数
设 vy = (从字典 球 获取 "vy") * 摩擦系数
' 速度过小则停止
如果 数学.绝对值(vx) < 0.05 且 数学.绝对值(vy) < 0.05
设 vx = 0
设 vy = 0
' 边界反弹
如果 x - 球半径 < 0
设 x = 球半径
设 vx = -vx * 0.8 ' 反弹衰减
否则 如果 x + 球半径 > 球台宽度
设 x = 球台宽度 - 球半径
设 vx = -vx * 0.8
如果 y - 球半径 < 0
设 y = 球半径
设 vy = -vy * 0.8
否则 如果 y + 球半径 > 球台高度
设 y = 球台高度 - 球半径
设 vy = -vy * 0.8
' 更新字典
设 球 = 创建字典("x"→x, "y"→y, "vx"→vx, "vy"→vy, "radius"→球半径, "color"→从字典球获取"color", "number"→从字典球获取"number", "active"→true)
设 列表第i项(球列表) = 球
' 入袋检测
调用 检查入袋(球, i)
设 i = i + 1
' 碰撞检测
调用 碰撞检测()
' 重绘
调用 绘制球桌()
球与球碰撞
定义 碰撞检测()
设 i = 1
当 i ≤ 列表长度(球列表)
如果 从字典 列表第i项(球列表) 获取 "active"
设 j = i + 1
当 j ≤ 列表长度(球列表)
如果 从字典 列表第j项(球列表) 获取 "active"
调用 检查两球碰撞(i, j)
设 j = j + 1
设 i = i + 1
定义 检查两球碰撞(idx1, idx2)
设 球1 = 列表第idx1项(球列表)
设 球2 = 列表第idx2项(球列表)
设 dx = (从字典 球2 获取 "x") - (从字典 球1 获取 "x")
设 dy = (从字典 球2 获取 "y") - (从字典 球1 获取 "y")
设 距离 = 数学.平方根(dx*dx + dy*dy)
设 最小距离 = 2 * 球半径
如果 距离 < 最小距离 且 距离 > 0
' 碰撞法向量
设 nx = dx / 距离
设 ny = dy / 距离
' 相对速度
设 dvx = (从字典 球1 获取 "vx") - (从字典 球2 获取 "vx")
设 dvy = (从字典 球1 获取 "vy") - (从字典 球2 获取 "vy")
' 法向速度分量
设 dvn = dvx * nx + dvy * ny
如果 dvn > 0 ' 只处理正在接近的球
' 等质量弹性碰撞:交换法向速度分量
设 v1x = (从字典 球1 获取 "vx") - dvn * nx
设 v1y = (从字典 球1 获取 "vy") - dvn * ny
设 v2x = (从字典 球2 获取 "vx") + dvn * nx
设 v2y = (从字典 球2 获取 "vy") + dvn * ny
' 更新速度
设 新球1 = 设字典值(球1, "vx", v1x * 0.95)
设 新球1 = 设字典值(新球1, "vy", v1y * 0.95)
设 新球2 = 设字典值(球2, "vx", v2x * 0.95)
设 新球2 = 设字典值(新球2, "vy", v2y * 0.95)
设 列表第idx1项(球列表) = 新球1
设 列表第idx2项(球列表) = 新球2
' 分离重叠的球
设 重叠 = 最小距离 - 距离
设 新球1 = 设字典值(新球1, "x", (从字典新球1获取"x") - 重叠/2*nx)
设 新球1 = 设字典值(新球1, "y", (从字典新球1获取"y") - 重叠/2*ny)
设 新球2 = 设字典值(新球2, "x", (从字典新球2获取"x") + 重叠/2*nx)
设 新球2 = 设字典值(新球2, "y", (从字典新球2获取"y") + 重叠/2*ny)
入袋检测
定义 检查入袋(球, 索引)
设 bx = 从字典 球 获取 "x"
设 by = 从字典 球 获取 "y"
设 i = 1
当 i ≤ 列表长度(袋口位置)
设 袋口 = 列表第i项(袋口位置)
设 px = 从字典 袋口 获取 "x"
设 py = 从字典 袋口 获取 "y"
设 距离 = 数学.平方根((bx-px)^2 + (by-py)^2)
如果 距离 < 袋口半径
' 入袋!
设 球 = 设字典值(球, "active", false)
设 列表第索引项(球列表) = 球
设 分数 = 分数 + 10
标签_分数.文本 = "分数:" & 分数
如果 从字典 球 获取 "number" = 0
标签_状态.文本 = "白球入袋!犯规"
设 i = 列表长度(袋口位置) + 1 ' 跳出
设 i = i + 1
击球控制
拖动瞄准
当 Canvas_球桌.拖动(被拖动, 起X, 起Y, 当X, 当Y, 上X, 上Y)
如果 被拖动 且 等待击球
' 从白球到拖动位置画瞄准线
设 白球 = 列表第1项(球列表) ' 假设第1个是白球
设 bx = 从字典 白球 获取 "x"
设 by = 从字典 白球 获取 "y"
调用 Canvas_球桌.画线(bx, by, 当X, 当Y)
当 Canvas_球桌.触摸释放(上X, 上Y)
设 白球 = 列表第1项(球列表)
设 bx = 从字典 白球 获取 "x"
设 by = 从字典 白球 获取 "y"
设 dx = bx - 上X ' 反方向
设 dy = by - 上Y
设 距离 = 数学.平方根(dx*dx + dy*dy)
设 力度 = 滑块_力度.当前位置 / 100 * 20 ' 最大速度20
如果 距离 > 0
设 vx = dx / 距离 * 力度
设 vy = dy / 距离 * 力度
设 新白球 = 设字典值(白球, "vx", vx)
设 新白球 = 设字典值(新白球, "vy", vy)
设 列表第1项(球列表) = 新白球
绘制
定义 绘制球桌()
调用 Canvas_球桌.清除()
' 绘制袋口
设 i = 1
当 i ≤ 列表长度(袋口位置)
设 p = 列表第i项(袋口位置)
调用 Canvas_球桌.画圆(从字典p获取"x", 从字典p获取"y", 袋口半径, 黑色)
设 i = i + 1
' 绘制球
设 i = 1
当 i ≤ 列表长度(球列表)
设 球 = 列表第i项(球列表)
如果 从字典 球 获取 "active"
调用 Canvas_球桌.画圆(
从字典球获取"x", 从字典球获取"y",
球半径, 从字典球获取"color")
' 显示球号
调用 Canvas_球桌.画文字(
从字典球获取"x"-5, 从字典球获取"y"+5,
从字典球获取"number", 白色)
设 i = i + 1
常见问题
Q1: 碰撞后球会卡住?
确保碰撞后有分离步骤(将重叠的球推开)。
Q2: 性能卡顿?
- 减少Clock频率(16ms → 33ms)
- 球数量多时碰撞检测慢(O(n²)),可优化空间分区
Q3: 球运动不自然?
调整摩擦系数和反弹衰减系数,多测试找到合适参数。
总结
| 核心技术 | 关键 |
|---|---|
| 碰撞检测 | 距离 < 两球半径之和 |
| 弹性碰撞 | 交换法向速度分量 |
| 摩擦力 | 每帧速度乘以衰减系数 |
| 入袋 | 球心到袋口距离 < 袋口半径 |
| 击球 | 反方向拖动,力度控制速度 |
版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。
扫码添加客服咨询