diff --git "a/DungeonShooting_Document/\345\274\200\345\217\221\346\227\245\345\277\227.md" "b/DungeonShooting_Document/\345\274\200\345\217\221\346\227\245\345\277\227.md" index 2c807bd..d7a93f1 100644 --- "a/DungeonShooting_Document/\345\274\200\345\217\221\346\227\245\345\277\227.md" +++ "b/DungeonShooting_Document/\345\274\200\345\217\221\346\227\245\345\277\227.md" @@ -1,6 +1,6 @@ --- -### 2023-09-05 +### 2023-09-06 经过两个月漫长的开发, 新版地牢编辑器初版已经开发完成, 目的为了更加方便开发者和玩家制作地图, 新版地牢编辑器提供功能如下: * 地牢项目管理功能, 可以管理地牢组和房间, 支持房间预览, 支持显示异常房间, 房间支持配置权重 * 绘制Tile功能, 并且支持验证Tile是否绘制异常, 自动生成地牢导航网格 diff --git "a/DungeonShooting_Document/\351\241\271\347\233\256\345\270\256\345\212\251\346\226\207\346\241\243.md" "b/DungeonShooting_Document/\351\241\271\347\233\256\345\270\256\345\212\251\346\226\207\346\241\243.md" index 3abd9ef..39bdd3a 100644 --- "a/DungeonShooting_Document/\351\241\271\347\233\256\345\270\256\345\212\251\346\226\207\346\241\243.md" +++ "b/DungeonShooting_Document/\351\241\271\347\233\256\345\270\256\345\212\251\346\226\207\346\241\243.md" @@ -1,48 +1,9 @@ 前言: 该文档仅针对`DungeonShooting_Godot`目录下的Godot工程 -第一次编写日期: 2023-04-01 -**注意:** 该文档版本比较老, 已经有很多接口和操作方式被更改但是为同步文档, 请谨慎阅读! +**注意:** 新版文档编写中... 目录: - - * [1.启动项目](#1启动项目) - * [2.项目资源](#2项目资源) - * [2.1.目录结构](#21目录结构) - * [2.2.脚本获取资源](#22脚本获取资源) - * [2.3.重新生成ResourcePath](#23重新生成resourcepath) - * [3.游戏框架](#3游戏框架) - * [3.1.简述](#31简述) - * [3.2.游戏核心系统](#32游戏核心系统) - * [3.2.1.什么是ActivityObject](#321什么是activityobject) - * [3.2.2.什么是Activity模板场景](#322什么是activity模板场景) - * [3.2.3.如何创建ActivityObject](#323如何创建activityobject) - * [第一步, 创建模板场景:](#第一步-创建模板场景-) - * [第二步, 创建脚本:](#第二步-创建脚本-) - * [实例化ActivityObject](#实例化activityobject) - * [3.2.4.自定义RegisterActivity](#324自定义registeractivity) - * [3.2.5.ActivityObject常用功能](#325activityobject常用功能) - * [自定义组件](#自定义组件) - * [运动控制](#运动控制) - * [垂直方向运动](#垂直方向运动) - * [协程](#协程) - * [3.3.地牢](#33地牢) - * [3.3.1.地牢概述](#331地牢概述) - * [3.3.2.创建模板房间](#332创建模板房间) - * [创建模板房间](#创建模板房间) - * [绘制房间](#绘制房间) - * [配置房间门生成位置](#配置房间门生成位置) - * [ActivityMark标记](#activitymark标记) - * [扩展ActivityMark标记](#扩展activitymark标记) - * [3.4.UI系统](#34ui系统) - * [3.4.1.UI系统概述](#341ui系统概述) - * [3.4.2.UI代码生器](#342ui代码生器) - * [创建UI](#创建ui) - * [打开UI](#打开ui) - * [3.4.3.常用功能](#343常用功能) - * [生命周期](#生命周期) - * [包裹UI节点的IUiNode](#包裹ui节点的iuinode) - --- ## 1.启动项目 @@ -91,7 +52,8 @@ **游戏核心系统**: 以游戏玩法为中心的逻辑代码, 包括玩家, 敌人, 武器, 被动, 道具, 地牢生成, 房间规则, 存档逻辑等 **UI模块系统**: 用户操作界面的逻辑代码 -**代码生成系统**: 自动生成便于开发的资源的逻辑代码, 包括生成UI模板, 生成地牢模板, 生成代码等 +**代码生成系统**: 自动生成便于开发的资源的逻辑代码 +**编辑器系统**: 用于用于自定义游戏内容 ### 3.2.游戏核心系统 @@ -116,77 +78,8 @@ 通过下面这张图可以了解游戏中的物体与`ActivityObject`的关系 (注意: 该图为早期开发版本的继承关系图, 后面开发可能会有修改) ![](文档资源/2023-03-26_030144.png) -#### 3.2.2.什么是Activity模板场景 -定义: `Activity模板场景`是指可以可以被实例化出`ActivityObject`对象的场景, 但是场景根节点必须是`ActivityObjectTemplate`节点 -上面定义看起来有矛盾: `ActivityObjectTemplate`没有继承`ActivityObject`, 为啥以它为根节点的场景却能实例化出`ActivityObject`? -这就得提到一个概念: **场景与脚本分离**, 顾名思义, 场景中的节点与`ActivityObject`的脚本是完全分离的, 场景中的节点并没有挂载`ActivityObject`脚本, 在编辑器中它们是两互不干涉的. -游戏运行中, 如果需要实例化`ActivityObject`, 那么就先需要在`ActivityObject`脚本代码中指定该物体的模板场景, 实例化过程中游戏会先实例化出模板场景, 再用`ActivityObject`的实例顶替掉模板场景的根节点, 因此就能打到最终的效果. -为什么要这么做? 原因很简单, 因为我们的游戏是一个Roguelite游戏, 因此游戏中肯定会有大量的武器道具和敌人来填充内容, 但是总会有类似功能或者类似场景结构的物体, 这样就没有必要每一个物体都新建一个单独的场景, 而是让功能让这些类似功能或者结构的物体使用同一个场景, 但为了因对有不同行为逻辑的物体, 我们就设计了一套**场景与脚本分离**的设计模式来因对上述情况 -总结: `Activity模板场景`是不挂载逻辑脚本的, 但是`ActivityObject`必须包含使用的模板场景, 并由统一的Api来实例化`ActivityObject`对象, 至于`ActivityObject`如何绑定模板场景, 请看: 3.2.3.如何创建一个`ActivityObject` - -通过下面这张图可以更好的立即`Activity模板场景`和`ActivityObject`的关系 -(缺张图...) - -#### 3.2.3.如何创建ActivityObject -这里的创建分为两步: -##### 第一步, 创建模板场景: -创建一个空场景, 并且添加`ActivityObjectTemplate`节点 -![](文档资源/image_4.png) -创建完成后编辑器会自动创建必要的子节点 -![](文档资源/image_5.png) -此时就可以随意添加子节点和重命名更节点了, 最后记得保存到`./prefab`文件夹下 -**注意**: `ShadowSprite`,`AnimatedSprite`,`Collision`这三个节点不能改名, 但是可以修改属性和添加子节点 - -##### 第二步, 创建脚本: -创建脚本放到在`./src/game`下, 脚本必须继直接或间接承`ActivityObject`, 并且需要在类上加`[RegisterActivity(id, path)]`标记用于注册对象, `物体id`必须唯一 -源代码: [RegisterActivity.cs](../DungeonShooting_Godot/src/framework/activity/RegisterActivity.cs) -参考代码如下: -```csharp -using Godot; - -[RegisterActivity("物体唯一Id", "模板场景路径")] -public partial class YourActivity : ActivityObject -{ - -} -``` -为了方便区分物体类型, 可以使用`ActivityIdPrefix`类中的常量来添加`id`前缀, 目前支持的类型如下: -* **Role**: 角色 -* **Enemy**: 敌人 -* **Weapon**: 武器 -* **Bullet**: 子弹 -* **Shell**: 弹壳 -* **Other**: 其他类型, 例如门, 箱子等 -* **Test**: 测试物体 - -例如我们创建一个敌人, 那么`[RegisterActivity()]`就可以这么写: -```csharp -[RegisterActivity(ActivityIdPrefix.Enemy + "0001", ResourcePath.prefab_role_Enemy_tscn)] -``` - -##### 实例化ActivityObject -可通过`ActivityObject.Create(id)`创建物体, 这个`id`可以结合`ActivityIdPrefix`, 那么创建敌人最终可以这样写 -```csharp -var enemy = ActivityObject.Create(ActivityIdPrefix.Enemy + "0001"); -``` - -#### 3.2.4.自定义RegisterActivity -某些情况下需要更改`RegisterActivity`的参数, 或者需要对实例化出来的`ActivityInstance`进行统一的操作, 那么就需要我们自己写一个子类来继承`RegisterActivity`. -操作`ActivityInstance`需要重写: -```csharp -/// -/// 该函数在物体实例化后调用, 可用于一些自定义操作, 参数为实例对象 -/// -public virtual void CustomHandler(ActivityObject instance) -{ -} -``` - -例子: 注册武器, [RegisterWeapon.cs](../DungeonShooting_Godot/src/game/item/weapon/RegisterWeapon.cs) -由于创建武器必须指定武器属性数据, 那么原来的`[RegisterActivity()]`就不适用了, `RegisterWeapon`重写了构造函数, 改变了初始化参数, 并且重写`CustomHandler()`, 对`ActivityInstance`进行初始化属性操作 - -#### 3.2.5.ActivityObject常用功能 +#### 3.2.2.ActivityObject常用功能 ##### 自定义组件 这个功能类似于`Unity`的`MonoBehaviour`, 组件必须继承`Component`类, 组件的作用是拆分功能代码, 开发者可以将相同功能的代码放入同一个组件中, 与`Godot`的`Node`不同的是, 挂载到`ActivityObject`上的组件并不会生成一个`Node`节点, 它相比于`Node`更加轻量 @@ -364,90 +257,6 @@ * **商店**: 玩家买卖道具装备的房间 * **事件房间**: 触发剧情或者解锁NPC的房间 -#### 3.3.2.创建模板房间 -图块层级概述(后续补上, 先默认使用`resource/map/tileset/TileSet1.tres`) - -##### 创建模板房间 -项目提供了一套创建模板房间的工具, 点击`tools`页签, 找到`创建地牢房间`这一项, 输入模板房间名称(注意房间名称不能重复), 即可创建房间, 创建房间完成后会创建房间配置数据, 路径为`resource/map/tiledata/xxx.json`, 并将配置数据注册到`resource/map/RoomConfig.json`中 -![](文档资源/image_7.png) - -##### 绘制房间 -创建好的房间会自动在编辑器中打开, 为场景的根节点选好`TileSet`后就可以画房间了 -![](文档资源/image_8.png) -编辑器会自动计算出房间位置轮廓(绿色线)和导航区域(红色和黄色线), 并绘制出来, 按下`ctrs`+`s`, 编辑器就会将位置轮廓和导航信息存入`resource/map/tiledata/xxx.json`下 -注意, 为了避免Ai运动时卡墙角, 所以计算导航轮廓时特意与墙预留了半个格子的距离, 也就是说如果存在单格的道路, 导航计算就会出错, 所以在画道路时必须为两格以上的宽度, 像下面这两种情况就是不被允许的, 编辑器会绘制出错误的格子 -![](文档资源/image_9.png) -![](文档资源/image_10.png) -如果计算导航网格出错, 那么编辑器将不会保存房间配置信息 - -##### 配置房间门生成位置 -如果某些模板房间需要在指定区域内生成门, 那么就需要设置房间门生成区域 -在模板场景中选中根节点, 再勾选`Enable Edit` -![](文档资源/image_11.png) -此时将鼠标放置在房间轮廓的绿线上就会显示生成区域 -![](文档资源/image_12.png) -点击鼠标左键即可创建门区域, 如果悬停时显示红色方块, 则表示不能在此处创建门区域 -创建门生成区域的约束: 区域不能重叠, 且两个区域的间距至少为4格, 每个区域至少4格宽度 -![](文档资源/gif_1.gif) -新建的区域默认为4格宽度, 如果需要调整宽度, 可以拖拽区域两侧的点来调整范围 -![](文档资源/gif_2.gif) -如果需要删除区域, 则悬停到区域两侧任意一个点上, 按下鼠标中建即可删除 -![](文档资源/gif_3.gif) -门区域需要对齐地面地砖 -![](文档资源/image_13.png) - -注意: -* 如果一个模板房间不设置门生成区域, 则默认四边任何位置都可能生成门 -* 配置好一个房间的门生成区域后, 如果绘制房间时改变了房间的大小或者位置, 则编辑器会清空配置的门区域 -* 编辑门区域功能属于扩展编辑器功能, 因此单纯改变门区域数据不会在场景页签上标`*`(编辑器不会认为该资源有修改), 修改后需要及时按下`ctrl`+`s`保存, 以免造成数据丢失 - -##### ActivityMark标记 - -`ActivityMark`用于模板房间中创建`ActivityObject`对象, 并支配置指定的物体, 第几波生成该物体以及生成物体延时时间 -源代码: [ActivityMark.cs](../DungeonShooting_Godot/src/framework/map/mark/ActivityMark.cs) - -在房间根节点中创建`ActivityMark`对象 -![](文档资源/image_18.png) -创建完成后可以看到地图上多了一个`X`, 这个叉就是生成物体的位置, 可自由调整位置 -![](文档资源/image_19.png) -然后就可以在编辑器中设置`ActivityMark`数据了, `ActivityMark`有以下可以导出的属性: -```csharp -/// -/// 物体类型 -/// -[Export] -public ActivityIdPrefix.ActivityPrefixType Type = ActivityIdPrefix.ActivityPrefixType.NonePrefix; - -/// -/// 物体id -/// -[Export] -public string ItemId; - -/// -/// 所在层级 -/// -[Export] -public RoomLayerEnum Layer = RoomLayerEnum.NormalLayer; - -/// -/// 该标记在第几波调用 BeReady, -/// 一个房间内所以敌人清完即可进入下一波 -/// -[Export] -public int WaveNumber = 1; - -/// -/// 延时执行时间,单位:秒 -/// -[Export] -public float DelayTime = 0; -``` - -###### 扩展ActivityMark标记 -项目中提供了以下几个扩展`ActivityMark`属性的节点: -* [WeaponMark](../DungeonShooting_Godot/src/framework/map/mark/WeaponMark.cs): 创建武器设置弹药等 -* [EnemyMark](../DungeonShooting_Godot/src/framework/map/mark/EnemyMark.cs): 创建敌人并设置武器和弹药 ### 3.4.UI系统 @@ -513,17 +322,21 @@ /// /// 当前ui显示时调用 /// -public abstract void OnShowUi(); +public virtual void OnShowUi() +{ +} /// /// 当前ui隐藏时调用 /// -public abstract void OnHideUi(); +public virtual void OnHideUi() +{ +} /// /// 销毁当前ui时调用 /// -public virtual void OnDisposeUi() +public virtual void OnDestroy() { } ```