Unity教程(十四)敌人空闲和移动的实现

news2024/12/27 13:04:51

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

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

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

Unity教程(十三)敌人状态机
Unity教程(十四)敌人空闲和移动的实现


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


文章目录

  • Unity开发2D类银河恶魔城游戏学习笔记
  • 前言
  • 一、概述
  • 二、创建动画(可不看,自行实现即可)
    • (1)创建Enemy_Skeleton并添加动画师相关组件
    • (2)创建空闲状态和移动状态动画
  • 三、添加刚体、碰撞盒和碰撞检测(可不看,自行实现即可)
    • (1)添加刚体
    • (2)添加碰撞盒
    • (3)添加碰撞检测
  • 四、实现空闲状态和移动状态
    • (1)修改构造函数
    • (2)实现空闲状态
    • (2)实现移动状态
  • 总结 完整代码
    • Enemy
    • Enemy_Skeleton
    • SkeletonIdleState
    • SkeletonMoveState


前言

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

本节实现敌人的空闲状态和移动状态。
对应b站视频:
【Unity教程】从0编程制作类银河恶魔城游戏P49

一、概述

本节中我们实现骷髅的空闲状态和移动状态。
骷髅小怪在地面上来回踱步,遇到地面边缘或者墙壁时翻转且由移动状态转换为空闲状态。
每隔一个时间间隔由空闲状态转换为移动状态。
在这里插入图片描述
在这里插入图片描述


看过前面Player相关步骤的,创建动画和添加碰撞检测的部分可跳过不看,自行实现。 记得将Animator中的Sorting Layer改为Enemy即可。

二、创建动画(可不看,自行实现即可)

(1)创建Enemy_Skeleton并添加动画师相关组件

骷髅的精灵表存放路径为
Assets->Graphics->Enemies->Skeleton
在这里插入图片描述
此处所用素材已经分割好了,如果想学习从头到尾分割精灵表可以参照
Unity教程(零)Unity和VS的使用相关内容


将Skeleton Idle的0帧拖入场景中,并重命名为Animator。
接着将它的Sorting Layer改为Enemy,骷髅显示在上层。
在这里插入图片描述
在这里插入图片描述
为Animator创建一个空的父对象,重命名为Enemy_Skeleton。
在这里插入图片描述
在这里插入图片描述
此时Animator并不在Enemy_Skeleton的中心位置。接下来将Animator图片调整到父对象的中心。
在层次面板选中Animator,移动它的位置,这里调整后Animator的x坐标大概为0.25
在这里插入图片描述
在层次面板中点击选中Animator,添加Animator组件
在右侧面板中点击下方Add Component添加组件,搜索Animator并点击添加

在这里插入图片描述
在这里插入图片描述

(2)创建空闲状态和移动状态动画

整理一下之前的动画和控制器。大致如下:
在这里插入图片描述
动画的存放
在这里插入图片描述

在这里插入图片描述
控制器的存放
在这里插入图片描述


在controllers文件夹中创建Enemy_Skeleton控制器。
右击创建Animator Controlller,命名为Skeleton_AC
右键->Create->Animator Controller
在这里插入图片描述
拖动Skeleton_AC到Animator的Animator组件上
在这里插入图片描述
创建空闲状态动画skeletonIdle
skeletonIdle在Skeleton Idle精灵表标号3-10,采样率改为12
具体讲解见Unity教程(零)Unity和VS的使用相关内容
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

同理,skeletonMove,将Skeleton Walk精灵表整个加入,采样率改为10
在这里插入图片描述
在这里插入图片描述
连接状态机,并添加过渡条件Idle和Move,并修改过渡设置
在这里插入图片描述
Entry->skeletonMove的过渡,加条件变量
在这里插入图片描述

skeletonMove->Exit的过渡,加条件变量并更改设置
在这里插入图片描述

三、添加刚体、碰撞盒和碰撞检测(可不看,自行实现即可)

(1)添加刚体

要让敌人按照物理规则正常反应我们还要添加刚体组件。
Add Component->Rigidbody 2D
并进行如下设置:
在刚体中设置Constraints->Freeze Rotation->Z,冻结Z轴,这样骷髅在移动时就不会倒下了。
Collision Detection碰撞检测选择Continuous,防止骷髅没有检测碰撞而穿过其他对象。
Interpolate插值方式选择Interpolate,因为有时物理与图形的不同步会导致视觉抖动,打开插值可以进行平滑。
Gravity Scale设置为4,防止被玩家撞下平台。


