Unity開發2D類銀河惡魔城游戲學習筆記
Unity開發2D類銀河惡魔城游戲學習筆記目錄
技能系統
Unity教程(二十一)技能系統 基礎部分
Unity教程(二十二)技能系統 分身技能
Unity教程(二十三)技能系統 擲劍技能(上)基礎實現
Unity教程(二十四)技能系統 擲劍技能(中)技能變種實現
Unity教程(二十五)技能系統 擲劍技能(下)凍結時間實現
如果你更習慣用知乎
Unity開發2D類銀河惡魔城游戲學習筆記目錄
文章目錄
- Unity開發2D類銀河惡魔城游戲學習筆記
- 技能系統
- 前言
- 一、概述
- 二、類型設置
- 三、彈射類型的實現
- (1)創建參數
- (2)彈射的實現
- (3)彈射傷害的實現
- (3)問題改進
- 三、穿刺類型的實現
- (1)創建參數
- (2)穿刺的實現
- (3)穿刺傷害的實現
- (4)問題改進
- 四、盤旋類型的實現
- (1)創建參數
- (2)盤旋的實現
- (3)盤旋傷害的實現
- 總結 完整代碼
- SwordSkill.cs
- Sword_Skill_Controller.cs
前言
注意:Udemy上更新了課程,原來的版本刪掉了。新版教程與舊版順序相差較大,并且改用Unity6。但這個筆記已經寫到一半了,所以還是按照舊版來。
本文為Udemy課程The Ultimate Guide to Creating an RPG Game in Unity學習筆記,如有錯誤,歡迎指正。
本節實現角色投劍技能的變種。
Udemy課程地址
對應視頻:
Bouncy sword
Setting sword type
Pierce sword
Saw spin sword
一、概述
本節實現投劍技能的幾種變種類型,包括常規、彈射、穿刺和盤旋,并實現對敵人的傷害。
二、類型設置
在Sword_Skill腳本中創建枚舉類型SwordType表示四種投擲類型。
添加變量Sword_Skill類中添加投劍類型變量,默認為常規。
public enum SwordType
{Regular,Bounce,Pierce,Spin
}
public class Sword_Skill : Skill
{public SwordType swordType = SwordType.Regular;
}
三、彈射類型的實現
彈射要實現的效果是在擊中一個敵人后,在附近敵人身上彈射攻擊幾次,彈射次數歸零后返回。
因此,實現時要在擊中某個敵人后,檢測以劍為中心一定半徑范圍內的敵人并記錄。依據列表讓劍在敵人之間移動,同時設置好動畫,擊中時取消嵌入,彈射次數歸零后返回。
(1)創建參數
首先在Sword_Skill中添加彈射類型需要更改的變量,包括彈射的重力、次數、速度。
在創建劍時判斷劍的類型,給劍的重力賦值,將彈跳相關參數傳入控制器。這部分要寫在設置Sword的參數之前,因為要先給劍的重力賦值再通過SetupSword傳入。
[Header("Bounce Info")][SerializeField] private float bounceGravity;[SerializeField] private int bounceAmount;[SerializeField] private float bounceSpeed;public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();if(swordType == SwordType.Bounce){swordGravity = bounceGravity;newSwordScript.SetupBounce(true, bounceAmount, bounceSpeed);}newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}
在Sword_Skill_Controller中添加彈射所需參數
添加SetupBounce函數設置參數
[Header("Bounce Info")]private bool isBouncing;private int amountOfBounce;private float bounceSpeed;public void SetupBounce(bool _isBouncing, int _amountOfBounces , float _bounceSpeed){isBouncing = _isBouncing;amountOfBounce = _amountOfBounces;bounceSpeed = _bounceSpeed;}
(2)彈射的實現
在彈射類型時讓劍一直保持旋轉動畫的狀態,并且不掛載到角色下隨角色移動。
private void StuckInto(Collider2D collision){canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;if(isBouncing)return;anim.SetBool("Rotation", false);transform.parent = collision.transform;}
在Sword_Skill_Controller中添加列表存儲范圍內敵人,添加當前索引變量。設置彈射參數時將列表初始化。
在玩家擊中第一個敵人時,獲取一定范圍內的敵人,并將它們存在列表中。
將這部分提取出來,重命名為SetupTargetsForBounce函數。
[Header("Bounce Info")]private bool isBouncing;private int amountOfBounce;private float bounceSpeed;private List<Transform> enemyTarget;private int targetIndex;public void SetupBounce(bool _isBouncing, int _amountOfBounces , float _bounce){isBouncing = _isBouncing;amountOfBounce = _amountOfBounces;enemyTarget = new List<Transform>();}private void OnTriggerEnter2D(Collider2D collision){if (isReturning)return;SetupTargetsForBounce(collision);StuckInto(collision);}private void SetupTargetsForBounce(Collider2D collision){if (collision.GetComponent<Enemy>() != null){if (isBouncing && enemyTarget.Count <= 0){Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 10);foreach (var hit in colliders){if (hit.GetComponent<Enemy>() != null)enemyTarget.Add(hit.transform);}}}}
在Update中實現劍在敵人中彈射移動。當處于彈射類型且在上面步驟中能獲取到敵人時,讓劍以一定速度從現在的位置向當前索引敵人位置移動。移動到劍與敵人間距很小時,將索引增加,換向下一個目標彈射。
由于敵人數目可能小于彈射次數,可以進行取模操作,當彈射完整個列表的敵人時,從列表第一個敵人再次開始彈射。
每次彈射時,將彈射次數減一,彈射次數小于等于零時,結束彈射并讓劍返回。
將彈射的實現部分提取成函數BounceLogic。
private void Update(){if (canRotate){transform.right = rb.velocity;}if (isReturning){transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, player.transform.position) < 1)player.CatchTheSword();}BounceLogic();}private void BounceLogic(){if (isBouncing && enemyTarget.Count > 0){transform.position = Vector2.MoveTowards(transform.position, enemyTarget[targetIndex].position, bounceSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, enemyTarget[targetIndex].position) < 0.1f){targetIndex = (targetIndex + 1) % enemyTarget.Count;bounceAmount--;if (bounceAmount <= 0){isBouncing = false;isReturning = true;}}}}
將骷髏復制一個方便測試。
在技能管理器中給彈射參數賦上合適的值。
效果如下:
(3)彈射傷害的實現
每次距離接近時調用當前敵人的Damage函數即可。
private void BounceLogic(){if (isBouncing && enemyTarget.Count > 0){transform.position = Vector2.MoveTowards(transform.position, enemyTarget[targetIndex].position, bounceSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, enemyTarget[targetIndex].position) < 0.1f){enemyTarget[targetIndex].GetComponent<Enemy>().Damage();targetIndex = (targetIndex + 1) % enemyTarget.Count;bounceAmount--;if (bounceAmount <= 0){isBouncing = false;isReturning = true;}}}}
(3)問題改進
這時存在一個問題,劍如果在碰撞在地面和墻體上會一直空轉。
解決方式很簡單,在實現嵌入墻體功能的StuckInto函數中,修改彈射類型返回的條件。當處于彈射類型且擊中了其他東西時,由于沒有與敵人發生碰撞,因此獲取不到敵人的列表,只需添加條件獲取到的敵人數量大于0時才可返回即可。
private void StuckInto(Collider2D collision){canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;if(isBouncing && enemyTarget.Count > 0)return;anim.SetBool("Rotation", false);transform.parent = collision.transform;}
由于劍的碰撞面積過大,容易出現卡在邊角的情況,修改碰撞盒大小,還可以增加一點偏移,調一下碰撞盒位置。
這里發現一個小問題,敵人和敵人之間不應該碰撞,改變碰撞矩陣。
三、穿刺類型的實現
穿刺要實現的效果是根據穿刺次數,直線穿過所有敵人造成傷害。
實現直線穿刺可以將劍的重力設置小一些,就會產生直線的軌跡。穿刺時關閉旋轉動畫取消嵌入,檢測敵人并造成傷害。
(1)創建參數
首先在Sword_Skill中添加穿刺類型需要更改的變量,包括穿刺的重力、次數。
由于每個類型都要設置重力,就直接將設置重力提出來作為一個函數,為每個類型設置不同重力,在Start中調用函數。
CreateSword中依舊調用控制器中的設置函數,設置不同類型的參數,將穿刺次數傳入。
[Header("Pierce Info")][SerializeField] private int pierceAmount;[SerializeField] private float pierceGravity;protected override void Start(){base.Start();GenerateDots();SetupGravity();}private void SetupGravity(){if (swordType == SwordType.Bounce)swordGravity = bounceGravity;else if (swordType == SwordType.Pierce)swordGravity = pierceGravity;}public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();if (swordType == SwordType.Bounce)newSwordScript.SetupBounce(true, bounceAmount, bounceSpeed);else if (swordType == SwordType.Pierce)newSwordScript.SetupPierce(pierceAmount);newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}
在Sword_Skill_Controller中添加穿刺所需參數
添加SetupPierce函數設置參數
[Header("Pierce Info")]private float pierceAmount;public void SetupPierce(int _pierceAmount){pierceAmount = _pierceAmount;}
(2)穿刺的實現
在穿刺類型時讓劍在穿刺次數不為零時,不打開旋轉動畫,且穿過敵人不嵌入其中。
在Sword_Skill_Controller中添加
public void SetupSword(Vector2 _dir, float _gravityScale, Player _player, float _returnSpeed){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;returnSpeed = _returnSpeed;if(pierceAmount<=0)anim.SetBool("Rotation", true);}private void StuckInto(Collider2D collision){if(pierceAmount >0 && collision.GetComponent<Enemy>() != null){pierceAmount--;return;}canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;if(isBouncing && enemyTarget.Count > 0)return;anim.SetBool("Rotation", false);transform.parent = collision.transform;}
在技能管理器中選擇穿刺類型,并給穿刺的參數賦上合適的值。
(3)穿刺傷害的實現
在觸發器函數中調用,當劍的觸發器檢測到敵人時,敵人調用Damage函數。
教程里用了C#中的 ?. Null條件運算符。詳情請見C#官方文檔
在這里的作用是判斷獲取的敵人是否為空,不為空則調用Damage函數。
private void OnTriggerEnter2D(Collider2D collision){if (isReturning)return;collision.GetComponent<Enemy>()?.Damage();SetupTargetsForBounce(collision);StuckInto(collision);}private void SetupTargetsForBounce(Collider2D collision){if (collision.GetComponent<Enemy>() != null){if (isBouncing && enemyTarget.Count <= 0){Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 10);foreach (var hit in colliders){if (hit.GetComponent<Enemy>() != null)enemyTarget.Add(hit.transform);}}}}
(4)問題改進
現在創建的劍會一直存在,在某些角度下會飛出去很遠很難收回,因此需要添加劍在一定時間后自動銷毀。
創建銷毀劍的函數DestroyMe。使用Invoke在一定時間后調用。
private void DestroyMe(){Destroy(gameObject);}public void SetupSword(Vector2 _dir, float _gravityScale, Player _player, float _returnSpeed){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;returnSpeed = _returnSpeed;if(pierceAmount<=0)anim.SetBool("Rotation", true);Invoke("DestroyMe", 7);}
為了方便觀察效果,我先把銷毀時間調一個很小的數。
四、盤旋類型的實現
盤旋要實現的效果是扔出劍后,劍命中敵人或達到最遠距離后,懸停對敵人造成傷害,持續時間結束后返回。
盤旋狀態分為兩段,前半段劍正常移動,判斷最遠距離和擊中敵人兩個條件,達成時切換到懸停狀態。懸停狀態每隔一段冷卻時間檢測敵人對敵人造成傷害,懸停持續時長結束后返回。
(1)創建參數
首先在Sword_Skill中添加盤旋類型需要更改的變量,包括盤旋的最遠距離、持續時長、冷卻時間等。設置重力,調用SetupSpin設置參數。
這里if-else如果覺得別扭,也可Alt+Enter進入子菜單,轉換為switch語句。
[Header("Spin Info")][SerializeField] private float maxTravelDistance;[SerializeField] private float spinDuration;[SerializeField] private float spinGravity;private void SetupGravity(){if (swordType == SwordType.Bounce)swordGravity = bounceGravity;else if (swordType == SwordType.Pierce)swordGravity = pierceGravity;else if(swordType == SwordType.Spin)swordGravity = spinGravity;}public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();if (swordType == SwordType.Bounce)newSwordScript.SetupBounce(true, bounceAmount, bounceSpeed);else if (swordType == SwordType.Pierce)newSwordScript.SetupPierce(pierceAmount);else if (swordType== SwordType.Spin)newSwordScript.SetupSpin(true, maxTravelDistance, spinDuration);newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}
在Sword_Skill_Controller中添加盤旋所需參數
添加SetupSpin函數設置參數
[Header("Spin Info")]private bool isSpinning;private float maxTravelDistance;private float spinDuration;private float spinTimer;private bool wasStopped;public void SetupSpin(bool _isSpinning, float _maxTravelDistance,float _spinDuration){isSpinning = _isSpinning;maxTravelDistance = _maxTravelDistance;spinDuration = _spinDuration;}
(2)盤旋的實現
創建函數SpinLogic進行盤旋的實現,在Update函數中調用它,具體實現如下:
創建函數StopWhenSpinning,進行懸停的操作,將wasStopped設置為true并凍結劍的位置。此時開始懸停,設置盤旋的計時器。
處于盤旋狀態時,要判斷劍飛出去的距離有沒有到達最大距離限制,在到達最大距離時讓劍懸停。在StuckInto函數中添加盤旋狀態時返回,劍不嵌入物體中。
開始懸停后讓計時器遞減,直到時間歸零讓劍停止盤旋返回。
private void Update(){if (canRotate){transform.right = rb.velocity;}if (isReturning){transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, player.transform.position) < 1)player.CatchTheSword();}BounceLogic();SpinLogic();}private void SpinLogic(){ if(isSpinning){if(Vector2.Distance(player.transform.position, transform.position) > maxTravelDistance && !wasStopped)StopWhenSpinning();}if(wasStopped){spinTimer -= Time.deltaTime;if(spinTimer < 0){isReturning = true;isSpinning = false;}}} private void StopWhenSpinning(){wasStopped = true;rb.constraints = RigidbodyConstraints2D.FreezePosition;spinTimer = spinDuration;}private void StuckInto(Collider2D collision){if(pierceAmount >0 && collision.GetComponent<Enemy>() != null){pierceAmount--;return;}if (isSpinning)return;canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;if(isBouncing && enemyTarget.Count > 0)return;anim.SetBool("Rotation", false);transform.parent = collision.transform;}
在技能管理器中設置參數
效果如下:
下邊這一一小塊選做:
依據教程還有一部分修改,在擊中第一個敵人后不管有沒有達到最遠距離直接進入懸停,只需在StuckInto函數中調用StopWhenSpinning函數。這里根據自己想法設置即可。
注意:
這種做法會產生一個問題,每次和敵人產生碰撞時都會刷新一次懸停持續時間,敵人數量很多時持續時間會多次刷新。但是劍的觸發器范圍并不大,敵人進入的時間差別也不大,所以影響還可以接受。
如果想改進,可以考慮不將將計時器重置寫在StopWhenSpinning中,而是只在超出距離和第一次有敵人進入觸發器時重置。或者干脆將spinDuration改成整個盤旋類型的技能持續時長,而不是懸停那一小段的。在這里就不詳細實現了。
private void StuckInto(Collider2D collision){if(pierceAmount >0 && collision.GetComponent<Enemy>() != null){pierceAmount--;return;}if (isSpinning){StopWhenSpinning();return;}canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;if(isBouncing && enemyTarget.Count > 0)return;anim.SetBool("Rotation", false);transform.parent = collision.transform;}
(3)盤旋傷害的實現
在Sword_Skill中添加參數hitCoolDown,表示每隔多久攻擊一次。將它傳入控制器中。
[Header("Spin Info")][SerializeField] private float maxTravelDistance;[SerializeField] private float spinDuration;[SerializeField] private float spinGravity;[SerializeField] private float hitCooldown;public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();if (swordType == SwordType.Bounce)newSwordScript.SetupBounce(true, bounceAmount, bounceSpeed);else if (swordType == SwordType.Pierce)newSwordScript.SetupPierce(pierceAmount);else if (swordType== SwordType.Spin)newSwordScript.SetupSpin(true, maxTravelDistance, spinDuration, hitCooldown);newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}
在Sword_Skill_Controller中,添加冷卻時間和計時器。
處于盤旋懸停狀態時,每隔一個冷卻時間進行一次攻擊,由計時器控制。每次計時器歸零時,檢測周圍的敵人,對敵人產生一次傷害,并重置計時器。
private float hitTimer;private float hitCooldown;public void SetupSpin(bool _isSpinning, float _maxTravelDistance,float _spinDuration, float _hitCooldown){isSpinning = _isSpinning;maxTravelDistance = _maxTravelDistance;spinDuration = _spinDuration;hitCooldown = _hitCooldown;}private void Update(){if (canRotate){transform.right = rb.velocity;}if (isReturning){transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, player.transform.position) < 1)player.CatchTheSword();}BounceLogic();SpinLogic();}private void SpinLogic(){if (isSpinning){if (Vector2.Distance(player.transform.position, transform.position) > maxTravelDistance && !wasStopped){StopWhenSpinning();}}if (wasStopped){spinTimer -= Time.deltaTime;if (spinTimer < 0){isReturning = true;isSpinning = false;}hitTimer -= Time.deltaTime;if (hitTimer < 0){hitTimer = hitCooldown;Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 1);foreach (var hit in colliders){if (hit.GetComponent<Enemy>() != null)hit.GetComponent<Enemy>().Damage();}}}}
設置傷害間隔時間。
效果如下:
總結 完整代碼
SwordSkill.cs
添加枚舉類型,表示四種投擲方式。
添加三種類型投擲方式的相關參數,并傳遞到控制器。
添加設置重力的函數。
//Sword_Skill:擲劍技能
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public enum SwordType
{Regular,Bounce,Pierce,Spin
}public class Sword_Skill : Skill
{public SwordType swordType = SwordType.Regular;[Header("Skill Info")][SerializeField] private GameObject swordPrefab;[SerializeField] private Vector2 launchForce;[SerializeField] private float swordGravity;[SerializeField] private float returnSpeed;[Header("Aim dots")][SerializeField] private int numberOfDots;[SerializeField] private float spaceBetweenDots;[SerializeField] private GameObject dotPrefab;[SerializeField] private Transform dotsParent;private GameObject[] dots;private Vector2 finalDir;[Header("Bounce Info")][SerializeField] private float bounceGravity;[SerializeField] private int bounceAmount;[SerializeField] private float bounceSpeed;[Header("Pierce Info")][SerializeField] private int pierceAmount;[SerializeField] private float pierceGravity;[Header("Spin Info")][SerializeField] private float maxTravelDistance;[SerializeField] private float spinDuration;[SerializeField] private float spinGravity;[SerializeField] private float hitCooldown;protected override void Start(){base.Start();GenerateDots();SetupGravity();}protected override void Update(){if(Input.GetKeyUp(KeyCode.Mouse1))finalDir = new Vector2(AimDirection().normalized.x * launchForce.x , AimDirection().normalized.y * launchForce.y);if(Input.GetKey(KeyCode.Mouse1)){for(int i = 0; i < dots.Length; i++){dots[i].transform.position = DotsPosition(i * spaceBetweenDots);}}}private void SetupGravity(){if (swordType == SwordType.Bounce)swordGravity = bounceGravity;else if (swordType == SwordType.Pierce)swordGravity = pierceGravity;else if(swordType == SwordType.Spin)swordGravity = spinGravity;}public void CreateSword(){GameObject newSword = Instantiate(swordPrefab, player.transform.position, transform.rotation);Sword_Skill_Controller newSwordScript = newSword.GetComponent<Sword_Skill_Controller>();if (swordType == SwordType.Bounce)newSwordScript.SetupBounce(true, bounceAmount, bounceSpeed);else if (swordType == SwordType.Pierce)newSwordScript.SetupPierce(pierceAmount);else if (swordType== SwordType.Spin)newSwordScript.SetupSpin(true, maxTravelDistance, spinDuration, hitCooldown);newSwordScript.SetupSword(finalDir, swordGravity, player, returnSpeed);player.AssignNewSword(newSword);DotsActive(false);}#region 瞄準public Vector2 AimDirection(){Vector2 playerPosition = player.transform.position;Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);Vector2 direction = mousePosition - playerPosition;return direction;}private void GenerateDots(){dots = new GameObject[numberOfDots];for (int i = 0; i < numberOfDots; i++){dots[i] = Instantiate(dotPrefab, player.transform.position, Quaternion.identity, dotsParent);dots[i].SetActive(false);}}public void DotsActive(bool _isActive){for (int i = 0; i < dots.Length; i++){dots[i].SetActive(_isActive);}}private Vector2 DotsPosition(float t){Vector2 position = (Vector2)player.transform.position +new Vector2(AimDirection().normalized.x * launchForce.x, AimDirection().normalized.y * launchForce.y) * t +0.5f * (Physics2D.gravity * swordGravity) * (t * t);return position;}#endregion
}
Sword_Skill_Controller.cs
實現三種投擲類型的具體邏輯,添加對敵人的傷害。
//Sword_Skill_Controller:擲劍技能控制器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Sword_Skill_Controller : MonoBehaviour
{private Animator anim;private Rigidbody2D rb;private CircleCollider2D cd;private Player player;private bool canRotate = true;private bool isReturning;private float returnSpeed = 12;private float freezeTimeDuration;[Header("Bounce Info")]private bool isBouncing;private int bounceAmount;private float bounceSpeed;private List<Transform> enemyTarget;private int targetIndex;[Header("Pierce Info")]private float pierceAmount;[Header("Spin Info")]private bool isSpinning;private float maxTravelDistance;private float spinDuration;private float spinTimer;private bool wasStopped;private float hitTimer;private float hitCooldown;private void Awake(){anim = GetComponentInChildren<Animator>();rb = GetComponent<Rigidbody2D>();cd = GetComponent<CircleCollider2D>();}private void DestroyMe(){Destroy(gameObject);}public void SetupSword(Vector2 _dir, float _gravityScale, Player _player, float _returnSpeed, float _freezeTimeDuration){player = _player;rb.velocity = _dir;rb.gravityScale = _gravityScale;returnSpeed = _returnSpeed;freezeTimeDuration = _freezeTimeDuration;if (pierceAmount<=0)anim.SetBool("Rotation", true);Invoke("DestroyMe", 7.0f);}public void SetupBounce(bool _isBouncing, int _bounceAmount , float _bounceSpeed){isBouncing = _isBouncing;bounceAmount = _bounceAmount;bounceSpeed = _bounceSpeed;enemyTarget = new List<Transform>();}public void SetupPierce(int _pierceAmount){pierceAmount = _pierceAmount;}public void SetupSpin(bool _isSpinning, float _maxTravelDistance,float _spinDuration, float _hitCooldown){isSpinning = _isSpinning;maxTravelDistance = _maxTravelDistance;spinDuration = _spinDuration;hitCooldown = _hitCooldown;}public void ReturnSword(){rb.constraints = RigidbodyConstraints2D.FreezeAll;transform .parent = null;isReturning = true;}private void Update(){if (canRotate){transform.right = rb.velocity;}if (isReturning){transform.position = Vector2.MoveTowards(transform.position, player.transform.position, returnSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, player.transform.position) < 1)player.CatchTheSword();}BounceLogic();SpinLogic();}private void SpinLogic(){if (isSpinning){if (Vector2.Distance(player.transform.position, transform.position) > maxTravelDistance && !wasStopped){StopWhenSpinning();}}if (wasStopped){spinTimer -= Time.deltaTime;if (spinTimer < 0){isReturning = true;isSpinning = false;}hitTimer -= Time.deltaTime;if (hitTimer < 0){hitTimer = hitCooldown;Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 1);foreach (var hit in colliders){if (hit.GetComponent<Enemy>() != null)hit.GetComponent<Enemy>().Damage();}}}}private void BounceLogic(){if (isBouncing && enemyTarget.Count > 0){transform.position = Vector2.MoveTowards(transform.position, enemyTarget[targetIndex].position, bounceSpeed * Time.deltaTime);if (Vector2.Distance(transform.position, enemyTarget[targetIndex].position) < 0.1f){enemyTarget[targetIndex].GetComponent<Enemy>().Damage();targetIndex = (targetIndex + 1) % enemyTarget.Count;bounceAmount--;if (bounceAmount <= 0){isBouncing = false;isReturning = true;}}}}private void OnTriggerEnter2D(Collider2D collision){if (isReturning)return;if(collision.GetComponent<Enemy>()!=null){Enemy enemy = collision.GetComponent<Enemy>();enemy.Damage();if (!isBouncing && !isSpinning)enemy.StartCoroutine("FreezeTimeFor", freezeTimeDuration);}SetupTargetsForBounce(collision);StuckInto(collision);}private void SetupTargetsForBounce(Collider2D collision){if (collision.GetComponent<Enemy>() != null){if (isBouncing && enemyTarget.Count <= 0){Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, 10);foreach (var hit in colliders){if (hit.GetComponent<Enemy>() != null)enemyTarget.Add(hit.transform);}}}}private void StopWhenSpinning(){wasStopped = true;rb.constraints = RigidbodyConstraints2D.FreezePosition;spinTimer = spinDuration;}private void StuckInto(Collider2D collision){if(pierceAmount >0 && collision.GetComponent<Enemy>() != null){pierceAmount--;return;}if (isSpinning){//StopWhenSpinning();return;}canRotate = false;cd.enabled = false;rb.isKinematic = true;rb.constraints = RigidbodyConstraints2D.FreezeAll;if(isBouncing && enemyTarget.Count > 0)return;anim.SetBool("Rotation", false);transform.parent = collision.transform;}}