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

news2024/11/27 12:37:32

更新日期: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.coloradmin.cn/o/1816313.html

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

相关文章

CorelDRAW 2024开启设计新纪元,终身永久版与中文破解版的全面解析及安装攻略

当我们谈论图形设计软件时&#xff0c;CorelDRAW无疑是一个响亮的名字。作为一款强大的矢量图形编辑工具&#xff0c;它以其丰富的功能和用户友好的界面赢得了全球设计师的喜爱。随着CorelDRAW 2024的发布&#xff0c;这个备受瞩目的版本带来了前所未有的创新特性&#xff0c;进…

Linux基础IO【II】

今天&#xff0c;我们接着在上一篇文章的基础上&#xff0c;继续学习基础IO。观看本文章之前&#xff0c;建议先看&#xff1a;Linux基础IO【I】&#xff0c;那&#xff0c;我们就开始吧&#xff01; 一.文件描述符 1.重新理解文件 文件操作的本质&#xff1a;进程和被打开文件…

【网络编程】套接字类型与协议设置

协议&#xff1a;计算机对话的通信规则&#xff0c;简而言之是为了完成数据交换而定好的约定。 #include<sys/socket.h> int socket(int domain, int type,int protocol); //domian使用的协议族信息&#xff0c;type传输类型&#xff0c;protocol计算机间通信使用的协议…

苹果WWDC重磅发布的IOS 18、Apple Intelligence背后的技术分析!

2024年6月10日&#xff0c;在2024年WWDC全球开发者大会上&#xff0c;苹果推出了Apple Intelligence&#xff0c;这是深度集成到iOS 18、iPadOS 18和macOS Sequoia中的个人智能系统。 为了让大模型能在 iPhone 端侧跑&#xff0c;苹果还是做了很多事情的。接下来就跟大家介绍一…

使用Java进行网络采集:代理IP与参数传递详解

在Java编程语言中&#xff0c;参数传递机制是一个常见的讨论话题。理解这一点对于编写高效且无错误的Java代码至关重要。本文将探讨Java的参数传递机制&#xff0c;解析其究竟是“按引用传递”还是“按值传递”&#xff0c;并结合网络爬虫技术的实例&#xff0c;展示如何在实际…

7 款便捷好用的AI API

AI API&#xff08;人工智能应用程序接口&#xff09;是一种软件中间件&#xff0c;它允许开发者和企业通过编程方式访问和集成人工智能服务到他们自己的应用程序、网站或系统中。AI API通常由专业的AI服务提供商开发和维护&#xff0c;使得用户无需深入了解复杂的AI算法和模型…

UML相关1

汽车租赁系统中的用例图简述(10分) 本系统根据功能可以分为三个用例图&#xff1a; 客户用例图&#xff1a;主要描述客户注册、登录、找回密码、查询车辆信息&#xff08;包括所有车辆信息、已借车辆信息、租赁历史信息&#xff09;、修改个人信息、网上预订车辆、电话预定车…

【强化学习】gymnasium自定义环境并封装学习笔记

【强化学习】gymnasium自定义环境并封装学习笔记 gym与gymnasium简介gymgymnasium gymnasium的基本使用方法使用gymnasium封装自定义环境官方示例及代码编写环境文件__init__()方法reset()方法step()方法render()方法close()方法 注册环境创建包 Package&#xff08;最后一步&a…

【QT5】<知识点> QT常用知识(更新中)

目录 一、更改文本颜色和格式 二、QT容器类 三、字符串与整数、浮点数之间的转换 四、QString常用功能 五、SpinBox的属性介绍 六、滑动、滚动、进度条和表盘LCD 七、时间、日期、定时器 一、更改文本颜色和格式 动态设置字体粗体&#xff1a;QFont对象的setBold方法动态…

Yapi代码执行 waf绕过实战记录

本文记录了2021年一次有趣的客户目标测试实战。这次经历颇为特别&#xff0c;因此我将其整理成笔记&#xff0c;并在此分享&#xff0c;希望对大家有所帮助。 事件起因 疫情在家办公&#xff0c;准备开始划水的一天&#xff0c;这时接到 boss 的电话说要做项目&#xff0c;老…

微调技术:人工智能领域的神奇钥匙

