【Unity】RPG2D龙城纷争(二)关卡、地块

更新日期:2024年6月12日。
项目源码:后续章节发布

索引

  • 简介
    • 地块(Block)
      • 一、定义地块类
      • 二、地块类型
      • 三、地块渲染
      • 四、地块索引
    • 关卡(Level)
      • 一、定义关卡类
      • 二、关卡基础属性
      • 三、地块集合
      • 四、关卡初始化
      • 五、关卡销毁
      • 六、回合制逻辑

简介

本章我们将从零开始实现关卡,不过,我们的目的是实现关卡与地块的基本逻辑,更复杂的功能我们打算在开发关卡编辑器时再做涉及,正所谓一步一个坎,就没有迈不过去的高山。

地块(Block)

地块作为关卡的组成元素,他是一个正方形的格子(当然也可以是菱形,针对斜视角游戏,不过这不在框架的支持范畴内,但做出一定的修改即可实现),每一个地块都有属于他自己的一些属性。

一、定义地块类

首先,我们定义地块类Block

    /// <summary>/// 地块/// </summary>[DisallowMultipleComponent]public class Block : HTBehaviour{}

Block继承至HTBehaviour,使得它可以挂载到游戏物体上,然后作为一个地块对象(为不需要一个物体上多次挂载的组件定义DisallowMultipleComponent是一个好习惯,这将提高容错率,如果你的代码会给别人使用的话)。

很多同学热衷于在类继承链上规避MonoBehaviour,其实完全没有这个必要,只要你不滥用MonoBehaviour的生命周期,它将提供给你几大优势:
1.属性可编辑(Inspector面板);
2.属性可调试(Inspector面板);
3.对象可追踪(Scene里面有则有,无则无);
4.对象可销毁(当确定不再使用时,Destroy它,而不用置null后交给GC)。

二、地块类型

经过深思熟虑,我们将地块类型划分为如下几种:

  • 地面
  • 山体
  • 森林
  • 湖泊
  • 雪地
  • 障碍

编写代码:

    /// <summary>/// 地块/// </summary>[DisallowMultipleComponent]public class Block : HTBehaviour{/// <summary>/// 类型/// </summary>[Label("类型")] public BlockType Type;}/// <summary>
/// 地块类型
/// </summary>
public enum BlockType
{/// <summary>/// 地面/// </summary>[Remark("地面")]Ground = 0,/// <summary>/// 山体/// </summary>[Remark("山体")]Moutain = 1,/// <summary>/// 森林/// </summary>[Remark("森林")]Forest = 2,/// <summary>/// 湖泊/// </summary>[Remark("湖泊")]Water = 3,/// <summary>/// 雪地/// </summary>[Remark("雪地")]Snow = 4,/// <summary>/// 障碍/// </summary>[Remark("障碍")]Obstacle = 5
}

然后,我们设计每种类型的地块拥有的交互权限如下:

