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;
}
}
}
