Unity教程(十三)敌人状态机

2024-09-03 10:12

本文主要是介绍Unity教程(十三)敌人状态机,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Unity开发2D类银河恶魔城游戏学习笔记

Unity教程(零)Unity和VS的使用相关内容
Unity教程(一)开始学习状态机
Unity教程(二)角色移动的实现
Unity教程(三)角色跳跃的实现
Unity教程(四)碰撞检测
Unity教程(五)角色冲刺的实现
Unity教程(六)角色滑墙的实现
Unity教程(七)角色蹬墙跳的实现
Unity教程(八)角色攻击的基本实现
Unity教程(九)角色攻击的改进

Unity教程(十)Tile Palette搭建平台关卡
Unity教程(十一)相机
Unity教程(十二)视差背景

Unity教程(十三)敌人状态机

如果你更习惯用知乎
Unity开发2D类银河恶魔城游戏学习笔记目录


文章目录

  • Unity开发2D类银河恶魔城游戏学习笔记
  • 前言
  • 一、概述
  • 二、实体类Entity的创建和继承
    • (1)创建实体类
    • (2)派生Player类与Enemy类
  • 三、敌人状态基类和状态机
    • (1)敌人状态基类
    • (2)敌人状态机
    • (3)创建SkeletonIdleState和SkeletonMoveState
  • 四、创建骷髅小怪Enemy_Skeleton


前言

本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记,如有错误,欢迎指正。

本节实现敌人状态机。创建实体类,派生出敌人和玩家。

对应b站视频:
【Unity教程】从0编程制作类银河恶魔城游戏P47
【Unity教程】从0编程制作类银河恶魔城游戏P48
【Unity教程】从0编程制作类银河恶魔城游戏P49


一、概述

本节我们开始在游戏中添加敌人。
由于敌人很多部分与玩家相似,于是抽象出实体类Entity来复用二者都有的部分。
敌人和玩家都继承自Entity。
敌人的种类有很多,我们在敌人类Enemy的基础上,创建一个骷髅小怪Enemy_Skeleton。
此外与玩家的实现对应,我们还需要敌人状态基类EnemyState,和敌人状态机EnemyStateMechine。

在这里插入图片描述

二、实体类Entity的创建和继承

(1)创建实体类

先整理一下文件,把之前的玩家相关脚本放在同一文件夹Player下,把视差背景脚本拖入Scripts文件夹。
在这里插入图片描述
我们考虑Player和Enemy共有的必须的功能。
首先可以先包含,Awake()、 Start()、Update()三个基本函数。对于一个最简单的来回踱步的小怪,碰撞、翻转和速度设置也是它们共有的基础功能。对应共有的组件要包括刚体和动画师。
在Entity中给需要在子类中重写的函数添加virtual设置为虚函数。
Entity应如下所示:
在这里插入图片描述
代码为

//Entity:实体类
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;public class Entity : MonoBehaviour
{[Header("Flip Info")]protected bool facingRight = true;public int facingDir { get; private set; } = 1;[Header("Collision Info")][SerializeField] protected Transform groundCheck;[SerializeField] protected float groundCheckDistance;[SerializeField] protected Transform wallCheck;[SerializeField] protected float wallCheckDistance;[SerializeField] protected LayerMask whatIsGround;#region 组件public Rigidbody2D rb { get; private set; }public Animator anim { get; private set; }#endregionprotected virtual void Awake(){}//获取组件protected virtual void Start(){rb= GetComponent<Rigidbody2D>();anim= GetComponentInChildren<Animator>();}// 更新protected virtual void Update(){}#region 速度设置//速度置零public void ZeroVelocity() => rb.velocity = new Vector2(0, 0);//设置速度public void SetVelocity(float _xVelocity, float _yVelocity){rb.velocity = new Vector2(_xVelocity, _yVelocity);FlipController(_xVelocity);}#endregion#region 翻转//翻转实现public virtual void Flip(){facingDir = -1 * facingDir;facingRight = !facingRight;transform.Rotate(0, 180, 0);}//翻转控制public virtual void FlipController(float _x){if (_x > 0 && !facingRight)Flip();else if (_x < 0 && facingRight)Flip();}#endregion#region 碰撞//碰撞检测public virtual bool isGroundDetected() => Physics2D.Raycast(groundCheck.position, Vector2.down, groundCheckDistance, whatIsGround);public virtual bool isWallDetected() => Physics2D.Raycast(wallCheck.position, Vector2.right * facingDir, wallCheckDistance, whatIsGround);//绘制碰撞检测protected virtual void OnDrawGizmos(){Gizmos.DrawLine(groundCheck.position, new Vector3(groundCheck.position.x, groundCheck.position.y - groundCheckDistance));Gizmos.DrawLine(wallCheck.position, new Vector3(wallCheck.position.x + wallCheckDistance, wallCheck.position.y));}#endregion
}

