最近學習unity2D,想實現一個回旋鏢武器,發出后就可以在角色周圍回旋。
一、目標
1.不是一次性的,扔出去、返回、沒有了;而是扔出去,返回到角色后方相同距離,再次返回;再次返回,永遠不停止。
2.銷毀的時機,是這個武器與角色再次重合的時候,相當于回收回旋鏢;如果不回收(例如跳躍躲開),那就一直轉。
3.角色的位置會移動,回旋鏢得多次判斷下一個目標點的位置。
4.回旋鏢的速度,需要從最大速度開始,到接近目標點的時候逐漸減速;返回時也要從最大速度開始,到接近目標點的時候減速。
總的來說,就是類似《忍者龍劍傳3》里,那個十字回旋鏢武器的效果,如果跳躍躲開,就會一直在身邊轉。
二、問題
實際寫代碼實現的時候,才發現有一堆坑。
1.回旋鏢的坐標,不能完全到達目標點,因為unity是按幀算的,每幀移動多少速度的話,可能會超過目標點,或者沒有到達目標點,有誤差。
2.如果只以接近目標點判斷的話,會丟失距離,每次回旋的距離逐漸變近,最后停留原地不動了。
3.如果角色坐標移動,回旋鏢計算目標點并重新移動時,經常速度變為0,卡在原地不動(不確定哪里出bug了)。
三、代碼部分
//這部分代碼,在武器代碼cs部分float weaponRange = 3;float weaponSpeed = 15;//當前已飛行的距離float distanceTravelled = 0f;//當前武器位置到目標點的總距離float distanceToTarget;//目標點在哪邊bool targetIsRight = false;[SerializeField] Transform weaponPos;Func<Vector2> weaponPosFunc;Vector2 targetPos;Vector2 nowSpeed;Animator animator;Rigidbody2D rb2d;SpriteRenderer sprite;CircleCollider2D cc2d;void Awake(){animator = GetComponent<Animator>();cc2d = GetComponent<CircleCollider2D>();rb2d = GetComponent<Rigidbody2D>();sprite = GetComponent<SpriteRenderer>();}//發射武器方法,其他類調用public void Shoot(bool isFaceingRight) {Vector2 newTarget;//其他類傳來的,一開始武器起點坐標Vector2 v = weaponPosFunc();//bool類型,武器圖片,如果角色面朝右邊就是true,就翻轉圖片,否則不翻轉sprite.flipX = isFaceingRight;//bool類型,武器的目標點方向,后面會用targetIsRight = isFaceingRight;//賦值,剛開始武器到目標點的距離distanceToTarget = Vector2.Distance(weaponPos.position, targetPos);//首次武器目標點,直線,看是角色面前還是背后//targetPos就是武器目標點if (!isFaceingRight) { newTarget = new Vector2(v.x - weaponRange, v.y);targetPos = newTarget;}else{newTarget = new Vector2(v.x + weaponRange, v.y);targetPos = newTarget;}}//更新方法,系統會每幀調用void Update(){// 向目標位置飛行,并逐步減速,到達目標點要掉頭,反復重復MoveTowardsTarget();}void MoveTowardsTarget(){//已經走過的距離/物體到目標的距離float dPercent = distanceTravelled / distanceToTarget;if(dPercent < 0){dPercent = -dPercent;}if(dPercent > 1){dPercent = 1;}// 計算當前的速度因子,使物體從快到慢;weaponSpeed是初始武器速度//Lerp方法,就是dPercent為0時,返回第一個參數;為1時返回第二個參數;0-1之間時就返回第一個參數到第二個參數之間的值float speedFactor = Mathf.Lerp(weaponSpeed, 0f, dPercent);//確定一個最慢速度,否則速度因子接近0,越來越慢,很難到目標if(speedFactor < 2f){speedFactor = 2f;}// 計算朝目標位置的方向,方向向量Vector2 direction = (targetPos - (Vector2)weaponPos.position).normalized;//如果方向向量變為0,就沒辦法移動了,此時要指定一個默認方向向量if(direction.x == 0 && direction.y == 0){//如果目標點在右邊,方向向量就是右邊;否則左邊if (targetIsRight){direction = new Vector2(1,0);}else{direction = new Vector2(-1, 0);}}//設置物體的速度,方向向量*速度因子//設置了這個,武器就會開始移動rb2d.velocity = direction*speedFactor;//保存當前速度,后續可能用nowSpeed = direction * speedFactor;// 更新已飛行的距離,剛開始是0;每一幀移動速度*每一幀時間=路程,然后累加distanceTravelled += rb2d.velocity.magnitude * Time.deltaTime;//打印日志,武器到目標點的距離//Debug.Log("distanceToTarget"+ distanceToTarget);// 如果 (超過目標點 或者 接近目標點) 并且 至少走夠了武器路程//需要分左右,才能知道是沒到目標點還是超過目標點,需要超過目標點;是否超過目標點就先用x判斷了;y又復雜了,還得分上下//防止沒到目標點就卡住,又加了接近目標點if (targetIsRight){if ( ((targetPos.x <= weaponPos.position.x) || distanceToTarget<=0.1) && distanceTravelled >= weaponRange){//重置目標點FreshTargetPos();}}else{if ( ((targetPos.x >= weaponPos.position.x) || distanceToTarget<=0.1) && distanceTravelled >= weaponRange){//重置目標點FreshTargetPos();}}}void FreshTargetPos() {//玩家當前坐標,實時獲取(玩家的武器發射點的坐標)Vector2 p = weaponPosFunc();//武器當前坐標Vector2 w = weaponPos.position;//w點關于p點的對稱點//targetPos = new Vector2( 2*p.x -w.x , 2*p.y -w.y );// 計算朝目標位置的方向,方向向量,擴大武器范圍倍數Vector2 direction = (p - (Vector2)w).normalized * weaponRange;//算出下一個目標點targetPos = new Vector2(direction.x+p.x, direction.y+p.y);//看目標點到底在哪里,然后判斷有沒有超過目標點if (targetPos.x > weaponPos.position.x){targetIsRight = true;}else{targetIsRight = false;}//已走過的路程歸零distanceTravelled = 0;// 重新計算物體到目標的距離,weaponPos是當前武器坐標,targetPos是目標點坐標;得到的是距離distanceToTarget = Vector2.Distance(weaponPos.position, targetPos);}
四、備注
unity代碼比較多,就貼重要部分了,關于坐標的計算;
目前代碼就是這個版本了,大概實現了需求;
武器當前速度處理還可能有些問題,但是可以在玩家周圍回旋了;
如果玩家靜止或者直線移動,武器就在x軸上回旋;
如果玩家跳躍,武器就會斜方向回旋到玩家背后;
感覺情況有些復雜,可能哪里還有bug,歡迎留言指正。
五、效果圖片
如圖,可以在角色周圍水平回旋,斜方向也可以。