需求
在弹弓类、投射类游戏中,抛物线的绘制是十分常用的功能需求。
该需求的实现也较为简单,但未实现过的同学或者刚入门3d游戏开发的同学,可能没有确切的实现方法,羽毛就本文给大家浅谈抛物线的实现及实现过程的避坑。
效果
(引用Marbo Toss 游戏截图)
问题分析
由高中物理知识可得,抛物线运动可分解为:
- 垂直方向y的加速度运动。加速度f方向为垂直向下,大小为g,在游戏中,g可随意指定。
- Vy =Vy0 + g * t ,Sy =Vy0 * t + 1/2 * g* t * t
- 水平平面存在x,z两个方向的匀速运动
- Vx = Vx0, Sx = Vx0 * t
- Vz = Vz0, Sz = Vz0 * t
因此,我们只需要从t=0时,即发射点开始,在相同时间间隔内绘制轨迹点,当这些离散的点连接起来时,便形成了抛物线。
实现
在程序实现时,可分为一下几个步骤:
- 预生成一个包含N个轨迹点Node对象的数组ballCurvelist,并将所有的Node添加为发射点的child节点,active设置为false。
- 需要显示抛物线时,更新所有node的active为true,并根据抛物线运动公式计算第N个点间隔dt的x,y,z坐标值,刷新position。
核心代码
- 初始化
cc.loader.loadRes(url, (err, prefab) => {
if (err == null) {
let ballItem = cc.instantiate(prefab);
for (let count = 0; count < curvePointNum; count++) {
let ball: Node = cc.instantiate(ballItem);
ball.active = false;
this.node.addChild(ball);
this.ballCurveList.push(ball);
}
} else {
console.log("加载错误", url);
}
});
复制代码
- 描绘抛物线
public showCurve(startPos: Vec3, startSpeed: Vec3) {
var dx = startPos.x;
var dy = startPos.y;
var dz = startPos.z;
const dt = this.deltaTime;
for (var count = 0; count < this.ballCurveList.length; count++) {
//每个点的坐标都是上一个点坐标加上位移值
dx += startSpeed.x * dt;
dz += startSpeed.z * dt;
dy += startSpeed.y * dt + 0.5 * this.gravityY * dt * dt;
let ball = this.ballCurveList[count];
ball.active = true;
ball.setWorldPosition(dx, dy, dz);
}
}
复制代码
注意的点
为了使小球运动轨迹与轨迹线一直,通过变换速度信息控制小球的运动。update中每帧使用与画轨迹球相同的dt计算小球的实时位置。
private curPos: Vec3 = null;
update(dt: number) {
dt = this.deltaTime;
//点击发射,并且未碰撞目标体的情况下一直保持运动
if (this.isFire && !this.isCollider) {
if (this.curPos == null) {
this.curPos = this.node.getWorldPosition().clone();
}
this.curPos.x += dt * this.dirSpeed.x;
this.curPos.y += dt * this.dirSpeed.y + this.gravityY * dt * dt / 2;
this.curPos.z += dt * this.dirSpeed.z;
this.node.setWorldPosition(this.curPos);
}
}
复制代码
小结
抛物线绘制的主要要点是对抛物线运动过程的理解,以及抛物线绘制结果与被抛物体运动轨迹统一的问题。只要想清楚这两点,实现起来就是分分钟的事情拉。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END