在这里插入图片描述

(2)添加碰撞盒

给骷髅添加碰撞盒并调整到合适大小
Add Component->Box Collider 2D
在这里插入图片描述
在这里插入图片描述

2D碰撞盒和2D刚体详细讲解请见 官方手册2D物理系统参考。

(3)添加碰撞检测

在Enemmy_Skeleton下创建空物体并重命名为GroundCheck
右键->Create Empty->重命名为GroundCheck
在这里插入图片描述

将GroundCheck拖入并增大GroundCheckDistance。将GroundCheck移到合适的位置,这次我们把它放在骷髅身前。
在这里插入图片描述

在这里插入图片描述

将Enemy_Skeleton直接拖入墙体检测,调整WallCheckDistance到合适的值。
WhatIsGround选择Ground
在这里插入图片描述
在这里插入图片描述

四、实现空闲状态和移动状态

(1)修改构造函数

上一节中我们创建了SkeletonIdleState和SkeletonMoveState两个脚本
我们希望在两个脚本中获取Enemy_Skeleton的实例,并调用它的某些变量。于是我们添加变量穿入骷髅小怪的实例,同时在构造函数里为它赋值。
两个脚本中构造函数分别改为


    private Enemy_Skeleton enemy;

    public SkeletonIdleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy,string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName)
    {
        enemy = _enemy;
    }
    private Enemy_Skeleton enemy;

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

同时在Enemy_Skeleton中创建状态的函数也要做相应修改。

    protected override void Awake()
    {
        base.Awake();
        idleState = new SkeletonIdleState(stateMachine,this,this,"Idle");
        moveState = new SkeletonMoveState(stateMachine, this,this, "Move");
    }

这里的修改比较令人费解。考虑了一下最后我的理解是:
我们希望在两个脚本中获取Enemy_Skeleton的实例。但在定义EnemyState时,考虑到它适用于所有类型的敌人,所以使用了敌人基类Enemy。
首先我们要了解一些C#继承的相关知识,更具体的可以自行了解。在这里我们需要知道,子类实例化时会先执行父类的构造函数,再执行子类的构造函数。父类有了带参数的构造函数,子类的构造函数也可以不和父类的构造函数一样。但是如果父类定义了带参数的构造函数同时没有无参重载,那么在子类中必须对父类的带参数的构造进行赋值。
这就导致由它派生的两个脚本中,构造函数必须有敌人基类这个变量。如果想获取不同敌人独有的变量和函数,就只能再添加一个参数传具体的敌人类,即这里的Enemy_Skeleton。

(2)实现空闲状态

空闲状态的实现思路与Player基本一致,但略有不同的是骷髅处于来回移动的状态,并且在移动间隙会站一会儿再继续移动。
我们在Enemy中添加两个变量,一个是移动速度moveSpeed,一个是骷髅停顿的时间idleTime。

    [Header("Move Info")]
    public float moveSpeed = 1.5f;
    public float idleTime = 2.0f;

SkeletonIdleState代码如下:

//SkeletonIdleState:骷髅空闲状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SkeletonIdleState : EnemyState
{

    private Enemy_Skeleton enemy;

    public SkeletonIdleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy,string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName)
    {
        enemy = _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);
    }
}

(2)实现移动状态

我们要在移动状态中设置骷髅的速度。
在骷髅碰到墙壁或者碰到平台边缘时翻转并转为空闲状态停住一会儿。
在这里插入图片描述
代码如下:

//SkeletonMoveState:骷髅移动状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SkeletonMoveState : EnemyState
{

    private Enemy_Skeleton enemy;

    public SkeletonMoveState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName)
    {
        enemy = _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.isGroundDetected() || enemy.isWallDetected())
        {
            enemy.Flip();
            stateMachine.ChangeState(enemy.idleState);
        }
    }
}


效果如下:
在这里插入图片描述

总结 完整代码

Enemy

添加移动信息

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : Entity
{
    [Header("Move Info")]
    public float moveSpeed = 1.5f;
    public float idleTime = 2.0f;

    public EnemyStateMachine stateMachine;

    protected override void Awake()
    {
        base.Awake();
        stateMachine = new EnemyStateMachine();
    }


    protected override void Update()
    {
        base.Update();
        stateMachine.currentState.Update();
    }
}

Enemy_Skeleton

修改创建状态的语句。