在人工智能的浪潮中&#xff0c;深度学习技术凭借其强大的数据处理和学习能力&#xff0c;已成为推动科技进步的重要引擎。然而&#xff0c;深度学习模型的训练往往需要大量的数据和计算资源&#xff0c;这在某些特定场景下成为了限制其发展的瓶颈。为了解决这个问题&#xff0…

SolidWorks 2016 SP5安装教程

软件介绍 Solidworks软件功能强大&#xff0c;组件繁多。 Solidworks有功能强大、易学易用和技术创新三大特点&#xff0c;这使得SolidWorks 成为领先的、主流的三维CAD解决方案。 SolidWorks 能够提供不同的设计方案、减少设计过程中的错误以及提高产品质量。SolidWorks 不仅…

JavaWeb6 Tomcat+postman请求、响应

Web服务器 对HTTP协议操作进行封装&#xff0c;简化web程序开发 部署web项目&#xff0c;对外提供网上信息浏览服务 Tomcat 轻量级web服务器&#xff0c;支持servlet&#xff0c;jsp等少量javaEE规范 也被称为web容器&#xff0c;servlet容器 Springboot有内置Tomcat nginx…

网络编程2----UDP简单客户端服务器的实现

首先我们要知道传输层提供的协议主要有两种&#xff0c;TCP协议和UDP协议&#xff0c;先来介绍一下它们的区别&#xff1a; 1、TCP是面向连接的&#xff0c;UDP是无连接的。 连接的本质是双方分别保存了对方的关键信息&#xff0c;而面向连接并不意味着数据一定能正常传输到对…

[CUDA 学习笔记] 稀疏矩阵向量乘法(SpMV) CUDA 实现与优化

稀疏矩阵向量乘法(SpMV) CUDA 实现与优化 本文主要围绕基于 CUDA 的 SpMV 实现进行介绍, 包括几种典型稀疏矩阵存储格式下 SpMV 的朴素实现, 以及 CSR 格式下的几种优化实现. 稀疏矩阵存储格式 稀疏矩阵即含有大量零元的矩阵. 对于稀疏矩阵, 像稠密矩阵一样使用二维数组来存…

物业管理的隐形杀手:纸质点检表,你还在用吗?

在日常的生活中&#xff0c;我们经常会看到小区物业保洁、客服人员在工作岗位忙忙碌碌&#xff0c;但忽略了默默为我们提供舒适环境的“隐形守护者”——物业设施设备。然而&#xff0c;一旦这些设备出现故障&#xff0c;我们的日常生活就会陷入混乱。那么&#xff0c;如何确保…

比特币不是解决货币伦理的「灵丹妙药」

原文标题&#xff1a;《Bitcoin is no ‘silver bullet’ for money’s ethical problems》 撰文&#xff1a;Stephen Katte 编译&#xff1a;Chris&#xff0c;Techub News 本文来源香港Web3媒体&#xff1a;Techub News 比特币和法定货币经常因货币伦理问题而受到批评&am…

AcWing 1639:拓扑顺序 ← 链式前向星

【题目来源】https://www.acwing.com/problem/content/1641/【题目描述】 这是 2018 年研究生入学考试中给出的一个问题&#xff1a; 以下哪个选项不是从给定的有向图中获得的拓扑序列&#xff1f; 现在&#xff0c;请你编写一个程序来测试每个选项。 【输入格式】 第一行包含两…

ffmpeg实现视频播放 ----------- Javacv

什么是Javacv和FFmpeg&#xff1f; Javacv是一个专门为Java开发人员提供的计算机视觉库&#xff0c;它基于FFmpeg和Opencv库&#xff0c;提供了许多用于处理图 像、视频和音频的功能。FFmpeg是一个开源的音视频处理工具集&#xff0c;它提供了用于编码、解码、转换和播放音视频…

MyBatis 参数上的处理的细节内容

1. MyBatis 参数上的处理的细节内容 文章目录 1. MyBatis 参数上的处理的细节内容2. MyBatis 参数上的处理3. 准备工作4. 单个(一个)参数4.1 单个(一个)简单类型作为参数4.2 单个(一个) Map集合 作为参数4.3 单个(一个) 实体类POJO作为参数 5. 多个参数5.1 Param注解(命名参数)…