Unity类银河战士恶魔城学习总结(P180 Enemy Shady 幽影)

教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/

本章节传造了一个会自爆的敌人暗影殉道者

Enemy_Shady.cs

功能与逻辑

  1. 状态机管理

    • 定义了多个状态(如 idleState, moveState, deadState, stunnedState, battleState),每个状态都有对应的逻辑和动画。
    • 使用状态机(stateMachine)动态管理敌人的行为,通过调用 stateMachine.ChangeState 切换状态。
  2. 特殊属性

    • battleStateMoveSpeed: 战斗状态的移动速度。
    • explosivePrefab: 特殊攻击的爆炸物预制体。
    • growSpeedmaxSize: 爆炸物增长速度和最大尺寸。
    • 这些属性定义了暗影法师特有的战斗行为。
  3. 特殊功能

    • 特殊攻击 (AnimationSepcialAttackTrigger):
      • 在敌人执行特殊攻击时,会生成一个爆炸物(explosivePrefab),并根据暗影法师的属性(如爆炸增长速度、最大尺寸、攻击距离)进行初始化。
      • 改变敌人的物理属性(禁用碰撞检测 cd.enabled,重力影响 rb.gravityScale)。
    • 死亡处理 (Die):
      • 切换到死亡状态,可能会触发死亡动画或特效。
    • 被击晕 (CanBeStunned):
      • 检查敌人是否能被击晕,如果可以,则切换到 stunnedState
    • 自毁功能 (SelfDestroy):
      • 用于销毁自身对象,可能是在死亡动画完成后调用。
cs 复制代码
using System.Collections;
using UnityEngine;

public class Enemy_Shady : Enemy
{

    [Header("暗影法师特殊信息")]//Shady specific info
    public float battleStateMoveSpeed ;

    [SerializeField] private GameObject explosivePrefab;
    [SerializeField] private float growSpeed;
    [SerializeField] private float maxSize;


    #region States

    public ShadyIdleState idleState { get; private set; }
    public ShadyMoveState moveState { get; private set; }
    public ShadyDeadState deadState { get; private set; }
    public ShadyStunnedState stunnedState { get; private set; }
    public ShadyBattleState battleState { get; private set; }

    #endregion


    protected override void Awake()
    {
        base.Awake();

        idleState = new ShadyIdleState(this, stateMachine, "Idle", this);
        moveState = new ShadyMoveState(this, stateMachine, "Move", this);

        deadState = new ShadyDeadState(this, stateMachine, "Dead", this);

        stunnedState = new ShadyStunnedState(this, stateMachine, "Stunned", this);
        battleState = new ShadyBattleState(this, stateMachine, "MoveFast", 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);
    }


    public override void AnimationSepcialAttackTrigger()
    {
        GameObject newExplosive = Instantiate(explosivePrefab, transform.position, Quaternion.identity);

        newExplosive.GetComponent<Explosive_Controller>().SetupExplosive(stats, growSpeed, maxSize, attackDistance);


        cd.enabled = false;
        rb.gravityScale = 0;
    }

    public void SelfDestroy() => Destroy(gameObject);

}

Explosive_Controller.cs

功能与逻辑分析

  1. 生长逻辑

    • Update 方法中,通过 Vector2.Lerp 使爆炸物逐渐放大,直到达到 maxSize
    • 当尺寸接近 maxSize 时,停止生长(canGrow = false),并触发爆炸动画 (anim.SetTrigger("Explode"))。
  2. 初始化逻辑

    • SetupExplosive 方法:
      • 初始化爆炸物的相关参数,包括属性来源、增长速度、最大尺寸、爆炸半径。
      • 设置动画组件 Animator
  3. 爆炸逻辑

    • 动画事件触发AnimationExplodeEvent 方法):
      • 使用 Physics2D.OverlapCircleAll 检测爆炸半径内的所有碰撞体。
      • 遍历检测到的对象:
        • 如果对象具有 CharacterStats 组件,说明它是可以受到攻击的单位。
        • 调用其 SetupKnockbackDir 方法,可能实现击退效果。
        • 调用爆炸物来源的 DoDamage 方法,对目标造成伤害。
  4. 销毁逻辑

    • SelfDestroy 方法:
      • 销毁当前爆炸物对象。
      • 应该在爆炸动画完成或某个条件触发后被调用。
cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class Explosive_Controller : MonoBehaviour
{
    private Animator anim;
    private CharacterStats myStats;
    private float growSpeed = 15;
    private float maxSize = 6;
    private float explosionRadius;

    private bool canGrow = true ;

    private void Update()
    {
        if (canGrow)
        {
            transform.localScale = Vector2.Lerp(transform.localScale, new Vector2(maxSize, maxSize), growSpeed * Time.deltaTime);
            explosionRadius = transform.localScale.x / 2; // 动态调整爆炸半径
        }

        if (maxSize - transform.localScale.x < .01f)
        {
            canGrow = false;
            anim.SetTrigger("Explode");
        }
    }



    public void SetupExplosive(CharacterStats _myStats,float _groSpeed,float _maxSize,float _radius)
    {
        anim = GetComponent<Animator>();

        myStats = _myStats;
        growSpeed = _groSpeed;
        maxSize = _maxSize;
        explosionRadius = _radius;
    }



    private void AnimationExplodeEvent()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, explosionRadius);

        foreach (var hit in colliders)
        {
            if (hit.GetComponent<CharacterStats>() != null)
            {
                hit.GetComponent<Entity>().SetupKnockbackDir(transform);
                myStats.DoDamage(hit.GetComponent<CharacterStats>());
            }
        }
    }

    private void SelfDestroy() => Destroy(gameObject);

}