//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; }
    #endregion

    protected override void Awake()
    {
        base.Awake();
        idleState = new SkeletonIdleState(stateMachine,this,this,"Idle");
        moveState = new SkeletonMoveState(stateMachine, this,this, "Move");
    }

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

        stateMachine.Initialize(idleState);
    }

    protected override void Update()
    {
        base.Update();
    }

}

SkeletonIdleState

设置计时器初值,实现到移动状态的切换。

//SkeletonIdleState:骷髅空闲状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SkeletonIdleState : EnemyState
{

    private Enemy_Skeleton enemy;

    public SkeletonIdleState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy,string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName)
    {
        enemy = _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);
    }
}

SkeletonMoveState

设置骷髅的速度,实现到空闲状态的切换

/SkeletonMoveState:骷髅移动状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SkeletonMoveState : EnemyState
{

    private Enemy_Skeleton enemy;

    public SkeletonMoveState(EnemyStateMachine _stateMachine, Enemy _enemyBase, Enemy_Skeleton _enemy, string _animBoolName) : base(_stateMachine, _enemyBase, _animBoolName)
    {
        enemy = _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.isGroundDetected() || enemy.isWallDetected())
        {
            enemy.Flip();
            stateMachine.ChangeState(enemy.idleState);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2109299.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MT6895(天玑8100)处理器规格参数_MTK联发科平台方案

MT6895平台 采用台积电5nm工艺&#xff0c;与天玑 8000 相比性能提升 20% &#xff0c;搭载4 个 2.85GHz A78 核心 4 个 2.0GHz A55 核心&#xff0c;CPU能效比上一代提高 25% 。GPU 采用了第三代的Valhall Arm Mali-G610 MC6架构&#xff0c;拥有6核心&#xff0c;搭配天玑81…

ubuntu22.04 qemu 安装windows on arm虚拟机

ubuntu22.04 qemu 安装windows on arm虚拟机 iso: https://uupdump.net/ https://massgrave.dev/windows_arm_links vivo driver: https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.262-2/ qemu sudo apt update sudo a…

Java笔试面试题AI答之JDBC(1)

文章目录 1. 什么是JDBC&#xff1f;2. 驱动(Driver)在JDBC中的角色&#xff1f;3. JDBC PreparedStatement比Statement有什么优势&#xff1f;1. 预编译和性能提升2. 参数化查询和安全性3. 更好的可读性和可维护性4. 支持批量操作5. 缓存机制&#xff08;特定数据库环境&#…

【自考zt】【数据结构】【21.10】

【关键字】 数据元素基本单位、抽象数据类型、上三角压缩对称矩阵、排序O&#xff08;n2&#xff09;、不宜链表快排 循环队列入队下标、二叉链表空指针、无相连通图边数差、B树非根结点关键字 链栈无头结点 单链表前二节点和、邻接矩阵度、二叉排序树 一、单选 二、填…

WGCLOUD可以监测交换机的哪些指标数据

WGCLOUD有个模块SNMP监测&#xff0c;可以用于监测交换机、防火墙等设备 监测的指标包括&#xff1a;上行流量&#xff0c;下行流量&#xff0c;每个接口的传输速率&#xff08;包括上行和下行&#xff09;&#xff0c;每个接口的状态&#xff0c;基本信息&#xff0c;温度&am…

Kafka【十一】数据一致性与高水位(HW :High Watermark)机制

【1】数据一致性 Kafka的设计目标是&#xff1a;高吞吐、高并发、高性能。为了做到以上三点&#xff0c;它必须设计成分布式的&#xff0c;多台机器可以同时提供读写&#xff0c;并且需要为数据的存储做冗余备份。 图中的主题有3个分区&#xff0c;每个分区有3个副本&#xf…

解决商店汽水兑换问题——利用贪心算法与循环结构

解决商店汽水兑换问题——利用贪心算法与循环结构 在某商店中,有一种特别的促销活动:三个空汽水瓶可以换一瓶汽水。而且,如果空瓶数量不足,还可以向老板借空瓶(但必须要归还)。给定初始的空瓶数量,如何计算最多可以喝到多少瓶汽水?这个问题可以通过贪心算法来高效解决…

windows手工杀毒-寻找可疑进程之线程

上篇回顾&#xff1a;windows手工杀毒-寻找可疑进程之进程模块-CSDN博客 上篇我们介绍了如何通过进程模块寻找可疑进程&#xff0c;进程模块文件按照PE格式存储&#xff0c;我们可以使用IDA等静态分析&#xff08;不需要运行文件&#xff0c;只看文件内容&#xff09;工…

仕考网:军队文职人员公开招考笔试考试大纲

考试方式和时限 考试方式为闭卷笔试&#xff0c;考试时限为120分钟。 试卷分值和试题类型 试卷满分为100分&#xff0c;试题类型为客观性试题。 测查内容 测查内容主要包括基本知识和岗位能力&#xff0c;具体内容如下。 1.基本知识 2.岗位能力 ①言语理解与表达 ②数…

redis主从+高可用切换+负载均衡

1. redis主从配置 # 在master中 cp sentinel.conf /etc/redis/ vim /etc/redis/sentinel.conf scp /etc/redis/sentinel.conf server2:/etc/redis/ scp /etc/redis/sentinel.conf server3:/etc/redis/ redis-sentinel /etc/redis/sentinel.conf # 启动监控# 在slave中 redis-s…

Java SpringBoot集成Vue.js,构建茶园茶农文化交流平台,四步实现高效互动,MySQL存储数据更稳定

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

LevelDB RockesDB LSM

LevelDB levelDB是同样也是一个Key-value数据库&#xff0c;但是相对于Redis、memcache来说&#xff0c;levelDB是基于内存-磁盘来实现的&#xff0c;但在大部分场景下也表现出了不逊色于Redis、Memcache的性能。levelDB由google实现并开源&#xff0c;轻松支持billion量级的数…

解读:以RTC为基,AI为脑的“超拟人”AI实时互动解决方案

我们打造了一款满足想象与应用的智能体——AI实时互动。 谈谈AI智能体 当AI变得足够聪明时&#xff0c;用户与AI的交互将变得真实自然。于是&#xff0c;构建高拟真AI与用户的实时交互&#xff0c;已经成为企业提升数智化生产力的新思路。 在这个交互过程中&#xff0c;存在一…

html备忘录

备忘录 网站收藏数据&#xff1a; 网站收藏.js const webLinks [{ title: "智能翻译", src: "https://fanyi.baidu.com" },{ title: "哔哩哔哩", src: "https://www.bilibili.com" },{ title: "百度一下&#xff0c;你就知道&…

图欧科技-IMYAI智能助手24年8月更新日志大汇总(含史诗级更新)

IMYAI史诗级更新 图欧君最近行程排得满满当当 但请各位小伙伴放心 我们的更新步伐从未停歇 而这次我们IMYAI主站也迎来了史诗级升级 一起来看看这个8月 我们又新增了哪些功能和优化吧~ 8.26&#xff5c;更新日志 模型选择界面鼠标悬停左右按钮可以自动滚动&#xff08;手机端长…

在人工智能的浪潮中:AI大模型的涌现、商业变革与产品经理的应对策略

在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;作为一股不可阻挡的力量&#xff0c;正以前所未有的速度重塑着我们的世界。其中&#xff0c;AI大模型以其惊人的涌现能力和创造力&#xff0c;成为了这场技术革命中的璀璨明星。正如达尔文所言&#xff1a;…

ARM切换工作模式,异常处理

切换工作模式 处理软中断 preserve8area reset, code, readonlycode32entryb start ;resetnop ;undefb deal_swi ;swinop ;prefetch abortnop ;data abortnop ;reservednop ;irpnop ;fiq deal_swistmfd sp!,{r4-r12,lr};保护现场sub r1,lr,#4 …

代码随想录刷题day23丨39. 组合总和,40.组合总和II, 131.分割回文串

代码随想录刷题day23丨39. 组合总和&#xff0c;40.组合总和II&#xff0c; 131.分割回文串 1.题目 1.1组合总和 题目链接&#xff1a;39. 组合总和 - 力扣&#xff08;LeetCode&#xff09; 视频讲解&#xff1a;带你学透回溯算法-组合总和&#xff08;对应「leetcode」力…

计算机网络基础笔记(二)

计算机网络基础笔记&#xff08;二&#xff09; OSI网络模型 osi&#xff08;开放系统互联–Open System Interconnect&#xff09;模型是一种通信协议的框架&#xff0c;作用是在不同计算机系统之间互联。该模型间通信分为七个层次&#xff0c;每个层次负责特定的功能&#…

设计模式-单例模式工厂模式

3.1 单例模式 1.概念 用类来实现单例。由于某种需要&#xff0c;要保证一个类在程序的生命周期中只有一个实例&#xff0c;并且提供该实例的全局访问方法。 2.结构三要素 1)私有的静态对象属性private static instance&#xff0c;它的类型就是当前类的对象&#xff0c;静态…