限制個人做網站愛客crm
最終效果
文章目錄
- 最終效果
- 前言
- 素材下載:
- 玩家移動跳躍控制
- 攻擊動畫配置
- 輕攻擊
- 重攻擊
- 攻擊時禁止移動和攻擊移動補償
- 敵人擊退和播放受擊動畫
- 受擊特效
- 攻擊停頓和屏幕震動
- 局部頓幀(補充)
- 參考
- 源碼
- 完結
前言
注意本文為自己的學習記錄筆記,主要是對游戲攻擊 連擊 輕重攻擊和打擊感進行探究,其中打擊感實現一般依靠播放受擊動畫、擊退、攻擊特效、時停和屏幕震動反饋等來實現,如果你有其他的好方法也歡迎補充。
素材下載:
人物
https://legnops.itch.io/red-hood-character
敵人
https://jesse-m.itch.io/skeleton-pack
環(huán)境
https://szadiart.itch.io/pixel-fantasy-caves
攻擊特效
https://v-ktor.itch.io/pixelated-attackhit-animations
玩家移動跳躍控制
public class PlayerController : MonoBehaviour
{[Header("移動和跳躍參數")] public float moveSpeed; // 移動速度public float jumpForce; // 跳躍力量new private Rigidbody2D rigidbody; // 剛體組件private Animator animator; // 動畫控制器private float input; // 輸入private bool isGround; // 是否在地面上[SerializeField] private LayerMask layer; // 地面碰撞層[SerializeField] private Vector3 check; // 地面檢測向量void Start(){rigidbody = GetComponent<Rigidbody2D>();animator = GetComponent<Animator>();}void Update(){input = Input.GetAxisRaw("Horizontal");isGround = Physics2D.OverlapCircle(transform.position + new Vector3(check.x, check.y, 0), check.z, layer);animator.SetFloat("Horizontal", rigidbody.velocity.x);animator.SetFloat("Vertical", rigidbody.velocity.y);animator.SetBool("isGround", isGround);Move();Attack();}void Move(){// 根據輸入來移動角色rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);// 處理跳躍if (Input.GetButtonDown("Jump") && isGround){rigidbody.velocity = new Vector2(0, jumpForce);animator.SetTrigger("Jump"); // 觸發(fā)跳躍動畫}// 根據水平速度方向更新角色朝向if (rigidbody.velocity.x < 0)transform.localScale = new Vector3(-1, 1, 1); // 向左else if (rigidbody.velocity.x > 0)transform.localScale = new Vector3(1, 1, 1); // 向右}private void OnDrawGizmos(){Gizmos.DrawWireSphere(transform.position + new Vector3(check.x, check.y, 0), check.z);}}
攻擊動畫配置
攻擊動畫分為輕攻擊和重攻擊
輕攻擊
重攻擊
[Header("攻擊")]
public float interval = 2f; // 攻擊間隔時間
private float timer; // 計時器
private bool isAttack; // 是否正在攻擊
private string attackType; // 攻擊類型
private int comboStep; // 連擊步驟void Update()
{//...Attack();
}void Attack()
{// 輕攻擊輸入檢測if (Input.GetKeyDown(KeyCode.Return) && !isAttack){isAttack = true;attackType = "Light";comboStep++; // 連擊步驟加一if (comboStep > 3)comboStep = 1; // 連擊步驟循環(huán)timer = interval; // 設置計時器animator.SetTrigger("LightAttack"); // 觸發(fā)輕攻擊動畫animator.SetInteger("ComboStep", comboStep); // 設置連擊步驟參數}// 重攻擊輸入檢測if (Input.GetKeyDown(KeyCode.RightShift) && !isAttack){isAttack = true;attackType = "Heavy";comboStep++; // 連擊步驟加一if (comboStep > 3)comboStep = 1; // 連擊步驟循環(huán)timer = interval; // 設置計時器animator.SetTrigger("HeavyAttack"); // 觸發(fā)重攻擊動畫animator.SetInteger("ComboStep", comboStep); // 設置連擊步驟參數}// 處理連擊計時器if (timer != 0){timer -= Time.deltaTime;if (timer <= 0){timer = 0;comboStep = 0; // 重置連擊步驟}}
}//攻擊結束
public void AttackOver()
{isAttack = false;
}
配置每個
攻擊動畫,在執(zhí)行的位置執(zhí)行攻擊結束事件,通常攻擊結束事件都不會放在動畫的最后一幀,因為連擊一般都存在預輸入,也就是在上一個動畫還未結束時就進行輸入,這樣能很好的提升combo的連貫性
效果
攻擊時禁止移動和攻擊移動補償
攻擊時我們不希望玩家還能移動,但是簡單粗暴的禁止移動又會影響我們的攻擊操作手感,在死亡細胞等游戲中,攻擊時會朝前方以一個較小的速度移動,這樣可以一定程度的補償攻擊時無法移動的缺陷
[Header("攻擊補償速度")]
public float lightSpeed; // 輕攻擊速度
public float heavySpeed; // 重攻擊速度void Move()
{// 如果玩家沒有在攻擊狀態(tài)下,根據輸入來移動角色if (!isAttack)rigidbody.velocity = new Vector2(input * moveSpeed, rigidbody.velocity.y);else{// 如果正在攻擊,則根據攻擊類型設置速度if (attackType == "Light")rigidbody.velocity = new Vector2(transform.localScale.x * lightSpeed, rigidbody.velocity.y);else if (attackType == "Heavy")rigidbody.velocity = new Vector2(transform.localScale.x * heavySpeed, rigidbody.velocity.y);}// ...
}
配置
效果
敵人擊退和播放受擊動畫
配置敵人受擊動畫
配置玩家每個動畫的攻擊范圍
新增Enemy 敵人腳本,實現擊退和受擊動畫
public class Enemy : MonoBehaviour
{public float speed; // 敵人的移動速度private Vector2 direction; // 受擊時的移動方向private bool isHit; // 是否正在受擊狀態(tài)private AnimatorStateInfo info; // 動畫狀態(tài)信息private Animator animator; // 敵人的主動畫控制器new private Rigidbody2D rigidbody; // 敵人的剛體組件void Start(){// 獲取組件的引用animator = transform.GetComponent<Animator>();rigidbody = transform.GetComponent<Rigidbody2D>();}void Update(){info = animator.GetCurrentAnimatorStateInfo(0); // 獲取當前動畫狀態(tài)信息if (isHit){rigidbody.velocity = direction * speed; // 根據受擊方向設置速度if (info.normalizedTime >= .6f)isHit = false; // 當動畫播放超過60%時結束受擊狀態(tài)}}// 外部調用,使敵人進入受擊狀態(tài)public void GetHit(Vector2 direction){transform.localScale = new Vector3(-direction.x, 1, 1); // 根據受擊方向調整朝向isHit = true; // 進入受擊狀態(tài)this.direction = direction; // 設置受擊方向animator.SetTrigger("Hit"); // 播放主動畫的受擊動畫狀態(tài)}
}
修改PlayerController,調用擊退敵人
private void OnTriggerEnter2D(Collider2D other)
{if (other.CompareTag("Enemy")){// 根據角色朝向確定敵人受擊方向if (transform.localScale.x > 0)other.GetComponent<Enemy>().GetHit(Vector2.right); // 右側受擊else if (transform.localScale.x < 0)other.GetComponent<Enemy>().GetHit(Vector2.left); // 左側受擊}
}
效果
受擊特效
特效動畫配置
修改Enemy
private Animator hitAnimator; // 敵人的受擊特效動畫控制器hitAnimator = transform.GetChild(0).GetComponent<Animator>(); // 敵人的受擊特效動畫控制器在子對象中// 外部調用,使敵人進入受擊狀態(tài)public void GetHit(Vector2 direction){//...hitAnimator.SetTrigger("Hit"); // 播放受擊特效動畫}
效果
攻擊停頓和屏幕震動
新增AttackSense
public class AttackSense : MonoBehaviour
{private static AttackSense instance;public static AttackSense Instance{get{if (instance == null)instance = FindObjectOfType<AttackSense>(); // 查找當前場景中的 AttackSense 實例return instance;}}private bool isShake; // 是否正在進行攝像機震動// 暫停游戲一段時間public void HitPause(int duration){StartCoroutine(Pause(duration));}// 使用協程實現暫停功能IEnumerator Pause(int duration){float pauseTime = duration / 60f; // 將幀數轉換為實際暫停時間Time.timeScale = 0.2f; // 將游戲時間縮放設為0.2yield return new WaitForSecondsRealtime(pauseTime); // 等待指定的暫停時間Time.timeScale = 1; // 恢復游戲時間正常}// 觸發(fā)攝像機震動效果public void CameraShake(float duration, float strength){if (!isShake) // 如果當前沒有進行震動StartCoroutine(Shake(duration, strength));}// 使用協程實現攝像機震動效果IEnumerator Shake(float duration, float strength){isShake = true; // 標記正在進行震動Transform camera = Camera.main.transform; // 獲取主攝像機的 Transform 組件Vector3 startPosition = camera.position; // 記錄攝像機震動前的初始位置while (duration > 0){// 將攝像機位置隨機偏移一定范圍來模擬震動效果camera.position = Random.insideUnitSphere * strength + startPosition;duration -= Time.deltaTime; // 每幀減去時間yield return null; // 等待下一幀}camera.position = startPosition; // 震動結束后將攝像機位置恢復到初始位置isShake = false; // 結束震動狀態(tài)}
}
修改PlayerController調用
[Header("打擊感")]
public float shakeTime; // 搖晃時間
public int lightPause; // 輕攻擊暫停時間
public float lightStrength; // 輕攻擊相機震動強度
public int heavyPause; // 重攻擊暫停時間
public float heavyStrength; // 重攻擊相機震動強度private void OnTriggerEnter2D(Collider2D other)
{if (other.CompareTag("Enemy")){// 根據攻擊類型處理打擊感if (attackType == "Light"){AttackSense.Instance.HitPause(lightPause); // 輕攻擊暫停AttackSense.Instance.CameraShake(shakeTime, lightStrength); // 相機震動}else if (attackType == "Heavy"){AttackSense.Instance.HitPause(heavyPause); // 重攻擊暫停AttackSense.Instance.CameraShake(shakeTime, heavyStrength); // 相機震動}// 根據角色朝向確定敵人擊退方向if (transform.localScale.x > 0)other.GetComponent<Enemy>().GetHit(Vector2.right); // 右側受擊else if (transform.localScale.x < 0)other.GetComponent<Enemy>().GetHit(Vector2.left); // 左側受擊}
}
配置參數
最終效果
局部頓幀(補充)
前面的頓幀這個思路其實不太好,可以看看有的游戲,他們不是所有物體都停頓的,一般是攻擊者和受擊者停頓,其他的不受影響。animator有個scale播放速度scale值應該可以實現這種效果,連續(xù)攻擊可能用局部頓幀好一點,下面大概分享一下思路
public class AttackController : MonoBehaviour
{public Animator animator;public float slowMotionTimeScale = 0.5f; // 慢動作時的時間縮放值public void PerformAttack(){StartCoroutine(AttackCoroutine());}IEnumerator AttackCoroutine(){// 播放攻擊動畫前先設置慢動作animator.speed = slowMotionTimeScale;// 等待攻擊動畫播放完成yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length / slowMotionTimeScale);// 恢復正常時間縮放animator.speed = 1f;}
}
在這個示例中,PerformAttack 方法可以被調用來開始攻擊動作。在攻擊動作開始時,時間縮放被設置為 slowMotionTimeScale,然后通過 Coroutine 等待攻擊動畫播放完成后,再恢復為正常速度。
通過這種方法,你可以在游戲中實現局部的動畫停頓效果,而不是整體減速,從而增強游戲的視覺沖擊力和玩家的體驗。
參考
https://www.bilibili.com/video/BV1fX4y1G7tv
源碼
整理好我會放上來
完結
贈人玫瑰,手有余香!如果文章內容對你有所幫助,請不要吝嗇你的點贊評論和關注
,以便我第一時間收到反饋,你的每一次支持
都是我不斷創(chuàng)作的最大動力。當然如果你發(fā)現了文章中存在錯誤
或者有更好的解決方法
,也歡迎評論私信告訴我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奮斗的開發(fā)者,出于興趣愛好,最近開始自學unity,閑暇之余,邊學習邊記錄分享,站在巨人的肩膀上,通過學習前輩們的經驗總是會給我很多幫助和啟發(fā)!php是工作,unity是生活!如果你遇到任何問題,也歡迎你評論私信找我, 雖然有些問題我也不一定會,但是我會查閱各方資料,爭取給出最好的建議,希望可以幫助更多想學編程的人,共勉~