Unity C#经典状态机基类设计 小白学习笔记

https://youtu.be/-jkT4oFi1vk?si=izRR0C1xHx3-I23E 教程来源,油管AdamCYounis

一、c#状态机(游戏)

在看Unity入门教程,刚好看到这个,感觉挺好的,记录一下:

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

public abstract class State : MonoBehaviour
{
    public bool isComplete { get; protected set; }
    protected float startTime;
    public float time => Time.time - startTime;
    public virtual void Enter() { }
    public virtual void Do() { }
    public virtual void FixDo() { }
    public virtual void Exit() { }
}

注解1:public bool isComplete { get; protected set; }相当于
private bool _isComplete; // 私有字段存储实际值

public bool isComplete
    {
        get                           //公共的
        {
            return _isComplete;
        }
        protected set           //受保护的
        {
            _isComplete = value;
        }
    }
简化版: 编译器自动创建私有字段
完整版: 手动创建私有字段和访问器

注解2:public float time => Time.time - startTime;相当于
public float time
{
    get
    {
        return Time.time - startTime;
    }
}
简化版: => 是表达式主体语法,用于只读计算属性
完整版: 传统的get访问器写法

注解3:virtual的功能
virtual关键字在C#中用于创建可被重写的方法,让子类能够提供自己的实现。

二、数值映射函数

public static float Map(float value , float min1,float max1,float min2,float max2,bool clamp = false)
{
    float val = min2 + (max2 - min2) * ((value-min1) / (max1-min1));
    return clamp ? Mathf.Clamp(val,Mathf.Min(min2,max2),Mathf.Max(min2,max2)):val;
}//有用的数值映射函数,比如从[0,10]映射到[0,100],3映射为30,还可以将摄氏度映射为华氏度等

注解:有用的数值映射函数,比如从[0,10]映射到[0,100],3映射为30,还可以将摄氏度映射为华氏度等,返回偏移值:

\frac{val-min2}{max2-min2} = \frac{value-min1}{max1-min1}

1、bool clamp = flase 是c#的语法,如果没有赋值就保留clamp = false的默认赋值

2、mathf.min以及mathf.max的作用主要是在反向映射的情况下不出错,但实际上映射出来是相反的,比如,[0,10]映射到[100,0],那么3会映射为70,这个暂时没什么用,后面可能会用到吧。

3、Mathf.Clamp(value, min, max) 的作用是限制数值范围

  • 如果value < min,返回min
  • 如果value > max,返回max
  • 如果min ≤ value ≤ max,返回value

具体应用:在跳跃的时候刚好播放完整动画“Air”,(Speed = 0保证动画不会在停在一帧不动的时候乱跳)

void UpdateAirborne()
{
    float time = Map(rigidbody2.velocity.y, -jumpForce, jumpForce, 0, 1, true);
    animator.Play("Air",0,time);
    animator.speed = 0; 
    if(isGrounded)
    stateComplete = true;
}

注:animator.play(,,)中间那个0指的是animator的baseLayer层,index是0

三、角色控制代码供参考(最简单的版本),用于实现角色走路跳跃等,包括动画内容,不需要在animator界面中拉transition了:

using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;

public class PlayerScript : MonoBehaviour
{
    public BoxCollider2D boxCollider2D;
    public CapsuleCollider2D capsuleCollider2D;
    public Animator animator;
    private Rigidbody2D rigidbody2;
    public float runSpeed = 5f;
    public float horizontalSpeed = 0f;
    //bool isFacingLeft = false;
    public float jumpForce = 17f;
    private bool isGrounded;
    private bool stateComplete = true;
    private LayerMask groundLayer;
    enum PlayerState { Idle,Running,Airborne}
    PlayerState state;
    void Start()
    {
        rigidbody2 = GetComponent<Rigidbody2D>();
        groundLayer = LayerMask.GetMask("Ground");
    }

    // Update is called once per frame
    void Update()
    {
        FlipDetection2();
        MoveWithInput();
        if (stateComplete)
        {
            SelectState();
        }
        UpdateState();
    }
    private void FixedUpdate()
    {
        CheckGround();
    }
    void MoveWithInput()
    {
        horizontalSpeed = Input.GetAxisRaw("Horizontal") * runSpeed;
        //animator.SetFloat("speed", Mathf.Abs(horizontalSpeed));
        if (Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0)
        {
            rigidbody2.velocity = new Vector2(horizontalSpeed, rigidbody2.velocity.y);
        }
        //垂直速度
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            rigidbody2.velocity = new Vector2(rigidbody2.velocity.x, jumpForce);
        }
    }
    void CheckGround()
    {
        //  Debug.Log(isGrounded);
        int sumGround = Physics2D.OverlapAreaAll(boxCollider2D.bounds.min, boxCollider2D.bounds.max, groundLayer).Length;
        if (sumGround > 0) { 
        isGrounded = true;
        }
        else
        {
            isGrounded = false;
        }
    }
    void FlipDetection2()
    {
        if (horizontalSpeed != 0)
        {
            float direction = Mathf.Sign(horizontalSpeed);
            //Mathf.Sign(float f); Returns a value of 1 when f is 0 or greater. Returns a value of -1 when f is negative.
            transform.localScale = new Vector3( direction* Mathf.Abs(transform.localScale.x), transform.localScale.y, 1);
        }
    }
    void UpdateState()
    {
        switch (state)
        {
            case PlayerState.Idle:
                UpdateIdle();
                break;
            case PlayerState.Running:
                UpdateRunning();
                break;
            case PlayerState.Airborne:
                UpdateAirborne();
                break;
        }
    }
    void UpdateRunning()
    {
        float velX = rigidbody2.velocity.x;
        animator.speed = Mathf.Abs(velX) / runSpeed;
        if(!isGrounded|| Mathf.Abs(velX) <= 0.1)
        stateComplete = true;
    }
    void UpdateIdle()
    {
        if(horizontalSpeed == 0 || !isGrounded)
        stateComplete = true;
    }
    void UpdateAirborne()
    {
        float time = Map(rigidbody2.velocity.y, -jumpForce, jumpForce, 0, 1, true);
        animator.Play("Air",0,time);
        animator.speed = 0; 
        if(isGrounded)
        stateComplete = true;
    }
    void SelectState()
    {
        stateComplete = false;
        if (isGrounded)
        {
            if (horizontalSpeed == 0)
            {
                state = PlayerState.Idle;
                StartIdle();
            }
            else
            {
                state = PlayerState.Running;
                StartRunning();
            }
        }
        else
        {
            state = PlayerState.Airborne;
            StartAirborne();
        }
    }
    void StartRunning()
    {
        animator.Play("Run");
    }
    void StartIdle()
    {
        animator.Play("Idle");
    }
    void StartAirborne()
    {
        animator.Play("Air"); 
    }
    public static float Map(float value , float min1,float max1,float min2,float max2,bool clamp = false)
    {
        float val = min2 + (max2 - min2) * ((value-min1) / (max1-min1));
        return clamp ? Mathf.Clamp(val,Mathf.Min(min2,max2),Mathf.Max(min2,max2)):val;
    }//有用的数值映射函数,比如从[0,10]映射到[0,100],3映射为30,还可以将摄氏度映射为华氏度等

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值