Unity开发类银河恶魔城游戏学习笔记
Unity教程(零)Unity和VS的使用相关内容
Unity教程(一)开始学习状态机
Unity教程(二)角色移动的实现
Unity教程(三)角色跳跃的实现
Unity教程(四)碰撞检测
Unity教程(五)角色冲刺的实现
文章目录
前言
本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记,如有错误,欢迎指正。
本节进行角色跳跃的实现。
对应b站视频:
【Unity教程】从0编程制作类银河恶魔城游戏P31
精灵表素材。
素材链接:https://pan.baidu.com/s/1PnN2t7xOcNpPDLteLm4EJw?pwd=7pec。
提取码: 7pec
一、概述
本节实现角色跳跃需要创建超级状态PlayerGroundedState接地状态和PlayerAirState空中状态。
超级状态是一种用来分层控制的状态,它之下可以有很多简单状态,能对简单状态进行控制。举例说明,比如接地状态下角色可以是移动和闲置的,符合某种条件时可以从接地状态转换成另一个超级状态空中状态,这时角色不能再移动和闲置,但可以在空中状态下转换状态。通过这种方式创建分层状态机,就是状态机中又嵌套了一个状态机,通过分离来减少过渡条件,减少状态机的复杂度。我参照了文章。
状态转换条件如下:
可以看到转换到跳跃状态是在超级状态接地状态中进行的,这保证了无论角色处于闲置还是移动状态时都能进行跳跃。如果没有接地状态的控制可能会出现这种情况,角色可以在空中不断继续跳跃升天。
在实现时我们需要添见PlayerJumpState跳跃状态和两个超级状态PlayerGroundedState接地状态和PlayerAirState空中状态,它们都继承自状态基类PlayerState。同时我们需要修改PlayerIdleState空闲状态和PlayerMoveState移动状态让它们继承自PlayerGroundedState接地状态。具体如图:
二、创建超级状态
创建两个超级状态PlayerGroundedState接地状态和PlayerAirState空中状态。
把它们继承的类改为状态基类PlayerState,并生成构造函数、生成重写。基本操作具体讲解见
Unity教程(零)Unity和VS的使用相关内容
修改PlayerIdleState空闲状态和PlayerMoveState移动状态让它们继承自PlayerGroundedState接地状态
三、创建跳跃状态的混合树动画
层次面板中选中Animator,在Animation面板中创建两个动画playerJump跳跃动画和playerFall下落动画
playerJump精灵表标号41-43,采样率改为10
playerFall精灵表标号46-48,采样率改为10
具体讲解见Unity教程(零)Unity和VS的使用相关内容
你会看到动画师中多出两个状态
删掉palyerJump和playerFall这两个块,创建混合树动画,命名为Jump/Fall
右键->Create State->From New Blend Tree
把混合树参数重命名为yVelocity,到时候我们需要动画按照角色y方向速度混合。
双击Jump/Fall混合树进入混合树的进一步编辑
在右侧面板添加两个动画
"+"号->Add Motion Field->拖入动画
结果如下:
勾掉Automate Threshholds,将PlayerFall的Threshhold改为-1
关于Threshold阈值的理解
参照博客Unity动画系统详解5:BlendTree混合树是什么?和 官方手册 混合树1D混合
在此混合树中我们使用的是1D混合,Threshold代表的是混合树参数在等于阈值时完全使用这个动画。
顺带一提,这个钟表图标Time Scale可以调节动画的播放速度,比如你想让动画播放速度变为原来的2倍,可以把它设置为2。
在这里可以检查混合树的效果
将右下Blend Tree这一小条向上拖
拖动Blend Tree的参数滑动条查看不同参数时的动画
可以看到动画随参数变化
连接状态机,并添加过渡条件Jump,并修改过渡设置
如需更详细过程讲解,见Unity教程(零)Unity和VS的使用相关内容
添加条件变量
连接过渡
进入Jump/Fall的过渡
退出Jump/Fall的过渡
四、跳跃的实现
跳跃的实现要经过这样一个过程:
在角色处于接地状态PlayerGroundedState时接收到跳跃键按下的输入,转换到跳跃状态PlayerJumpState,在跳跃状态中设置跳跃速度。然后当跳跃至顶点时角色开始下落,在y方向的速度开始为负,这时切换到空中状态PlayerAirState。在落到地面时,y方向速度为0,角色切换为闲置状态。
创建脚本PlayerJumpState,继承于PlayerState,生成构造函数和重写。
在Player添加PlayerJumpState和PlayerAirState两个状态,在Awake函数中创建这两个状态,它们都通过条件变量Jump去控制动画。
#region 状态
public PlayerStateMachine StateMachine { get; private set; }
public PlayerState idleState { get; private set; }
public PlayerState moveState { get; private set; }
public PlayerState jumpState { get; private set; }
public PlayerState airState { get; private set; }
#endregion
//创建对象
private void Awake()
{
StateMachine = new PlayerStateMachine();
idleState = new PlayerIdleState(StateMachine, this, "Idle");
moveState = new PlayerMoveState(StateMachine, this, "Move");
jumpState = new PlayerJumpState(StateMachine, this, "Jump");
airState = new PlayerAirState(StateMachine, this, "Jump");
anim = GetComponentInChildren<Animator>();
rb = GetComponent<Rigidbody2D>();
}
完善PlayerJumpState实现设置跳跃和切换状态,实现大致如下:
在player中添加变量jumpForce用以设置跳跃
public float jumpForce = 12f;
顺带Gravity Scale改为3.5
接下来在PlayerJumpState中进行完善。
在进入Jump状态时设置y方向速度实现跳跃。
public override void Enter()
{
base.Enter();
rb.velocity = new Vector2(rb.velocity.x, player.jumpForce);
}
在y方向速度开始小于0时,角色下落切换到空中状态
public override void Update()
{
base.Update();
if(rb.velocity.y < 0)
stateMachine.ChangeState(player.airState);
}
完善PlayerGroundedState,按下空格时切换到跳跃状态
//更新
public override void Update()
{
base.Update();
if (Input.GetKeyDown(KeyCode.Space))
stateMachine.ChangeState(player.jumpState);
}
完善PlayerAirState,实现落地后切换为闲置状态
public override void Update()
{
base.Update();
if(rb.velocity.y == 0)
stateMachine.ChangeState(player.idleState);
}
完成后运行,效果如下:
你会发现还有一个小问题,下落的时候并没有切换到下落的动画,依旧是向上跳跃时的,因为我们还没有设置混合树的参数。
我们在PlayerState中设置,确保它在跳跃和空中状态都可用。
//更新状态
public virtual void Update()
{
xInput = Input.GetAxisRaw("Horizontal");
player.anim.SetFloat("yVelocity", rb.velocity .y);
}
这样我们就实现了跳跃和下落动画的混合
总结 完整代码
Player.cs
添加创建跳跃状态和空中状态,定义跳跃力度
//Player:玩家
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[Header("Move Info")]
public float moveSpeed = 8f;
public int facingDir { get; private set; } = 1;
private bool facingRight = true;
public float jumpForce = 12f;
#region 组件
public Animator anim { get; private set; }
public Rigidbody2D rb { get; private set; }
#endregion
#region 状态
public PlayerStateMachine StateMachine { get; private set; }
public PlayerState idleState { get; private set; }
public PlayerState moveState { get; private set; }
public PlayerState jumpState { get; private set; }
public PlayerState airState { get; private set; }
#endregion
//创建对象
private void Awake()
{
StateMachine = new PlayerStateMachine();
idleState = new PlayerIdleState(StateMachine, this, "Idle");
moveState = new PlayerMoveState(StateMachine, this, "Move");
jumpState = new PlayerJumpState(StateMachine, this, "Jump");
airState = new PlayerAirState(StateMachine, this, "Jump");
anim = GetComponentInChildren<Animator>();
rb = GetComponent<Rigidbody2D>();
}
// 设置初始状态
private void Start()
{
StateMachine.Initialize(idleState);
}
// 更新
private void Update()
{
StateMachine.currentState.Update();
}
//设置速度
public void SetVelocity(float _xVelocity, float _yVelocity)
{
rb.velocity = new Vector2(_xVelocity, _yVelocity);
FlipController(_xVelocity);
}
//翻转实现
public void Flip()
{
facingDir = -1 * facingDir;
facingRight = !facingRight;
transform.Rotate(0, 180, 0);
}
//翻转控制
public void FlipController(float _x)
{
if (_x > 0 && !facingRight)
Flip();
else if(_x < 0 && facingRight)
Flip();
}
}
PlayerGroundedState.cs
实现接地状态切换到跳跃,并且被PlayerIdleState,PlayerMoveState继承
//超级状态PlayerGroundedState:接地状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerGroundedState : PlayerState
{
//构造函数
public PlayerGroundedState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName)
{
}
//进入
public override void Enter()
{
base.Enter();
}
//退出
public override void Exit()
{
base.Exit();
}
//更新
public override void Update()
{
base.Update();
if (Input.GetKeyDown(KeyCode.Space))
stateMachine.ChangeState(player.jumpState);
}
}
PlayerJumpState.cs
实现设置跳跃速度,实现切换到PlayerAirState空中状态
//PlayJumpState:跳跃状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerJumpState : PlayerState
{
public PlayerJumpState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName)
{
}
public override void Enter()
{
base.Enter();
rb.velocity = new Vector2(rb.velocity.x, player.jumpForce);
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
if(rb.velocity.y < 0)
stateMachine.ChangeState(player.airState);
}
}
PlayerAirState.cs
实现切换到闲置状态
//超级状态PlayerAirState:空中状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAirState : PlayerState
{
//构造函数
public PlayerAirState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName) : base(_stateMachine, _player, _animBoolName)
{
}
//进入
public override void Enter()
{
base.Enter();
}
//退出
public override void Exit()
{
base.Exit();
}
//更新
public override void Update()
{
base.Update();
if(rb.velocity.y == 0)
stateMachine.ChangeState(player.idleState);
}
}
PlayerState.cs
实现混合树参数的设置
//PlayerState:状态基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerState
{
protected PlayerStateMachine stateMachine;
protected Player player;
protected Rigidbody2D rb;
protected float xInput;
private string animBoolName;
//构造函数
public PlayerState(PlayerStateMachine _stateMachine, Player _player, string _animBoolName)
{
this.stateMachine = _stateMachine;
this.player = _player;
this.animBoolName = _animBoolName;
}
//进入状态
public virtual void Enter()
{
//进入动画播放条件变量设置
player.anim.SetBool(animBoolName, true);
//获取刚体
rb = player.rb;
}
//更新状态
public virtual void Update()
{
xInput = Input.GetAxisRaw("Horizontal");
player.anim.SetFloat("yVelocity", rb.velocity .y);
}
//退出状态
public virtual void Exit()
{
//退出动画播放条件变量设置
player.anim.SetBool(animBoolName, false);
}
}