类型角色可行走角色可攻击(站在上面的敌人或穿过它攻击其他敌人)
地面
山体受限(拥有飞檐走壁可行走,行走速度减1/2
森林受限(行走速度减1/2
湖泊受限(拥有踏水神行可行走,行走速度减1/2
雪地受限(行走速度减2/3
障碍受限(拥有隔山打牛可穿透障碍进行攻击

需注意的是,任意地块,当上面站有敌人时,将不可行走,不可跨越,当站有队友时,不可行走,可跨越。

也即是说,我们可以使用角色摆出特定的阵型,以拦住敌方的行走路线,或达到包围的效果。

三、地块渲染

地块的渲染我们决定选择SpriteRenderer,且一个地块仅渲染一张图片,那么,在Block类中需要持有地块渲染器的引用,我们添加代码:

    /// <summary>/// 地块/// </summary>[DisallowMultipleComponent]public class Block : HTBehaviour{/// <summary>/// 目标渲染器/// </summary>[Label("目标渲染器")] public SpriteRenderer Target;/// <summary>/// 类型/// </summary>[Label("类型")] public BlockType Type;}

四、地块索引

然后,我们想一想地块还需要些什么属性,哦对了,我想我们在后续一定会需要检索地块(也即是根据索引找到一个地块),而我们的关卡采用二维平铺布局,所有地块存储的数据结构应当是一个二维数组最合适,所以地块的索引我们定义为二维的下标Vector2Int

    /// <summary>/// 地块/// </summary>[DisallowMultipleComponent]public class Block : HTBehaviour{/// <summary>/// 目标渲染器/// </summary>[Label("目标渲染器")] public SpriteRenderer Target;/// <summary>/// 类型/// </summary>[Label("类型")] public BlockType Type;/// <summary>/// 位置/// </summary>[Label("位置")] public Vector2Int Pos;}

地块类的属性暂时就想到这么多,不必追求一次就考虑全面,后续根据情况补充即可,接下来我们定义关卡类。

关卡(Level)

关卡用于容纳并绘制一系列地块,以及后期容纳角色等其他的一系列东西。

一、定义关卡类

我们定义关卡类Level

    /// <summary>/// 关卡/// </summary>[DisallowMultipleComponent]public class Level : SingletonBehaviourBase<Level>{}

Level继承至单例行为基类SingletonBehaviourBase,使得它可以挂载到游戏物体上,并作为单例始终全局唯一(同一时刻,场景中的关卡肯定只能有一个)。

二、关卡基础属性

我们先为关卡设计一些基础的属性:

        /// <summary>/// 关卡索引/// </summary>[Label("关卡索引")] public int Index;/// <summary>/// 关卡名称/// </summary>[Label("关卡名称")] public string Name;/// <summary>/// 关卡背景音乐/// </summary>[Label("关卡背景音乐")] public AudioClip BGAudio;/// <summary>/// 地图/// </summary>[Label("地图")] public AStarGrid Map;/// <summary>/// 地图尺寸/// </summary>  [Label("地图尺寸")] public Vector2Int MapSize;/// <summary>/// 角色根节点/// </summary>[Label("角色根节点")] public Transform RolesRoot;/// <summary>/// 特效根节点/// </summary>[Label("特效根节点")] public Transform EffectsRoot;
属性名称属性详解
关卡索引关卡唯一标识符,也用作保存、加载关卡时的索引
地图所有地块对象的根节点,此处类型为AStarGrid(A*寻路网格),兼并寻路功能
地图尺寸地图的尺寸,用于描述地图的宽、高
角色根节点所有角色对象的根节点
特效根节点所有特效对象的根节点

地块、角色、特效都归于单一的根节点,即方便管理所有对象,又方便统一划层,也即是规定渲染器的遮挡层,这三者的遮挡层关系应当是:特效 > 角色 > 地块,具体如何实现遮挡,我们先不考虑这么多。

完事后我们的Level在层级面板应当是这样的(三个子对象也即是地块、角色、特效的根节点):
在这里插入图片描述

三、地块集合

定义一个二维数组存储所有地块:

        /// <summary>/// 所有的地块/// </summary>public Block[,] Blocks { get; private set; }

此处注意,将其定义为property的原因是,使其规避序列化功能(因为不需要序列化,关卡在初始化时搜寻所有地块即可),且提升访问安全性。

四、关卡初始化

然后,定义一个初始化方法,用于在关卡加载到场景中后,执行他自身的所有初始化操作,此处我们避开MonoBehaviour的生命周期方法AwakeStart,因为在此处他们是不受控的,这也是我上面所提到的不滥用生命周期的另一个意思。

        /// <summary>/// 初始化/// </summary>public virtual void Initialize(){//Map为所有地块根节点,即可从Map搜寻所有地块Blocks = new Block[MapSize.x, MapSize.y];Block[] blocks = Map.GetComponentsInChildren<Block>(true);for (int i = 0; i < blocks.Length; i++){//地块的Pos下标,即代表了在二维数组中的索引,我们后续会开发关卡编辑器,Pos的赋值交由编辑器来完成,所以这里只管取Pos值Block block = blocks[i];Blocks[block.Pos.x, block.Pos.y] = block;}}

Initialize方法作为主动调用方法,在我们自行加载关卡完成后主动调用即可。

五、关卡销毁

同理,应当定义一个销毁方法,用于在关卡销毁时执行一些操作,虽然我们现在还没有需要做的(地块、角色、特效等都属于关卡物体子节点,会跟着一起销毁,无需额外操作),但事先将其规划好总没错。

        /// <summary>/// 销毁/// </summary>public virtual void Dispose(){}

六、回合制逻辑

为了实现回合制逻辑,我们先定义如下几个属性:

        /// <summary>/// 当前的回合/// </summary>[PropertyDisplay("当前的回合")]public int Round { get; private set; } = 1;/// <summary>/// 当前回合的行动阵营/// </summary>[PropertyDisplay("当前回合的行动阵营")]public RoleCamp RoundCamp { get; private set; } = RoleCamp.Player;/// <summary>/// 关卡状态/// </summary>[PropertyDisplay("关卡状态")]public LevelState State { get; private set; } = LevelState.InProgress;/// <summary>/// 角色阵营/// </summary>public enum RoleCamp{/// <summary>/// 玩家/// </summary>Player = 0,/// <summary>/// 敌人/// </summary>Enemy = 1}/// <summary>/// 关卡状态/// </summary>public enum LevelState{/// <summary>/// 进行中/// </summary>InProgress,/// <summary>/// 已通关/// </summary>Passed,/// <summary>/// 已失败/// </summary>Failed}

此处应该很好理解了,我们按字面意思来就行了,根据最初的设计,每一个回合:玩家先行动,然后是敌人行动,敌人行动完毕后此回合结束,进入下一回合(循环往复)

此时,我们发现,完整的回合制逻辑在未编写角色(Role)类前,并不太好写出来,所以我们先放下,将复杂的事情留到后面一步步拆解。

接下来我们准备引入角色(Role)类,不过,看了一眼窗外,今日天色已晚,不宜working…

那么,择日再战吧。

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

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

相关文章

VCG显示——汉字,数字,图像

详细的介绍资料&#xff1a; 【从零开始走进FPGA】 玩转VGA http://www.cnblogs.com/spartan/archive/2011/08/16/2140546.html 【FPGA实验】基于DE2-115平台的VGA显示_vga接口实验 de2-115-CSDN博客 【FPGA】VGA显示文字、彩条、图片——基于DE2-115-CSDN博客 一.VCG原理 1.1…

时序预测 | MATLAB实现TCN-Transformer时间序列预测

时序预测 | MATLAB实现TCN-Transformer时间序列预测 目录 时序预测 | MATLAB实现TCN-Transformer时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.MATLAB实现TCN-Transformer时间序列预测&#xff1b; 2.运行环境为Matlab2023b及以上&#xff1b; 3.data为数…

Python **运算符(python**kwargs:参数解包)(kwargs:keyword arguments)

文章目录 Python中的 ** 运算符&#xff1a;参数解包参数解包基础语法和示例 在函数定义中使用 **示例代码 使用场景和好处1. 灵活性&#xff1a;使用 **kwargs 允许函数设计得更加灵活&#xff0c;可以接受未来可能增加的新参数而无需修改函数定义。2. 可读性和可维护性&#…

Kali中安装和使用docker的学习笔记

一、常见命令 ctrl 、shift、 &#xff1a; 窗口变大&#xff1b; ctrl 、- &#xff1a;窗口变小&#xff1b; ctrl L&#xff1a; 清屏 &#xff1b; sudo su : 切换root 用户&#xff1b; ip addr / ifconfig: 获取IP地址&#xff1b; systemctl start ssh…

B端颜值无所谓?麻痹自己可以,麻痹业务人员和客户试一试。

很多老铁觉得B端系统颜值和体验无所谓&#xff0c;功能好就行了&#xff0c;我不认同这种说法&#xff0c;我觉得优秀的B端系统应该是内外兼修的&#xff0c;而不是偏科的。你想一想你费尽研发的系统&#xff0c;就是因为颜值问题&#xff0c;你的业务人员没信息推销&#xff0…

北方工业大学24计算机考研情况,学硕专硕都是国家线复试!

北方工业大学&#xff08;North China University of Technology&#xff0c;NCUT&#xff09;&#xff0c;简称“北方工大”&#xff0c;位于北京市&#xff0c;为一所以工为主、文理兼融&#xff0c;具有学士、硕士、博士培养层次的多科性高等学府&#xff0c;是中华人民共和…

GitLab教程(四):分支(branch)和合并(merge)

文章目录 1.分支&#xff08;branch&#xff09;&#xff08;1&#xff09;分支的概念&#xff08;2&#xff09;branch命令 2.合并&#xff08;merge&#xff09;&#xff08;1&#xff09;三个命令pullfetchmergegit fetchgit mergegit pull &#xff08;2&#xff09;合并冲…

【计算机网络仿真实验-实验2.6】带交换机的RIP路由协议

实验2.6 带交换机的rip路由协议 1. 实验拓扑图 2. 实验前查看是否能ping通 不能 3. 三层交换机配置 switch# configure terminal switch(config)# hostname s5750 !将交换机更名为S5750 S5750# configure terminal S5750(config)#vlan 10 S5750(config-vlan)#exit S57…

PyTorch 维度变换-Tensor基本操作

以如下 tensor a 为例&#xff0c;展示常用的维度变换操作 >>> a torch.rand(4,3,28,28) >>> a.shape torch.Size([4, 3, 28, 28])view / reshape 两者功能完全相同: a.view(shape) >>> a.view(4,3,28*28) ## a.view(4,3,28,28) 可恢复squeeze…

【LLM】吴恩达『微调大模型』课程完全笔记

Finetuning Large Language Models 版权说明&#xff1a; 『Finetuning Large Language Models』是DeepLearning.AI出品的免费课程&#xff0c;版权属于DeepLearning.AI(https://www.deeplearning.ai/)。 本文是对该课程内容的翻译整理&#xff0c;只作为教育用途&#xff0c;不…

数据分析必备:一步步教你如何用matplotlib做数据可视化(2)

1、Matplotlib Anaconda Anaconda是Python和R编程语言的免费开源发行版&#xff0c;用于大规模数据处理&#xff0c;预测分析和科学计算。 该分发使包管理和部署变得简单容易。 Matplotlib和许多其他有用的(数据)科学工具构成了分发的一部分。 包版本由包管理系统Conda管理。 …

旅游网站(携程旅行网页学习 vue3+element)

旅游网站 1. 创建项目 在你要创建项目的路径下打开vscode&#xff0c;新建终端&#xff0c;然后输入vue ui,进入Vue项目管理器。选择“创建”&#xff0c;确定项目路径&#xff0c;并点击“在此创建新项目”。在项目文件夹中输入项目名称&#xff0c;点击下一步&#xff1b;选…

CMU最新论文:机器人智慧流畅的躲避障碍物论文详细讲解

CMU华人博士生Tairan He最新论文&#xff1a;Agile But Safe: Learning Collision-Free High-Speed Legged Locomotion 代码开源&#xff1a;Code: https://github.com/LeCAR-Lab/ABS B站实际效果展示视频地址&#xff1a;bilibili效果地址 我会详细解读论文的内容,让我们开始吧…

Python读取wps中的DISPIMG图片格式

需求&#xff1a; 读出excel的图片内容&#xff0c;这放在微软三件套是很容易的&#xff0c;但是由于wps的固有格式&#xff0c;会出现奇怪的问题&#xff0c;只能读出&#xff1a;类似于 DISPIMG(“ID_2B83F9717AE1XXXX920xxxx644C80DB1”,1) 【该DISPIMG函数只有wps才拥有】 …

关于使用‘rt-thread-master‘包从新创建对应开发板型号工程遇到相关问题

问题1:裁剪完成后在ENV中使用命令每次使用scons --targetmdk5重新生成工程后在工程中自己选择的单片机型号会变成默认问题 解决: 通过生成的“template.uvprojx”打开工程&#xff0c;在devicezhong 更改成自己要选择的单片机型号&#xff0c;然后保存&#xff1b;再次通过&qu…

IDEA创建SpringBoot项目教程,讲解超详细!!!(2024)

前言 在创建Spring Boot项目时&#xff0c;为了确保项目的顺利构建和运行&#xff0c;我们依赖于JDK&#xff08;Java开发工具包&#xff09;和Maven仓库。 JDK作为Java编程的基础&#xff0c;提供了编译和运行Java应用程序所需的核心类库和工具。 JDK安装配置教程&#xff1…

大数据工程师如何做到数据可视化?

好的数据可视化作品都是通过不断的数据对比分析实战出来的。 今天给大家带来一篇大数据工程师干货&#xff0c;从多角度解析做数据可视化的重要性&#xff0c;并解读一些适用的应用场景。大数据工程师们刷到这篇文章时一定要进来看看&#xff0c;满满的干货。 目录 1. 什么是数…

【Java】图书管理系统-控制台输出

项目原码压缩包在我主页的资源中免费领取。&#xff08;在IDEA中运行&#xff0c;启动类在src -> Main 中运行&#xff09; 图书管理系统 设计一个简单的控制台输出的图书管理系统&#xff0c;我们首先需要明确其基本功能、设计内容以及设计要求。这个系统可以包括以下几个…

STM32-CAN

一、CAN总线简介 1.1 CAN简介 CAN 是 Controller Area Network 的缩写&#xff08;以下称为 CAN&#xff09;&#xff0c;是 ISO 国际标准化的串行通信 协议。异步半双工。 ISO11898&#xff1a;123kbps~1Mbps。 ISO11519&#xff1a;125kbps 特点&#xff1a; 多主控制没…

自动驾驶仿真:Carsim转向传动比设置

文章目录 一、转向传动比概念二、如何设置转向传动比1、C factor概念2、Steer Kinematics概念3、传动比计算公式 三、转向传动比验证 一、转向传动比概念 转向传动比&#xff08;Steering Ratio&#xff09;表示方向盘转动角度与车轮转动角度之间的关系。公式如下&#xff1a;…