【Unity】RPG2D龙城纷争(三)角色、角色数据集

更新日期:2024年6月18日。
项目源码:第五章发布(正式开始游戏逻辑的章节)

索引

  • 简介
    • 角色数据集(RoleDataSet)
      • 一、定义角色数据集类
      • 二、角色基础数据(公共数据)
    • 角色(Role)
      • 一、定义角色类
      • 二、角色其他数据(专有数据)
      • 三、角色状态
      • 四、角色移动与停留
      • 五、角色攻击与被击
      • 六、角色剧情对话
      • 七、角色经验与升级
      • 六、角色死亡

简介

本章我们将实现角色类,角色类主要用以装载角色数据集,从而用以驱动角色(移动、剧情对话、攻击等),不过角色驱动逻辑的主场在寻路系统战斗系统模块,这里我们就不将跨度拉得太大了,以免闪了腰。

角色数据集(RoleDataSet)

在考虑角色之前,我们必须先考虑角色数据集(可以理解为角色的一些重要、公共数据的存储集)

试想一下,某一关敌方出现了20个士兵,这些士兵的基础属性需要我们每一个都为其单独定义吗?

肯定不能,这不科学!所以,对于士兵的一些公共数据,我们应当单独定义为角色数据集,然后给所有士兵使用相同的数据集即可。

一、定义角色数据集类

首先,我们定义角色数据集类RoleDataSet

    /// <summary>/// 角色数据集/// </summary>[Serializable][CreateAssetMenu(menuName = "HTFramework/★ GameComponent/RPG2D/Role Asset", order = 300)]public sealed class RoleDataSet : DataSetBase{}

CreateAssetMenu使得他可以通过指定菜单路径创建。

二、角色基础数据(公共数据)