ShadyBattleState.cs

cs 复制代码
using System.Collections;
using UnityEngine;

public class ShadyBattleState : EnemyState
{
    private Transform player;
    private Enemy_Shady enemy;
    private int moveDir;

    public float defaultSpeed;

    public ShadyBattleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Shady _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
    {
        enemy = _enemy;
    }

    public override void Enter()
    {
        base.Enter();

        defaultSpeed = enemy.moveSpeed;

        enemy.moveSpeed = enemy.battleStateMoveSpeed;

       
        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)
                enemy.stats.KillEntity();
        }
        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();

        enemy.moveSpeed = defaultSpeed;
    }

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

ShadyIdleState.cs

cs 复制代码
using System.Collections;
using UnityEngine;

public class ShadyIdleState : ShadyGroundedState
{
    public ShadyIdleState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Shady _enemy) : base(_enemyBase, _stateMachine, _animBoolName,_enemy)
    {
    }

    public override void Enter()
    {
        base.Enter();

        stateTimer = enemy.idleTime;
    }


    public override void Exit()
    {
        base.Exit();

        AudioManager.instance.PlaySFX(24, enemy.transform);
    }

    public override void Update()
    {
        base.Update();

        if (stateTimer < 0)
        {
            stateMachine.ChangeState(enemy.moveState);
        }
    }
}

ShadyMoveState.cs

cs 复制代码
using System.Collections;
using UnityEngine;

public class ShadyMoveState : ShadyGroundedState
{
    

    public ShadyMoveState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Shady _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();

            stateMachine.ChangeState(enemy.idleState);
        }
    }
}

ShadyGroundedState.cs

cs 复制代码
using System.Collections;
using UnityEngine;

public class ShadyGroundedState : EnemyState
{

    protected Transform player;
    protected Enemy_Shady enemy;

    public ShadyGroundedState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName,Enemy_Shady _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) < enemy.agroDistance)
        {
            stateMachine.ChangeState(enemy.battleState);
        }
    }
}

ShadyStunnedState.cs

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShadyStunnedState : EnemyState
{
    private Enemy_Shady enemy;

    public ShadyStunnedState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Shady enemy) : base(_enemyBase, _stateMachine, _animBoolName)
    {
        this.enemy = enemy;
    }

    public override void Enter()
    {
        base.Enter();

        enemy.fx.InvokeRepeating("RedColorBlink", 0, .1f); //这行代码使用 InvokeRepeating 方法,每隔 0.1 秒调用一次 RedColorBlink 方法。

        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);//Invoke 方法用于在指定的延迟时间后调用某个方法。在这里,延迟时间为 0
    }

    public override void Update()
    {
        base.Update();

        if (stateTimer < 0)
            stateMachine.ChangeState(enemy.idleState);

    }
}

ShadyDeadState.cs

cs 复制代码
using UnityEngine;

public class ShadyDeadState : EnemyState
{
    private Enemy_Shady enemy;

    public ShadyDeadState(Enemy _enemyBase, EnemyStateMachine _stateMachine, string _animBoolName, Enemy_Shady _enemy) : base(_enemyBase, _stateMachine, _animBoolName)
    {
        enemy = _enemy;
    }

    public override void Enter()
    {
        base.Enter();


    }

    public override void Update()
    {
        base.Update();

        if(triggerCalled)
            enemy.SelfDestroy();
    }
}
相关推荐
伍贰什丿12 分钟前
C语言学习day22:URLDownloadToFile函数/开发文件下载工具
c语言·c++·学习
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇06】变量类型转化和异常捕获
开发语言·游戏·unity·c#·游戏引擎
伍贰什丿1 小时前
C语言学习day22:ReadProcessMemory函数/游戏内存数据读取工具开发
c语言·开发语言·学习·游戏
小码编匠1 小时前
C# 中最大化 HttpWebRequest 性能实现高效并发请求
后端·c#·.net
zmd-zk2 小时前
shuffle——spark
大数据·分布式·python·学习·spark
九月镇灵将2 小时前
爬虫逆向学习(十四):分享一下某数通用破解服务开发经验
爬虫·学习·逆向·补环境·vm2
南宫生2 小时前
力扣-图论-14【算法学习day.64】
java·学习·算法·leetcode·图论
坐井观老天2 小时前
使用 WPF 和 C# 绘制覆盖有阴影高度图的 3D 表面
开发语言·c#·wpf
Eggbreaker20772 小时前
Unity UI Button 事件优先级调整技术方案
ui·unity·游戏引擎
LuckyLay3 小时前
Golang学习笔记_11——指针
笔记·学习·golang·指针