cocos版本:2.4.4
Demo地址:跟踪导弹Demo
效果图:
实现原理:
需求是导弹飞到目的地
cocos坐标系中角度
通过Math.atan2可以获得导弹和目的地之间的角度
let missile:cc.Node; //导弹 let targetPos:cc.Vec2; //目的地 let radian = Math.atan2(targetPos.y - missile.y, targetPos.x - missile.x); //导弹和目的地之间弧度 let angle = radian*180/Math.PI; //弧度转换成角度
导弹每帧进行角度旋转和移动
let moveAngle = 2; //角速度(每帧角度变化) let moveRadian = moveAngle*Math.PI/180; //弧度(角速度转成弧度) let moveSpeed = 5; //移动速度 missile.angle += moveAngle; //角度每帧变化 missile.x += Math.cos(moveRadian)*moveSpeed; //x变化 missile.y += Math.sin(moveRadian)*moveSpeed; //y变化
导弹位置都到达目的地,那么导弹的追踪完成。
15是临近值,如果直接判断 导弹位置 = 目的地位置,那么导弹可能会在目的地附近不停的抖动。
//判断导弹和目的地的距离,如果小于一定值(这里取15),则判定到达目的地。 if(cc.Vec2.distance(missile.position, targetPosition) < 15){ //导弹到达目的地 }
跟踪导弹效果就是追着一个位置变化的物体,所以只要不停的改变目的地targetPos,再进行计算就行了。
注意点:
由于Math.atan2在边界值180和-180交接处,从-180瞬间变化到180,或者从180瞬间变化到-180,所以在计算时要处理这种情况,不然导弹会反复绕圈。
导弹的代码:
const { ccclass, property } = cc._decorator; @ccclass export default class Missile extends cc.Component { /**目标地点 */ public targetPos: cc.Vec2 = new cc.Vec2(); /**角速度 */ public angleSpeed: number = 5; /**移动速度 */ public moveSpeed: number = 5; /**移动限制距离,相距目的地距离>moveLimit时才会移动,防止抖动 */ private moveLimit: number = 20; /**每帧变化的角度 */ private stepAngle: number = null; /** * 设置目标点 * @param xPos x坐标 * @param yPos y坐标 */ public setTargetPos(xPos: number, yPos: number) { this.targetPos.x = xPos; this.targetPos.y = yPos; this.stepAngle = null; } /**重置 */ public reset() { this.stepAngle = null; } update() { //距离目的地一定距离才会移动,防止抖动 if (Math.sqrt(Math.pow(this.node.x - this.targetPos.x, 2) + Math.pow(this.node.y - this.targetPos.y, 2)) > this.moveLimit) { //导弹和目标点之间弧度、角度 let targetRadian = Math.atan2(this.targetPos.y - this.node.y, this.targetPos.x - this.node.x); let targetAngle = targetRadian * 180 / Math.PI; //导弹当前角度 let curAngle = this.node.angle; //导弹当前角度和导弹最终指向角度的差 let distAngle = targetAngle - curAngle; //导弹转动的角速度,根据角度差来判断顺时针或逆时针。( stepAngle=null时才会计算旋转角度,为了防止导弹出现在边界值附近左右摇摆的问题,一旦确定旋转角度方向,则不再改变) if (this.stepAngle == null) { this.stepAngle = distAngle > 0 ? this.angleSpeed : -this.angleSpeed; } //临界值判断 (Math.atan2会在边界值位置直接从180变成-180,或-180变成180,导致永远到不了目标角度) if (Math.abs((curAngle + 360) % 360 - (targetAngle + 360) % 360) > (Math.abs(this.stepAngle) + 1)) { //角度未达到临近值才会++,防止抖动 if (Math.abs(this.node.angle - targetAngle) > (Math.abs(this.stepAngle) + 1)) { this.node.angle += this.stepAngle; } } //移动 let nextRadian = this.node.angle * Math.PI / 180; this.node.x += Math.cos(nextRadian) * this.moveSpeed; this.node.y += Math.sin(nextRadian) * this.moveSpeed; } } }