角色的基础数据(公共数据:不同的角色之间共用)我们决定采用六边形属性:速度、生命、攻击、防御、敏捷、会心

  • 速度:决定了角色每一次移动跑得有多远。
  • 生命:决定了角色是否活着;
  • 攻击:决定了角色攻击别人时产生的伤害值;
  • 防御:决定了角色被别人攻击时受到的伤害值;
  • 敏捷:决定了攻击别人时的命中率;
  • 会心:决定了攻击别人时的暴击率。
    public sealed class RoleDataSet : DataSetBase{/// <summary>/// 速度/// </summary>public int Speed;/// <summary>/// 生命/// </summary>public int HP;/// <summary>/// 攻击/// </summary>public int ATK;/// <summary>/// 防御/// </summary>public int DEF;/// <summary>/// 敏捷/// </summary>public int DEX;/// <summary>/// 会心/// </summary>public int CRI;}

不过,思索了2分钟后,我们发觉这样的基础属性好像拉不开等级差距啊,100级跟1级竟然是一样的生命值!打起架来不分上下!?

不行不行,上面这些只能算是初始属性,还得定义相应的随等级增加的属性才行(成长属性),唰唰唰敲完代码:

    public sealed class RoleDataSet : DataSetBase{/// <summary>/// 初始速度/// </summary>[Label("初始速度"), Drawer("初始属性", true)] public int BasicSpeed;/// <summary>/// 初始生命/// </summary>[Label("初始生命")] public int BasicHP;/// <summary>/// 初始攻击/// </summary>[Label("初始攻击")] public int BasicATK;/// <summary>/// 初始防御/// </summary>[Label("初始防御")] public int BasicDEF;/// <summary>/// 初始敏捷/// </summary>[Label("初始敏捷")] public int BasicDEX;/// <summary>/// 初始会心/// </summary>[Label("初始会心")] public int BasicCRI;/// <summary>/// 生命成长值/// </summary>[Label("生命成长值"), Drawer("属性成长值", true)] public int GrowthHP;/// <summary>/// 攻击成长值/// </summary>[Label("攻击成长值")] public int GrowthATK;/// <summary>/// 防御成长值/// </summary>[Label("防御成长值")] public int GrowthDEF;/// <summary>/// 敏捷成长值/// </summary>[Label("敏捷成长值")] public int GrowthDEX;/// <summary>/// 会心成长值/// </summary>[Label("会心成长值")] public int GrowthCRI;}

这下看起来合理了,我们将初始属性成长属性分开,使得不同类型的角色、不同等级的角色拥有了各自的属性成长曲线。

考虑到速度属性的独特性,比如速度也有成长的话,哪怕是最小值1,50级后也能一回合移动50格,这是要逆天啊!所以必须剥夺他的成长权利。

然后注意,使用Drawer特性可以将序列化字段分段整合进可折叠的抽屉中,大大提升了可读性,他的检视面板将是这样的:
在这里插入图片描述

角色(Role)

一、定义角色类

然后,我们定义角色类Role(很明显一个Role将持有一个RoleDataSet数据集):

    [DisallowMultipleComponent]public class Role : HTBehaviour{/// <summary>/// 角色数据集/// </summary>[Label("角色数据集"), SerializeField] internal RoleDataSet DataSet;}

二、角色其他数据(专有数据)

角色的其他数据(专有数据:每一个角色专有的)我们先定义如下这些:

    [DisallowMultipleComponent]public class Role : HTBehaviour{/// <summary>/// 头像顶部(一个位置标记,用以标记角色顶部,一些功能可能会用到,比如剧情对话、头顶飘字)/// </summary>[Label("头像顶部")] public Transform Top;/// <summary>/// 头像渲染器(角色在场景中显示为一个头像方块,这就是渲染器)/// </summary>[Label("头像渲染器")] public SpriteRenderer Head;/// <summary>/// 边框渲染器(角色头像的边框,用以区分阵营,比如青色为己方,红色为敌方)/// </summary>[Label("边框渲染器")] public SpriteRenderer Border;/// <summary>/// 阴影渲染器(角色移动时,身下显示的阴影,用以识别角色当前所在地块)/// </summary>[Label("阴影渲染器")] public SpriteRenderer Shadow;/// <summary>/// 角色头像(角色默认显示头像)/// </summary>[Label("角色头像")] public Sprite HeadImage;/// <summary>/// 角色头像(灰色)(角色禁用时显示头像,比如本回合已行动)/// </summary>[Label("角色头像(灰色)")] public Sprite HeadImage_Gray;/// <summary>/// 角色ID(角色唯一标识符,不能重复)/// </summary>[Label("角色ID")] public string ID;/// <summary>/// 角色姓名/// </summary>[Label("角色姓名")] public string Name;/// <summary>/// 角色等级/// </summary>[Label("角色等级")] public int Grade = 1;/// <summary>/// 角色阵营/// </summary>[Label("角色阵营")] public RoleCamp Camp = RoleCamp.Player;}/// <summary>/// 角色阵营/// </summary>public enum RoleCamp{/// <summary>/// 玩家/// </summary>Player = 0,/// <summary>/// 敌人/// </summary>Enemy = 1}

三、角色状态

然后考虑到某些角色一开始并不存在于场景中(可能存在但不可见),到达指定回合后才支援登场,所以再定义如下属性:

    [DisallowMultipleComponent]public class Role : HTBehaviour{/// <summary>/// 角色状态(还未登场的不可进行交互,活动中的可以进行交互,即便死亡的角色,也属于活动中)/// </summary>[Label("角色状态")] public RoleState State = RoleState.Actived;/// <summary>/// 登场回合(初始状态为NotYetOnStage的角色)/// </summary>[Label("登场回合")] public int ComeOnStageRound = 2;/// <summary>/// 登场地块(初始状态为NotYetOnStage的角色)/// </summary>[Label("登场地块")] public Block ComeOnStageBlock;/// <summary>/// 是否限制移动(如果为true,角色将无法移动,比如某些大BOSS,强大的实力不允许他们下场参战:你们这些喽啰尽管上前送死便是!)/// </summary>[Label("是否限制移动")] public bool IsRestrictMove = false;}/// <summary>/// 角色状态/// </summary>public enum RoleState{/// <summary>/// 还未登场/// </summary>NotYetOnStage = 0,/// <summary>/// 活动中/// </summary>Actived = 1}

四、角色移动与停留

很明显的是角色当前停留在哪个地块是一个重要的数据,即便我们还没有规划战斗系统寻路系统该怎么写,仅仅考虑到角色按地块移动、按地块距离攻击等逻辑,也必须知道角色当前在哪里(也即是角色坐标):

    [DisallowMultipleComponent]public class Role : HTBehaviour{/// <summary>/// 停留的地块/// </summary>[Label("停留的地块")] public Block StayBlock;}

不过,回想起上一篇的地块类(Block),我想如果我们拿到一个地块,也应该能够知道他上面是否站有角色(一个地块只能站一个角色)才对,这应当也是一个重要的数据,所以我们添加代码到Block:

    /// <summary>/// 地块/// </summary>[DisallowMultipleComponent]public class Block : HTBehaviour{/// <summary>/// 停留的角色/// </summary>[Label("停留的角色")] public Role StayRole;}

如此便将RoleBlock建立双向关联,无论我们拿到其中哪一个,都能进一步判断到角色当前的位置。

五、角色攻击与被击

经过深思,这里不再需要相关属性,角色数据集已完成所有定义。

六、角色剧情对话

经过深思,这里不再需要相关属性(因为剧情对话应该与关卡绑定,不属于任何一个角色)。

七、角色经验与升级

角色应当能够积累经验值,用以升级(至于如何升级,这也应当不是角色自身能管的,他只管积累经验,如何升级定然是交给升级系统):

    [DisallowMultipleComponent]public class Role : HTBehaviour{/// <summary>/// 拥有经验值/// </summary>[Label("拥有经验值")] public int HaveExp;}

六、角色死亡

我们来理一理角色的业务逻辑:

1.还未登场的时候是看不见的。

2.登场后,活着的时候可以:移动、攻击、剧情对话等。

3.如果死了(生命小于等于0),角色也将不可见,且不再执行活着时候的逻辑(也即是需要知道角色是否已经死亡)。

那么加入如下属性(这里定义为property,一是这些属性无需序列化,二是他们的值在改变时方便做出一些操作):

    [DisallowMultipleComponent]public class Role : HTBehaviour{/// <summary>/// 死亡音效(死亡时,将如何惨叫)/// </summary>[Label("死亡音效")] public AudioClip DeadAudio;/// <summary>/// 是否死亡/// </summary>[PropertyDisplay("是否死亡")]public bool IsDead{get{return _isDead;}private set{_isDead = value;if (_isDead){//死亡后,应当与站立的地块断开关联if (StayBlock != null){StayBlock.StayRole = null;StayBlock = null;}//且不再显示角色IsShow = false;//_isTriggerDeadEvent,在某些时候我们不想让角色死亡时触发事件和惨叫//,比如重载存档,已经被打死的角色不可能再让他们全部惨叫一遍if (_isTriggerDeadEvent){//如果可以惨叫,则惨叫if (DeadAudio != null) Main.m_Audio.PlayOneShoot(DeadAudio);//抛出角色死亡事件Main.m_Event.Throw(Main.m_ReferencePool.Spawn<EventRoleDead>().Fill(LastOpponent, this));}}}}/// <summary>/// 是否显示角色(未登场角色,死亡角色,都使用此属性隐藏自己)/// </summary>[PropertyDisplay("是否显示角色")]public bool IsShow{get{return gameObject.activeSelf;}set{gameObject.SetActive(value);}}/// <summary>/// 最后一次攻击自己的对手(让别人知道是谁kill了你,以免报仇时杀错人)/// </summary>public Role LastOpponent { get; set; }}

然后是一个简单的角色死亡事件定义:

    /// <summary>/// 角色死亡事件/// </summary>public sealed class EventRoleDead : EventHandlerBase{/// <summary>/// 击杀他的角色/// </summary>public Role Killer { get; private set; }/// <summary>/// 死亡的目标角色/// </summary>public Role Target { get; private set; }/// <summary>/// 填充数据,所有属性、字段的初始化工作可以在这里完成/// </summary>public EventRoleDead Fill(Role killer, Role target){Killer = killer;Target = target;return this;}/// <summary>/// 重置引用,当被引用池回收时调用/// </summary>public override void Reset(){}}

需要注意的是,目前我们仅在数据结构设计阶段,先不用考虑这些数据如何赋值,我想我们后面总会实现他的。

不过,回想上一章的结尾,我们计划中准备写的回合制逻辑依然不知如何下手,好吧,正所谓计划赶不上变化,而且凡事不能一蹴而就,这俩理由足够给自己一个交代了,闪人便是。

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

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

相关文章

最优化第六讲练习题

使用牛顿法 def f(vec):x1,x2vec[0],vec[1]return x1*x1/22*x2*x2def first_order(vec):x1,x2vec[0],vec[1]return np.array((x1,4*x2))x0np.array((2,1)) #初始点 secnp.array([[1,0],[0,4]]) #二阶导 try:invnp.linalg.inv(sec) except:print("矩阵不存在逆矩阵")…

React Native工程运行时下载gradle超时问题

React Native工程在运行Android的时候会下载gradle&#xff0c;但是由于众所周知的问题&#xff0c;总是下载失败&#xff0c;这时可以通过修改 <APP_ROOT>/android/wrapper/gradle-wrapper.properties 文件中 distributionUrl 参数使用国内 gradle 镜像来提高下载速度。…

基于YOLOv5的交通标志检测的设计与实现

简介 在智能交通系统中,交通标志的准确检测与识别对提高道路安全和交通效率至关重要。为了实现这一目标,我们开发了一种基于YOLOv5目标检测模型的交通标志检测系统。本报告将详细介绍该系统的实际应用与实现,包括系统架构、功能实现、使用说明、检测示例、数据集获取与介绍…

Rocky Linux archive下载地址

Index of /vault/rocky/https://dl.rockylinux.org/vault/rocky/

2024年安全员-A证证考试题库及安全员-A证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年安全员-A证证考试题库及安全员-A证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机出的…

ArcGIS arcpy代码工具——批量要素裁剪栅格影像

系列文章目录 ArcGIS arcpy代码工具——批量对MXD文件的页面布局设置修改 ArcGIS arcpy代码工具——数据驱动工具批量导出MXD文档并同步导出图片 ArcGIS arcpy代码工具——将要素属性表字段及要素截图插入word模板 ArcGIS arcpy代码工具——定制属性表字段输出表格 ArcGIS arc…

C++ 79 之 自己写异常类

#include <iostream> #include <string> using namespace std;class MyOutOfRange : public exception{ // 选中exception右键 转到定义 复制一份 virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW 进行函数重写 public: string m_msg;M…

计算机毕设JAVA——学习考试管理系统(基于SpringBoot+Vue前后端分离的项目)

学习考试管理系统 概要系统架构技术运行环境系统功能项目演示图片 概要 网络上许多计算机毕设项目开发前端界面设计复杂、不美观&#xff0c;而且功能结构十分单一&#xff0c;存在很多雷同的项目&#xff1a;页面基本上就是套用固定模板&#xff0c;换个颜色、改个文字&#…

实战电商大数据项目搭建||电商大数据采集||电商API接口

我会提供给你大概1亿条真实的互联网用户上网数据&#xff0c;至于来源&#xff0c;我先不告诉你&#xff0c;绝对是你在网络上无法找到的宝贵数据源。 此外&#xff0c;还会给你提供一个基于当前数据特点而设计的大数据处理方案。 当然&#xff0c;为了防止用户的隐私部分被泄露…

Yarn的安装与使用详细教程

一、引言 Yarn是一个快速、可靠、安全的JavaScript包管理器&#xff0c;用于管理项目的依赖项。与npm&#xff08;Node.js的包管理器&#xff09;类似&#xff0c;Yarn具有许多优点&#xff0c;包括更快的安装速度、更简洁的输出以及更安全的依赖解析。在本教程中&#xff0c;…

nuscenes 数据集学习笔记

目录 这个讲的比较细致&#xff1a; 安装&#xff1a; 数据信息类型: Data 传感器 读取数据&#xff1a; boxes3d_to_corners3d 这个讲的比较细致&#xff1a; NuSences 数据集解析以及 nuScenes devkit 的使用_nuscenes-devkit-CSDN博客 安装&#xff1a; pip install…

如何提升外链网站的收录率?

要提高外链网站的收录率&#xff0c;要明确的一点是&#xff0c;被收录的外链才能发挥最大的作用&#xff0c;因此&#xff0c;提升收录率是首要任务。一个有效的方法是使用GPC爬虫池&#xff0c;这样可以大幅度提高谷歌蜘蛛对众多外链网站页面的抓取频率 通过GPC爬虫池的引导…

【LeetCode热题 100】螺旋矩阵

leetcode原地址&#xff1a;https://leetcode.cn/problems/spiral-matrix/description 描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8…

【笔记】记录一下git忽略文件.gitignore文件,方便查找

.gitignore文件 常见需要忽略的文件和目录 HELP.md target/ !.mvn/wrapper/maven-wrapper.jar !**/src/main/** !**/src/test/**### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache### IntelliJ IDEA ### .idea *.iws *.iml *…

使用Scala爬取安居客房产信息并存入CSV文件

使用Scala爬取安居客房产信息并存入CSV文件 本篇博客中&#xff0c;我们将介绍如何使用Scala语言编写一个简单的程序&#xff0c;来爬取安居客&#xff08;Anjuke&#xff09;网站上的房产信息&#xff0c;并将这些信息存储到CSV文件中。这个示例将涵盖HTTP请求、HTML解析、数…

00 - matlab m_map地学绘图工具安装及简单使用教程

00 - matlab m_map地学绘图工具安装及简单使用教程 0. 引言1. m_map工具的获取及配置过程2. 绘图示例3. 结语 0. 引言 m_map是MATLAB中的一个绘图工具包&#xff0c;用于绘制地图和地理数据。它提供了一系列函数&#xff0c;可以用来绘制地理投影、添加地理特征、绘制等值线图等…

用nginx设置缓存

浏览器缓存 首先明白http的几个字段&#xff0c; 但expire不一定有用&#xff0c;火狐依然每次304&#xff0c; 谷歌即使没有expire也一样用浏览器缓存&#xff0c;不发送请求。难道expire没有 谷歌就认为永久&#xff1f;expire有就遵守时间&#xff1f; Chrome中的from me…

「漏洞复现」真内控国产化开发平台 preview 任意文件读取漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

[SAP ABAP] 运算符与操作符

1.算数运算符 算术运算符描述加法-减法*乘法/除法MOD取余 示例1 输出结果: 输出结果: 2.比较运算符 比较运算符描述示例 等于 A B A EQ B <> 不等于 A <> B A NE B >大于 A > B A GT B <小于 A < B A LT B >大于或等于 A > B A GE B <小…

AI都那么发达了,我还有必要学习编程吗

尽管 AI 技术在不断发展&#xff0c;但学习编程仍然具有重要的意义和价值。 以下是一些原因&#xff1a; 培养逻辑思维和解决问题的能力&#xff1a;编程需要你思考问题、设计算法和解决难题。通过学习编程&#xff0c;你可以锻炼自己的逻辑思维能力&#xff0c;提高分析和解决…