1. 攻擊邏輯
在Entity中初始化兩個變量,因為在每個角色幾乎都擁有攻擊狀態。這兩個變量分別是transform類,接收一個坐標和一個半徑畫一個圓作為攻擊的判定范圍
public Transform attackCheck;
public float attackCheckRadius;
為了可視化攻擊范圍,我們使用輔助畫圖幫助我們對范圍進行設定
protected virtual void OnDrawGizmos()
{Gizmos.DrawWireSphere(attackCheck.position,attackCheckRadius);
}
對于敵人的處理也是如此,不要忘記將我們實體Transform傳入我們的變量。
之后將創建attack方法,作用于動畫中的某一幀,在這一幀中才是真正造成了攻擊,也就是應用到了某一函數方法。有經驗的讀者想必已經想到了事件功能。
創建一個新的腳本命名為PlayerAnimationTrigger,將動畫中某一幀與這個腳本中的某一個方法相結合就可以產生當動畫執行至某一幀后執行當前腳本事件的效果。
我們編寫腳本在動畫這一幀中檢查二維空間中所有與這個圓重疊的Collider2D的組件,并將這些組件存儲在colliders數組中。
private void AttackTrigger()
{Collider2D[] colliders = Physics2D.OverlapCircleAll(player.attackCheck.position,player.attackCheckRadius);foreach (var hit in colliders){if (hit.GetComponent<Enemy>() != null){hit.GetComponent<Enemy>().Damage();}}
}
-
Physics2D.OverlapCircleAll
:這是Unity Physics2D模塊中的一個靜態方法,用于檢測并返回與指定圓形區域相交的所有Collider2D組件。這個方法不依賴于物理引擎的碰撞檢測(即不依賴于速度或力的計算),而是直接基于碰撞體的幾何形狀和位置進行判斷。 -
注意:默認情況下,
Physics2D.OverlapCircleAll
會檢測所有層的碰撞體。如果你只想檢測特定層的碰撞體,可以使用Physics2D.OverlapCircleAll(Vector2 position, float radius, int layerMask)
的重載版本,其中layerMask
參數允許你指定要檢測的層
然后獲取敵人組件中Enemy腳本,若不為空,則執行其中的Damage方法。
2. 碰撞器檢測
在默認情況下,兩個碰撞體相互碰撞會造成位移,為了防止敵人與玩家之間造成位移,我們需要修改如下設定:點擊左上角Edit,選中Project Settings,找到Physics2D中的layer Collision Matrix,將對應兩個圖層勾選取消即可。
3. 被擊打效果
3.1 被擊打變色
我們需要創建一個新的材料作為被擊打時變換的樣子,將shader(著色器)搜索設定為GUI
被擊打變色的底層邏輯就是在某一事件發生后,將物體的材料改變,持續多久之后恢復原有狀態。
新建一個協程EntityFX
public class EntityFX
{private SpriteRenderer sr;[SerializeField] private Material hitMat;private Material originalMat;private void Start(){sr = GetComponentInChildren<SpriteRenderer>();originalMat = sr.material;}private IEumerator FlashFX(){sr.material = hitMat; //更改材料yield return new WaitForSeconds(.2f); //暫停協程,等待0.2ssr.material = originalMat; }
}
sr.material = hitMat;
:這行代碼將某個渲染器(假設為?sr
)的材質更改為?hitMat
。這通常用于顯示一個“被擊中”或“激活”的視覺效果。yield return new WaitForSeconds(.2f);
:這行代碼暫停協程的執行,等待 0.2 秒。WaitForSeconds
?是一個特殊的 yield 指令,用于在協程中創建等待時間。sr.material = originalMat;
:等待時間過后,將渲染器的材質改回原始材質?originalMat
,從而完成閃爍效果
將上述類定義好之后,我們還需要在角色父類中實例化此類才可進行調用。
對于我們的damage方法,我們添加:
Public virtual void Damage()
{fx.StartCoroutine("FlashFX");
}
其中StartCoroutine是一個協程函數,允許你在游戲運行時異步執行代碼,而不需要使用多線程的復雜性。協程(Coroutine)可以視為一個可以暫停和恢復的函數。
協程函數通常返回一個IEnumerator
類型,這是通過System.Collections
命名空間提供的。在協程函數內部,你可以使用yield return
語句來暫停協程的執行,并在未來的某個時間點恢復執行。
3.2 擊退狀態
在角色父類中定義擊退相關信息
[SerializeField] protected Vector2 knockbackDirection;
protected bool isKnocked;
[SerializeField] protected float knockbackDuration;
聲明擊退函數,被擊打了向后退一定距離。
protected virtual IEnumerator Hitknockback()
{isKnocked = true;rb.velocity = new Vector2(knockbackDirection.x * -facingDir,knockbackDirection.y);yield return new WaitForSeconds(knockbackDuration);isKnocked = false;
}//在前進函數中我們需要設定,如果被擊退則無法繼續前進
public void SetVelocity(float _xvelocity,float _yvelocity)
{if (isKnocked){return;}
}
在damage方法中加入這一事件:
public virtual void Damage()
{StartCoroutine("HitKnockback");
}
4. 防守與反擊效果
4.1 敵人被反擊效果
將相關動畫以及動畫參數,邏輯設定好之后,根據狀態機創建一個新的狀態,不要忘記在角色中聲明該狀態SkeltonStunnedState,然后聲明相應的構造函數以及重構。
與之前一樣,在主函數中聲明相應信息。stunDuration,stunDirection。
public class SkeltonStunnedState
{public override void Enter(){base.Enter();stateTimer = enemy.StunDuration;rb.velocity = new Vector2(-enemy.facingDir * enemy.stunDirection.x,enemy.stunDirection.y);}public override void Update(){base.Update();if (stateTimer < 0){statemachine.changeState(enemy.idleState);}}
}
讓我們會到EntityFX渲染類中,對攻擊效果進行進一步設定,有著閃爍的效果,相應的也要設定取消這個效果的方法。
private void RedcolorBlink()
{if (sr.color != Color.white){sr.color = Color.white;}else{sr.color = Color.red;}
}private void CancelRedBlink()
{CancelInvoke();sr.color = Color.white;
}
在進入狀態時聲明閃爍相應秒數:
public override void Enter()
{enemy.fx.InvokeRepeating("RedColorBlink",0,.1f); //分別是調用方法,延遲時間,持續時間
}public override void Exit()
{base.Exit();enemy.fx.Invoke("CancelRedBlink",0);
}
Invoke
函數是MonoBehaviour類中的一個非常有用的方法,它允許你在指定的延遲時間后自動調用另一個MonoBehaviour中的方法,而無需使用額外的線程或定時器。
4.2 防守反擊
將動畫的邏輯與相關參數設定好之后進行腳本的編輯
public class PlayerCounterAttackState:PlayerState
{//構造函數public override void Enter(){base.Enter();stateTimer = player.counterAttackDuration;player.anim.SetBool("SuccessfulCounterAttack",false); //反擊成功參數先設置為0}public override void Exit(){}public override void Update(){base.Update();player.ZeroVelocity(); //防守期間不能移動Collider2D[] colliders = Physics2D.OverlapCircleAll(player.attackCheck.position,player.attackCheckRadius);foreach (var hit in colliders) //如果防守期間檢測到攻擊{if (hit.GetComponent<Enemy>() != null){stateTimer = 10;player.anim.SetBool("SuccessfulCounterAttack",true);}}if (stateTimer < 0 || triggerCalled){stateMachine.ChangeState(player.idleState); //時間到了或者動畫結束,自動結束該狀態}}
}
后續對于玩家進行指定的事件操作即可。