(2)派生Player类与Enemy类

抽象出Entity后我们改为由它派生出Player
在这里插入图片描述

//Player:玩家
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Player : Entity
{[Header("Attack details")]public Vector2[] attackMovement;public bool isBusy { get; private set; }[Header("Move Info")]public float moveSpeed = 8f;public float jumpForce = 12f;[Header("Dash Info")][SerializeField] private float dashCoolDown;private float dashUsageTimer;public float dashSpeed=25f;public float dashDuration=0.2f;public float dashDir { get; private set; }#region 状态public PlayerStateMachine StateMachine { get; private set; }public PlayerIdleState idleState { get; private set; }public PlayerMoveState moveState { get; private set; }public PlayerJumpState jumpState { get; private set; }public PlayerAirState airState { get; private set; }public PlayerDashState dashState { get; private set; }public PlayerWallSlideState wallSlideState { get; private set; }public PlayerWallJumpState wallJumpState { get; private set; }public PlayerPrimaryAttackState primaryAttack { get; private set; }  #endregion//创建对象protected override void Awake(){base.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");dashState = new PlayerDashState(StateMachine, this, "Dash");wallSlideState = new PlayerWallSlideState(StateMachine, this, "WallSlide");wallJumpState = new PlayerWallJumpState(StateMachine, this, "Jump");primaryAttack = new PlayerPrimaryAttackState(StateMachine, this, "Attack");}// 设置初始状态protected override void Start(){base.Start();StateMachine.Initialize(idleState);}// 更新protected override void Update(){base.Update();StateMachine.currentState.Update();CheckForDashInput();}public IEnumerator BusyFor(float _seconds){isBusy = true;yield return new WaitForSeconds(_seconds);isBusy = false;}//设置触发器public void AnimationTrigger() => StateMachine.currentState.AnimationFinishTrigger();//检查冲刺输入public void CheckForDashInput(){dashUsageTimer -= Time.deltaTime;if (Input.GetKeyDown(KeyCode.LeftShift) && dashUsageTimer<0){dashUsageTimer = dashCoolDown;dashDir = Input.GetAxisRaw("Horizontal");if (dashDir == 0)dashDir = facingDir;StateMachine.ChangeState(dashState);}}}

修改完后运行一下,检查是否破坏原有功能


接着我们创建敌人Enemy类,
在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy : Entity
{public EnemyStateMachine stateMachine;protected override void Awake(){base.Awake();stateMachine = new EnemyStateMachine();}protected override void Update(){base.Update();stateMachine.currentState.Update();}
}

三、敌人状态基类和状态机

(1)敌人状态基类

敌人状态基类和玩家状态基类也基本相似,其中具有状态可能共同用到的变量,状态的构造函数,和状态的进入更新和退出。
在这里插入图片描述

//EnemyState:敌人状态基类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EnemyState
{protected EnemyStateMachine stateMachine;protected Enemy enemyBase;private string animBoolName;protected float stateTimer;protected bool triggerCalled;//构造函数public EnemyState(EnemyStateMachine _stateMachine, Enemy _enemyBase, string _animBoolName){this.stateMachine = _stateMachine;this.enemyBase = _enemyBase;this.animBoolName = _animBoolName;}public virtual void Enter(){triggerCalled = false;enemyBase.anim.SetBool(animBoolName, true);}public virtual void Update(){stateTimer-= Time.deltaTime;}public virtual void Exit(){enemyBase.anim.SetBool(animBoolName, false);}
}

(2)敌人状态机

敌人状态机中包含当前状态,和初始化状态机和改变状态的函数
在这里插入图片描述

//EnemyStateMachine:状态机
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EnemyStateMachine
{//当前状态public EnemyState currentState { get; private set; }//初始化public void Initialize(EnemyState _startState){currentState = _startState;currentState.Enter();}//改变状态public void ChangeState(EnemyState _newState){currentState.Exit();currentState = _newState;currentState.Enter();}
}

(3)创建SkeletonIdleState和SkeletonMoveState

我们先创建两个类用于骷髅小怪状态机的初始化,至于两个状态的具体内容,将在下一节进行具体实现。
Alt+Enter从子菜单中使用生成构造函数和生成重写。
在这里插入图片描述
骷髅小怪的空闲状态:

//SkeletonIdleState:骷髅空闲状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonIdleState : EnemyState
{public SkeletonIdleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();}
}

