首页 > 其他分享 >Unity类银河战士恶魔城学习总结(P175 Enemy Slime史莱姆)

Unity类银河战士恶魔城学习总结(P175 Enemy Slime史莱姆)

时间:2024-12-13 12:57:13浏览次数:10  
标签:Enemy stateMachine enemy P175 史莱姆 base override using public

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili

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

本章节制作了新的敌人史莱姆

可爱的小史莱姆

史莱姆的状态机

Enemy_Slime.cs

功能概述

Enemy_Slime 是一个针对史莱姆敌人的控制脚本,用于管理史莱姆的各个状态行为,例如空闲、移动、攻击、眩晕和死亡状态。通过使用状态机设计模式,它实现了模块化、扩展性良好的敌人行为控制。

主要特点

  1. 模块化设计

    • 各个行为逻辑被封装在单独的状态类中,每个状态负责特定功能。
    • 通过状态机轻松切换状态,简化了复杂行为逻辑的管理。
  2. 状态机集成

    • 状态切换通过 stateMachine.ChangeState() 实现,避免了大量的条件判断。
  3. 动画集成

    • 每个状态都绑定了对应的动画名称,支持动画与逻辑的同步。
  4. 可扩展性

    • 新增行为只需定义新的状态类并在 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。
  • 满足条件则切换到 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

特点

  1. 玩家交互

    检测玩家状态、位置,并根据条件切换攻击或退出战斗。
  2. 战斗策略

    包括追逐、攻击、切换状态,体现了战斗的基本 AI 行为。
  3. 动态调整

    冷却时间随机化(minAttackCoolDownmaxAttackCoolDown
  4. 状态机集成

    清晰地实现了战斗状态与其他状态(如攻击、空闲、移动)的切换。

用途

  • 用于实现史莱姆与玩家交战的行为逻辑。
  • 为玩家提供挑战性,适合在近战型敌人 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

相关文章

  • 「CCPC 2023 北京市赛」史莱姆工厂
    由于每次合并可以刻画为向外延伸,那么考虑区间\(\text{dp}\),设\(dp_{l,r,m,c}\)表示考虑了\([l,r]\)且剩下了一个质量为\(m\in[0,K)\)颜色为\(c\)的史莱姆的答案。状态过大且转移方程不便于优化而考虑优化状态,由于对于一个极短的需要合并成一个史莱姆的区间\([p,q]\),......
  • The Forest Enemy Pack(2D动画角色游戏模型)
    这个包包含14个适用于platformer和2drpg游戏的动画角色。动画总帧数:1785用于动画的所有精灵都具有透明背景,并准备有1500x1200和750x600两种尺寸。对于每个角色,你也可以找到具有单独身体部位的精灵表,这样你就可以轻松地制作自己的动画。它们有PNG和PSD格式。示例场景包含......
  • 关于我转生变成史莱姆这档事 第三季[追更记录]
    第48.5集(第00集)更新3月30日闲话:迪亚波罗日记第49集(第01集)更新4月05日恶魔与谋策第50集(第02集)更新4月12日圣人的意图第51集(第03集)更新4月19日和平的日子第52集(第04集)更新4月26日各自的职责第53集(第05集)更新5月03日两翼会议第54集(第06集)更......
  • 数学森林/洛谷P1750 出栈序列
    原创新思路求解出栈序列问题。问题描述:数学家小王经过千辛万苦长途跋涉终于来到了数学森林。无奈森林入口有很多个小矮人镇守。小矮人拿出一套题目让小王抽取一道题目说解出题目方能进入数学森林。题目如下:给定一个大小为c(最多可以同时存储c个元素)的堆栈,输入n个入栈的数,请输......
  • [题解]P1757 通天之分组背包
    P1757通天之分组背包分组背包模板题。总共\(s\)组,每组最多取一个物品,实际上就是一个物品总数为\(s\)的背包。for(inti=1;i<=s;i++){//枚举组 for(intj=1;j<=n[i];j++){//枚举每组的元素 for(intk=1;k<=m;k++){//枚举背包大小 f[i][k]=max(f[i][k],f[i-1][k]); if(......
  • [题解]CF61E Enemy is weak
    CF61EEnemyisweak如下图,第\(i\)行\(j\)列表示第\(j\)个数结尾,向前长度为\(i\)的逆序子序列个数。递推方式见下图。第一行全为\(1\)。要填第\(2\)行的值,就往前找所有\(>\)当前元素的位置,把它们第\(1\)行的值加起来。要填第\(3\)行的值,就往前找所有\(>\)当前元素的位置,把......
  • P1757 通天之分组背包
    原题链接题解对于每个容量,当前组\(i\)而言,放的决策有\(size(i)+1\)种code#include<bits/stdc++.h>usingnamespacestd;structunit{intw,v;};vector<unit>G[1005];intmain(){intm,n;cin>>m>>n;intlen=0;for(inti=1;i&......
  • Unity-敌人(Enemy)
    Unity-敌人(Enemy)引言​ 敌人是每个游戏中不可缺少的部分,设计得好的敌人可以给游戏增添很多乐趣,设计得差的则会非常影响我们的游戏体验。​ 经过这段时间的学习,我们已经接触了非常多的敌人代码的写法,但是就是没有系统的归类,导致每次写敌人,都要从头开始。现在是时候将他们进行一......
  • Programming abstractions in C阅读笔记:p166-p175
    《ProgrammingAbstractionsInC》学习第58天,p166-p175总结。一、技术总结1.斐波那契数列(FibonacciSequenc)(1)斐波那契数列来源斐波那契数列来自于《LiberAbaci》一书里兔子繁殖问题,相关资料很多,这里不赘述。(2)关于《LiberAbaci》一书《LiberAbaci》——Liber:abook......
  • P1757 通天之分组背包 题解
    思路分组背包模版题,不多说。代码#include<bits/stdc++.h>#definelllonglong#defineldlongdoubleusingnamespacestd;inlinevoidread(int&x){ x=0; shortflag=1; charc=getchar(); while(c<'0'||c>'9'){ if(c=='-......