Unity教程(二十四)技能系統 投劍技能(中)技能變種實現

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/90986.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/90986.shtml
英文地址,請注明出處:http://en.pswp.cn/web/90986.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

局域網TCP通過組播放地址rtp推流和拉流實現實時喊話

應用場景&#xff0c;安卓端局域網不用ip通過組播放地址實現實時對講功能發送端: ffmpeg -f alsa -i hw:1 -acodec aac -ab 64k -ac 2 -ar 16000 -frtp -sdp file stream.sdp rtp://224.0.0.1:14556接收端: ffmpeg -protocol whitelist file,udp,rtp -i stream.sdp -acodec pcm…

基于深度學習的醫學圖像分析:使用YOLOv5實現細胞檢測

最近研學過程中發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊鏈接跳轉到網站人工智能及編程語言學習教程。讀者們可以通過里面的文章詳細了解一下人工智能及其編程等教程和學習方法。下面開始對正文內容的…

32.768KHZ 3215晶振CM315D與NX3215SA應用全場景

在現代電子設備中&#xff0c;一粒米大小的晶振&#xff0c;卻是掌控時間精度的“心臟”。CITIZEN的CM315D系列與NDK的NX3215SA系列晶振便是其中的佼佼者&#xff0c;它們以 3.2 1.5 mm 的小尺寸”(厚度不足1mm)&#xff0c;成為智能設備中隱形的節奏大師。精準計時的奧秘這兩…

嵌軟面試——ARM Cortex-M寄存器組

Cortex-M內存架構包含16個通用寄存器&#xff0c;其中R0-R12是13個32位的通用寄存器&#xff0c;另外三個寄存器是特殊用途&#xff0c;分別是R13&#xff08;棧指針&#xff09;,R14&#xff08;鏈接寄存器&#xff09;,R15&#xff08;程序計數器&#xff09;。對于處理器來說…

7.DRF 過濾、排序、分頁

過濾Filtering 對于列表數據可能需要根據字段進行過濾&#xff0c;我們可以通過添加django-fitlter擴展來增強支持。 pip install django-filter在配置文件中增加過濾器類的全局設置&#xff1a; """drf配置信息必須全部寫在REST_FRAMEWORK配置項中""…

二、CUDA、Pytorch與依賴的工具包

CUDA Compute Unified Device Architecture&#xff08;統一計算架構&#xff09;。專門用于 GPU 通用計算 的平臺 編程接口。CUDA可以使你的程序&#xff08;比如矩陣、神經網絡&#xff09;由 GPU 執行&#xff0c;這比CPU能快幾十甚至上百倍。 PyTorch 是一個深度學習框架…

SpringCloude快速入門

近期簡單了解一下SpringCloude微服務,本文主要就是我學習中所記錄的筆記,然后具體原理可能等以后再來深究,本文可能有些地方用詞不專業還望包容一下,感興趣可以參考官方文檔來深入學習一下微服務,然后我的下一步學習就是docker和linux了。 nacos: Nacos 快速開始 | Nacos 官網…

GPT Agent與Comet AI Aent瀏覽器對比橫評

1. 架構設計差異GPT Agent的雙瀏覽器架構&#xff1a;文本瀏覽器&#xff1a;專門用于高效處理大量文本內容&#xff0c;適合深度信息檢索和文獻追蹤&#xff0c;相當于Deep Research的延續可視化瀏覽器&#xff1a;具備界面識別與交互能力&#xff0c;可以點擊網頁按鈕、識別圖…

應用信息更新至1.18.0

增加DO權限 增加權限管理&#xff08;需DO支持&#xff09; 增加應用凍結隱藏&#xff08;需DO支持&#xff09; 增加權限委托&#xff08;需DO支持&#xff09; 增加特殊組件 ...

常用git命令集錦

git init 初始化 將當前目錄初始化為 git 本地倉庫&#xff0c;此時會在本地創建一個 .git 的文件夾 git init -q 靜默執行&#xff0c;就是在后臺執行 git init --bare –bare 參數,一般用來初始化一個空的目錄&#xff0c;作為遠程存儲倉庫 git init --template dir –templa…

