# SpotLight2025 **Repository Path**: NumbFish-Luo/spot-light-2025 ## Basic Information - **Project Name**: SpotLight2025 - **Description**: TapTap聚光灯GameJam2025项目,主题:你确定这不是BUG吗? - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-14 - **Last Updated**: 2025-10-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 聚光灯GameJam2025 ## 一、主题 **《你确定这不是BUG?》** 队伍名:八奏猫头鹰工作室 头脑风暴画布: https://www.figma.com/board/XD9RpPDIMmVeDirhLHHUNs/%E5%A4%B4%E8%84%91%E9%A3%8E%E6%9A%B4 策划案(配表、资产整理、项目管理): https://nz8c1w08xe.feishu.cn/wiki/AE7pw4FNMiSoMukZJq8cwDv6nyh Unity版本:2022.3.62f2c1 Gitee仓库:https://gitee.com/NumbFish-Luo/spot-light-2025 设计分辨率:1920x1080(PC横屏) 游戏类型:俯视视角2D RPG游戏(只是操作逻辑和数值有些反过来了) (Blues:下班时间(5天)&周末,晚上8~9点开始有空,到10点,固定9点讨论东西) ## 二、仓库结构 (只列出重要部分) ``` . ├── SpotLight2025/ # Unity工程根目录 │ └── Assets/ # Unity资源文件夹 │ ├── Artworks/ # 美术音效等相关资源 │ ├── Prefabs/ # 预制体 │ ├── Resources/ # Unity资源目录 │ │ └── Story/ # 剧本资源 │ ├── Scenes/ # 场景文件夹 │ │ ├── Main.unity # 主场景 │ │ ├── TestBlues.unity # Blues的测试场景 │ │ └── TestNumbfish.unity # 鳗鱼的测试场景 │ ├── Scripts/ # 代码目录 │ │ ├── Common/ # 工具等通用代码 │ │ ├── GameData/ # 游戏数据相关 │ │ ├── Gameplay/ # 和玩法相关的代码 │ │ │ └── GameManager.cs # 游戏管理器 │ │ ├── Test/ # 测试代码 │ │ │ ├── TestBlues.cs # Blues的测试代码 │ │ │ └── TestNumbfish.cs # 鳗鱼的测试代码 │ │ └── UI/ # UI代码 │ └── Tools/ # 工具或插件 ├── Story/ # 策划放的剧本文件 │ └── README.MD # 剧本语法说明文档 └── README.MD # 项目说明文档 ``` ## 三、程序架构 ### 3.1 开发步骤与思路 开发步骤如下 ```mermaid stateDiagram-v2 开始 --> 数据 开始 --> 展示:游戏世界 开始 --> 展示:UI 数据 --> 连接数据与展示 展示:游戏世界 --> 连接数据与展示 展示:UI --> 连接数据与展示 连接数据与展示 --> 优化 优化 --> 发布 发布 ``` 基本思路是把代码分成3个部分: - 数据:特点是无需继承Mono组件,不需要挂在什么东西上就能跑通游戏,是纯粹的数据和计算,用于保证游戏世界底层逻辑和交互是正确的(有时候还会用struct而不是class) - 展示:特点是需要继承Mono组件,是玩家看得到的部分,例如控制玩家上下左右移动这些代码 - 连接:**用于连接数据和展示,这部分代码最杂乱,但是杂乱是正常现象,放心去开发就行** > 实际上是MVP架构设计的Unity究极简化版,可以称之为**数据驱动组件模式(Data-Driven Component Pattern)**,具体执行的时候建议**怎么写舒服怎么来,不要太在意架构的严谨性** > > 相关阅读:[一文读懂MVC、MVP和MVVM架构 - 知乎](https://zhuanlan.zhihu.com/p/616953800) > 设计模式:面向对象的“套路” ### 3.2 数据部分 分工:【鳗鱼】 #### 3.2.1 剧情数据 【真完成】 ```mermaid --- title: namespace Story {...} --- classDiagram class AllData { + fileDict: Dict< str, FileData > + ReadFolder(path: str, ...) } class FileData { + name: str + paragraphDict: Dict< str, Paragraph > ... } class Paragraph { + name: str + dialogues: List< Dialogue > } class Dialogue { + who: str + say: str + options: List< Option > ... } class Option { + txt: str + doWhats: List< str > ... } AllData o-- FileData FileData o-- Paragraph Paragraph o-- Dialogue Dialogue o-- Option ``` 已经做了一个剧情数据解析器了,详见Assets下的StoryData.cs脚本和Story文件夹的README.MD文件。注意有`namespace Story`,数据类是`AllData`,在游戏开始时调用`ReadFolder(path: str, ...)`读取好指定文件夹下所有剧本文件,然后会存到一个字典结构里面。具体一行对话是存到`Dialogue`类里面,在需要时搜索调取指定数据来显示即可 #### 3.2.2 数值配置 【完成一大半,剩下等策划】 ```mermaid classDiagram class GameCfg { + PLAYER_OPENING_HP: const int + PLAYER_OPENING_HP_MAX: const int + OWL_HP_SKILL_CD: const float + OWL_HP_SKILL_VAL: const int + HP_POTION_VAL: const int ... } ``` 类似于有个配置表叫`GameCfg`,填了各种具体数值常量,例如游戏开局时玩家有多少血(开局残血),猫头鹰每隔多少秒恢复(实为扣除)多少血,血瓶恢复(实为扣除)多少血等 #### 3.2.3 游戏存档 【基本完成】 主要存到`GameData`里面 ##### 3.2.3.1 玩家以及其他角色 ```mermaid classDiagram class GameData { + playerData: PlayerData + npcDatas: Dict< str, NpcData > ... } class PlayerData { + money: int + items: List< ItemData > ... } class RoleData { + name: str + hp: int + hpMax: int ... } class EnemyData { + isBoss: bool } GameData o-- PlayerData RoleData <|-- PlayerData RoleData <|-- NpcData RoleData <|-- EnemyData ``` 需要一个`PlayerData`玩家数据类型记录玩家当前血量和血上限,金币数量,道具栏有哪些道具以及都是什么数量,然后`PlayerData`玩家数据是继承于`RoleData`角色数据的 `NpcData`里面要记录一下每个NPC是否对话过了,然后如果开了二周目则还要清空一下已经对话过的记录 `EnemyData`更多是临时数据,玩家进入森林或者城堡的时候才创建就行(也可以提前创建),不属于存档的一部分 ##### 3.2.3.2 猫头鹰 对的,猫头鹰本质上是物品,但是他比较特殊,会跟随玩家移动,数据方面的话只要存到`PlayerData.items`里面就行 ##### 3.2.3.3 二周目等全局信息 ```mermaid classDiagram class GameData { + newGamePlus: bool ... + StartNewGame() + StartNewGamePlus() } note for GameData "GameData.StartNewGamePlus() => foreach npc update story data..." ``` `newGamePlus`记录是不是二周目,二周目流程和一周目类似,但是对话会有所不同,开启二周目后需要调整NPC对话数据,执行`StartNewGamePlus()`函数调整 如果是一周目,则调用`StartNewGame()`初始化一下数据就行 ##### 3.2.3.4 存档和读档 ```mermaid classDiagram class GameData { + Save() + Load() } ``` 用Unity的存档系统即可,虽然这么做会存到注册表里面,调用`Save()`写存档,`Load()`读存档 ### 3.3 展示部分 【基本都没做】 这里开始基本上都要继承`MonoBehaviour`才行 #### 3.3.1 游戏世界 ##### 3.3.1.1 地图区域 【还没做】 分工:【Blues】 直接所有地图都放进去,不需要动态加载卸载 具体要做的事情:跟美术那边拿地图图片资源,和策划对一下地图上每个地方的功能(挡路的、是门要拿钥匙开、刷怪区域...) ##### 3.3.1.2 玩家 【架构完成,但是很多todo内容】 分工:【鳗鱼】 ```mermaid classDiagram class Role { - onHpChange: UnityAction # data: RoleData # rb: Rigidbody2D # anim: Animator # spine: ... ... # Init(data: RoleData) + RegisterOnHpChange(evt: UnityAction) + BeHit(dmg: int) + Die() ...() } class Player { - onMoneyChange: UnityAction - onItemChange: UnityAction ... + Init(data: PlayerData) # Update() } Role <|-- Player note for Player "Player.Init(data: PlayerData) => base.Init(data); usage: PlayerData d = (data as PlayerData);" ``` 玩家`Player`继承自角色`Role`,其中`Role.Init(data: RoleData)`传入的是`RoleData`基类,而且是`protected`的。然后继承的时候改成`public`,并且修改传入类型为`PlayerData`(实际上并没有`override`基类的函数)。在具体使用时需要手动`as`成子类 另外里面还提供了可注册事件,方便UI同步更新信息 ##### 3.3.1.3 NPC ```mermaid classDiagram class Npc { + Init(data: NpcData) } Role <|-- Npc ``` 简单继承一下`Role`就是`Npc`了,这个游戏`Npc`也是能被打死的所以也有血量计算的东西 ##### 3.3.1.4 敌人和Boss ```mermaid classDiagram class Enemy { + Init(data: EnemyData) # Update() - UpdateAI() } Role <|-- Enemy Enemy <|-- EnemySlime ``` 敌人这边则需要多一个`UpdateAI`的函数,例如进入攻击范围内时自动追踪攻击。然后如果有不同特殊类型的敌人(例如被打了会分裂的史莱姆),则再多一层继承 #### 3.3.2 UI ##### 3.3.2.1 标题界面 分工:【Blues】 开始游戏 继续游戏 (显示是不是二周目) 开发人员表 退出游戏 ##### 3.3.2.2 主UI界面 分工:【Blues】 金币 玩家血条 物品栏 (数据连接:关联一下玩家血条变化事件`RegisterOnHpChange(evt)`) ```C# // 1. 准备好了UI物体(Prefab): 例如MainUI // 2. 给UI物体挂相应组件: 例如MainUI.cs // 3. 在你的Test代码里面加关联 MainUI mainUI = ...; Player player = ...; player.RegisterOnHpChange(mainUI.UpdateHp); ``` ##### 3.3.2.3 对话框 分工:【鳗鱼】 人物头像 人物名称 人物说什么 弹出选项 长按一般是加速对话,但是这里是倒退(回到上一级) 需要接前面的剧情数据结构 ##### 3.3.2.4 交易界面 分工:【Blues】 购买时你加钱,物品减少 卖出才是购买 允许你的物品是负数数量,但是物品变负数会置灰 ##### 3.3.2.5 结局界面 分工:【Blues】 美术发挥,可能是播一张图,或者写一行字 保存进度并回标题界面 ### 3.4 连接数据与展示 #### 3.4.1 GameManager等各种管理器 ```mermaid classDiagram class GameManager { + ui: UIManager + snd: SndManager + fx: FxManager + storyData: Story.AllData + gameData: GameData ... + StartNewGame() + StartNewGamePlus() + SaveGame() + LoadGame() + QuitGame() ...() } ``` `GameManager`是一个单例,我们视之为游戏入口,以及连接所有乱七八糟功能的桥梁。所以里面代码行数必定会很长,这是正常现象 `UIManager`,`SndManager`,`FxManager`都不是单例(目的是减少单例的数量避免代码混乱),它需要放到`GameManager`里面 #### 3.4.2 游戏世界 看情况 #### 3.4.3 UI 可能剧情对话那一块比较复杂 其他看情况 ## 四、其他 ### 4.1 程序常用缩写表 | 缩写 | 原文 | 含义 | | ------------ | ------------------------------- | -------------------------------------- | | anim | aimation, animator | 动画,动画状态机 | | atk | attack | 攻击 | | bg | background | 背景 | | bgm | background music | 背景音乐 | | btn | button | 按钮 | | cd | cool-down | 冷却时间 | | cfg | config | 配置 | | dict | dictionary | 字典 | | dir | direction | 方向 | | dmg | damage | 伤害 | | evt | event | 事件 | | ex | extend | 扩展 | | fx, sfx, vfx | special effects, visual effects | 特效(我也不清楚为什么会变成这个缩写) | | gm | game manager | 游戏管理器 | | hp | health point | 血量 | | idx | index | 索引 | | mat | material | 材质 | | mono | mono behaviour | Unity组件 | | num | number | 数量 | | obj | object | 对象 | | pow | power | 能量 | | rb | rigid body | 刚体 | | snd | sound | 声音 | | spd | speed | 速度 | | str | string | 字符串 | | tf | transform | 变换 | | txt | text | 文本 | | u, d, l, r | up, down, left, right | 上下左右 | | ui | user interface | 用户界面 | | uim | user interface manager | UI管理器 | | val | value | 数值 | ### 4.2 Spine导入流程 1. 美术那边会导出一个文件夹,里面有`.atlas`后缀的文件,要改成`.atlas.txt`后缀 2. 接着打开`.json`文件,修改里面的版本号数据,将`"spine":"3.8.75"`改成`"spine":"3.8"` 3. 将文件夹拖入Unity工程中,放到`Assets/Artworks/Spine/`文件夹里面,Unity会自动多生成3个文件,包含`xxx_Atlas`数据,`xxx_Material`材质球和`xxx_SkeletonData`骨骼数据 4. 材质球指定一下`Main Texture`,然后勾选`Straight Alpha Texture` 5. 导入的贴图勾选`Advanced/Alpha Is Transparency`然后点`Apply` 6. 将`xxx_SkeletonData`拖到游戏场景中,选择`SkeletonAnimation`就可以生成一个Spine动画对象了 7. 点这个生成的Spine动画对象,选择`SkeletonAnimation`组件的`Animation Name`为你要播放的动画就行 ### 4.3 Spine渲染分离 1. 在`SkeletonAnimation`组件点`Advanced`里面的`Add Skeleton Utility`按钮,会自动加`Skeleton Utility`组件 2. 然后点`Skeleton Utility`组件的`Spawn Hierarchy`按钮,此时会报错,不影响操作,忽略即可 3. 然后再给Spine根节点手动添加一个`Skeleton Render Separator`组件,在`Skeleton Renderer/Separator Slot Names`里面添加你要分离的图层,例如添加`图层1`(剑的图层)、`hand1`(手的图层)、`arm1`(手臂的图层),点`Add the missing renderers(n)`(如果是第二次编辑,则接着依次点`Parts Renderers`的`Clear Parts Renderers`按钮和`Add Parts Renderer`按钮),就可以把角色图层分离了,会创建一些名称从0开始计数的物体 4. 找到你要隐藏的物体,例如1这个物体是剑,隐藏就行