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