skywalking安裝

一、簡介 SkyWalking是一款用于分布式系統跟蹤和性能監控的開源工具。它可以幫助開發人員了解分布式系統中不同組件之間的調用關系和性能指標&#xff0c;從而進行故障排查和性能優化。 它支持多種語言和框架&#xff0c;包括Java、.NET、Node.js等。它通過在應用程序中插入代…

利用DataStream和TrafficPeak實現大數據可觀察性

可觀察性工作流對于深入了解應用程序的健康狀況、客戶流量和整體性能至關重要。然而&#xff0c;要實現真正的可觀察性還面臨一些挑戰&#xff0c;包括海量的流量數據、數據保留、實施時間以及各項成本等。TrafficPeak是一款為Akamai云平臺打造&#xff0c;簡單易用、可快速部署…

jQuery 最新語法大全詳解(2025版)

引言 jQuery 作為輕量級 JavaScript 庫&#xff0c;核心價值在于 簡化 DOM 操作、跨瀏覽器兼容性和高效開發。盡管現代框架崛起&#xff0c;jQuery 仍在遺留系統維護、快速原型開發中廣泛應用。本文涵蓋 jQuery 3.6 核心語法&#xff0c;重點解析高效用法與最佳實踐。 一、jQu…

Android 15 修改截圖默認音量大小

概述 在 Android 15 中,截圖音效的默認音量可能過大,影響用戶體驗。本文將介紹如何通過修改系統源碼來調整截圖音效的默認音量大小。 修改位置 需要修改的文件路徑: frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt…

Python爬蟲實戰:快速采集教育政策數據(附官網工具庫API)

解鎖教育政策研究的數據金礦&#xff0c;用技術提升學術效率 在教育政策研究領域&#xff0c;獲取最新、最全面的政策文本是學術工作的基礎。傳統手動收集方式效率低下且容易遺漏關鍵政策&#xff0c;而Python爬蟲技術為教育研究者提供了高效的數據采集解決方案。本文將系統介…

驗證回文串-leetcode

如果在將所有大寫字符轉換為小寫字符、并移除所有非字母數字字符之后&#xff0c;短語正著讀和反著讀都一樣。則可以認為該短語是一個 回文串 。 字母和數字都屬于字母數字字符。 給你一個字符串 s&#xff0c;如果它是 回文串 &#xff0c;返回 true &#xff1b;否則&#xf…

嵌入式學習日志(十)

10 學習指針1 指針核心定義與本質1.1 指針與指針變量1、指針即地址&#xff0c;指針變量是存放地址的變量&#xff0c;其大小與操作系統位數相關&#xff1a;64 位系統中占 8 字節&#xff0c;32 位系統中占 4 字節。2、指針的核心功能是通過地址間接訪問目標變量&#xff0…

Anaconda創建環境報錯:CondaHTTPEFTOT: HTTP 403 FORBIDDEN for url

一、快速解決方案這類報錯的原因通常是由于 conda 無法訪問鏡像源或權限被服務器拒絕&#xff0c;以下是常見原因和對應的解決方案&#xff1a;檢查鏡像源拼寫是否正確conda config --show channels清華源鏡像示例如果不正確&#xff0c;先清除舊配置del %USERPROFILE%\.condar…

亞馬遜地址關聯暴雷:新算法下的賬號安全保衛戰

2025年Q3&#xff0c;上千個店鋪因共享稅代地址、海外倉信息重疊等問題被批量凍結&#xff0c;為行業敲響了“精細化合規”的警鐘。事件復盤&#xff1a;地址成為關聯風控的“致命開關”稅代機構違規引發“多米諾效應”事件的導火索指向稅代機構“saqibil”&#xff0c;其為降低…

在本地環境中運行 ‘dom-distiller‘ GitHub 庫的完整指南

在本地環境中運行 ‘dom-distiller’ GitHub 庫的完整指南 前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家&#xff0c;覺得好請收藏。點擊跳轉到網站。 1. 項目概述 ‘dom-distiller’ 是一個用于將網頁…