【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili
教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/
本章节制作了新的敌人史莱姆
可爱的小史莱姆
史莱姆的状态机
Enemy_Slime.cs
功能概述
Enemy_Slime
是一个针对史莱姆敌人的控制脚本,用于管理史莱姆的各个状态行为,例如空闲、移动、攻击、眩晕和死亡状态。通过使用状态机设计模式,它实现了模块化、扩展性良好的敌人行为控制。
主要特点
-
模块化设计:
- 各个行为逻辑被封装在单独的状态类中,每个状态负责特定功能。
- 通过状态机轻松切换状态,简化了复杂行为逻辑的管理。
-
状态机集成:
- 状态切换通过
stateMachine.ChangeState()
实现,避免了大量的条件判断。
- 状态切换通过
-
动画集成:
- 每个状态都绑定了对应的动画名称,支持动画与逻辑的同步。
-
可扩展性:
- 新增行为只需定义新的状态类并在
Awake()
中初始化即可,代码具有良好的可维护性。
- 新增行为只需定义新的状态类并在
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using UnityEngine;
//2024.11.9
public class Enemy_Slime : Enemy
{
#region States
public SlimeIdleState idleState { get; private set; }
public SlimeMoveState moveState { get; private set; }
public SlimeBattleState battleState { get; private set; }
public SlimeAttackState attackState { get; private set; }
public SlimeStunState stunnedState { get; private set; }
public SlimeDeathState deadState { get; private set; }
#endregion
protected override void Awake()
{
base.Awake();
SetupDefaultFacingDir(-1);
idleState = new SlimeIdleState(this, stateMachine, "Idle", this);
moveState = new SlimeMoveState(this, stateMachine, "Move", this);
battleState = new SlimeBattleState(this, stateMachine, "Move", this);
attackState = new SlimeAttackState(this, stateMachine, "Attack", this);
stunnedState = new SlimeStunState(this, stateMachine, "Stunned", this);
deadState = new SlimeDeathState(this, stateMachine, "Idle", this);
}
protected override void Start()
{
base.Start();
stateMachine.Initialize(idleState);
}
public override bool CanBeStunned()
{
if (base.CanBeStunned())
{
stateMachine.ChangeState(stunnedState);
return true;
}
return false;
}
public override void Die()
{
base.Die();
stateMachine.ChangeState(deadState);
}
}
SlimeGroundedState.cs
用途
- 用于史莱姆 AI,在地面状态下判断是否接近玩家并进入战斗模式。
- 提高状态切换的清晰性和管理效率。
Update
方法:
- 每帧检查是否满足切换到战斗状态的条件:
- 条件 1:通过
enemy.IsPlayerDetected()
判断玩家是否被检测到。 - 条件 2:检测敌人与玩家的距离是否小于 2.5。
- 条件 1:通过
- 满足条件则切换到
enemy.battleState
。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeGroundedState : EnemyState
{
protected Enemy_Slime enemy;
protected Transform player;
public SlimeGroundedState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Slime _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
enemy = _enemy;
}
public override void Enter()
{
base.Enter();
player = PlayerManager.instance.player.transform;//p63 3:43改
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
if (enemy.IsPlayerDetected() || Vector2.Distance(enemy.transform.position, player.transform.position) < 2.5)
{
stateMachine.ChangeState(enemy.battleState);
}
}
}
SlimeIdleState.cs
特点
- 实现了史莱姆的短暂空闲逻辑,表现为“静止一段时间后开始移动”。
- 基于状态机设计,切换逻辑清晰简洁,便于扩展。
用途
- 用于实现史莱姆的空闲状态,作为移动或攻击状态的过渡环节。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeIdleState : SlimeGroundedState
{
public SlimeIdleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _stateMachine, _animBoolName, _enemy)
{
}
public override void Enter()
{
base.Enter();
stateTimer = enemy.idleTime;
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
if (stateTimer < 0)
{
stateMachine.ChangeState(enemy.moveState);
}
}
}
SlimeMoveState.cs
Update
方法:
- 设置史莱姆的水平速度:
- 按当前朝向移动,速度为
moveSpeed
。
- 按当前朝向移动,速度为
- 环境检测:
- 检测到墙壁或前方无地面时:
- 调用
enemy.Flip()
反转方向。 - 切换到
idleState
,在反转后稍作停顿(通过stateTimer = 0.2f
)。
- 调用
- 检测到墙壁或前方无地面时:
特点
- 实现了史莱姆的基础移动行为。
- 具备简单的环境适应能力(如遇墙或无地面时自动调整)。
用途
- 处理史莱姆的自主移动,作为巡逻或与玩家互动的基础行为逻辑。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeMoveState : SlimeGroundedState
{
public SlimeMoveState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _stateMachine, _animBoolName, _enemy)
{
}
public override void Enter()
{
base.Enter();
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
enemy.SetVelocity(enemy.moveSpeed * enemy.facingDir, enemy.rb.velocity.y);
if (enemy.IsWallDetected() || !enemy.IsGroundDetected())//撞墙或者没有路反转
{
enemy.Flip();
stateTimer = 0.2f;
stateMachine.ChangeState(enemy.idleState);
}
}
}
SlimeAttackState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeAttackState : EnemyState
{
protected Enemy_Slime enemy;
public SlimeAttackState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Slime _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
this.enemy = _enemy;
}
public override void Enter()
{
base.Enter();
}
public override void Exit()
{
base.Exit();
enemy.lastTimeAttack = Time.time;
}
public override void Update()
{
base.Update();
enemy.SetZeroVelocity();//p57攻击敌人时被设置为0速度,所以不会被击退
if (triggerCalled)
stateMachine.ChangeState(enemy.battleState);
}
}
SlimeBattleState.cs
特点
-
玩家交互:
检测玩家状态、位置,并根据条件切换攻击或退出战斗。 -
战斗策略:
包括追逐、攻击、切换状态,体现了战斗的基本 AI 行为。 -
动态调整:
冷却时间随机化(minAttackCoolDown
和maxAttackCoolDown
) -
状态机集成:
清晰地实现了战斗状态与其他状态(如攻击、空闲、移动)的切换。
用途
- 用于实现史莱姆与玩家交战的行为逻辑。
- 为玩家提供挑战性,适合在近战型敌人 AI 中使用。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeBattleState : EnemyState
{
private Enemy_Slime enemy;
private Transform player;
private int moveDir;
public SlimeBattleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Slime enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
this.enemy = enemy;
}
public override void Enter()
{
base.Enter();
//player = GameObject.Find("Player").transform;
player = PlayerManager.instance.player.transform;
if (player.GetComponent<PlayerStats>().isDead)
stateMachine.ChangeState(enemy.moveState);
}
public override void Update()
{
base.Update();
if (enemy.IsPlayerDetected())
{
stateTimer = enemy.battleTime;
if (enemy.IsPlayerDetected().distance < enemy.attackDistance)
{
if (CanAttack())
stateMachine.ChangeState(enemy.attackState);
}
}
else
{
if (stateTimer < 0 || Vector2.Distance(player.transform.position, enemy.transform.position) > 10)//超过距离或者时间到了
stateMachine.ChangeState(enemy.idleState);
}
if (player.position.x > enemy.transform.position.x)
moveDir = 1;
else if (player.position.x < enemy.transform.position.x)
moveDir = -1;
if (Vector2.Distance(player.transform.position, enemy.transform.position) > 1)
enemy.SetVelocity(enemy.moveSpeed * moveDir, rb.velocity.y);
else
enemy.SetZeroVelocity();
}
public override void Exit()
{
base.Exit();
}
private bool CanAttack()
{
if (Time.time >= enemy.lastTimeAttack + enemy.attackCoolDown)
{
enemy.attackCoolDown = Random.Range(enemy.minAttackCoolDown, enemy.maxAttackCoolDown);
enemy.lastTimeAttack = Time.time;
return true;
}
else
return false;
}
}
SlimeStunState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeStunState : EnemyState
{
private Enemy_Slime enemy;
public SlimeStunState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Slime enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
this.enemy = enemy;
}
public override void Enter()
{
base.Enter();
enemy.fx.InvokeRepeating("RedColorBlink", 0, .1f);
stateTimer = enemy.stunDuration;
rb.velocity = new Vector2(-enemy.facingDir * enemy.stunDirection.x, enemy.stunDirection.y);
}
public override void Exit()
{
base.Exit();
enemy.fx.Invoke("CancelColorChange", 0);
}
public override void Update()
{
base.Update();
if (stateTimer < 0)
stateMachine.ChangeState(enemy.idleState);
}
}
SlimeDeathState.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//2024.12.9
public class SlimeDeathState : EnemyState
{
private Enemy_Slime enemy;
public SlimeDeathState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Slime enemy) : base(_enemyBase, _stateMachine, _animBoolName)
{
this.enemy = enemy;
}
public override void Enter()
{
base.Enter();
enemy.anim.SetBool(enemy.lastAnimBoolName, true);
enemy.anim.speed = 0;
enemy.cd.enabled = false;
stateTimer = .18f;
}
public override void Update()
{
base.Update();
if (stateTimer > 0)
rb.velocity = new Vector2(0, 10);
}
}
标签:Enemy,stateMachine,enemy,P175,史莱姆,base,override,using,public
From: https://blog.csdn.net/suzh1qian/article/details/144357471