骷髅小怪的移动状态:
//SkeletonMoveState:骷髅移动状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SkeletonMoveState : EnemyState
{public SkeletonMoveState(EnemyStateMachine _stateMachine, Enemy _enemyBase, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName){}public override void Enter(){base.Enter();}public override void Exit(){base.Exit();}public override void Update(){base.Update();}
}

四、创建骷髅小怪Enemy_Skeleton

我们以Enemy为基类可以派生出各种各样的有各自特点的敌人,在本教程中我们以骷髅Enemy_Skeleton为例制作一类小怪。
在这里插入图片描述
参照我们起始时Player创建状态机的过程。我们在Enemy基类中创建了状态机,现在要在Skeleton_Enemy中设置骷髅小怪的起始状态。
在Skeleton_Enemy中创建两个状态空闲和移动两个状态,将空闲状态作为起始状态。
在这里插入图片描述

//Enemy_Skeleton:骷髅敌人
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Enemy_Skeleton : Enemy
{#region 状态public SkeletonIdleState idleState { get; private set; }public SkeletonMoveState moveState { get; private set; }#endregionprotected override void Awake(){base.Awake();idleState = new SkeletonIdleState(stateMachine,this,"Idle");moveState = new SkeletonMoveState(stateMachine, this, "Move");}protected override void Start(){base.Start();stateMachine.Initialize(idleState);}protected override void Update(){base.Update();}}

这篇关于Unity教程(十三)敌人状态机的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132692

相关文章

Linux搭建Mysql主从同步的教程

《Linux搭建Mysql主从同步的教程》:本文主要介绍Linux搭建Mysql主从同步的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux搭建mysql主从同步1.启动mysql服务2.修改Mysql主库配置文件/etc/my.cnf3.重启主库my

SpringBoot操作MaxComputer方式(保姆级教程)

《SpringBoot操作MaxComputer方式(保姆级教程)》:本文主要介绍SpringBoot操作MaxComputer方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的... 目录引言uqNqjoe一、引入依赖二、配置文件 application.properties(信息用自己

Tomcat的下载安装与使用教程

《Tomcat的下载安装与使用教程》本文介绍了Tomcat的下载、安装和使用方法,包括在本机和云服务器上部署Tomcat的过程,以及解决启动失败问题的方法... 目录Tomcat的下载安装与使用Tomcat的下载与安装Tomcat在本机运行使用Tomcat在php云服务器上的使用总结Tomcat的下载安装与

SpringBoot基于沙箱环境实现支付宝支付教程

《SpringBoot基于沙箱环境实现支付宝支付教程》本文介绍了如何使用支付宝沙箱环境进行开发测试,包括沙箱环境的介绍、准备步骤、在SpringBoot项目中结合支付宝沙箱进行支付接口的实现与测试... 目录一、支付宝沙箱环境介绍二、沙箱环境准备2.1 注册入驻支付宝开放平台2.2 配置沙箱环境2.3 沙箱

IDEA接入Deepseek的图文教程

《IDEA接入Deepseek的图文教程》在本篇文章中,我们将详细介绍如何在JetBrainsIDEA中使用Continue插件接入DeepSeek,让你的AI编程助手更智能,提高开发效率,感兴趣的小... 目录一、前置准备二、安装 Continue 插件三、配置 Continue 连接 DeepSeek四

jdk21下载、安装详细教程(Windows、Linux、macOS)

《jdk21下载、安装详细教程(Windows、Linux、macOS)》本文介绍了OpenJDK21的下载地址和安装步骤,包括Windows、Linux和macOS平台,下载后解压并设置环境变量,最... 目录1、官网2、下载openjdk3、安装4、验证1、官网官网地址:OpenJDK下载地址:Ar

Java8需要知道的4个函数式接口简单教程

《Java8需要知道的4个函数式接口简单教程》:本文主要介绍Java8中引入的函数式接口,包括Consumer、Supplier、Predicate和Function,以及它们的用法和特点,文中... 目录什么是函数是接口?Consumer接口定义核心特点注意事项常见用法1.基本用法2.结合andThen链

C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)

《C#集成DeepSeek模型实现AI私有化的流程步骤(本地部署与API调用教程)》本文主要介绍了C#集成DeepSeek模型实现AI私有化的方法,包括搭建基础环境,如安装Ollama和下载DeepS... 目录前言搭建基础环境1、安装 Ollama2、下载 DeepSeek R1 模型客户端 ChatBo

JAVA集成本地部署的DeepSeek的图文教程

《JAVA集成本地部署的DeepSeek的图文教程》本文主要介绍了JAVA集成本地部署的DeepSeek的图文教程,包含配置环境变量及下载DeepSeek-R1模型并启动,具有一定的参考价值,感兴趣的... 目录一、下载部署DeepSeek1.下载ollama2.下载DeepSeek-R1模型并启动 二、J

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx