【Unity2D好项目分享】提高游戏人物打击感

学习目标:

最近在b站和Youtube上看到了很多好项目,这对已经了解一定程度的Untiy知识的小伙伴有很大帮助,今天在b站看到一个Up教学如何提高游戏人物打击感,今天就用文字解析的方式算是总结一下我学到的内容吧。

视频地址:使用Unity实现动作游戏的打击感_哔哩哔哩_bilibili本期视频使用资源:https://legnops.itch.io/red-hood-characterhttps://szadiart.itch.io/pixel-fantasy-caveshttps://v-ktor.itch.io/pixelated-attackhit-animations项目链接:https://github.com/RedFF0000/AttackSense视频内https://www.bilibili.com/video/BV1fX4y1G7tv?spm_id_from=333.788.header_right.fav_list.click

Github项目:

GitHub - RedFF0000/AttackSenseContribute to RedFF0000/AttackSense development by creating an account on GitHub.https://github.com/RedFF0000/AttackSense


学习内容:

首先将我们需要的素材导入​,然后切割图像做好每一个动画,由于素材中有作者已经制作好的预设体,大伙可以更直接的看到作者设置的动画条件,同样人物需要Sprite Renderer调整它的Order in Layer大一点保证显示到最上面,Rigibody2D和BoxCollider2D也是少不了的。

​​​​​​

再来说动画条件,作者只用少量的参数Parameters控制了很多动画条件,Idle和Run用Horizontal,而涉及跳跃和降落用isGround和Vertical。

而对于攻击动画条件,作者用两个Trigger“LightAttack”和"HeavyAttack"来判断轻攻击还是重攻击,各分为ComboStep用ComboStep来判断第几段攻击

到Exit状态则设置1秒的Exit Time

代码部分:

  用CharacterController.cs来实现人物的移动,跳跃以及攻击

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [Header("补偿速度")]
    public float lightSpeed;
    public float heavySpeed;
    [Header("打击感")]
    public float shakeTime;
    public int lightPause;
    public float lightStrength;
    public int heavyPause;
    public float heavyStrength;

    [Space]
    public float interval = 2f;
    private float timer;
    private bool isAttack;
    private string attackType;
    private int comboStep;

    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()
    {
        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);
        }

        if (Input.GetButtonDown("Jump") && isGround)
        {
            rigidbody.velocity = new Vector2(0, jumpForce);
            animator.SetTrigger("Jump");
        }

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

    void Attack()
    {
        if (Input.GetKeyDown(KeyCode.Return) && !isAttack)
        {
            isAttack = true;
            attackType = "Light";
            comboStep++;
            if (comboStep > 3)
                comboStep = 1;
            timer = interval;
            animator.SetTrigger("LightAttack");
            animator.SetInteger("ComboStep", comboStep);
        }
        if (Input.GetKeyDown(KeyCode.RightShift) && !isAttack)
        {
            isAttack = true;
            attackType = "Heavy";
            comboStep++;
            if (comboStep > 3)
                comboStep = 1;
            timer = interval;
            animator.SetTrigger("HeavyAttack");
            animator.SetInteger("ComboStep", comboStep);
        }


        if (timer != 0)
        {
            timer -= Time.deltaTime;
            if (timer <= 0)
            {
                timer = 0;
                comboStep = 0;
            }
        }
    }

    public void AttackOver()
    {
        isAttack = false;
    }

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

    private void OnDrawGizmos()
    {
        Gizmos.DrawWireSphere(transform.position + new Vector3(check.x, check.y, 0), check.z);
    }
}

在Player的Animation面板中需要为动画关键帧添加事件,在每个涉及Attack的动画中,在接近完成的时候添加一个事件,把我们写的public void AttackOver()添加上去

同时我们还需要两个脚本,一个是Enemy的,这里作者并没有给它血量让他只是一个沙包,有击退函数GetHit(),以及用AnimatorStateInfo检测动画播放的进度,当进度超过一定阈值的时候就会将isHit设置为false,同时为了增加敌人的受击感作者用另外的子对象的Animator

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public float speed;
    private Vector2 direction;
    private bool isHit;
    private AnimatorStateInfo info;

    private Animator animator;
    private Animator hitAnimator;
    new private Rigidbody2D rigidbody;

    void Start()
    {
        animator = transform.GetComponent<Animator>();
        hitAnimator = transform.GetChild(0).GetComponent<Animator>();
        rigidbody = transform.GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        info = animator.GetCurrentAnimatorStateInfo(0);
        if (isHit)
        {
            rigidbody.velocity = direction * speed;
            if (info.normalizedTime >= .6f)
                isHit = false;
        }
    }

    public void GetHit(Vector2 direction)
    {
        transform.localScale = new Vector3(-direction.x, 1, 1);
        isHit = true;
        this.direction = direction; ;
        animator.SetTrigger("Hit");
        hitAnimator.SetTrigger("Hit");
    }
}

最后我们还想要实现一下让受攻击时顿帧以及摄像机抖动,在Main Camera上创建一个AttackTry

代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AttackTry : MonoBehaviour
{
    //单例模式,不被场景加载销毁
    private static AttackTry instance;
    public static AttackTry Instance
    {
        get
        {
            if (instance == null)
                instance = GameObject.FindObjectOfType<AttackTry>();
            return instance;
        }
    }
    private bool isShake;

    public void HitPause(int duration)
    {
        StartCoroutine(Pause(duration));
    }
    IEnumerator Pause(int duration)
    {
        float pauseTime = duration / 60f;
        Time.timeScale = 0;
        yield return new WaitForSecondsRealtime(pauseTime);
        Time.timeScale = 1;
    }
    public void CameraShake(float duration, float strength)
    {
        if (!isShake)
        {
            StartCoroutine(Shake(duration, strength));
        }
    }
    IEnumerator Shake(float duration,float strength)
    {
        isShake = true;
        var camera = Camera.main.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;
    }
}

 再给人物设置好参数以及新建一个空对象来判断攻击范围

各个Tag:

最后在Project Setting的Physics 2D上早证玩家和敌人不会发生碰撞 


学习产出:

猜你喜欢

转载自blog.csdn.net/dangoxiba/article/details/124600462