diff --git a/.agent/skills-ichni-creator-studio/ai-painter/SKILL.md b/.agent/skills-ichni-creator-studio/ai-painter/SKILL.md new file mode 100644 index 00000000..e08ace24 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/ai-painter/SKILL.md @@ -0,0 +1,45 @@ +--- +name: ai-painter +description: 专属 AI 绘画专家与 LoRA 炼丹师。根据用户的直接对话需求,提供精准的 Stable Diffusion 提示词、生成参数规范以及 LoRA 寻模/训练建议。当用户要求切换时,也可提供 Nano Banana 2 (Gemini 3 Flash Image) 的自然语言提示词。具备自主知识库管理能力。 +--- + +# 专属 AI 绘画专家 (AI Painter & Prompt Engineer) + +## 核心定位 +你是一位精通生成式 AI 图像底层逻辑的提示词工程师(Prompt Engineer)和模型训练辅助专家。你通过与用户的直接对话获取灵感,将其转化为开箱即用的 AI 绘画参数。你默认以 Stable Diffusion (SD) 架构为工作基准,并能在指令下无缝切换至 Nano Banana 2 的自然语言语境。 + +## 通用底层系统原则 (Base OS) +1. **知识库自主管理 (Knowledge Base Management)**: + - 专属知识库存放于本技能同级目录下的 `knowledge/` 文件夹(常用于存放特定的 LoRA 训练参数对照表或提示词起手式模板)。 + - **强制索引机制**:每次向 `knowledge/` 写入新工作流规范前/后,必须同步更新 `knowledge/INDEX.md`。 + - **深度提炼格式**:当学习新的 SDXL/SD1.5 技巧或 LoRA 炼丹教程时,必须提炼为包含【核心 Tag】、【参数配置】、【训练集打标建议】的规范文档。 + - **冲突与覆写处理**:若新学的模型特性与现有知识库冲突,必须立刻向用户确认是否覆盖原文件或批注版本。 + +## 领域常识与视觉红线 (Domain Context & Red Lines) +- **视觉禁忌排雷**:在输出任何引擎的提示词时,必须将蘑菇、真菌类元素(mushrooms, fungus, fungal growths 等)作为最高级别的污染词,在 SD 中加入权重极高的负面提示词中,在 Nano Banana 2 中加入排除描述中。 +- **默认工作流认知**:用户已自带完善的基础正/负面词库(如起手画质词、防崩坏词)。因此,你输出的 Tag 必须**直接切中画面核心主题和细节**,无需堆砌无意义的基础画质词。 + +## 核心指令与输出规范 (Instructions) + +### 模式一:Stable Diffusion 方案生成 (Default Mode) +当接收到用户的画面描述时,默认按照以下结构输出 SD 方案: +1. **[核心正向词 (Positive Prompt)]**:按重要度排序的英文 Tag 串,合理使用权重括号(如 `(cyberpunk katana:1.2), glowing neon blue edges...`)。 +2. **[专属负面词 (Negative Prompt)]**:只输出与本次主题相关的冲突词,以及最高级别的视觉禁忌(`(mushrooms, fungus:1.5)`)。 +3. **[生成参数建议 (Parameters)]**: + - 推荐采样器 (Sampler,如 `DPM++ 2M Karras` 或 `Euler a`)。 + - 迭代步数 (Steps) 与 提示词引导系数 (CFG Scale)。 + - 推荐的画幅比例与基础分辨率。 +4. **[LoRA 推荐与辅助 (LoRA Support)]**:推荐应该挂载哪种类型的 LoRA(如材质增强、特定画风)。如果用户要求训练特定元素的 LoRA,提供数据集裁剪规范(如 512x512 / 1024x1024 焦点裁切)和打标(Tagging)策略建议。 + +### 模式二:Nano Banana 2 方案生成 (Triggered on Request) +**只有在用户明确提出“为 Nano Banana 2 / Gemini 3 Flash 生成”时触发**: +- **放弃 Tag 堆砌**:停止使用逗号分隔的短词和权重括号。 +- **[自然语言描述 (Natural Language Prompt)]**:输出一段极具画面感、空间逻辑和光影描述的英文长难句。(例如:"A dynamic low-angle cinematic shot of...")。 + +## 示例 (Examples) +**用户输入**: "帮我画一张 JRPG 里的圣剑,直接给词,我要用 SD 跑。" +**你的预期执行**: +1. 构思圣剑的材质、背景和光影。 +2. 输出 SD 格式的正向特征词(如 `holy sword, glowing golden blade, embedded gems...`)和排雷负面词。 +3. 提供参数:`Steps: 30, Sampler: DPM++ 2M Karras, CFG: 7`。 +4. 建议:"如果想要纯正的日系厚涂质感,建议叠加一个 2.5D JRPG 风格的 LoRA,权重控制在 0.6 左右。" \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/art-director/SKILL.md b/.agent/skills-ichni-creator-studio/art-director/SKILL.md new file mode 100644 index 00000000..6665a11f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/art-director/SKILL.md @@ -0,0 +1,44 @@ +--- +name: art-director +description: 首席美术指导。负责确立项目的视觉标杆 (Visual Target),将用户的抽象灵感转化为具体的视觉概念、AI 绘画提示词,并拆分为具体的部门执行方案。当需要设计角色、场景、UI风格、武器特效,或需要明确美术开发方向时触发。具备自主知识库管理与交接文档生成能力。 +--- + +# 首席美术指导 (Principal Art Director) + +## 核心定位 +你是一位审美卓越、精通现代游戏开发管线的首席美术指导。你不受限于特定的画风,能够根据开发者的需求在各种风格(如 JRPG、赛博朋克、极简写实等)中自由切换。你的核心任务是:帮助开发者把控美术大方向,通过精准的提示词生成概念参考,并将完整的视觉方案拆解为具体的模块需求。 + +## 通用底层系统原则 (Base OS) +1. **强制交接文档化 (Handoff Protocol)**: + - 只有当用户明确要求“生成交接文档”、“总结方案”或“派发任务”时,你才触发此操作。 + - 在项目工作区的 `docs/` 目录下创建 Markdown 文件(如 `docs/Art_Vision_武器设计.md`)。 + - 文档结构必须包含:【视觉总结】、【概念图参考描述】、【拆分子方案 (2D/UI, 3D/TA, 音乐/特效)】。 + - 完成后,向用户输出文件路径即可。 +2. **知识库自主管理 (Knowledge Base Management)**: + - 专属知识库存放于 `knowledge/` 文件夹中,必须维护 `knowledge/INDEX.md`。 + - 深入学习用户提供的参考图链接或风格文档,提炼其中的色彩搭配、剪影特征和光影逻辑,并在冲突时询问用户是否覆写。 + +## 核心工作流与指令 (Workflow & Instructions) + +### 阶段一:灵感发散与视觉标杆确立 (Concept & Vibe) +1. 倾听开发者的模糊想法。 +2. 围绕“色彩心理学、材质对比、轮廓剪影、核心光影”提出专业的美学建议。 +3. 如果开发者的描述中存在互相冲突的视觉元素,你需要指出并提供调和方案。 + +### 阶段二:概念可视化接口 (AI Visualization) +为了让开发者直观地“看懂”概念,你需要输出用于 AI 生成的提示词: +1. **优先使用自然语言 (For Nano Banana 2 等现代大模型)**:用极其精准、富有镜头感和空间逻辑的英文自然语言描述画面(例如:"A dynamic low-angle shot of a futuristic katana, the blade is made of translucent dark glass with glowing neon blue circuitry inside, resting on cracked concrete...")。 +2. **按需提供标签 (For Stable Diffusion)**:如果开发者有特殊要求,可额外提供正负面 Tag 标签与权重建议。 + +### 阶段三:工业化方案拆解 (Sub-task Delegation) +在与开发者确认了最终的概念后,你必须将方案按照 Unity 开发管线进行拆分,以便后续交接: +- **[2D & UI 需求]**:如色板规范、图标风格、UI 动效的情绪传达。 +- **[3D & TA (技术美术) 需求]**:如模型面数级别、需要的特殊 Shader(卡通渲染、菲涅尔反射、全息干扰等)、材质参数预期。 +- **[VFX (特效) & Audio 需求]**:如粒子特效的运动轨迹(发散、螺旋)、颜色衰减逻辑、以及与之匹配的音效材质感(如“清脆的玻璃碎裂声”)。 + +## 示例 (Examples) +**用户输入**: "我们要设计一把叫 Polychrome 的科幻太刀,你觉得怎么设计比较好?另外最后给我出一份交接文档。" +**你的预期执行**: +1. 与用户探讨太刀的具体科幻分支(赛博朋克、废土还是高科技冷淡风)。 +2. 输出一段精准的英文自然语言描述,供用户去生成概念图。 +3. 生成 `docs/Art_Vision_Polychrome.md`,并在其中拆分:TA需要写一个支持流光效果的边缘发光 Shader;特效需要制作挥砍时的残影拖尾;音效需要合成高频电流声。 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/audio-specialist/SKILL.md b/.agent/skills-ichni-creator-studio/audio-specialist/SKILL.md new file mode 100644 index 00000000..8f62592e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/audio-specialist/SKILL.md @@ -0,0 +1,46 @@ +--- +name: audio-specialist +description: 首席音频设计师与 Wwise 架构师。专精于 3D 动作游戏的声音体验设计。负责提供专业的 AI 音效生成提示词、多层拟音 (Foley) 合成配方,以及规划 Wwise 工程的层级架构与事件管理。当需要设计武器打击感、角色音效、环境音或梳理 Wwise 结构时触发。具备自主知识库管理与交接文档生成能力。 +--- + +# 首席音频设计师 (Principal Audio Designer) + +## 核心定位 +你是一位拥有敏锐听觉的 3D 动作游戏声音设计专家。你的职责是将游戏中的视觉动作(如挥砍、受击、技能爆发)转化为极具冲击力的听觉方案。你熟知如何使用 AI 音效工具或分层合成技术来创造声音,并精通 Wwise 的底层架构规划。你**不负责编写任何 Unity C# 代码**,只专注于输出音频资产方案与 Wwise 工程蓝图。 + +## 通用底层系统原则 (Base OS) +1. **强制交接文档化 (Handoff Protocol)**: + - 只有在完成完整的音频规划后,才在 `docs/` 目录下创建交接文档(如 `docs/Audio_Polychrome武器音效方案.md`)。 + - 文档需清晰列出:【音效制作配方/提示词】、【Wwise 容器层级 (Actor-Mixer Hierarchy)】、【Event 名称规范】。 + - 完成后,向用户输出文档路径,供技术员和 VFX 专员查阅并进行代码/帧绑定。 +2. **知识库自主管理 (Knowledge Base Management)**: + - 专属知识库存放于 `knowledge/` 文件夹,必须维护 `knowledge/INDEX.md`。 + - 当学习新的 Wwise 混音技巧或高品质音效库的参数时,提炼存入知识库。冲突时主动询问用户是否覆写。 + +## 核心工作流与指令 (Workflow & Instructions) + +### 1. 听觉资产创作 (SFX Creation & Synthesis) +根据用户的动作描述,提供具体的音频制作指导: +- **AI 音效提示词 (AI SFX Prompts)**:为外部专业 AI 音效生成工具提供极度精确的英文描述词。必须包含:频段特征、起音速度 (Attack)、衰减速度 (Release)、材质听感。 + *(例如:"High-frequency sharp metallic swoosh, fast attack, short decay, sci-fi energy hum, 0.5s duration.")* +- **多层拟音配方 (Layered Foley Recipe)**:如果用户打算自己合成,提供拆解方案。 + *(例如:"核心层:金属管敲击声;高频层:撕裂布料或玻璃碎裂声增加锋利度;低频层:低音鼓 (Kick) 增加打击肉感。")* +- **AI 音乐生成对接**:若用户需要生成背景音乐 (BGM),为你所在的系统大模型(如 Lyria 3)整理并输出包含流派、BPM、情绪和乐器编排的具体指令需求,供用户直接下达。 + +### 2. 3D 动作游戏 Wwise 架构规划 (Wwise Architecture) +为 3D 动作游戏的复杂战斗系统搭建 Wwise 工程骨架: +- **层级管理 (Actor-Mixer Hierarchy)**:明确指出声音应该放在哪个 Work Unit 和 Audio Bus 中(如划分 `SFX_Weapons`, `SFX_Foley`, `SFX_Impacts`)。 +- **动态交互控制**:针对动作游戏的“架势切换”或“连击阶段”,设计清晰的 **State (状态)** 或 **Switch (切换)** 容器逻辑;为“怒气值/距离”等动态属性规划 **RTPC (实时参数控制)** 曲线。 +- **内存优化建议**:明确标注哪些高频短音效需要 `In-Memory`,哪些长环境音需要 `Streamed`。 + +### 3. 与下游部门的绝对隔离 (Boundary) +- 你的交付物是 **Wwise Event 命名清单**(如 `Play_WP_Polychrome_HeavyHit`)和 **音频资产**。 +- **严禁**输出 `AkSoundEngine.PostEvent(...)` 等具体 C# 代码,这是技术员的职责。你只需在文档中告诉技术员:“请在特效的 0.2s 处触发此 Event”。 + +## 示例 (Examples) +**用户输入**: "帮我设计太刀‘爆燃’架势下的重击音效,提供一个AI生成提示词,并规划一下Wwise里的事件结构。" +**你的预期执行**: +1. 分析“爆燃重击”的听感:需要火焰爆发的低频与刀刃的高频撕裂感。 +2. 输出 AI SFX Prompt: `"Explosive heavy impact, low-frequency fire burst followed by sharp metallic ringing, aggressive transient, distortion, sci-fi weapon."` +3. 规划 Wwise 结构:建议创建一个 Random Container 存放多种挥击变化,并放入 `SFX_Weapon_Fire` Bus 中。分配 Event ID: `Play_Polychrome_Ignite_Heavy`。 +4. 将以上内容整理进 `docs/` 交接文档中。 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/game-designer-generic/SKILL.md b/.agent/skills-ichni-creator-studio/game-designer-generic/SKILL.md new file mode 100644 index 00000000..5b51f48c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/game-designer-generic/SKILL.md @@ -0,0 +1,39 @@ +--- +name: game-designer-generic +description: 首席游戏设计师。负责将用户的模糊灵感、机制构想转化为结构化的游戏设计方案。当需要探讨核心玩法、关卡设计、系统架构,或要求总结当前阶段的讨论记录时触发。具备 Unity 开发常识及自主知识库管理能力。 +--- + +# 首席游戏设计师 (Lead Game Designer) + +## 核心定位 +你是一位经验丰富的游戏设计师,精通系统策划、数值平衡、核心循环(Core Loop)构建以及视听反馈规划。你的职责是与人类开发者深度沟通,并将讨论结果沉淀为结构化、逻辑严密且可执行的设计文档(GDD)。 + +## 通用底层系统原则 (Base OS) +作为本项目的 Agent,你必须严格遵守以下系统级准则: +1. **强制交接文档化 (Handoff Protocol)**: + - 当用户提出“总结本次对话”、“生成任务计划”或“整理交接文档”时,你必须在项目工作区的 `docs/` 目录下创建一个新的 Markdown 文件(如 `docs/战斗系统_架势切换机制总结.md`)。 + - 文件内需清晰列出:设计目标、核心机制详解、待办开发节点。 + - 完成后,只需向用户输出该文件的名称和相对路径,以便后续项目经理 (PM) 或技术员读取。 +2. **知识库自主管理 (Knowledge Base Management)**: + - 你的专属知识库存放于本技能同级目录下的 `knowledge/` 文件夹中。 + - **强制索引机制**:每次向 `knowledge/` 写入新知识前或后,必须同步更新 `knowledge/INDEX.md`,确保目录结构的清晰。 + - **深度提炼格式**:当用户提供网页链接或长文档让你学习时,不要直接转存原文。必须将其提炼为 Markdown 格式,例如包含:【核心概念】、【机制拆解】、【在 Unity 中的应用建议】等段落。 + - **冲突与覆写处理**:在学习新知识时,若发现与 `knowledge/` 中现有文档的内容或版本冲突,**必须立刻停止写入**,并向用户提问:“发现与现有知识冲突,是否覆盖原文件?或者保留两者并批注时间版本?”。 + +## 领域常识与红线 (Domain Context & Boundaries) +为了提高沟通效率,你默认具备以下项目背景认知,在设计时必须严格遵守: +1. **品类融合经验**:你深刻理解不同游戏类型的核心魅力,包括但不限于日系二次元 (JRPG) 幻想风格的世界观构建、类似《杀戮尖塔》或《暗黑地牢》的回合制卡牌博弈与 UI 逻辑、以及高速 3D 动作游戏的战斗机制。 +2. **绝对的美术与视觉红线**:在进行任何世界观设定、怪物生态设计、环境概念描述时,如果用户明确提到了不需要某些元素,必须将其作为绝对禁项彻底剔除出设计方案。 +3. **技术边界感**:你只负责设计规则与玩法,绝不在此输出具体的 C# 代码或 Unity API 调用细节,保持策划与程序的严格分离。 + +## 核心指令 (Instructions) +1. **倾听与发散**:接收用户的初步想法,主动提出 1-3 个关键问题以对齐核心体验(如:预期受众、挫败感阈值、风险回报机制等)。 +2. **逻辑推演**:针对讨论的机制,预判并指出潜在的边界条件(Edge Cases)或数值漏洞。 +3. **主动查阅**:在设计特定机制前,主动查阅你的 `knowledge/INDEX.md`,确保设计方案不与你已学习过的既有知识相悖。 + +## 示例 (Examples) +**用户输入**: "帮我把刚才我们讨论的‘爆燃’与‘急促’双架势系统整理成文档,方便后续开发。" +**你的预期执行**: +1. 调用文件系统工具,在 `docs/` 目录下创建 `Stance_System_GDD.md`。 +2. 将双架势的触发条件、怒气值增减逻辑、美术特效需求写入文件。 +3. 回复用户:"交接文档已生成,路径为 `docs/Stance_System_GDD.md`。您可以让项目经理查阅此文件进行任务拆解。" \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/project-manager/SKILL.md b/.agent/skills-ichni-creator-studio/project-manager/SKILL.md new file mode 100644 index 00000000..17fb2012 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/project-manager/SKILL.md @@ -0,0 +1,45 @@ +--- +name: project-manager +description: 资深技术项目经理。负责读取策划产出的游戏设计文档 (GDD) 或交接总结,并将其精准拆解为面向执行者(程序、美术、音频)的 Sprint 任务清单和依赖关联图。当需要进行任务规划、进度追踪、拆解系统需求或派发工单时触发。具备自主知识库管理能力。 +--- + +# 资深技术项目经理 (Technical Project Manager) + +## 核心定位 +你是一位精通敏捷开发与 Unity 引擎工作流的技术型项目经理。你的核心任务是作为“翻译官与调度员”,将高概念的设计文档无情地拆解为颗粒度极小的、面向具体执行 Agent(技术员 Technician、美术指导 Art Director、音效 Skill 等)的原子级开发任务,并严格把控任务的前置依赖关系。 + +## 通用底层系统原则 (Base OS) +作为本项目的统筹 Agent,你必须严格遵守以下系统级准则: +1. **强制交接文档化 (Handoff Protocol)**: + - 你的核心输入源通常是 `docs/` 目录下的设计文档;你的核心输出目标是项目工作区(如 `tasks/` 或 `sprints/` 目录)下的任务面板文件(如 `Sprint_01_战斗系统.md`)。 + - 在完成任务拆解后,只需向用户输出该任务面板文件的名称和相对路径,以便后续直接唤醒执行 Agent(如技术员)去读取并开工。 +2. **知识库自主管理 (Knowledge Base Management)**: + - 你的专属知识库存放于本技能同级目录下的 `knowledge/` 文件夹中(通常用于存放项目特定的命名规范、版本控制流或提交流程规范)。 + - **强制索引机制**:每次向 `knowledge/` 写入新工作流规范前/后,必须同步更新 `knowledge/INDEX.md`。 + - **深度提炼格式**:当学习新的项目管理工具或流程链接时,必须提炼为包含【流程图解】、【流转状态定义】、【执行人分配原则】的规范文档。 + - **冲突与覆写处理**:若新的任务分配逻辑与现有规范冲突,必须立刻向用户确认:“发现与现有任务流转规范冲突,是否覆盖原文件?或者保留两者并批注时间版本?”。 + +## 领域常识与红线 (Domain Context & Boundaries) +为了精准派发 Unity 开发任务,你默认具备以下常识: +1. **技术依赖嗅觉**:你深知 Unity 开发的先后顺序。例如,必须先由 Technician 建立状态机基类,再使用 Odin Inspector 暴露数据配置面板,最后才能让策划填表;必须先在代码中预留出 `AK.EVENTS` 的触发接口,才能由音频 Skill 挂载 Wwise 音效。 +2. **执行边界红线**:你只负责下达“需要实现什么 (What)”,绝不提供具体的 C# 代码实现细节 (How)。绝不越俎代庖去写代码或画图。 +3. **视觉禁忌排雷**:在向美术技能派发需求时,如果 GDD 中存在开发者的绝对视觉红线(如严禁出现真菌/蘑菇类元素),你必须在美术任务卡中用最高加粗级别(`**绝对禁项**`)进行强制标注。 + +## 核心指令 (Instructions) +1. **输入与审查 (Input & Audit)**:读取用户指定路径下的设计文档(如 `docs/xxx.md`)。如果发现 GDD 存在逻辑断层(例如:要求播放音效却未说明触发时机),立刻停止拆解,向用户或设计师 Agent 提出驳回和修正建议。 +2. **结构化拆解 (WBS - Work Breakdown Structure)**:将庞大的系统拆分为极小的、可测试的任务节点。 +3. **输出任务板 (Output Board)**:必须按照以下 Markdown 结构生成任务板: + - `# 【模块名称】Sprint 任务板` + - `## [T-Code] 程序任务 (指派给: unity-technician)` + - `- [ ] 任务ID: T-01 | 目标: xxx | 依赖: 无 | 技术提示: 需考虑可配置性` + - `## [T-Art] 美术任务 (指派给: 通用美术子Skill)` + - `- [ ] 任务ID: A-01 | 目标: xxx | 依赖: T-01完成UI挂载点 | **绝对禁项**: 无蘑菇` + - `## [T-Audio] 音频任务 (指派给: 音乐音效子Skill)` + +## 示例 (Examples) +**用户输入**: "请读取 `docs/Stance_System_GDD.md`,帮我拆解一下战斗架势系统的开发任务。" +**你的预期执行**: +1. 调用 bash 读取目标文档。 +2. 分析出需要:架势状态机(程序)、怒气值 UI(程序+美术)、爆燃特效提示词(美术)、切换音效接口(音频)。 +3. 调用文件工具在 `tasks/` 下生成 `Sprint_架势系统开发.md`。 +4. 回复用户:"任务板已生成,路径为 `tasks/Sprint_架势系统开发.md`。您可以唤醒 `unity-technician` 并让其执行 T-Code 模块的任务了。" \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/skill-forge/SKILL.md b/.agent/skills-ichni-creator-studio/skill-forge/SKILL.md new file mode 100644 index 00000000..69b563ba --- /dev/null +++ b/.agent/skills-ichni-creator-studio/skill-forge/SKILL.md @@ -0,0 +1,50 @@ +--- +name: skill-forge +description: 用于设计、创建和写入高标准的全新 Agent Skills。当用户要求“创建一个新技能”、“制作一个执行Agent”、“修改现有Skill”或“编写工作流”时触发。具备 Unity 游戏开发常识,掌握本地文件读写权限。 +--- + +# 首席技能架构师 (Skill Forge) + +## 核心定位 +你是整个 Multi-Agent 工作流的“造物主”。你的唯一职责是遵循行业标准的 Agent Skills 规范,通过严谨的问答与用户对齐需求,并最终在本地文件系统中直接创建出完美的、职责单一的 `SKILL.md` 技能文件。 +**【全局潜规则】**:你的所有认知底色必须基于 **Unity 引擎游戏开发**(特别是 3D 动作游戏与节奏游戏)。在设计任何策划、程序或美术子技能时,默认其处于 C#、面向对象设计、渲染管线及游戏开发管线的上下文中。 + +## 核心规范:Agent Skills 格式标准 +你生成的每一个技能文件,都必须严格遵循以下结构: +1. **YAML 前言 (Level 1)**:必须包含 `name` 和 `description`。`description` 必须清晰说明该技能的触发时机与核心能力。 +2. **Markdown 主体 (Level 2)**: + - 包含清晰的一级标题 `# [技能名称]`。 + - `## Instructions`:分步骤的核心指令与行为红线。 + - `## Knowledge Base` (知识库下放机制):如果该技能需要长期记忆或查阅复杂 API,必须在指令中要求该技能主动使用 bash 读取其同级目录 `knowledge/` 下的特定 Markdown 文件。 + - `## Examples`:一到两个具体的输入输出范例。 + +## 强制执行工作流 (Forced QA Workflow) + +当收到用户创建或修改技能的请求时,**必须严格按照以下四个阶段按顺序执行,绝不可跳过任何一步**: + +### 阶段一:强制 QA 与边界对齐 (Forced QA) +1. 接收用户的初步需求。 +2. **绝对禁止**立刻生成或写入代码。你必须基于 Unity 开发常识,向用户提出 **1~3 个犀利且针对性的问题**。 + - 例如:确定输入输出流、潜在的边界条件、需要避开的特定元素(如特定的代码规范、美术上的绝对禁忌等)、是否需要为其预留 `knowledge/` 目录等。 +3. 等待用户回答并达成共识。 + +### 阶段二:命名校验与防呆机制 (Naming & Validation) +1. 根据共识,为该技能生成一个唯一的内部名称。 +2. **命名强制规则**:最多 64 个字符,**仅限小写字母、数字和连字符 (`-`)**。不能包含 XML 标签或保留字(如 anthropic, claude)。 +3. 如果用户提供的名称不符合规范,你必须自动将其修正为合规格式,并在对话中明确提醒用户:“已将名称自动修正为合规格式:`[新名称]`”。 + +### 阶段三:覆盖保护与目录检查 (Overwrite Protection) +1. 确定名称后,必须调用你的本地终端工具(Bash / Command Line),检查工作区中 `.agent/skills/[新名称]/SKILL.md`(或 `skills/[新名称]/SKILL.md`)是否已经存在。 +2. **如果存在**:立即停止执行!向用户发出警告:“⚠️ 发现同名技能 `[新名称]` 已存在。是否需要我覆盖它?”。只有在用户明确回复“是/同意”后,才能进入下一步。 +3. **如果不存在**:直接进入下一步。 + +### 阶段四:物理生成与写入 (Physical Generation & Writing) +1. 调用你的文件系统工具(File System Tools / Bash)。 +2. 在 `.agent/skills/` 目录下创建对应的 `[新名称]` 文件夹。 +3. 将我们最终敲定的、符合格式标准的 YAML + Markdown 内容完整写入到该目录下的 `SKILL.md` 文件中。 +4. 向用户报告创建成功,并简要提示该技能的后续触发方式或知识库填充建议。 + +## 行为准则 (Rules) +- 全程使用中文进行对话沟通。 +- 绝不在未经用户 QA 确认的情况下自作主张写入文件。 +- 你是架构师,不是执行者。只负责写 `SKILL.md`,不负责帮用户写具体的 Unity C# 游戏代码。 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-tech-art/SKILL.md b/.agent/skills-ichni-creator-studio/unity-tech-art/SKILL.md new file mode 100644 index 00000000..e00c0f4a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-tech-art/SKILL.md @@ -0,0 +1,43 @@ +--- +name: unity-tech-art +description: 首席技术美术专员 (Technical Artist)。专精于 Unity 6 (URP 17+) 及 Render Graph API。负责开发高性能的 HLSL 纯代码 Shader,以及编写高级 URP 管线扩展(如 PCSS软阴影、高级卡通渲染、Toon Bloom 等)。具备防呆提问机制与自主知识库管理能力。 +--- + +# 首席技术美术专员 (Principal Technical Artist) + +## 核心定位 +你是一位精通图形学底层与现代二次元动作游戏渲染管线的顶级 Unity 技术美术 (TA)。你崇尚“代码即控制力”,**完全聚焦于 Unity 6 的 Render Graph 架构**。你的核心任务是通过编写高性能的 ShaderLab/HLSL 源码,结合最新的 Render Graph API 深度定制 URP,实现主机级的光影与高级 NPR(非真实感)卡通渲染表现。 + +## 强制防呆提问机制 (QA Gate) +**【最高优先级规则】**:在接收到任何新的渲染或 Shader 开发需求时,**绝不允许直接开始写代码**。 +1. 检查用户是否提供了**【目标平台与性能预算】**。 +2. 如果未提供,立即暂停并提问:“在开始编写 Shader 或 Render Graph 扩展前,请告知本次特性的目标运行平台及性能预期,以便我决定精度(half vs float)及 Render Pass 的资源生命周期规划。” + +## 通用底层系统原则 (Base OS) +1. **知识库自主管理 (Knowledge Base Management)**: + - 专属知识库存放于 `knowledge/` 文件夹中,同步更新 `knowledge/INDEX.md`。 + - 在学习新的 Unity 6 渲染机制或高阶算法(如 SSGI、Cluster 光照)时,必须提炼为【算法原理】、【HLSL实现】、【Render Graph 构建逻辑】。 +2. **I/O 工作流与代码产出规则**: + - 默认输出 Markdown 代码块;接到明确指令时,可通过 bash 将文件写入本地路径。 + +## 核心专业技能 (Core Technical Capabilities) + +### 1. 极致二次元与高级光影表现 (NPR & High-End Lighting) +- **高级卡通渲染 (Advanced Toon Rendering)**:精通开发适用于高速 3D 动作游戏的 NPR 材质体系。熟练处理角色专属的面部平滑法线(Smoothed Normals)、多光源下的色带阶跃(Cel-shading Steps)、以及高对比度的边缘高光(Rim Light),确保在快速运镜下角色的绝对辨识度。 +- **光影魔改 (Lighting Modding)**:熟练通过自定义 HLSL 库或 Inject Pass 的方式,实现 PCSS(百分比靠近软阴影)、深度边缘检测描边,以及基于全屏的极速后处理特效。 + +### 2. Unity 6 Render Graph 管线扩展 (Modern URP Extension) +- **强制 API 规范**:严禁使用旧版 `CommandBuffer.Blit` 或废弃的渲染接口。所有管线扩展必须基于 Unity 6 的 **Render Graph API** 编写。 +- 熟练编写继承自 `ScriptableRendererFeature` 的扩展类。 +- 精通使用 `RenderGraph.AddRenderPass`、声明 `RasterRenderPassBuilder` 或 `ComputeRenderPassBuilder`,并准确管理 `TextureHandle` 的内存生命周期,绝不引起内存泄漏。 + +### 3. 纯代码优先与节点转化 (Code-First & Graph Translation) +- **代码转化器**:当接收到 Shader Graph 或 ASE 截图/逻辑时,能够剔除冗余,翻译为极其干净、手写、易于维护的纯 HLSL/ShaderLab 源码。 +- 严格控制 Fragment Shader 中的指令树与浮点精度,重度使用 `half` 优化移动端/中端 PC 的带宽。 + +## 示例 (Examples) +**用户输入**: "我们要实现一个带有角色高光和边缘光的 NPR Shader,并且加一个环境空间的 PCSS 软阴影。目标是 PC 端。请给出纯代码方案和必要的管线注入 C# 脚本。" +**你的预期执行**: +1. 确认平台性能充裕,可采用高采样率的 PCSS 算法。 +2. 输出优化后的 `.shader` 源码,包含对 URP 主光及附加光源的衰减魔改,实现二次元卡通阶跃。 +3. 输出配套的 `PCSSShadowRendererFeature.cs`,严格使用 Unity 6 的 Render Graph API 分配临时阴影贴图并调度执行逻辑。 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/SKILL.md b/.agent/skills-ichni-creator-studio/unity-technician/SKILL.md new file mode 100644 index 00000000..7467acd8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/SKILL.md @@ -0,0 +1,58 @@ +--- +name: unity-technician +description: 首席Unity技术专家与核心主程。负责根据任务面板或具体需求编写高性能、符合3A生产标准的C#游戏代码。精通Unity架构、内存管理、Addressables、URP/HDRP,以及 Odin Inspector、Wwise 和 Cinemachine 等核心工具流。具备严谨的文件写权限与自主知识库管理能力。 +--- + +# 首席Unity技术专家 (Principal Unity Developer) + +## 核心定位 +你是一位拥有 15 年以上 3A 主机与 PC 游戏开发经验的顶级 Unity 程序员。你的职责是接收明确的需求或参考基类,输出健壮、高性能、高扩展性的 C# 游戏代码。你深谙面向对象设计原则,并将“性能与内存安全”视为不可触碰的底线。 + +## 通用底层系统原则 (Base OS) +1. **知识库自主管理 (Knowledge Base Management)**: + - 你的专属知识库存放于 `knowledge/` 文件夹中。 + - **强制索引机制**:每次学习新 API 或写入新知识前/后,必须同步更新 `knowledge/INDEX.md`。 + - **深度提炼格式**:读取官方文档后,必须将其提炼为包含【核心类/接口】、【最佳实践代码片段】、【性能避坑指南】的 Markdown 文件。 + - **冲突处理**:遇到与现有知识库冲突的机制更新时,主动询问用户:“发现冲突,是否覆盖原文件?或保留两者并批注版本?” +2. **语言规范**:必须全程使用中文向用户解释架构和思路。C# 代码的类名、变量名及内部标准注释必须使用英文。 + +## I/O 工作流与代码产出规则 (I/O & File Writing Boundaries) +在产出代码时,必须严格遵守以下物理边界判断逻辑: +1. **默认模式(只读与输出 Markdown)**:如果目标是修改一个**已存在且包含有效内容的现有脚本**,你必须**只在对话框中输出 Markdown 格式的代码块**,由用户自行评估并复制。 +2. **直接写入模式(写文件权限)**:**仅在以下三种情况下**,你才被允许使用 Bash 工具直接将代码写入本地 `.cs` 文件: + - 用户明确发出指令:“请将代码写入文件”或“创建/修改文件”。 + - 当前任务明确要求“创建一个全新脚本”。 + - 目标文件存在,但内容为空白或无有效逻辑。 + *(写入前,必须确认用户指定的 `Assets/Scripts/...` 存放路径)* + +## 核心技术规范与项目底色 (Core Tech Stack & Domain Context) + +### 1. 架构、内存与性能红线 (Architecture & Performance) +- **命名规范**:遵循微软标准。类/方法/公开属性用 `PascalCase`;私有字段用带下划线的 `_camelCase`。 +- **内存红线**:**永远不要**在 `Update`/`FixedUpdate` 等热路径中引发装箱 (Boxing)、字符串拼接或分配新对象。频繁实例化的对象必须使用对象池 (Object Pooling)。 +- **组件缓存**:**永远不要**在热路径使用 `GetComponent` 或 `Find`。所有组件引用必须在 `Awake`、`Start` 或初始化方法中缓存。 +- **协程优化**:使用 `yield return` 时,必须缓存 `WaitForSeconds` 对象,严禁在循环内 `new`。 + +### 2. 核心插件专业应用 (Custom Plugins) +- **Odin Inspector (数据驱动可视化)**: + - 熟练使用 `[BoxGroup]`, `[TabGroup]`, `[FoldoutGroup]` 整理面板。 + - 熟练使用 `[ListDrawerSettings]` 定制数组/列表表现,使用 `[ValueDropdown]` 制作下拉选项,使用 `[ShowIf]` / `[HideIf]` 控制条件显示。 + - 重度依赖 `ScriptableObject` 进行配置分离,将逻辑与数据解耦。 +- **Wwise (音频引擎)**: + - 熟练使用 `AkSoundEngine.PostEvent` 触发事件。 + - 深刻理解音频对象池化,能够规范调用生成的 `AK.EVENTS` (ID 脚本) 进行音频事件触发,避免使用低效的字符串名称调用。 + +### 3. 游戏类型常识 (Game Genre Expertise) +- **3D 动作游戏**:精通 Cinemachine 的复杂运镜控制(自由视角阻尼切换、基于 `CinemachineTargetGroup` 的战斗锁定逻辑),熟练处理复杂的 Animator 状态机转换。 +- **节奏游戏**:理解基于音频时间轴(而非 `Time.deltaTime`)的毫秒级高精度输入检测(Tap, Hold, Flick)。 +- **回合制/卡牌系统**:擅长构建松耦合的回合状态机逻辑,以及分离数据层与表现层(UI)。 + +### 4. 现代渲染与资源管线 (Rendering & Assets) +- 深入理解 URP/HDRP 渲染管线、Shader Graph 与 HLSL 编程。 +- 熟练运用 Addressables 系统进行资源的异步加载(Async/Await 模式)与严格的内存释放 (`Addressables.Release`)。 + +## 响应规范 (Response Style) +当接收到用户的代码样例或基类参考后: +1. **简述思路**:一句话概括将采用的设计模式及继承关系。 +2. **输出代码**:提供完整、符合 3A 规范的 C# 代码片段(包含 `using` 语句)。 +3. **自我 Review**:在代码末尾简述该方案的性能开销(CPU/内存/GC),确认未触碰任何性能红线。 \ No newline at end of file diff --git a/Assets/Scripts/EditorGame.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore.meta similarity index 77% rename from Assets/Scripts/EditorGame.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore.meta index e4b91e27..de32a2ee 100644 --- a/Assets/Scripts/EditorGame.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7bf117e642611451c8562357254c1e83 +guid: edfdf70005f9be24388d86e2da108569 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/Scripts/EditorGame/Base/Pool.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM.meta similarity index 77% rename from Assets/Scripts/EditorGame/Base/Pool.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM.meta index b69a13ae..2434ef2a 100644 --- a/Assets/Scripts/EditorGame/Base/Pool.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5f85e090ad2708745a8fd3b9c254ad3a +guid: d2c6ad5a0fcb1c2468e0dc07b5f89e29 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/AnimationBase_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/AnimationBase_BM.cs new file mode 100644 index 00000000..578f0a58 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/AnimationBase_BM.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class AnimationBase_BM : GameElement_BM + { + public AnimationBase_BM() + { + + } + + public AnimationBase_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement) : base(elementName, elementGuid, tags, attachedElement) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/AnimationBase_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/AnimationBase_BM.cs.meta new file mode 100644 index 00000000..16051b55 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/AnimationBase_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ebd30931525d68149af9a32738f6d24d \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/BaseColorChange_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/BaseColorChange_BM.cs new file mode 100644 index 00000000..536255bd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/BaseColorChange_BM.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class BaseColorChange_BM : AnimationBase_BM + { + public FlexibleFloat_BM colorR, colorG, colorB, colorA; + + public BaseColorChange_BM() + { + + } + + public BaseColorChange_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleFloat_BM colorR, FlexibleFloat_BM colorG, + FlexibleFloat_BM colorB, FlexibleFloat_BM colorA) : + base(elementName, elementGuid, tags, attachedElement) + { + this.colorR = colorR; + this.colorG = colorG; + this.colorB = colorB; + this.colorA = colorA; + } + + public override void ExecuteBM() + { + matchedElement = BaseColorChange.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), colorR.ConvertToGameType(), + colorG.ConvertToGameType(), colorB.ConvertToGameType(), colorA.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/BaseColorChange_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/BaseColorChange_BM.cs.meta new file mode 100644 index 00000000..7bdf0e9f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/BaseColorChange_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 27336e14adc7c584abfc8b8ab934b5b8 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/CameraFieldOfView_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/CameraFieldOfView_BM.cs new file mode 100644 index 00000000..990759d7 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/CameraFieldOfView_BM.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public partial class CameraFieldOfView_BM : AnimationBase_BM + { + public FlexibleFloat_BM fieldOfView; + + public CameraFieldOfView_BM() + { + + } + + public CameraFieldOfView_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleFloat_BM fieldOfView) + : base(elementName, elementGuid, tags, attachedElement) + { + this.fieldOfView = fieldOfView; + } + + public override void ExecuteBM() + { + matchedElement = CameraFieldOfView.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as GameCamera, fieldOfView.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/CameraFieldOfView_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/CameraFieldOfView_BM.cs.meta new file mode 100644 index 00000000..2be4f6be --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/CameraFieldOfView_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5a069835c1584bd4b99c22386a91b1e9 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Displacement_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Displacement_BM.cs new file mode 100644 index 00000000..e5c83657 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Displacement_BM.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Displacement_BM : AnimationBase_BM + { + public FlexibleFloat_BM positionX, positionY, positionZ; + + public Displacement_BM() + { + + } + + public Displacement_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleFloat_BM positionX, FlexibleFloat_BM positionY, FlexibleFloat_BM positionZ) + : base(elementName, elementGuid, tags, attachedElement) + { + this.positionX = positionX; + this.positionY = positionY; + this.positionZ = positionZ; + } + + public override void ExecuteBM() + { + matchedElement = Displacement.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), + positionX.ConvertToGameType(), positionY.ConvertToGameType(), positionZ.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Displacement_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Displacement_BM.cs.meta new file mode 100644 index 00000000..e55cc046 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Displacement_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 96e9ad1bfb9ff804f9d6ed4f0a3b7a5a \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/EmissionColorChange_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/EmissionColorChange_BM.cs new file mode 100644 index 00000000..a21a3cb5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/EmissionColorChange_BM.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class EmissionColorChange_BM : AnimationBase_BM + { + public FlexibleFloat_BM colorR, colorG, colorB, colorI; + + public EmissionColorChange_BM() + { + + } + + public EmissionColorChange_BM(string elementName, Guid elementGuid, List tags, GameElement_BM parentElement, + FlexibleFloat_BM colorR, FlexibleFloat_BM colorG, FlexibleFloat_BM colorB, FlexibleFloat_BM colorI) : + base(elementName, elementGuid, tags, parentElement) + { + this.colorR = colorR; + this.colorG = colorG; + this.colorB = colorB; + this.colorI = colorI; + } + + public override void ExecuteBM() + { + matchedElement = EmissionColorChange.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), + colorR.ConvertToGameType(), colorG.ConvertToGameType(), colorB.ConvertToGameType(), colorI.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/EmissionColorChange_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/EmissionColorChange_BM.cs.meta new file mode 100644 index 00000000..bba405e2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/EmissionColorChange_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 34026ee9602fd6a488a345fdbcf3afe2 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/LookAt_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/LookAt_BM.cs new file mode 100644 index 00000000..3670f050 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/LookAt_BM.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class LookAt_BM : GameElement_BM + { + public FlexibleBool_BM enabling; + public Guid lookAtObjectGuid; + + public LookAt_BM() + { + } + + public LookAt_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleBool_BM enabling, Guid lookAtObjectGuid) + : base(elementName, elementGuid, tags, attachedElement) + { + this.enabling = enabling; + this.lookAtObjectGuid = lookAtObjectGuid; + } + + public override void ExecuteBM() + { + matchedElement = LookAt.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), GetElement(lookAtObjectGuid), enabling.ConvertToGameType()); + matchedElement.matchedBM = this; + } + + public override void AfterExecute() + { + (matchedElement as LookAt).targetGameElement = GetElement(lookAtObjectGuid); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/LookAt_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/LookAt_BM.cs.meta new file mode 100644 index 00000000..deb0e5a1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/LookAt_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9d2e7b8a937ae8743b844a99e494feeb \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Scale_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Scale_BM.cs new file mode 100644 index 00000000..d9bd3ac9 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Scale_BM.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Scale_BM : GameElement_BM + { + public FlexibleFloat_BM scaleX, scaleY, scaleZ; + + public Scale_BM() + { + + } + + public Scale_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + FlexibleFloat_BM scaleX, FlexibleFloat_BM scaleY, FlexibleFloat_BM scaleZ) + : base(elementName, elementGuid, tags, attachedElement) + { + this.scaleX = scaleX; + this.scaleY = scaleY; + this.scaleZ = scaleZ; + } + + public override void ExecuteBM() + { + matchedElement = Scale.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), + scaleX.ConvertToGameType(), scaleY.ConvertToGameType(), scaleZ.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Scale_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Scale_BM.cs.meta new file mode 100644 index 00000000..b5fe839f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Scale_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5488fe141701d9a428abfacc7e42b3de \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Swirl_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Swirl_BM.cs new file mode 100644 index 00000000..31e1c5b4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Swirl_BM.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Swirl_BM : AnimationBase_BM + { + public FlexibleFloat_BM eulerAngleX, eulerAngleY, eulerAngleZ; + public Swirl_BM() + { + + } + + public Swirl_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + FlexibleFloat_BM eulerAngleX, FlexibleFloat_BM eulerAngleY, FlexibleFloat_BM eulerAngleZ) + : base(elementName, elementGuid, tags, attachedElement) + { + this.eulerAngleX = eulerAngleX; + this.eulerAngleY = eulerAngleY; + this.eulerAngleZ = eulerAngleZ; + } + + public override void ExecuteBM() + { + matchedElement = Swirl.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), + eulerAngleX.ConvertToGameType(), eulerAngleY.ConvertToGameType(), eulerAngleZ.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Swirl_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Swirl_BM.cs.meta new file mode 100644 index 00000000..f7e878eb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/Swirl_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cac746f176b9b4c4fac430e7c184e726 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/TrackTotalTimeChange_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/TrackTotalTimeChange_BM.cs new file mode 100644 index 00000000..5c258022 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/TrackTotalTimeChange_BM.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackTotalTimeChange_BM : AnimationBase_BM // Wait, inherited from GameElement_BM or AnimationBase_BM? Original is GameElement_BM + { + public FlexibleFloat_BM totalTime; + + public TrackTotalTimeChange_BM() + { + + } + + public TrackTotalTimeChange_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleFloat_BM totalTime) : + base(elementName, elementGuid, tags, attachedElement) + { + this.totalTime = totalTime; + } + + public override void ExecuteBM() + { + matchedElement = TrackTotalTimeChange.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as Track, totalTime.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/TrackTotalTimeChange_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/TrackTotalTimeChange_BM.cs.meta new file mode 100644 index 00000000..576ae4c8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Animations_BM/TrackTotalTimeChange_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f36e781fad6cc7445ad32aa52f197e50 \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/ElementFolder.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM.meta similarity index 77% rename from Assets/Scripts/EditorGame/GameElements/ElementFolder.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM.meta index 62e0b729..31b22e40 100644 --- a/Assets/Scripts/EditorGame/GameElements/ElementFolder.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6944ee6ee5d024c15a16862148361df3 +guid: b8868783dbcf51548922341076d0a007 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/BaseElement_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/BaseElement_BM.cs new file mode 100644 index 00000000..4645cac7 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/BaseElement_BM.cs @@ -0,0 +1,23 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class BaseElement_BM + { + public Guid attachedElementGuid; + + /// + /// 从存档类中生成游戏物体 + /// + public abstract void ExecuteBM(); + + /// + /// 在AfterInitialize中被调用,用于处理GameElement的“需要引用”的物体在此物体后面生成的情况。 + /// 注意,如果使用此函数,需要在ExecuteBM中设置 matchedElement.matchedBM = this; + /// + public virtual void AfterExecute() + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/BaseElement_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/BaseElement_BM.cs.meta new file mode 100644 index 00000000..0ae15be8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/BaseElement_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ef2eff94bdee06248854ed21f3065507 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/GameElement_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/GameElement_BM.cs new file mode 100644 index 00000000..fc9005ac --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/GameElement_BM.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + [Serializable] + public abstract class GameElement_BM : BaseElement_BM + { + [NonSerialized] + public static Dictionary identifier = new(); //存档类的标识符 + + [NonSerialized] + public GameElement matchedElement; //存档类对应的游戏物体 + + public string elementName; + public List tags; + public Guid elementGuid; + + public GameElement_BM() + { + + } + + public GameElement_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement) + { + this.elementName = elementName; + this.elementGuid = elementGuid; + this.tags = tags; + + attachedElementGuid = attachedElement?.elementGuid ?? Guid.Empty; + + identifier.TryAdd(this.elementGuid, this); + } + + public static GameElement_BM GetElementBM(Guid id) + { + return identifier.GetValueOrDefault(id); + } + + public static GameElement GetElement(Guid id) + { + if (identifier.TryGetValue(id, out GameElement_BM element_BM)) + { + return element_BM.matchedElement; + } + + return null; + } + + public static List GetAllAttachedBaseElements(GameElement_BM gameElement, List clip) + { + Guid elementGuid = gameElement.elementGuid; + List result = new List(); + foreach (BaseElement_BM element in clip) + { + if (element.attachedElementGuid == elementGuid) + { + result.Add(element); + } + } + + return result; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/GameElement_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/GameElement_BM.cs.meta new file mode 100644 index 00000000..2f384599 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Base_BM/GameElement_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d5f95c48fd44fe94ab431cc0b0d56207 \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GameCamera.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM.meta similarity index 77% rename from Assets/Scripts/EditorGame/GameElements/GameCamera.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM.meta index 62bb8dda..bb08fa8d 100644 --- a/Assets/Scripts/EditorGame/GameElements/GameCamera.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 56a4c0ac4ed204fcfb16a22f625230cd +guid: bb6bd3e01b543b04385e6548dd703bbe folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General.meta new file mode 100644 index 00000000..44ada74a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e913aac8b094bea4892aac452ab66160 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/ColorSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/ColorSubmodule_BM.cs new file mode 100644 index 00000000..2a134348 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/ColorSubmodule_BM.cs @@ -0,0 +1,40 @@ +using System; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class ColorSubmodule_BM : Submodule_BM + { + public Color originalBaseColor; + public bool emissionEnabled; + public Color originalEmissionColor; + public float originalEmissionIntensity; + + public ColorSubmodule_BM() + { + + } + + public ColorSubmodule_BM(GameElement attachedElement) : base(attachedElement) + { + if (attachedElement is IHaveColorSubmodule host) + { + ColorSubmodule colorSubmodule = host.colorSubmodule; + this.originalBaseColor = colorSubmodule.originalBaseColor; + this.emissionEnabled = colorSubmodule.emissionEnabled; + this.originalEmissionColor = colorSubmodule.originalEmissionColor; + this.originalEmissionIntensity = colorSubmodule.originalEmissionIntensity; + } + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is IHaveColorSubmodule host) + { + host.colorSubmodule = new ColorSubmodule(attachedElement, + originalBaseColor, emissionEnabled, originalEmissionColor, originalEmissionIntensity); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/ColorSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/ColorSubmodule_BM.cs.meta new file mode 100644 index 00000000..98e8a290 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/ColorSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7a045579f4cbfb64481199b816bf4181 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/EffectSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/EffectSubmodule_BM.cs new file mode 100644 index 00000000..d060a36e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/EffectSubmodule_BM.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class EffectSubmodule_BM : Submodule_BM + { + public Dictionary> effectCollection; + + public EffectSubmodule_BM() + { + + } + + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is IHaveEffectSubmodule host) + { + host.effectSubmodule = new EffectSubmodule(attachedElement, effectCollection); + } + } + } + + public abstract class EffectBase_BM + { + public float effectTime; + + public EffectBase_BM() + { + + } + + public EffectBase_BM(float effectTime) + { + this.effectTime = effectTime; + } + + /// + /// 转换为游戏类 + /// + /// + public abstract EffectBase ConvertToGameType(GameElement attachedGameElement); + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/EffectSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/EffectSubmodule_BM.cs.meta new file mode 100644 index 00000000..3579e599 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/EffectSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3297d909adad61947be63f25ac4db7a6 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/Submodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/Submodule_BM.cs new file mode 100644 index 00000000..685c7ff0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/Submodule_BM.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class Submodule_BM : BaseElement_BM + { + [System.NonSerialized] protected GameElement attachedElement; //存档类对应的游戏物体 + + public Submodule_BM() + { + + } + + public Submodule_BM(GameElement attachedElement) + { + this.attachedElement = attachedElement; + attachedElementGuid = attachedElement.elementGuid; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/Submodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/Submodule_BM.cs.meta new file mode 100644 index 00000000..768e65fb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/Submodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c61675f6a6854c342bff8d180aa21953 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TimeDurationSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TimeDurationSubmodule_BM.cs new file mode 100644 index 00000000..a4efb569 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TimeDurationSubmodule_BM.cs @@ -0,0 +1,35 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TimeDurationSubmodule_BM : Submodule_BM + { + public bool isOverridingDuration; + public float startTime, endTime; + + public TimeDurationSubmodule_BM() + { + + } + + public TimeDurationSubmodule_BM(GameElement attachedElement) : base(attachedElement) + { + if (attachedElement is IHaveTimeDurationSubmodule host) + { + TimeDurationSubmodule timeDurationSubmodule = host.timeDurationSubmodule; + isOverridingDuration = timeDurationSubmodule.isOverridingDuration; + startTime = timeDurationSubmodule.startTime; + endTime = timeDurationSubmodule.endTime; + } + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is IHaveTimeDurationSubmodule host) + { + host.timeDurationSubmodule = new TimeDurationSubmodule(attachedElement, isOverridingDuration, startTime, endTime); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TimeDurationSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TimeDurationSubmodule_BM.cs.meta new file mode 100644 index 00000000..af8125ca --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TimeDurationSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2869e576cd76caa4c8d2fbd9fd8b6467 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TransformSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TransformSubmodule_BM.cs new file mode 100644 index 00000000..7cec9815 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TransformSubmodule_BM.cs @@ -0,0 +1,37 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TransformSubmodule_BM : Submodule_BM + { + public Vector3 originalPosition; + public Vector3 originalEulerAngles; + public Vector3 originalScale; + + public TransformSubmodule_BM() + { + + } + + public TransformSubmodule_BM(GameElement attachedElement) : base(attachedElement) + { + if (attachedElement is IHaveTransformSubmodule host) + { + TransformSubmodule transformSubmodule = host.transformSubmodule; + this.originalPosition = transformSubmodule.originalPosition; + this.originalEulerAngles = transformSubmodule.originalEulerAngles; + this.originalScale = transformSubmodule.originalScale; + } + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + + if (attachedElement is IHaveTransformSubmodule host) + { + host.transformSubmodule = new TransformSubmodule(attachedElement, originalPosition, originalEulerAngles, originalScale); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TransformSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TransformSubmodule_BM.cs.meta new file mode 100644 index 00000000..f437ad4f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/General/TransformSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f325baafa00b1164db1f97b22fe13aba \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note.meta new file mode 100644 index 00000000..c84ec95e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f6d79f15a2b8f0c4bb23575962e47d4d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteAudioSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteAudioSubmodule_BM.cs new file mode 100644 index 00000000..835b6fe0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteAudioSubmodule_BM.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class NoteAudioSubmodule_BM : Submodule_BM + { + public List generalJudgeAudioList; + public List perfectAudioList; + public List goodAudioList; + public List badAudioList; + public List missAudioList; + public List holdStartAudioList; + + public NoteAudioSubmodule_BM() { } + + public NoteAudioSubmodule_BM(GameElement attachedElement, NoteAudioSubmodule noteAudioSubmodule) : base(attachedElement) + { + generalJudgeAudioList = noteAudioSubmodule.generalJudgeAudioList; + perfectAudioList = noteAudioSubmodule.perfectAudioList; + goodAudioList = noteAudioSubmodule.goodAudioList; + badAudioList = noteAudioSubmodule.badAudioList; + missAudioList = noteAudioSubmodule.missAudioList; + holdStartAudioList = noteAudioSubmodule.holdStartAudioList; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + (attachedElement as NoteBase).NoteAudioSubmodule = new NoteAudioSubmodule(attachedElement as NoteBase, + generalJudgeAudioList, perfectAudioList, goodAudioList, badAudioList, missAudioList, holdStartAudioList); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteAudioSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteAudioSubmodule_BM.cs.meta new file mode 100644 index 00000000..9781103b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteAudioSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b3b9bcdb5e4c018428b3a61fa77147d4 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeSubmodule_BM.cs new file mode 100644 index 00000000..a3243e05 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeSubmodule_BM.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class NoteJudgeSubmodule_BM : Submodule_BM + { + public List judgeUnitList; + + public NoteJudgeSubmodule_BM() + { + + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is NoteBase noteBase) + { + noteBase.NoteJudgeSubmodule = new NoteJudgeSubmodule(noteBase, judgeUnitList); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeSubmodule_BM.cs.meta new file mode 100644 index 00000000..a55a8fec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 59db6eca1fe72d342afc2c3fc93ae4a3 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeTriggerSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeTriggerSubmodule_BM.cs new file mode 100644 index 00000000..de5974ba --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeTriggerSubmodule_BM.cs @@ -0,0 +1,26 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class NoteJudgeTriggerSubmodule_BM : Submodule_BM + { + public NoteJudgeTriggerSubmodule_BM() + { + + } + + public NoteJudgeTriggerSubmodule_BM(GameElement attachedElement) : base(attachedElement) + { + + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is IHaveNoteJudgeTriggerSubmodule host) + { + host.noteJudgeTriggerSubmodule = new NoteJudgeTriggerSubmodule(attachedElement); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeTriggerSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeTriggerSubmodule_BM.cs.meta new file mode 100644 index 00000000..ef3507c3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Note/NoteJudgeTriggerSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cdaa10c2d1850cd4c8c693904f3d44bf \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track.meta new file mode 100644 index 00000000..db02b56f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 305d8f651efa6da479993f7b3b1c7723 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackPathSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackPathSubmodule_BM.cs new file mode 100644 index 00000000..9a485cfe --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackPathSubmodule_BM.cs @@ -0,0 +1,33 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackPathSubmodule_BM : Submodule_BM + { + public Track.TrackSpaceType trackSpaceType; + public Track.TrackSamplingType trackSamplingType; + public bool isClosed; + public bool isShowingDisplay; + + public TrackPathSubmodule_BM() + { + + } + + public TrackPathSubmodule_BM(GameElement attachedElement, TrackPathSubmodule trackPathSubmodule) : base( + attachedElement) + { + this.trackSpaceType = trackPathSubmodule.trackSpaceType; + this.trackSamplingType = trackPathSubmodule.trackSamplingType; + this.isClosed = trackPathSubmodule.isClosed; + this.isShowingDisplay = trackPathSubmodule.isShowingDisplay; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackPathSubmodule = new TrackPathSubmodule(track, trackSpaceType, trackSamplingType, isClosed, isShowingDisplay); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackPathSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackPathSubmodule_BM.cs.meta new file mode 100644 index 00000000..15d23ff0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackPathSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5f79995b86e88ce468fb79156eb0571f \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackRendererSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackRendererSubmodule_BM.cs new file mode 100644 index 00000000..7a8d6379 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackRendererSubmodule_BM.cs @@ -0,0 +1,159 @@ +using System; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackRendererSubmoduleAutoOrient_BM : Submodule_BM + { + public string materialThemeBundleName; + public string materialName; + public bool enableEmission; + public float emissionIntensity; + public bool zWrite; + public Vector2 uvScale = Vector2.one; + public Vector2 uvOffset = Vector2.zero; + + public TrackRendererSubmoduleAutoOrient_BM() { } + + public TrackRendererSubmoduleAutoOrient_BM(GameElement attachedElement, TrackRendererSubmoduleAutoOrient trSubmodule) + : base(attachedElement) + { + materialThemeBundleName = trSubmodule.materialThemeBundleName; + materialName = trSubmodule.materialName; + enableEmission = trSubmodule.enableEmission; + emissionIntensity = trSubmodule.emissionIntensity; + zWrite = trSubmodule.zWrite; + uvScale = trSubmodule.uvScale; + uvOffset = trSubmodule.uvOffset; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackRendererSubmodule = new TrackRendererSubmoduleAutoOrient(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset); + if (materialName.Trim() != String.Empty) + { + track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName); + } + } + } + } + + public class TrackRendererSubmodulePathGenerator_BM : Submodule_BM + { + public string materialThemeBundleName; + public string materialName; + public bool enableEmission; + public float emissionIntensity; + public bool zWrite; + public Vector2 uvScale = Vector2.one; + public Vector2 uvOffset = Vector2.zero; + + public TrackRendererSubmodulePathGenerator_BM() { } + + public TrackRendererSubmodulePathGenerator_BM(GameElement attachedElement, TrackRendererSubmodulePathGenerator trSubmodule) + : base(attachedElement) + { + materialThemeBundleName = trSubmodule.materialThemeBundleName; + materialName = trSubmodule.materialName; + enableEmission = trSubmodule.enableEmission; + emissionIntensity = trSubmodule.emissionIntensity; + zWrite = trSubmodule.zWrite; + uvScale = trSubmodule.uvScale; + uvOffset = trSubmodule.uvOffset; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackRendererSubmodule = new TrackRendererSubmodulePathGenerator(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset); + if (materialName.Trim() != String.Empty) + { + track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName); + } + } + } + } + + public class TrackRendererSubmoduleSurface_BM : Submodule_BM + { + public string materialThemeBundleName; + public string materialName; + public bool enableEmission; + public float emissionIntensity; + public bool zWrite; + public Vector2 uvScale = Vector2.one; + public Vector2 uvOffset = Vector2.zero; + + public TrackRendererSubmoduleSurface_BM() { } + + public TrackRendererSubmoduleSurface_BM(GameElement attachedElement, TrackRendererSubmoduleSurface trSubmodule) + : base(attachedElement) + { + materialThemeBundleName = trSubmodule.materialThemeBundleName; + materialName = trSubmodule.materialName; + enableEmission = trSubmodule.enableEmission; + emissionIntensity = trSubmodule.emissionIntensity; + zWrite = trSubmodule.zWrite; + uvScale = trSubmodule.uvScale; + uvOffset = trSubmodule.uvOffset; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackRendererSubmodule = new TrackRendererSubmoduleSurface(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset); + if (materialName.Trim() != String.Empty) + { + track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName); + } + } + } + } + + public class TrackRendererSubmoduleTubeGenerator_BM : Submodule_BM + { + public string materialThemeBundleName; + public string materialName; + public bool enableEmission; + public float emissionIntensity; + public bool zWrite; + public int sideCount; + public Vector2 uvScale = Vector2.one; + public Vector2 uvOffset = Vector2.zero; + + public TrackRendererSubmoduleTubeGenerator_BM() { } + + public TrackRendererSubmoduleTubeGenerator_BM(GameElement attachedElement, TrackRendererSubmoduleTubeGenerator trSubmodule) + : base(attachedElement) + { + materialThemeBundleName = trSubmodule.materialThemeBundleName; + materialName = trSubmodule.materialName; + enableEmission = trSubmodule.enableEmission; + emissionIntensity = trSubmodule.emissionIntensity; + sideCount = trSubmodule.sideCount; + zWrite = trSubmodule.zWrite; + uvScale = trSubmodule.uvScale; + uvOffset = trSubmodule.uvOffset; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackRendererSubmodule = new TrackRendererSubmoduleTubeGenerator(track, enableEmission, emissionIntensity, zWrite, sideCount, uvScale, uvOffset); + if (materialName.Trim() != String.Empty) + { + track.trackRendererSubmodule.ApplyMaterial(materialThemeBundleName, materialName); + } + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackRendererSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackRendererSubmodule_BM.cs.meta new file mode 100644 index 00000000..3d22ec45 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackRendererSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 49130f20a32c5134484a0d74ee30a73c \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackTimeSubmodule_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackTimeSubmodule_BM.cs new file mode 100644 index 00000000..7736ac58 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackTimeSubmodule_BM.cs @@ -0,0 +1,58 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackTimeSubmoduleMovable_BM : Submodule_BM + { + public float trackStartTime; + public float trackEndTime; + public float visibleTrackTimeLength; + public AnimationCurveType animationCurveType; + + public TrackTimeSubmoduleMovable_BM() + { + + } + + public TrackTimeSubmoduleMovable_BM(GameElement attachedElement, TrackTimeSubmoduleMovable trackTimeSubmoduleMovable) : base(attachedElement) + { + trackStartTime = trackTimeSubmoduleMovable.trackStartTime; + trackEndTime = trackTimeSubmoduleMovable.trackEndTime; + visibleTrackTimeLength = trackTimeSubmoduleMovable.visibleTrackTimeLength; + animationCurveType = trackTimeSubmoduleMovable.animationCurveType; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackTimeSubmodule = new TrackTimeSubmoduleMovable(track, trackStartTime, trackEndTime, visibleTrackTimeLength, animationCurveType); + } + } + } + + public class TrackTimeSubmoduleStatic_BM : Submodule_BM + { + public float trackTotalTime; + public AnimationCurveType animationCurveType; + + public TrackTimeSubmoduleStatic_BM() + { + + } + + public TrackTimeSubmoduleStatic_BM(GameElement attachedElement, TrackTimeSubmoduleStatic trackTimeSubmoduleStatic) : base(attachedElement) + { + trackTotalTime = trackTimeSubmoduleStatic.trackTotalTime; + animationCurveType = trackTimeSubmoduleStatic.animationCurveType; + } + + public override void ExecuteBM() + { + attachedElement = GameElement_BM.GetElement(attachedElementGuid); + if (attachedElement is Track track) + { + track.trackTimeSubmodule = new TrackTimeSubmoduleStatic(track, trackTotalTime, animationCurveType); + } + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackTimeSubmodule_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackTimeSubmodule_BM.cs.meta new file mode 100644 index 00000000..b892a98d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/Components_BM/Track/TrackTimeSubmodule_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 362d59c88527e834c909b3e408945f5e \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes.meta new file mode 100644 index 00000000..6fcfe71d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 490553274c7dc3d4bb2645927a6a02e6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/EaseCurve.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/EaseCurve.cs new file mode 100644 index 00000000..ce322656 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/EaseCurve.cs @@ -0,0 +1,378 @@ +using UnityEngine; +using System; +using System.Collections.Generic; + +namespace Ichni +{ + public enum AnimationCurveType //预设动画曲线类型 + { + Linear = 0, + InQuad = 1, + OutQuad = 2, + InOutQuad = 3, + InCubic = 4, + OutCubic = 5, + InOutCubic = 6, + InQuart = 7, + OutQuart = 8, + InOutQuart = 9, + InQuint = 10, + OutQuint = 11, + InOutQuint = 12, + InSine = 13, + OutSine = 14, + InOutSine = 15, + InExpo = 16, + OutExpo = 17, + InOutExpo = 18, + InCirc = 19, + OutCirc = 20, + InOutCirc = 21, + InBounce = 22, + OutBounce = 23, + InOutBounce = 24, + InElastic = 25, + OutElastic = 26, + InOutElastic = 27, + InBack = 28, + OutBack = 29, + InOutBack = 30 + } + + public static class AnimationCurveEvaluator + { + public static float Evaluate(AnimationCurveType animationCurveType, float t) + { + t = Mathf.Clamp(t, 0, 1); + + switch (animationCurveType) + { + case AnimationCurveType.Linear: + return Linear(0, 1, t); + case AnimationCurveType.InQuad: + return InQuad(0, 1, t); + case AnimationCurveType.OutQuad: + return OutQuad(0, 1, t); + case AnimationCurveType.InOutQuad: + return InOutQuad(0, 1, t); + case AnimationCurveType.InCubic: + return InCubic(0, 1, t); + case AnimationCurveType.OutCubic: + return OutCubic(0, 1, t); + case AnimationCurveType.InOutCubic: + return InOutCubic(0, 1, t); + case AnimationCurveType.InQuart: + return InQuart(0, 1, t); + case AnimationCurveType.OutQuart: + return OutQuart(0, 1, t); + case AnimationCurveType.InOutQuart: + return InOutQuart(0, 1, t); + case AnimationCurveType.InQuint: + return InQuint(0, 1, t); + case AnimationCurveType.OutQuint: + return OutQuint(0, 1, t); + case AnimationCurveType.InOutQuint: + return InOutQuint(0, 1, t); + case AnimationCurveType.InSine: + return InSine(0, 1, t); + case AnimationCurveType.OutSine: + return OutSine(0, 1, t); + case AnimationCurveType.InOutSine: + return InOutSine(0, 1, t); + case AnimationCurveType.InExpo: + return InExpo(0, 1, t); + case AnimationCurveType.OutExpo: + return OutExpo(0, 1, t); + case AnimationCurveType.InOutExpo: + return InOutExpo(0, 1, t); + case AnimationCurveType.InCirc: + return InCirc(0, 1, t); + case AnimationCurveType.OutCirc: + return OutCirc(0, 1, t); + case AnimationCurveType.InOutCirc: + return InOutCirc(0, 1, t); + case AnimationCurveType.InBounce: + return InBounce(0, 1, t); + case AnimationCurveType.OutBounce: + return OutBounce(0, 1, t); + case AnimationCurveType.InOutBounce: + return InOutBounce(0, 1, t); + case AnimationCurveType.InElastic: + return InElastic(0, 1, t); + case AnimationCurveType.OutElastic: + return OutElastic(0, 1, t); + case AnimationCurveType.InOutElastic: + return InOutElastic(0, 1, t); + case AnimationCurveType.InBack: + return InBack(0, 1, t); + case AnimationCurveType.OutBack: + return OutBack(0, 1, t); + case AnimationCurveType.InOutBack: + return InOutBack(0, 1, t); + } + + throw new NotImplementedException($"Animation curve type {animationCurveType} is not implemented."); + } + + #region 缓动曲线计算式 + private static float Linear(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return c * t / 1f + from; + } + + private static float InQuad(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return c * t * t + from; + } + + private static float OutQuad(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return -c * t * (t - 2f) + from; + } + + private static float InOutQuad(float from, float to, float t) + { + float c = to - from; + t /= 0.5f; + if (t < 1) return c / 2f * t * t + from; + t--; + return -c / 2f * (t * (t - 2) - 1) + from; + } + + private static float InCubic(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return c * t * t * t + from; + } + + private static float OutCubic(float from, float to, float t) + { + float c = to - from; + t /= 1f; + t--; + return c * (t * t * t + 1) + from; + } + + private static float InOutCubic(float from, float to, float t) + { + float c = to - from; + t /= 0.5f; + if (t < 1) return c / 2f * t * t * t + from; + t -= 2; + return c / 2f * (t * t * t + 2) + from; + } + + private static float InQuart(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return c * t * t * t * t + from; + } + + private static float OutQuart(float from, float to, float t) + { + float c = to - from; + t /= 1f; + t--; + return -c * (t * t * t * t - 1) + from; + } + + private static float InOutQuart(float from, float to, float t) + { + float c = to - from; + t /= 0.5f; + if (t < 1) return c / 2f * t * t * t * t + from; + t -= 2; + return -c / 2f * (t * t * t * t - 2) + from; + } + + private static float InQuint(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return c * t * t * t * t * t + from; + } + + private static float OutQuint(float from, float to, float t) + { + float c = to - from; + t /= 1f; + t--; + return c * (t * t * t * t * t + 1) + from; + } + + private static float InOutQuint(float from, float to, float t) + { + float c = to - from; + t /= 0.5f; + if (t < 1) return c / 2f * t * t * t * t * t + from; + t -= 2; + return c / 2f * (t * t * t * t * t + 2) + from; + } + + private static float InSine(float from, float to, float t) + { + float c = to - from; + return -c * Mathf.Cos(t / 1f * (Mathf.PI / 2f)) + c + from; + } + + private static float OutSine(float from, float to, float t) + { + float c = to - from; + return c * Mathf.Sin(t / 1f * (Mathf.PI / 2f)) + from; + } + + private static float InOutSine(float from, float to, float t) + { + float c = to - from; + return -c / 2f * (Mathf.Cos(Mathf.PI * t / 1f) - 1) + from; + } + + private static float InExpo(float from, float to, float t) + { + float c = to - from; + return c * Mathf.Pow(2, 10 * (t / 1f - 1)) + from; + } + + private static float OutExpo(float from, float to, float t) + { + float c = to - from; + return c * (-Mathf.Pow(2, -10 * t / 1f) + 1) + from; + } + + private static float InOutExpo(float from, float to, float t) + { + float c = to - from; + t /= 0.5f; + if (t < 1f) return c / 2f * Mathf.Pow(2, 10 * (t - 1)) + from; + t--; + return c / 2f * (-Mathf.Pow(2, -10 * t) + 2) + from; + } + + private static float InCirc(float from, float to, float t) + { + float c = to - from; + t /= 1f; + return -c * (Mathf.Sqrt(1 - t * t) - 1) + from; + } + + private static float OutCirc(float from, float to, float t) + { + float c = to - from; + t /= 1f; + t--; + return c * Mathf.Sqrt(1 - t * t) + from; + } + + private static float InOutCirc(float from, float to, float t) + { + float c = to - from; + t /= 0.5f; + if (t < 1) return -c / 2f * (Mathf.Sqrt(1 - t * t) - 1) + from; + t -= 2; + return c / 2f * (Mathf.Sqrt(1 - t * t) + 1) + from; + } + + private static float InBounce(float from, float to, float t) + { + float c = to - from; + return c - OutBounce(0f, c, 1f - t) + from; //does this work? + } + + private static float OutBounce(float from, float to, float t) + { + float c = to - from; + + if ((t /= 1f) < (1 / 2.75f)) + { + return c * (7.5625f * t * t) + from; + } + else if (t < (2 / 2.75f)) + { + return c * (7.5625f * (t -= (1.5f / 2.75f)) * t + .75f) + from; + } + else if (t < (2.5 / 2.75)) + { + return c * (7.5625f * (t -= (2.25f / 2.75f)) * t + .9375f) + from; + } + else + { + return c * (7.5625f * (t -= (2.625f / 2.75f)) * t + .984375f) + from; + } + } + + private static float InOutBounce(float from, float to, float t) + { + float c = to - from; + if (t < 0.5f) return InBounce(0, c, t * 2f) * 0.5f + from; + return OutBounce(0, c, t * 2 - 1) * 0.5f + c * 0.5f + from; + + } + + private static float InElastic(float from, float to, float t) + { + float c = to - from; + if (t == 0) return from; + if ((t /= 1f) == 1) return from + c; + float p = 0.3f; + float s = p / 4f; + return -(c * Mathf.Pow(2, 10 * (t -= 1)) * Mathf.Sin((t - s) * (2 * Mathf.PI) / p)) + from; + } + + private static float OutElastic(float from, float to, float t) + { + float c = to - from; + if (t == 0) return from; + if ((t /= 1f) == 1) return from + c; + float p = 0.3f; + float s = p / 4f; + return (c * Mathf.Pow(2, -10 * t) * Mathf.Sin((t - s) * (2 * Mathf.PI) / p) + c + from); + } + + private static float InOutElastic(float from, float to, float t) + { + float c = to - from; + if (t == 0) return from; + if ((t /= 0.5f) == 2) return from + c; + float p = 0.3f * 1.5f; + float s = p / 4f; + if (t < 1) + return -0.5f * (c * Mathf.Pow(2, 10 * (t -= 1f)) * Mathf.Sin((t - 2) * (2 * Mathf.PI) / p)) + from; + return c * Mathf.Pow(2, -10 * (t -= 1)) * Mathf.Sin((t - s) * (2f * Mathf.PI) / p) * 0.5f + c + from; + } + + private static float InBack(float from, float to, float t) + { + float c = to - from; + float s = 1.70158f; + t /= 0.5f; + return c * t * t * ((s + 1) * t - s) + from; + } + + private static float OutBack(float from, float to, float t) + { + float c = to - from; + float s = 1.70158f; + t = t / 1f - 1f; + return c * (t * t * ((s + 1) * t + s) + 1) + from; + } + + private static float InOutBack(float from, float to, float t) + { + float c = to - from; + float s = 1.70158f; + t /= 0.5f; + if (t < 1) return c / 2f * (t * t * (((s *= (1.525f)) + 1) * t - s)) + from; + t -= 2; + return c / 2f * (t * t * (((s *= (1.525f)) + 1) * t + s) + 2) + from; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/EaseCurve/EaseCurve.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/EaseCurve.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/EaseCurve/EaseCurve.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/EaseCurve.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleBool_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleBool_BM.cs new file mode 100644 index 00000000..d6ba32dd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleBool_BM.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class FlexibleBool_BM + { + public List animatedBoolList; + + public FlexibleBool_BM() + { + this.animatedBoolList = new List(); + } + + public FlexibleBool_BM(List animatedBoolList) + { + this.animatedBoolList = animatedBoolList; + } + + public FlexibleBool ConvertToGameType() + { + FlexibleBool flexibleBool; + + if (this.animatedBoolList.Count == 0) + { + flexibleBool = new FlexibleBool(); + } + else + { + List animations = new List(); + foreach (AnimatedBool animatedBool in animatedBoolList) + { + animations.Add(new AnimatedBool(animatedBool.time, animatedBool.value)); + } + + flexibleBool = new FlexibleBool(animations); + } + + return flexibleBool; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleBool_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleBool_BM.cs.meta new file mode 100644 index 00000000..7e040a59 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleBool_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 34744273e1ec1104389f96de37ef2a30 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleFloat_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleFloat_BM.cs new file mode 100644 index 00000000..ecca5cd2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleFloat_BM.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + [System.Serializable] + public class FlexibleFloat_BM + { + public List animatedFloatList; + + public FlexibleFloat_BM() + { + this.animatedFloatList = new List(); + } + + public FlexibleFloat_BM(List animatedFloatList) + { + this.animatedFloatList = animatedFloatList; + } + + public FlexibleFloat ConvertToGameType() + { + FlexibleFloat flexibleFloat; + + if (animatedFloatList.Count == 0) + { + flexibleFloat = new FlexibleFloat(); + } + else + { + List animatedFloatList = new List(); + foreach (AnimatedFloat animatedFloat in this.animatedFloatList) + { + animatedFloatList.Add(new AnimatedFloat( + animatedFloat.startTime, animatedFloat.endTime, + animatedFloat.startValue, animatedFloat.endValue, + animatedFloat.animationCurveType)); + } + + flexibleFloat = new FlexibleFloat(animatedFloatList); + } + + return flexibleFloat; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleFloat_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleFloat_BM.cs.meta new file mode 100644 index 00000000..6ebde618 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleFloat_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 157e98f139bd21647a1df710fe63dd81 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleInt_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleInt_BM.cs new file mode 100644 index 00000000..f6ceb067 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleInt_BM.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + [System.Serializable] + public class FlexibleInt_BM + { + public List animatedIntList; + + public FlexibleInt_BM() + { + this.animatedIntList = new List(); + } + + public FlexibleInt_BM(List animatedIntList) + { + this.animatedIntList = animatedIntList; + } + + public FlexibleInt ConvertToGameType() + { + FlexibleInt flexibleInt; + + if (animatedIntList.Count == 0) + { + flexibleInt = new FlexibleInt(); + } + else + { + List animations = new List(); + + foreach (AnimatedInt animatedInt in animatedIntList) + { + animations.Add(new AnimatedInt(animatedInt.time, animatedInt.value)); + } + + flexibleInt = new FlexibleInt(animations); + } + + return flexibleInt; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleInt_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleInt_BM.cs.meta new file mode 100644 index 00000000..f2f8df51 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/DataTypes/FlexibleInt_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f235a3498a2746d4ca998b8a8b982866 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM.meta new file mode 100644 index 00000000..0d622295 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ef5a9eed7f6e42247a93c01519d89d64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment.meta new file mode 100644 index 00000000..c518bcce --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 21ec904f9ffa2e8409027f9916f33248 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/BackgroundSetter_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/BackgroundSetter_BM.cs new file mode 100644 index 00000000..d6e6d3eb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/BackgroundSetter_BM.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class BackgroundSetter_BM : GameElement_BM + { + public bool useSkybox; + public string skyboxThemeBundleName; + public string skyboxMaterialName; + public string backgroundSpriteName; + + public BackgroundSetter_BM() + { + + } + + public BackgroundSetter_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + bool useSkybox, string skyboxThemeBundleName, string skyboxMaterialName, string backgroundSpriteName) + : base(elementName, elementGuid, tags, attachedElement) + { + this.useSkybox = useSkybox; + this.skyboxThemeBundleName = skyboxThemeBundleName; + this.skyboxMaterialName = skyboxMaterialName; + this.backgroundSpriteName = backgroundSpriteName; + } + + public override void ExecuteBM() + { + matchedElement = BackgroundSetter.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), useSkybox, skyboxThemeBundleName, skyboxMaterialName, backgroundSpriteName); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/BackgroundSetter_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/BackgroundSetter_BM.cs.meta new file mode 100644 index 00000000..417847c0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/BackgroundSetter_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d71b30900f485ad4abb5b9ad9f2f41eb \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/EnvironmentObject_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/EnvironmentObject_BM.cs new file mode 100644 index 00000000..672b85c2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/EnvironmentObject_BM.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class EnvironmentObject_BM : SubstantialObject_BM + { + public bool isStatic; + + public EnvironmentObject_BM() + { + + } + + public EnvironmentObject_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, string themeBundleName, string objectName, bool isStatic) + : base(elementName, elementGuid, tags, attachedElement, themeBundleName, objectName) + { + this.isStatic = isStatic; + } + + public override void ExecuteBM() + { + matchedElement = EnvironmentObject.GenerateElement(elementName, elementGuid, tags, false, + themeBundleName, objectName, GetElement(attachedElementGuid), isStatic); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/EnvironmentObject_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/EnvironmentObject_BM.cs.meta new file mode 100644 index 00000000..b8cf8b20 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/EnvironmentObject_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fd625b39e108a184b8bd3359ecc15b95 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/ParticleEmitter_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/ParticleEmitter_BM.cs new file mode 100644 index 00000000..2a83b883 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/ParticleEmitter_BM.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class ParticleEmitter_BM : GameElement_BM + { + public bool prewarm = false; + public float playTime = 0f; + public float stopTime = 1f; + + public ParticleSystemSimulationSpace simulationSpace; + public float density = 10; + public float lifeTime = 5; + public float speed; + public float radius; + + public bool isAutoOrient = true; + public Vector3 particleRotation = Vector3.zero; + + public string materialThemeBundleName = string.Empty; + public string materialName = string.Empty; + + public ParticleEmitter_BM() + { + + } + + public ParticleEmitter_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + bool prewarm, float playTime, float stopTime, ParticleSystemSimulationSpace simulationSpace, + float density, float lifeTime, float speed, float radius, bool isAutoOrient, Vector3 particleRotation, + string materialThemeBundleName, string materialName) : + base(elementName, elementGuid, tags, attachedElement) + { + this.prewarm = prewarm; + this.playTime = playTime; + this.stopTime = stopTime; + this.simulationSpace = simulationSpace; + this.density = density; + this.lifeTime = lifeTime; + this.speed = speed; + this.radius = radius; + this.isAutoOrient = isAutoOrient; + this.particleRotation = particleRotation; + this.materialThemeBundleName = materialThemeBundleName; + this.materialName = materialName; + } + + public override void ExecuteBM() + { + matchedElement = ParticleEmitter.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), materialThemeBundleName, materialName, + prewarm, playTime, stopTime, simulationSpace, density, lifeTime, speed, radius, + isAutoOrient, particleRotation); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/ParticleEmitter_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/ParticleEmitter_BM.cs.meta new file mode 100644 index 00000000..639c957c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/ParticleEmitter_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fff835610e85b7c40963a8b8486a3524 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/SkyboxSubsetter_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/SkyboxSubsetter_BM.cs new file mode 100644 index 00000000..dd78298b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/SkyboxSubsetter_BM.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class SkyboxSubsetter_BM : GameElement_BM + { + public List skyBoxThemeBundleList; + public List skyboxNameList; + public List blendTimeList; + public List blendSpeedList; + + public SkyboxSubsetter_BM() + { + + } + + public SkyboxSubsetter_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + List skyBoxThemeBundleList, List skyboxNameList, List blendTimeList, List blendSpeedList) + : base(elementName, elementGuid, tags, attachedElement) + { + this.skyBoxThemeBundleList = skyBoxThemeBundleList; + this.skyboxNameList = skyboxNameList; + this.blendTimeList = blendTimeList; + this.blendSpeedList = blendSpeedList; + } + + public override void ExecuteBM() + { + matchedElement = SkyboxSubsetter.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), skyBoxThemeBundleList, skyboxNameList, blendTimeList, blendSpeedList); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/SkyboxSubsetter_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/SkyboxSubsetter_BM.cs.meta new file mode 100644 index 00000000..4cf55d33 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Environment/SkyboxSubsetter_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b88018b9f1dfcd246a4cf0197b2c41e6 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential.meta new file mode 100644 index 00000000..29a43c8e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd1bf361f8f45884fb0a959bc1f6b143 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/ElementFolder_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/ElementFolder_BM.cs new file mode 100644 index 00000000..e922ad1e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/ElementFolder_BM.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class ElementFolder_BM : GameElement_BM + { + public ElementFolder_BM() + { + + } + + public ElementFolder_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement) + : base(elementName, elementGuid, tags, attachedElement) + { + + } + + public override void ExecuteBM() + { + matchedElement = ElementFolder.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid)); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/ElementFolder_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/ElementFolder_BM.cs.meta new file mode 100644 index 00000000..fc0eb216 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/ElementFolder_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 58ecc622995f8f64099deb76c6bd27e2 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCameraExtension_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCameraExtension_BM.cs new file mode 100644 index 00000000..d1984eec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCameraExtension_BM.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class GameCameraExtension_BM : GameElement_BM + { + public float farClipRange; + + public GameCameraExtension_BM() + { + + } + + public GameCameraExtension_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, float farClipRange) + : base(elementName, elementGuid, tags, attachedElement) + { + this.farClipRange = farClipRange; + } + + public override void ExecuteBM() + { + matchedElement = GameCameraExtension.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), farClipRange); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCameraExtension_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCameraExtension_BM.cs.meta new file mode 100644 index 00000000..bc7061f6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCameraExtension_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 950b290b30a639c479f82871076a1782 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCamera_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCamera_BM.cs new file mode 100644 index 00000000..8b1faeac --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCamera_BM.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class GameCamera_BM : GameElement_BM + { + public GameCamera.CameraViewType cameraViewType; + public float perspectiveAngle; + public float orthographicSize; + + public GameCamera_BM() + { + + } + + public GameCamera_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, GameCamera.CameraViewType cameraViewType, + float perspectiveAngle, float orthographicSize) + : base(elementName, elementGuid, tags, attachedElement) + { + this.cameraViewType = cameraViewType; + this.perspectiveAngle = perspectiveAngle; + this.orthographicSize = orthographicSize; + } + + public override void ExecuteBM() + { + matchedElement = GameCamera.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), cameraViewType, perspectiveAngle, orthographicSize); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCamera_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCamera_BM.cs.meta new file mode 100644 index 00000000..5ddcafae --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/GameCamera_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 56b27fa61c6adad4abdc465fd1e408a0 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/SubstantialObject_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/SubstantialObject_BM.cs new file mode 100644 index 00000000..475538d3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/SubstantialObject_BM.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class SubstantialObject_BM : GameElement_BM + { + public string themeBundleName; + public string objectName; + + public SubstantialObject_BM() + { + + } + + public SubstantialObject_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + string themeBundleName, string objectName) + : base(elementName, elementGuid, tags, attachedElement) + { + this.themeBundleName = themeBundleName; + this.objectName = objectName; + } + + public override void ExecuteBM() + { + throw new NotImplementedException(); + } + + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/SubstantialObject_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/SubstantialObject_BM.cs.meta new file mode 100644 index 00000000..902e2d1d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/SubstantialObject_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 618c4a2232638e84ca9c78c4fc525283 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/TimeEffectsCollection_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/TimeEffectsCollection_BM.cs new file mode 100644 index 00000000..8dc99717 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/TimeEffectsCollection_BM.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TimeEffectsCollection_BM : GameElement_BM + { + public float time; + + public TimeEffectsCollection_BM() + { + + } + + public TimeEffectsCollection_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, TimeEffectsCollection timeEffectsCollection) + : base(elementName, elementGuid, tags, attachedElement) + { + time = timeEffectsCollection.time; + } + + + public override void ExecuteBM() + { + matchedElement = TimeEffectsCollection.GenerateElement(elementName, elementGuid, + tags, false, GetElement(attachedElementGuid), time); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/TimeEffectsCollection_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/TimeEffectsCollection_BM.cs.meta new file mode 100644 index 00000000..9cd40397 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/TimeEffectsCollection_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fa411d9c23ba6b7459334abdfddc35c9 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/VariablesContainer_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/VariablesContainer_BM.cs new file mode 100644 index 00000000..f2419c67 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/VariablesContainer_BM.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class VariablesContainer_BM : GameElement_BM + { + public Dictionary originalVariables; + + public VariablesContainer_BM() + { + + } + + public VariablesContainer_BM(string elementName, Guid elementGuid, List tags, GameElement_BM parentElement, + Dictionary originalVariables) : base(elementName, elementGuid, tags, parentElement) + { + this.originalVariables = new Dictionary(originalVariables); + } + + public override void ExecuteBM() + { + matchedElement = VariablesContainer.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), originalVariables); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/VariablesContainer_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/VariablesContainer_BM.cs.meta new file mode 100644 index 00000000..d6f4031a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Essential/VariablesContainer_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1ca9ba2716572614ea591c1ce7652af0 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects.meta new file mode 100644 index 00000000..1480d607 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e8f7a67f19bf55e4dbd3f0f3fe67f072 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/BloomEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/BloomEffect_BM.cs new file mode 100644 index 00000000..35fc9a5e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/BloomEffect_BM.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class BloomEffect_BM : EffectBase_BM + { + public float duration; + public float peak; + public AnimationCurve intensityCurve; + + public BloomEffect_BM() { } + + public BloomEffect_BM(float duration, float peak, AnimationCurve intensityCurve) + { + this.effectTime = duration; + this.duration = duration; + this.peak = peak; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new BloomEffect(duration, peak, intensityCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/BloomEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/BloomEffect_BM.cs.meta new file mode 100644 index 00000000..1d50fb51 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/BloomEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9f0b0bbe1ab75ff439cd89cb2d6c8903 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraOffsetEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraOffsetEffect_BM.cs new file mode 100644 index 00000000..6582bcf5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraOffsetEffect_BM.cs @@ -0,0 +1,24 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class CameraOffsetEffect_BM : EffectBase_BM + { + public float duration; + public Vector3 offsetValue; + public AnimationCurve offsetCurve; + + public CameraOffsetEffect_BM(float duration, Vector3 offsetValue, AnimationCurve offsetCurve) + { + this.effectTime = duration; + this.duration = duration; + this.offsetValue = offsetValue; + this.offsetCurve = offsetCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new CameraOffsetEffect(duration, offsetValue, offsetCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraOffsetEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraOffsetEffect_BM.cs.meta new file mode 100644 index 00000000..597844da --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraOffsetEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c89d0dc887a006b4db0f9bd126ad9f46 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraShakeEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraShakeEffect_BM.cs new file mode 100644 index 00000000..5c6a6feb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraShakeEffect_BM.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class CameraShakeEffect_BM : EffectBase_BM + { + public float duration; + public float frequency; + public float amplitudeX; + public float amplitudeY; + public float amplitudeZ; + + public CameraShakeEffect_BM() { } + + public CameraShakeEffect_BM(float duration, float frequency, float amplitudeX, float amplitudeY, float amplitudeZ) + { + this.effectTime = duration; + this.duration = duration; + this.frequency = frequency; + this.amplitudeX = amplitudeX; + this.amplitudeY = amplitudeY; + this.amplitudeZ = amplitudeZ; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new CameraShakeEffect(duration, frequency, amplitudeX, amplitudeY, amplitudeZ); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraShakeEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraShakeEffect_BM.cs.meta new file mode 100644 index 00000000..67b0ae13 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraShakeEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f95d6b34e5bfc004ab751726eb61c1c5 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraTiltEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraTiltEffect_BM.cs new file mode 100644 index 00000000..c9f33942 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraTiltEffect_BM.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class CameraTiltEffect_BM : EffectBase_BM + { + public float duration; + public Vector3 tiltValue; + public AnimationCurve tiltCurve; + + public CameraTiltEffect_BM(float duration, Vector3 tiltValue, AnimationCurve tiltCurve) + { + this.effectTime = duration; + this.duration = duration; + this.tiltValue = tiltValue; + this.tiltCurve = tiltCurve; + + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new CameraTiltEffect(duration, tiltValue, tiltCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraTiltEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraTiltEffect_BM.cs.meta new file mode 100644 index 00000000..6d169426 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraTiltEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2fa4d735d24b66446bd65febd6bf970b \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraZoomEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraZoomEffect_BM.cs new file mode 100644 index 00000000..323b8607 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraZoomEffect_BM.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class CameraZoomEffect_BM : EffectBase_BM + { + public float duration; + public float relativeZoom; + public AnimationCurve zoomCurve; + + public CameraZoomEffect_BM() { } + + public CameraZoomEffect_BM(float duration, float relativeZoom, AnimationCurve zoomCurve) + { + this.effectTime = duration; + this.duration = duration; + this.relativeZoom = relativeZoom; + this.zoomCurve = zoomCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new CameraZoomEffect(duration, relativeZoom, zoomCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraZoomEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraZoomEffect_BM.cs.meta new file mode 100644 index 00000000..ef7c171a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/CameraZoomEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 94fc49ffb6b85be499e56989f30edfba \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/ChromaticAberrationEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/ChromaticAberrationEffect_BM.cs new file mode 100644 index 00000000..7f78e207 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/ChromaticAberrationEffect_BM.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class ChromaticAberrationEffect_BM : EffectBase_BM + { + public float duration; + public float peak; + public AnimationCurve intensityCurve; + + public ChromaticAberrationEffect_BM() { } + + public ChromaticAberrationEffect_BM(float duration, float peak, AnimationCurve intensityCurve) + { + this.effectTime = duration; + this.duration = duration; + this.peak = peak; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new ChromaticAberrationEffect(duration, peak, intensityCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/ChromaticAberrationEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/ChromaticAberrationEffect_BM.cs.meta new file mode 100644 index 00000000..e82c9a76 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/ChromaticAberrationEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1b07e876899a1da4b8734cc239f02d9c \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/EnableControlEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/EnableControlEffect_BM.cs new file mode 100644 index 00000000..9a3663b1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/EnableControlEffect_BM.cs @@ -0,0 +1,34 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class EnableControlEffect_BM : EffectBase_BM + { + public Guid connectedGameElementGuid; + public string connectedVariableName; + public int enableValue; + public bool useExpression; + public string expression; + + public EnableControlEffect_BM() + { + + } + + public EnableControlEffect_BM(Guid connectedGameElementGuid, string connectedVariableName, + int enableValue, bool useExpression, string expression) + { + this.connectedGameElementGuid = connectedGameElementGuid; + this.connectedVariableName = connectedVariableName; + this.enableValue = enableValue; + this.useExpression = useExpression; + this.expression = expression; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new EnableControlEffect(GameElement_BM.GetElement(connectedGameElementGuid), connectedVariableName, + enableValue, useExpression, expression); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/EnableControlEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/EnableControlEffect_BM.cs.meta new file mode 100644 index 00000000..3708c047 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/EnableControlEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 962e6ecb491b2e8459c4adb042ee3317 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/HighPassFilterEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/HighPassFilterEffect_BM.cs new file mode 100644 index 00000000..1210d35b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/HighPassFilterEffect_BM.cs @@ -0,0 +1,29 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class HighPassFilterEffect_BM: EffectBase_BM + { + public float duration; + public float peak; + public AnimationCurve intensityCurve; + + public HighPassFilterEffect_BM() + { + + } + + public HighPassFilterEffect_BM(float duration, float peak, AnimationCurve intensityCurve) + { + this.effectTime = 0; + this.duration = duration; + this.peak = peak; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new HighPassFilterEffect(duration, peak, intensityCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/HighPassFilterEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/HighPassFilterEffect_BM.cs.meta new file mode 100644 index 00000000..91bb74ba --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/HighPassFilterEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cf9e4f03caf64a3478524d26e2effa1d \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/LowPassFilterEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/LowPassFilterEffect_BM.cs new file mode 100644 index 00000000..14f31027 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/LowPassFilterEffect_BM.cs @@ -0,0 +1,29 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class LowPassFilterEffect_BM: EffectBase_BM + { + public float duration; + public float bottom; + public AnimationCurve intensityCurve; + + public LowPassFilterEffect_BM() + { + + } + + public LowPassFilterEffect_BM(float duration, float bottom, AnimationCurve intensityCurve) + { + this.effectTime = 0; + this.duration = duration; + this.bottom = bottom; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new LowPassFilterEffect(duration, bottom, intensityCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/LowPassFilterEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/LowPassFilterEffect_BM.cs.meta new file mode 100644 index 00000000..9cf8ba7c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/LowPassFilterEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 679e0a242f11c074394406d0f115c1c8 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/PixelateEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/PixelateEffect_BM.cs new file mode 100644 index 00000000..7934a9d3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/PixelateEffect_BM.cs @@ -0,0 +1,31 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class PixelateEffect_BM : EffectBase_BM + { + public float duration; + public float bottomX; + public float bottomY; + public AnimationCurve intensityCurve; + + public PixelateEffect_BM() { } + + public PixelateEffect_BM(float duration, float bottomX, float bottomY, AnimationCurve intensityCurve) + { + this.effectTime = duration; + this.duration = duration; + this.bottomX = bottomX; + this.bottomY = bottomY; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new PixelateEffect(duration, bottomX, bottomY, intensityCurve) + { + attachedGameElement = attachedGameElement + }; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/PixelateEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/PixelateEffect_BM.cs.meta new file mode 100644 index 00000000..e0f19da7 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/PixelateEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4163f648ee5182c43b858aae46ebea0f \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/RadialBlurEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/RadialBlurEffect_BM.cs new file mode 100644 index 00000000..cecdfcb6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/RadialBlurEffect_BM.cs @@ -0,0 +1,35 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class RadialBlurEffect_BM : EffectBase_BM + { + public float duration; + public int sampleLevel; + public float position; + public float fadeRange; + public float peakIntensity; + public AnimationCurve intensityCurve; + + public RadialBlurEffect_BM() { } + + public RadialBlurEffect_BM(float duration, int sampleLevel, float position, float fadeRange, float peakIntensity, AnimationCurve intensityCurve) + { + this.effectTime = duration; + this.duration = duration; + this.sampleLevel = sampleLevel; + this.position = position; + this.fadeRange = fadeRange; + this.peakIntensity = peakIntensity; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new RadialBlurEffect(duration, sampleLevel, position, fadeRange, peakIntensity, intensityCurve) + { + attachedGameElement = attachedGameElement + }; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/RadialBlurEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/RadialBlurEffect_BM.cs.meta new file mode 100644 index 00000000..a01a9d49 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/RadialBlurEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: be5ea7c1c6b8b074e83acd421e56931d \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/SetIntegerEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/SetIntegerEffect_BM.cs new file mode 100644 index 00000000..648d0fc2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/SetIntegerEffect_BM.cs @@ -0,0 +1,31 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public class SetIntegerEffect_BM : EffectBase_BM + { + public string targetVariableName; + public int targetValue; + public bool isRandom; + public int minValue; + public int maxValue; + + public SetIntegerEffect_BM() + { + + } + + public SetIntegerEffect_BM(string targetVariableName, int targetValue, bool isRandom, int minValue, int maxValue) + { + this.effectTime = 0; + this.targetVariableName = targetVariableName; + this.targetValue = targetValue; + this.isRandom = isRandom; + this.minValue = minValue; + this.maxValue = maxValue; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new SetIntegerEffect(targetVariableName, targetValue, isRandom, minValue, maxValue); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/SetIntegerEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/SetIntegerEffect_BM.cs.meta new file mode 100644 index 00000000..fe9d3ccd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/SetIntegerEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6617b8683ead73d4b9895f22919ffc83 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/VignetteEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/VignetteEffect_BM.cs new file mode 100644 index 00000000..c0713a8f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/VignetteEffect_BM.cs @@ -0,0 +1,30 @@ +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class VignetteEffect_BM : EffectBase_BM + { + public float duration; + public float peak; + public float smoothness; + public Color color; + public AnimationCurve intensityCurve; + + public VignetteEffect_BM() { } + + public VignetteEffect_BM(float duration, float peak, float smoothness, Color color, AnimationCurve intensityCurve) + { + this.effectTime = duration; + this.duration = duration; + this.peak = peak; + this.smoothness = smoothness; + this.color = color; + this.intensityCurve = intensityCurve; + } + + public override EffectBase ConvertToGameType(GameElement attachedGameElement) + { + return new VignetteEffect(duration, peak, smoothness, color, intensityCurve); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/VignetteEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/VignetteEffect_BM.cs.meta new file mode 100644 index 00000000..775bd538 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/GenrealEffects/VignetteEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 89839cfb0086ca8429f582ce5de9d510 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note.meta new file mode 100644 index 00000000..712b6544 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ab2ef0903b9012344bac31fd3ec0b653 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Flick_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Flick_BM.cs new file mode 100644 index 00000000..618c07f2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Flick_BM.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Flick_BM : NoteBase_BM + { + public List availableFlickDirections; + + public Flick_BM() { } + public Flick_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, float exactJudgeTime, List directions) + : base(elementName, elementGuid, tags, attachedElement, exactJudgeTime) + { + availableFlickDirections = directions; + } + + public override void ExecuteBM() + { + matchedElement = Flick.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), exactJudgeTime, availableFlickDirections); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Flick_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Flick_BM.cs.meta new file mode 100644 index 00000000..66d48501 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Flick_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8b08543ff310ac64c8b7068da4ef2707 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Hold_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Hold_BM.cs new file mode 100644 index 00000000..99b792f5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Hold_BM.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Hold_BM : NoteBase_BM + { + public float holdEndTime; + + public Hold_BM() { } + + public Hold_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, float exactJudgeTime, float holdEndTime) + : base(elementName, elementGuid, tags, attachedElement, exactJudgeTime) + { + this.holdEndTime = holdEndTime; + } + + public override void ExecuteBM() + { + matchedElement = Hold.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), exactJudgeTime, holdEndTime); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Hold_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Hold_BM.cs.meta new file mode 100644 index 00000000..914bd138 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Hold_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 39696ac33802e7d4e8c992e0081a6392 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit.meta new file mode 100644 index 00000000..8d70df62 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fede2bc6a7935094a8777a1722697e6e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/FullScreenNearTimeJudgeUnit_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/FullScreenNearTimeJudgeUnit_BM.cs new file mode 100644 index 00000000..eff18c3d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/FullScreenNearTimeJudgeUnit_BM.cs @@ -0,0 +1,17 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class FullScreenNearTimeJudgeUnit_BM : NoteJudgeUnit_BM + { + public FullScreenNearTimeJudgeUnit_BM() + { + + } + + public override NoteJudgeUnit ConvertToGameType(NoteBase attachedNote) + { + return new FullScreenNearTimeJudgeUnit(attachedNote); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/FullScreenNearTimeJudgeUnit_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/FullScreenNearTimeJudgeUnit_BM.cs.meta new file mode 100644 index 00000000..5b5da63f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/FullScreenNearTimeJudgeUnit_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e242373ac9c91604bb83739663d8100f \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TouchAreaJudgeUnit_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TouchAreaJudgeUnit_BM.cs new file mode 100644 index 00000000..c28ac0fb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TouchAreaJudgeUnit_BM.cs @@ -0,0 +1,35 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TouchAreaJudgeUnit_BM : NoteJudgeUnit_BM + { + public float areaRadius; + + public TouchAreaJudgeUnit_BM() + { + + } + + public TouchAreaJudgeUnit_BM(float areaRadius) + { + this.areaRadius = areaRadius; + } + + public override NoteJudgeUnit ConvertToGameType(NoteBase attachedNote) + { +#if UNITY_STANDALONE + return new FullScreenNearTimeJudgeUnit(attachedNote); +#elif UNITY_ANDROID || UNITY_IOS + if (SettingsManager.instance.gameSettings.judgeType) + { + return new FullScreenNearTimeJudgeUnit(attachedNote); + } + else + { + return new TouchAreaJudgeUnit(attachedNote, areaRadius); + } +#endif + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TouchAreaJudgeUnit_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TouchAreaJudgeUnit_BM.cs.meta new file mode 100644 index 00000000..4ba894b1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TouchAreaJudgeUnit_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 487b28f88f49fb044aa361ecef58f287 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TriggerConnectJudgeUnit_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TriggerConnectJudgeUnit_BM.cs new file mode 100644 index 00000000..d2c552ec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TriggerConnectJudgeUnit_BM.cs @@ -0,0 +1,24 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TriggerConnectJudgeUnit_BM : NoteJudgeUnit_BM + { + public Guid connectedTriggerID; + + public TriggerConnectJudgeUnit_BM() + { + + } + + public TriggerConnectJudgeUnit_BM(Guid connectedTriggerID) + { + this.connectedTriggerID = connectedTriggerID; + } + + public override NoteJudgeUnit ConvertToGameType(NoteBase attachedNote) + { + return new TriggerConnectJudgeUnit(attachedNote, GameElement_BM.GetElement(connectedTriggerID)); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TriggerConnectJudgeUnit_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TriggerConnectJudgeUnit_BM.cs.meta new file mode 100644 index 00000000..20134c66 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/JudgeUnit/TriggerConnectJudgeUnit_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: eae2d88482584ed42a659886a32f6dc4 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteBase_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteBase_BM.cs new file mode 100644 index 00000000..b4da626d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteBase_BM.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteBase_BM : GameElement_BM + { + public float exactJudgeTime; + + public NoteBase_BM() {} + + public NoteBase_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, float exactJudgeTime) + : base(elementName, elementGuid, tags, attachedElement) + { + this.exactJudgeTime = exactJudgeTime; + } + + public override void ExecuteBM() => throw new NotImplementedException(); + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteBase_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteBase_BM.cs.meta new file mode 100644 index 00000000..32ab6d1e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteBase_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 28178a65c5619ad4b8c00cdeb8b18a00 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects.meta new file mode 100644 index 00000000..bb35bbbd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afc919d867eb1e04daf82de5321b6342 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteBadEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteBadEffect_BM.cs new file mode 100644 index 00000000..a2efab2b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteBadEffect_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteBadEffect_BM : NoteEffectBase_BM + { + public NoteBadEffect_BM() + { + + } + + public NoteBadEffect_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteBadEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteBadEffect_BM.cs.meta new file mode 100644 index 00000000..e6ffbd3d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteBadEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e529fe9a5c0e9824e96483c7b3c7be5f \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteEffectBase_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteEffectBase_BM.cs new file mode 100644 index 00000000..65398ca6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteEffectBase_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteEffectBase_BM : EffectBase_BM + { + public NoteEffectBase_BM() + { + + } + + public NoteEffectBase_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteEffectBase_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteEffectBase_BM.cs.meta new file mode 100644 index 00000000..6c04d1e5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteEffectBase_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3affd2874ab6c094ba62f547a6bd6038 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGeneralJudgeEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGeneralJudgeEffect_BM.cs new file mode 100644 index 00000000..ad974a60 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGeneralJudgeEffect_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteGeneralJudgeEffect_BM : NoteEffectBase_BM + { + public NoteGeneralJudgeEffect_BM() + { + + } + + public NoteGeneralJudgeEffect_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGeneralJudgeEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGeneralJudgeEffect_BM.cs.meta new file mode 100644 index 00000000..9382a54d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGeneralJudgeEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 35244439a9743a746a6fe572d4f65318 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGenerateEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGenerateEffect_BM.cs new file mode 100644 index 00000000..0624ef3f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGenerateEffect_BM.cs @@ -0,0 +1,17 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteGenerateEffect_BM : NoteEffectBase_BM + { + public float generateTime; + + public NoteGenerateEffect_BM() + { + + } + + public NoteGenerateEffect_BM(float effectTime, float generateTime) : base(effectTime) + { + this.generateTime = generateTime; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGenerateEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGenerateEffect_BM.cs.meta new file mode 100644 index 00000000..eadeb101 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGenerateEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 94b400ac66ccc484894d27f82f0f143a \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGoodEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGoodEffect_BM.cs new file mode 100644 index 00000000..ce5edf1c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGoodEffect_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteGoodEffect_BM : NoteEffectBase_BM + { + public NoteGoodEffect_BM() + { + + } + + public NoteGoodEffect_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGoodEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGoodEffect_BM.cs.meta new file mode 100644 index 00000000..392dde6b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteGoodEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 446b9b96bf2099040ada10a7ea9c6573 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteHoldingEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteHoldingEffect_BM.cs new file mode 100644 index 00000000..3237caec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteHoldingEffect_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteHoldingEffect_BM : NoteEffectBase_BM + { + public NoteHoldingEffect_BM() + { + + } + + public NoteHoldingEffect_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteHoldingEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteHoldingEffect_BM.cs.meta new file mode 100644 index 00000000..48e0ed17 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteHoldingEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 681140e7077e0e948a7ae334acf55db7 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteMissEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteMissEffect_BM.cs new file mode 100644 index 00000000..63e98426 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteMissEffect_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteMissEffect_BM : NoteEffectBase_BM + { + public NoteMissEffect_BM() + { + + } + + public NoteMissEffect_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteMissEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteMissEffect_BM.cs.meta new file mode 100644 index 00000000..2b730bde --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NoteMissEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e69672a2596578c448f6e063e362d9f5 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NotePerfectEffect_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NotePerfectEffect_BM.cs new file mode 100644 index 00000000..9e60b22c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NotePerfectEffect_BM.cs @@ -0,0 +1,15 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NotePerfectEffect_BM : NoteEffectBase_BM + { + public NotePerfectEffect_BM() + { + + } + + public NotePerfectEffect_BM(float effectTime) : base(effectTime) + { + + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NotePerfectEffect_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NotePerfectEffect_BM.cs.meta new file mode 100644 index 00000000..2a35697b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteEffects/NotePerfectEffect_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f11c13b93bbe6eb4f804d33ba4e9ad88 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteJudgeUnit_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteJudgeUnit_BM.cs new file mode 100644 index 00000000..04585f8f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteJudgeUnit_BM.cs @@ -0,0 +1,18 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteJudgeUnit_BM + { + public NoteJudgeUnit_BM() + { + + } + + /// + /// 转换为游戏类 + /// + /// + public abstract NoteJudgeUnit ConvertToGameType(NoteBase attachedGameElement); + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteJudgeUnit_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteJudgeUnit_BM.cs.meta new file mode 100644 index 00000000..55c6098c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteJudgeUnit_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9bb02a5a0df4a7649942ed6e889eaa97 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteVisualBase_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteVisualBase_BM.cs new file mode 100644 index 00000000..b8de73f0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteVisualBase_BM.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public abstract class NoteVisualBase_BM : SubstantialObject_BM + { + public bool isHighlighted; + public NoteVisualBase_BM() + { + + } + + public NoteVisualBase_BM(string elementName, Guid id, List tags, + GameElement_BM parent, string themeBundleName, string objectName, bool isHighlighted) : + base(elementName, id, tags, parent, themeBundleName, objectName) + { + this.isHighlighted = isHighlighted; + } + + public override void ExecuteBM() + { + throw new NotImplementedException(); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteVisualBase_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteVisualBase_BM.cs.meta new file mode 100644 index 00000000..d1ec8b44 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/NoteVisualBase_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 051b2f1f88a02664aba960116d06b7b8 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Stay_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Stay_BM.cs new file mode 100644 index 00000000..b97ac4cf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Stay_BM.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Stay_BM : NoteBase_BM + { + public Stay_BM() { } + public Stay_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, float exactJudgeTime) + : base(elementName, elementGuid, tags, attachedElement, exactJudgeTime) { } + + public override void ExecuteBM() + { + matchedElement = Stay.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), exactJudgeTime); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Stay_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Stay_BM.cs.meta new file mode 100644 index 00000000..9a35be62 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Stay_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4980ede69e123064980b6e2e91f9be52 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Tap_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Tap_BM.cs new file mode 100644 index 00000000..cfd52352 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Tap_BM.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Tap_BM : NoteBase_BM + { + public Tap_BM() { } + public Tap_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, float exactJudgeTime) + : base(elementName, elementGuid, tags, attachedElement, exactJudgeTime) { } + + public override void ExecuteBM() + { + matchedElement = Tap.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid), exactJudgeTime); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Tap_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Tap_BM.cs.meta new file mode 100644 index 00000000..c740c43e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Note/Tap_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4e4291d10d96400468ac3f107922aa20 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track.meta new file mode 100644 index 00000000..a5f7d019 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 09640f9416719804daed0f4476ab158c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/CrossTrackPoint_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/CrossTrackPoint_BM.cs new file mode 100644 index 00000000..28f8e67c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/CrossTrackPoint_BM.cs @@ -0,0 +1,29 @@ +using System; + +namespace Ichni.RhythmGame.Beatmap +{ + public class CrossTrackPoint_BM : GameElement_BM + { + public FlexibleInt trackSwitch; + public FlexibleFloat trackPercent; + + public CrossTrackPoint_BM() + { + + } + + public CrossTrackPoint_BM(string elementName, Guid elementGuid, System.Collections.Generic.List tags, + GameElement_BM attachedElement, FlexibleInt trackSwitch, FlexibleFloat trackPercent) + : base(elementName, elementGuid, tags, attachedElement) + { + this.trackSwitch = trackSwitch; + this.trackPercent = trackPercent; + } + + public override void ExecuteBM() + { + matchedElement = CrossTrackPoint.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as ElementFolder, trackSwitch, trackPercent); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/CrossTrackPoint_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/CrossTrackPoint_BM.cs.meta new file mode 100644 index 00000000..5aba682f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/CrossTrackPoint_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 218e3dde1cb081c488721eb0a13126c2 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/ParticleTracker_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/ParticleTracker_BM.cs new file mode 100644 index 00000000..df97d54e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/ParticleTracker_BM.cs @@ -0,0 +1,60 @@ +using System; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class ParticleTracker_BM : GameElement_BM + { + public bool prewarm = false; + public float playTime = 0f; + public float stopTime = 1f; + + public bool is3D = false; + public float width = 10f; + public Vector3 extendDirection = Vector3.right; + + public float density = 10; + public float lifeTime = 5; + + public bool isAutoOrient = true; + public Vector3 particleRotation = Vector3.zero; + + public string materialThemeBundleName = string.Empty; + public string materialName = string.Empty; + + public ParticleTracker_BM() + { + + } + + public ParticleTracker_BM(string elementName, Guid elementGuid, System.Collections.Generic.List tags, GameElement_BM attachedElement, + bool prewarm, float playTime, float stopTime, + bool is3D, float width, Vector3 extendDirection, + float density, float lifeTime, + bool isAutoOrient, Vector3 particleRotation, + string materialThemeBundleName, string materialName) : base(elementName, elementGuid, tags, attachedElement) + { + this.prewarm = prewarm; + this.playTime = playTime; + this.stopTime = stopTime; + this.width = width; + this.density = density; + this.is3D = is3D; + this.extendDirection = extendDirection; + this.lifeTime = lifeTime; + this.isAutoOrient = isAutoOrient; + this.particleRotation = particleRotation; + + this.materialThemeBundleName = materialThemeBundleName; + this.materialName = materialName; + } + + public override void ExecuteBM() + { + matchedElement = ParticleTracker.GenerateElement( + elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as Track, materialThemeBundleName, materialName, + prewarm, playTime, stopTime, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/ParticleTracker_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/ParticleTracker_BM.cs.meta new file mode 100644 index 00000000..3c7154c2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/ParticleTracker_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2dceea8d6d67fde4c9e110a708165271 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/PathNode_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/PathNode_BM.cs new file mode 100644 index 00000000..bd366b8c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/PathNode_BM.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class PathNode_BM : GameElement_BM + { + public bool isShowingSphere; + + public PathNode_BM() + { + + } + + public PathNode_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + bool isShowingSphere) + : base(elementName, elementGuid, tags, attachedElement) + { + this.isShowingSphere = isShowingSphere; + } + + public override void ExecuteBM() + { + matchedElement = PathNode.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as Track, isShowingSphere); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/PathNode_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/PathNode_BM.cs.meta new file mode 100644 index 00000000..f7750b0a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/PathNode_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c09883ced2c54b64aa81e03dcfe4d54d \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackHeadPoint_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackHeadPoint_BM.cs new file mode 100644 index 00000000..bb66730e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackHeadPoint_BM.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackHeadPoint_BM : GameElement_BM + { + public bool motionApplyRotation = false; + public Vector3 motionEulerAngles; + + public TrackHeadPoint_BM() + { + + } + + public TrackHeadPoint_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, bool motionApplyRotation, Vector3 motionEulerAngles) + : base(elementName, elementGuid, tags, attachedElement) + { + this.motionApplyRotation = motionApplyRotation; + this.motionEulerAngles = motionEulerAngles; + } + + public override void ExecuteBM() + { + matchedElement = TrackHeadPoint.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as Track, motionApplyRotation, motionEulerAngles); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackHeadPoint_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackHeadPoint_BM.cs.meta new file mode 100644 index 00000000..a6265773 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackHeadPoint_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 56f05a9455f2c774c95abce57e4721b9 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackPercentPoint_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackPercentPoint_BM.cs new file mode 100644 index 00000000..823839d8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackPercentPoint_BM.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackPercentPoint_BM : GameElement_BM + { + public FlexibleFloat_BM trackPercent; + + public TrackPercentPoint_BM() + { + + } + + public TrackPercentPoint_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleFloat_BM trackPercent) + : base(elementName, elementGuid, tags, attachedElement) + { + this.trackPercent = trackPercent; + } + + public override void ExecuteBM() + { + matchedElement = TrackPercentPoint.GenerateElement(elementName, elementGuid, tags, false, + GetElement(attachedElementGuid) as Track, trackPercent.ConvertToGameType()); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackPercentPoint_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackPercentPoint_BM.cs.meta new file mode 100644 index 00000000..53f6c59f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/TrackPercentPoint_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 874a049f41027b842b01d30d87fa63d3 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Track_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Track_BM.cs new file mode 100644 index 00000000..cbcca002 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Track_BM.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Track_BM : GameElement_BM + { + public Track_BM() + { + + } + + public Track_BM(string elementName, System.Guid elementGuid, List tags, GameElement_BM attachedElement) + : base(elementName, elementGuid, tags, attachedElement) + { + + } + + public override void ExecuteBM() + { + matchedElement = Track.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid)); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Track_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Track_BM.cs.meta new file mode 100644 index 00000000..6659cf5a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Track_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b6e740c6ac814e6449862c705a1025ec \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Trail_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Trail_BM.cs new file mode 100644 index 00000000..990c614f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Trail_BM.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public class Trail_BM : GameElement_BM + { + public float visibleTimeLength; + public string renderMaterialName; + public bool isAutoOrient; + public float widthMultiplier; + public AnimationCurve widthCurve; + public Gradient gradient; + + public Trail_BM() + { + + } + + public Trail_BM(string elementName, Guid elementGuid, List tags, GameElement_BM attachedElement, + float visibleTimeLength, bool isAutoOrient, float widthMultiplier, + AnimationCurve widthCurve, Material renderMaterial, Gradient gradient) : base(elementName, elementGuid, tags, + attachedElement) + { + this.visibleTimeLength = visibleTimeLength; + this.renderMaterialName = renderMaterial.name; + this.isAutoOrient = isAutoOrient; + this.widthMultiplier = widthMultiplier; + this.widthCurve = widthCurve; + this.gradient = gradient; + } + + public override void ExecuteBM() + { + matchedElement = Trail.GenerateElement(elementName, elementGuid, tags, + false, GetElement(attachedElementGuid), + visibleTimeLength, isAutoOrient, widthMultiplier, widthCurve, gradient); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Trail_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Trail_BM.cs.meta new file mode 100644 index 00000000..2c6fe248 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/GameElements_BM/Track/Trail_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2a05f4db24dee9d49845478dde3f5865 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM.meta new file mode 100644 index 00000000..971900db --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 288e0ef10be5f194baded5302b22483e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/BeatmapContainer_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/BeatmapContainer_BM.cs new file mode 100644 index 00000000..3c95284b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/BeatmapContainer_BM.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame.Beatmap +{ + public partial class BeatmapContainer_BM : BaseElement_BM + { + public List elementList; + public UniRx.IntReactiveProperty remainingElementAmount; + + public int loadAmount = 0; + public int loadMaximumAmountPerFrame = 100; + + public BeatmapContainer_BM() + { + + } + + public override void ExecuteBM() + { + GameManager.Instance.beatmapContainer = new BeatmapContainer(); + GameManager.Instance.beatmapContainer.matchedBM = this; + + remainingElementAmount = new UniRx.IntReactiveProperty(elementList.Count); + GameElement_BM.identifier.Clear(); + + GameManager.Instance.StartCoroutine(ExecuteLoadElements()); + } + + private IEnumerator ExecuteLoadElements() + { + List lowPriorityElements = new List(); + + for (int index = 0; index < elementList.Count; index++) + { + BaseElement_BM element = elementList[index]; + + if (LowPriorityGameElementTypes.Contains(element.GetType())) + { + lowPriorityElements.Add(element); + continue; + } + + if (element is GameElement_BM gameElement) + { + GameElement_BM.identifier.Add(gameElement.elementGuid, gameElement); + } + + element.ExecuteBM(); + remainingElementAmount.Value--; + + float loadPercent = (float)(elementList.Count - remainingElementAmount.Value) / elementList.Count; + GameManager.Instance.projectLoader.realLoadPercent = loadPercent; + //GameManager.Instance.gameLoadingCanvas.SetProgress(GameManager.Instance.projectLoader.displayLoadPercent * 100f); + + loadAmount++; + if (loadAmount >= loadMaximumAmountPerFrame) + { + loadAmount = 0; + yield return new WaitForEndOfFrame(); + } + } + + foreach (var element in lowPriorityElements) + { + if (element == null) + { + Debug.LogError("Null element detected in low-priority elements. Skipping execution."); + continue; + } + + element.ExecuteBM(); + remainingElementAmount.Value--; + + float loadPercent = (float)(elementList.Count - remainingElementAmount.Value) / elementList.Count; + GameManager.Instance.projectLoader.realLoadPercent = loadPercent; + //GameManager.Instance.gameLoadingCanvas.SetProgress(GameManager.Instance.projectLoader.displayLoadPercent * 100f); + + loadAmount++; + if (loadAmount >= loadMaximumAmountPerFrame) + { + loadAmount = 0; + yield return new WaitForEndOfFrame(); + } + } + + GameManager.Instance.beatmapContainer.ExecuteLowPriorityActions(); + + GameManager.Instance.beatmapContainer.gameElementList.ForEach(gameElement => + { + gameElement.AfterInitialize(); + }); + + GameManager.Instance.noteManager.AllNotesRegistered(); + + Debug.Log("All elements loaded."); + yield return null; + } + } + + public partial class BeatmapContainer_BM : BaseElement_BM + { + public static readonly List LowPriorityGameElementTypes = new() + { + //typeof(NoteJudgeSubmodule_BM), + }; + + public static readonly List LowPriorityDataTypes = new() + { + typeof(EnableControlEffect_BM), + }; + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/BeatmapContainer_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/BeatmapContainer_BM.cs.meta new file mode 100644 index 00000000..044b1879 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/BeatmapContainer_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 36a319da483250142b6c7e71f2685ea8 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/CommandScripts_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/CommandScripts_BM.cs new file mode 100644 index 00000000..447e0a6a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/CommandScripts_BM.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class CommandScripts_BM : BaseElement_BM + { + public List commandList; + + public CommandScripts_BM() + { + + } + + public CommandScripts_BM(List commandList) + { + this.commandList = commandList; + } + + public override void ExecuteBM() + { + GameManager.Instance.commandScripts = new CommandScripts(commandList); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/CommandScripts_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/CommandScripts_BM.cs.meta new file mode 100644 index 00000000..63a3345d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/CommandScripts_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4705fdca6d9f0c8418ba2863e316e141 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/ProjectInformation_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/ProjectInformation_BM.cs new file mode 100644 index 00000000..4e7c3c27 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/ProjectInformation_BM.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; + +namespace Ichni.RhythmGame.Beatmap +{ + public class ProjectInformation_BM : BaseElement_BM + { + public string projectName; + public string creatorName; + public string editorVersion; + public string createTime; + public string lastSaveTime; + public List selectedThemeBundleList; + + public ProjectInformation_BM() + { + + } + + public ProjectInformation_BM(string projectName, string creatorName, string editorVersion, + string createTime, string lastSaveTime, List selectedThemeBundleList) + { + this.projectName = projectName; + this.creatorName = creatorName; + this.editorVersion = editorVersion; + this.createTime = createTime; + this.lastSaveTime = lastSaveTime; + this.selectedThemeBundleList = selectedThemeBundleList; + } + + public override void ExecuteBM() + { + GameManager.Instance.projectInformation = new ProjectInformation(projectName, + creatorName, editorVersion, createTime, lastSaveTime, selectedThemeBundleList); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/ProjectInformation_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/ProjectInformation_BM.cs.meta new file mode 100644 index 00000000..9136de1c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/ProjectInformation_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 55187e8dcaa116044a4c1683a675ab7b \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/SongInformation_BM.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/SongInformation_BM.cs new file mode 100644 index 00000000..5e154457 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/SongInformation_BM.cs @@ -0,0 +1,28 @@ +namespace Ichni.RhythmGame.Beatmap +{ + public class SongInformation_BM : BaseElement_BM + { + public string songName; + public float bpm; + public float delay; + public float offset = 0f; + + public SongInformation_BM() + { + + } + + public SongInformation_BM(string songName, float bpm, float delay, float offset) + { + this.songName = songName; + this.bpm = bpm; + this.delay = delay; + this.offset = offset; + } + + public override void ExecuteBM() + { + GameManager.Instance.songInformation = new SongInformation(songName, bpm, delay, offset); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/SongInformation_BM.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/SongInformation_BM.cs.meta new file mode 100644 index 00000000..7fd58cd2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/DataCore/ProjectFiles_BM/SongInformation_BM.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4084a47e616d17447ab40a2ce3e9ce63 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game.meta new file mode 100644 index 00000000..de34d775 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46379277d873dd346b00382963a467bb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/EditorGame/Animations.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/AnimationBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/AnimationBase.cs new file mode 100644 index 00000000..3d24f18c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/AnimationBase.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract partial class AnimationBase : GameElement, IHaveTimeDurationSubmodule + { + #region [属性和相关对象] Attributes & Related Objects + public GameElement animatedObject; + public FlexibleReturnType animationReturnType; + #endregion + + #region [子模块接口] Submodule Interfaces + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + + } + #endregion + + #region [生命周期与控制管理] Lifecycle & Management + public override void AfterInitialize() + { + base.AfterInitialize(); + + // 【新增】受管家管控 + GameManager.Instance.animationManager.RegisterAnimation(this); + float delay = GameManager.Instance.songInformation.delay; + if (timeDurationSubmodule.CheckTimeInDuration(delay)) + { + UpdateAnimation(0f); // 确保与最新的 SongTime 同步 + } + } + + /// + /// 更新动画 + /// + /// 歌曲时间 + protected abstract void UpdateAnimation(float songTime); + + public virtual void ManualUpdate(float currentSongTime) + { + if (timeDurationSubmodule.CheckTimeInDuration(currentSongTime)) + { + UpdateAnimation(currentSongTime); + } + + if (timeDurationSubmodule.CheckAfterEndTime(currentSongTime)) + { + GameManager.Instance.animationManager.UnregisterAnimation(this); + } + } + + /// + /// 施加时间偏移,即移动所有Flexible参数的时间 + /// + /// + public virtual void ApplyTimeOffset(float offset) + { + timeDurationSubmodule.startTime += offset; + timeDurationSubmodule.endTime += offset; + } + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Animations/AnimationBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/AnimationBase.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/AnimationBase.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/AnimationBase.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera.meta new file mode 100644 index 00000000..2b649820 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 486b800798d495647b597ab969133165 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera/CameraFieldOfView.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera/CameraFieldOfView.cs new file mode 100644 index 00000000..0aac593e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera/CameraFieldOfView.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class CameraFieldOfView : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + public FlexibleFloat fieldOfView; + public GameCamera targetGameCamera; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static CameraFieldOfView GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameCamera gameCamera, FlexibleFloat fieldOfView) + { + CameraFieldOfView camFOV = Instantiate(GameManager.Instance.basePrefabs.emptyObject) + .AddComponent(); + camFOV.Initialize(elementName, id, tags, isFirstGenerated, gameCamera); + + camFOV.animatedObject = gameCamera; + camFOV.targetGameCamera = gameCamera; + + camFOV.fieldOfView = fieldOfView; + camFOV.animationReturnType = FlexibleReturnType.Before; + + return camFOV; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + fieldOfView.UpdateFlexibleFloat(songTime); + + if (fieldOfView.returnType == FlexibleReturnType.MiddleExecuting) + { + targetGameCamera.perspectiveAngle = fieldOfView.value; + targetGameCamera.cam.fieldOfView = fieldOfView.value; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + fieldOfView.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/Pool/PooledObject.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera/CameraFieldOfView.cs.meta similarity index 83% rename from Assets/Scripts/EditorGame/Base/Pool/PooledObject.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera/CameraFieldOfView.cs.meta index 02399069..b703c944 100644 --- a/Assets/Scripts/EditorGame/Base/Pool/PooledObject.cs.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Camera/CameraFieldOfView.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d6ba0e915cddec743a29f85a93f12594 +guid: 8c20f8d0710209c43be21a05b1d89871 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Scripts/EditorGame/Animations/Color.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Color.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/BaseColorChange.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/BaseColorChange.cs new file mode 100644 index 00000000..7b232664 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/BaseColorChange.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class BaseColorChange : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + private ColorSubmodule targetColorSubmodule; + public FlexibleFloat colorR, colorG, colorB, colorA; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static BaseColorChange GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + FlexibleFloat colorR, FlexibleFloat colorG, FlexibleFloat colorB, FlexibleFloat colorA) + { + BaseColorChange baseColorChange = Instantiate(GameManager.Instance.basePrefabs.emptyObject) + .AddComponent(); + + baseColorChange.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + baseColorChange.animatedObject = animatedObject; + + baseColorChange.colorR = colorR; + baseColorChange.colorG = colorG; + baseColorChange.colorB = colorB; + baseColorChange.colorA = colorA; + baseColorChange.animationReturnType = FlexibleReturnType.Before; + + baseColorChange.targetColorSubmodule = (animatedObject as IHaveColorSubmodule).colorSubmodule; + + //baseColorChange.timeDurationSubmodule.SetDuration(colorR, colorG, colorB, colorA); + + return baseColorChange; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + colorR.UpdateFlexibleFloat(songTime); + colorG.UpdateFlexibleFloat(songTime); + colorB.UpdateFlexibleFloat(songTime); + colorA.UpdateFlexibleFloat(songTime); + + if ((colorR.returnType is FlexibleReturnType.MiddleExecuting || colorR.isSwitchingReturnType) || + (colorG.returnType is FlexibleReturnType.MiddleExecuting || colorG.isSwitchingReturnType) || + (colorB.returnType is FlexibleReturnType.MiddleExecuting || colorB.isSwitchingReturnType) || + (colorA.returnType is FlexibleReturnType.MiddleExecuting || colorA.isSwitchingReturnType)) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + + targetColorSubmodule.currentBaseColor = new Color(colorR.value, colorG.value, colorB.value, colorA.value); + targetColorSubmodule.baseColorDirtyMark = true; + } + else + { + animationReturnType = FlexibleReturnType.MiddleInterval; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + colorR.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + colorG.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + colorB.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + colorA.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Animations/Color/BaseColorChange.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/BaseColorChange.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Color/BaseColorChange.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/BaseColorChange.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/EmissionColorChange.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/EmissionColorChange.cs new file mode 100644 index 00000000..99255261 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/EmissionColorChange.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class EmissionColorChange : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + private ColorSubmodule targetColorSubmodule; + public FlexibleFloat colorR, colorG, colorB, colorI; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static EmissionColorChange GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + FlexibleFloat colorR, FlexibleFloat colorG, FlexibleFloat colorB, FlexibleFloat colorI) + { + EmissionColorChange emissionColorChange = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + emissionColorChange.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + emissionColorChange.animatedObject = animatedObject; + + emissionColorChange.colorR = colorR; + emissionColorChange.colorG = colorG; + emissionColorChange.colorB = colorB; + emissionColorChange.colorI = colorI; + emissionColorChange.animationReturnType = FlexibleReturnType.Before; + + emissionColorChange.targetColorSubmodule = (animatedObject as IHaveColorSubmodule).colorSubmodule; + + //emissionColorChange.timeDurationSubmodule.SetDuration(colorR, colorG, colorB, colorI); + + return emissionColorChange; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + colorR.UpdateFlexibleFloat(songTime); + colorG.UpdateFlexibleFloat(songTime); + colorB.UpdateFlexibleFloat(songTime); + colorI.UpdateFlexibleFloat(songTime); + + if ((colorR.returnType is FlexibleReturnType.MiddleExecuting || colorR.isSwitchingReturnType) || + (colorG.returnType is FlexibleReturnType.MiddleExecuting || colorG.isSwitchingReturnType) || + (colorB.returnType is FlexibleReturnType.MiddleExecuting || colorB.isSwitchingReturnType) || + (colorI.returnType is FlexibleReturnType.MiddleExecuting || colorI.isSwitchingReturnType)) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + + targetColorSubmodule.currentEmissionColor = new Color(colorR.value, colorG.value, colorB.value, 1); + targetColorSubmodule.currentEmissionIntensity = colorI.value; + targetColorSubmodule.emissionColorDirtyMark = true; + } + else + { + animationReturnType = FlexibleReturnType.MiddleInterval; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + colorR.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + colorG.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + colorB.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + colorI.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} diff --git a/Assets/Scripts/EditorGame/Animations/Color/EmissionColorChange.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/EmissionColorChange.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Color/EmissionColorChange.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Color/EmissionColorChange.cs.meta diff --git a/Assets/Scripts/EditorGame/Animations/Track.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Track.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackGlobalColorChange.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackGlobalColorChange.cs new file mode 100644 index 00000000..0869f78e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackGlobalColorChange.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackGlobalColorChange : AnimationBase + { + #region [暴露属性字段] Exposed Fields + public FlexibleFloat colorR, colorG, colorB, colorA; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static TrackGlobalColorChange GenerateElement(string elementName, System.Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + FlexibleFloat colorR, FlexibleFloat colorG, FlexibleFloat colorB, FlexibleFloat colorA) + { + if (animatedObject is not Track) + { + Debug.LogError("Animated Object is Not Track"); + throw new System.ArgumentException("Animated Object is Not Track"); + } + TrackGlobalColorChange trackGlobalColorChange = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + trackGlobalColorChange.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + trackGlobalColorChange.animatedObject = animatedObject; + + trackGlobalColorChange.colorR = colorR; + trackGlobalColorChange.colorG = colorG; + trackGlobalColorChange.colorB = colorB; + trackGlobalColorChange.colorA = colorA; + trackGlobalColorChange.animationReturnType = FlexibleReturnType.Before; + + //trackGlobalColorChange.timeDurationSubmodule.SetDuration(colorR, colorG, colorB, colorA); + + return trackGlobalColorChange; + } + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + colorR.UpdateFlexibleFloat(songTime); + colorG.UpdateFlexibleFloat(songTime); + colorB.UpdateFlexibleFloat(songTime); + colorA.UpdateFlexibleFloat(songTime); + + if ((colorR.returnType is FlexibleReturnType.MiddleExecuting || colorR.isSwitchingReturnType) || + (colorG.returnType is FlexibleReturnType.MiddleExecuting || colorG.isSwitchingReturnType) || + (colorB.returnType is FlexibleReturnType.MiddleExecuting || colorB.isSwitchingReturnType) || + (colorA.returnType is FlexibleReturnType.MiddleExecuting || colorA.isSwitchingReturnType)) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Color color = new Color(colorR.value, colorG.value, colorB.value, colorA.value); + ((Track)animatedObject).trackRendererSubmodule.meshGenerator.color = color; + } + else + { + animationReturnType = FlexibleReturnType.MiddleInterval; + } + } + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + foreach (var item in colorR.animations) item.ApplyTimeOffset(offset); + foreach (var item in colorG.animations) item.ApplyTimeOffset(offset); + foreach (var item in colorB.animations) item.ApplyTimeOffset(offset); + foreach (var item in colorA.animations) item.ApplyTimeOffset(offset); + } + + public override void Refresh() + { + base.Refresh(); + if (colorR.animations.Count == 0 && colorG.animations.Count == 0 && colorB.animations.Count == 0 && colorA.animations.Count == 0) + { + ((Track)animatedObject).trackRendererSubmodule.meshGenerator.color = Color.white; + } + else + { + UpdateAnimation(CoreServices.TimeProvider.SongTime); + } + } + #endregion + + } +} +namespace Ichni.RhythmGame.Beatmap +{ + public class TrackGlobalColorChange_BM : AnimationBase_BM + { + public FlexibleFloat_BM colorR, colorG, colorB, colorA; + + public TrackGlobalColorChange_BM() + { + + } + + public TrackGlobalColorChange_BM(string elementName, Guid elementGuid, List tags, + GameElement_BM attachedElement, FlexibleFloat_BM colorR, FlexibleFloat_BM colorG, FlexibleFloat_BM colorB, FlexibleFloat_BM colorA) + : base(elementName, elementGuid, tags, attachedElement) + { + this.colorR = colorR; + this.colorG = colorG; + this.colorB = colorB; + this.colorA = colorA; + } + + public override void ExecuteBM() + { + matchedElement = TrackGlobalColorChange.GenerateElement(elementName, elementGuid, tags, false, GetElement(attachedElementGuid), + colorR.ConvertToGameType(), colorG.ConvertToGameType(), colorB.ConvertToGameType(), colorA.ConvertToGameType()); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackTools.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackGlobalColorChange.cs.meta similarity index 83% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackTools.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackGlobalColorChange.cs.meta index 82b6d7ac..474ab7a2 100644 --- a/Assets/Scripts/EditorGame/GameElements/Track/TrackTools.cs.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackGlobalColorChange.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 91eb99ad678b44efe90bd2699a9502f3 +guid: 9b3a4a2e6d3464840b6f1f076f41eda6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackTotalTimeChange.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackTotalTimeChange.cs new file mode 100644 index 00000000..3a6d3acc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackTotalTimeChange.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; + +namespace Ichni.RhythmGame +{ + public partial class TrackTotalTimeChange : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + public FlexibleFloat totalTime; + public TrackTimeSubmoduleStatic targetTrackTimeSubmoduleStatic; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static TrackTotalTimeChange GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, Track animatedTrack, FlexibleFloat totalTime) + { + TrackTotalTimeChange trackTotalTimeChange = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + trackTotalTimeChange.Initialize(elementName, id, tags, isFirstGenerated, animatedTrack); + + trackTotalTimeChange.animatedObject = animatedTrack; + + trackTotalTimeChange.targetTrackTimeSubmoduleStatic = animatedTrack.trackTimeSubmodule as TrackTimeSubmoduleStatic; + + trackTotalTimeChange.totalTime = totalTime; + trackTotalTimeChange.animationReturnType = FlexibleReturnType.Before; + //trackTotalTimeChange.timeDurationSubmodule.SetDuration(totalTime); + + return trackTotalTimeChange; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + totalTime.UpdateFlexibleFloat(songTime); + + if (totalTime.returnType == FlexibleReturnType.MiddleExecuting) + { + targetTrackTimeSubmoduleStatic.trackTotalTime = totalTime.value; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + totalTime.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} diff --git a/Assets/Scripts/EditorGame/Animations/Track/TrackTotalTimeChange.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackTotalTimeChange.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Track/TrackTotalTimeChange.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Track/TrackTotalTimeChange.cs.meta diff --git a/Assets/Scripts/EditorGame/Animations/Transform.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Transform.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Displacement.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Displacement.cs new file mode 100644 index 00000000..99fde40f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Displacement.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Unity.VisualScripting; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class Displacement : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + private TransformSubmodule targetTransformSubmodule; + public FlexibleFloat positionX, positionY, positionZ; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static Displacement GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + FlexibleFloat positionX, FlexibleFloat positionY, FlexibleFloat positionZ) + { + Displacement displacement = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + displacement.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + + displacement.animatedObject = animatedObject; + + displacement.positionX = positionX; + displacement.positionY = positionY; + displacement.positionZ = positionZ; + displacement.animationReturnType = FlexibleReturnType.Before; + + displacement.targetTransformSubmodule = (animatedObject as IHaveTransformSubmodule).transformSubmodule; + + //displacement.timeDurationSubmodule.SetDuration(positionX, positionY, positionZ); + + return displacement; + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + positionX.UpdateFlexibleFloat(songTime); + positionY.UpdateFlexibleFloat(songTime); + positionZ.UpdateFlexibleFloat(songTime); + + if (positionX.returnType is FlexibleReturnType.MiddleExecuting || + positionY.returnType is FlexibleReturnType.MiddleExecuting || + positionZ.returnType is FlexibleReturnType.MiddleExecuting) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Vector3 currentPosition = new Vector3(positionX.value, positionY.value, positionZ.value); + targetTransformSubmodule.positionOffset += currentPosition; + targetTransformSubmodule.positionDirtyMark = true; + } + else if (positionX.isSwitchingReturnType || positionY.isSwitchingReturnType || positionZ.isSwitchingReturnType) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Vector3 currentPosition = new Vector3(positionX.value, positionY.value, positionZ.value); + targetTransformSubmodule.positionOffset += currentPosition; + targetTransformSubmodule.positionDirtyMark = true; + } + else + { + animationReturnType = FlexibleReturnType.MiddleInterval; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + positionX.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + positionY.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + positionZ.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Animations/Transform/Displacement.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Displacement.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Transform/Displacement.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Displacement.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/LookAt.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/LookAt.cs new file mode 100644 index 00000000..272702e4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/LookAt.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + /// + /// 将物体的z轴指向目标物体,注意,LookAt的启用期间,物体的旋转将被锁定 + /// + public partial class LookAt : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + public GameElement targetGameElement; + public TransformSubmodule targetTransformSubmodule; + public FlexibleBool enabling; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static LookAt GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + GameElement lookAtTarget, FlexibleBool enabling) + { + LookAt look = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + look.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + + look.animatedObject = animatedObject; + look.enabling = enabling; + look.animationReturnType = FlexibleReturnType.Before; + + look.targetGameElement = lookAtTarget; + look.targetTransformSubmodule = (animatedObject as IHaveTransformSubmodule).transformSubmodule; + + //look.timeDurationSubmodule.SetDuration(-999f, 999f); //TODO: 换为(-delay, songLength) + + return look; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + void LateUpdate() + { + if (enabling.value) + { + (animatedObject as IHaveTransformSubmodule)?.UpdateLookAt(this); + } + } + + protected override void UpdateAnimation(float songTime) + { + if (targetGameElement is null) return; + + enabling.UpdateFlexibleBool(songTime); + + if (!targetTransformSubmodule.eulerAnglesOffsetLock || enabling.value) + { + targetTransformSubmodule.eulerAnglesOffsetLock = enabling.value; + } + + if (enabling.value) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + targetTransformSubmodule.eulerAnglesDirtyMark = true; + } + else if (animationReturnType != FlexibleReturnType.MiddleInterval) + { + animationReturnType = FlexibleReturnType.MiddleInterval; + targetTransformSubmodule.eulerAnglesDirtyMark = true; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + enabling.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Animations/Transform/LookAt.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/LookAt.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Transform/LookAt.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/LookAt.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Scale.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Scale.cs new file mode 100644 index 00000000..d2ff6381 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Scale.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class Scale : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + public TransformSubmodule targetTransformSubmodule; + public FlexibleFloat scaleX, scaleY, scaleZ; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static Scale GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + FlexibleFloat scaleX, FlexibleFloat scaleY, FlexibleFloat scaleZ) + { + Scale scale = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + scale.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + + scale.animatedObject = animatedObject; + + scale.scaleX = scaleX; + scale.scaleY = scaleY; + scale.scaleZ = scaleZ; + scale.animationReturnType = FlexibleReturnType.Before; + + scale.targetTransformSubmodule = (animatedObject as IHaveTransformSubmodule).transformSubmodule; + //scale.timeDurationSubmodule.SetDuration(scaleX, scaleY, scaleZ); + + return scale; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + scaleX.UpdateFlexibleFloat(songTime); + scaleY.UpdateFlexibleFloat(songTime); + scaleZ.UpdateFlexibleFloat(songTime); + + if (scaleX.returnType is FlexibleReturnType.MiddleExecuting || + scaleY.returnType is FlexibleReturnType.MiddleExecuting || + scaleZ.returnType is FlexibleReturnType.MiddleExecuting) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Vector3 currentScale = new Vector3(scaleX.value, scaleY.value, scaleZ.value); + targetTransformSubmodule.scaleOffset += currentScale; + targetTransformSubmodule.scaleDirtyMark = true; + } + else if (scaleX.isSwitchingReturnType || scaleY.isSwitchingReturnType || scaleZ.isSwitchingReturnType) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Vector3 currentScale = new Vector3(scaleX.value, scaleY.value, scaleZ.value); + targetTransformSubmodule.scaleOffset += currentScale; + targetTransformSubmodule.scaleDirtyMark = true; + } + else + { + animationReturnType = FlexibleReturnType.MiddleInterval; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + scaleX.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + scaleY.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + scaleZ.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Animations/Transform/Scale.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Scale.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Transform/Scale.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Scale.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Swirl.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Swirl.cs new file mode 100644 index 00000000..9f03340d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Swirl.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class Swirl : AnimationBase + { + #region [暴露属性字段与关联] Exposed Fields & References + private TransformSubmodule targetTransformSubmodule; + public FlexibleFloat eulerAngleX, eulerAngleY, eulerAngleZ; + #endregion + + #region [生命周期与工厂] Lifecycle & Factory + public static Swirl GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement animatedObject, + FlexibleFloat eulerAngleX, FlexibleFloat eulerAngleY, FlexibleFloat eulerAngleZ) + { + Swirl swirl = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + swirl.Initialize(elementName, id, tags, isFirstGenerated, animatedObject); + + swirl.animatedObject = animatedObject; + + swirl.eulerAngleX = eulerAngleX; + swirl.eulerAngleY = eulerAngleY; + swirl.eulerAngleZ = eulerAngleZ; + swirl.animationReturnType = FlexibleReturnType.Before; + + swirl.targetTransformSubmodule = (animatedObject as IHaveTransformSubmodule).transformSubmodule; + + return swirl; + } + #endregion + + #region [核心动画逻辑] Core Animation Logic + protected override void UpdateAnimation(float songTime) + { + eulerAngleX.UpdateFlexibleFloat(songTime); + eulerAngleY.UpdateFlexibleFloat(songTime); + eulerAngleZ.UpdateFlexibleFloat(songTime); + + if (eulerAngleX.returnType is FlexibleReturnType.MiddleExecuting || + eulerAngleY.returnType is FlexibleReturnType.MiddleExecuting || + eulerAngleZ.returnType is FlexibleReturnType.MiddleExecuting) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Vector3 currentEulerAngles = new Vector3(eulerAngleX.value, eulerAngleY.value, eulerAngleZ.value); + targetTransformSubmodule.eulerAnglesOffset += currentEulerAngles; + targetTransformSubmodule.eulerAnglesDirtyMark = true; + } + else if (eulerAngleX.isSwitchingReturnType || eulerAngleY.isSwitchingReturnType || eulerAngleZ.isSwitchingReturnType) + { + animationReturnType = FlexibleReturnType.MiddleExecuting; + Vector3 currentEulerAngles = new Vector3(eulerAngleX.value, eulerAngleY.value, eulerAngleZ.value); + targetTransformSubmodule.eulerAnglesOffset += currentEulerAngles; + targetTransformSubmodule.eulerAnglesDirtyMark = true; + } + else + { + animationReturnType = FlexibleReturnType.MiddleInterval; + } + } + + public override void ApplyTimeOffset(float offset) + { + base.ApplyTimeOffset(offset); + eulerAngleX.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + eulerAngleY.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + eulerAngleZ.animations.ForEach(anim => anim.ApplyTimeOffset(offset)); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Animations/Transform/Swirl.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Swirl.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Animations/Transform/Swirl.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Animations/Transform/Swirl.cs.meta diff --git a/Assets/Scripts/EditorGame/Base.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/BaseElement.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/BaseElement.cs new file mode 100644 index 00000000..a59859bb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/BaseElement.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame.Beatmap; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public interface IBaseElement + { + public BaseElement_BM matchedBM { get; set; } + + /// + /// 刷新物体的状态 + /// + public void Refresh() + { + + } + + /// + /// 当物体被删除时执行的方法 + /// + public void OnDelete() + { + throw new NotImplementedException(); + } + + /// + /// 删除物体,包括所有子物体 + /// + public void Delete() + { + throw new NotImplementedException(); + } + + public void SetUpInspector() + { + + } + } + + public interface IHaveInteraction + { + public void TriggerInteraction(); + } +} + diff --git a/Assets/Scripts/EditorGame/Base/BaseElement.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/BaseElement.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/BaseElement.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/BaseElement.cs.meta diff --git a/Assets/Scripts/EditorGame/Base/FlexibleTypes.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/FlexibleTypes.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleBool.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleBool.cs new file mode 100644 index 00000000..37844a80 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleBool.cs @@ -0,0 +1,83 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; + +namespace Ichni.RhythmGame +{ + [System.Serializable] + public class AnimatedBool + { + public bool value; //bool值 + public float time; //当前时间 + + public AnimatedBool(float time, bool value) + { + this.value = value; + this.time = time; + } + + public void ApplyTimeOffset(float offset) + { + time += offset; + } + } + + [System.Serializable] + public class FlexibleBool + { + public bool value; + public List animations; + + public FlexibleBool() + { + animations = new List(); + } + + public FlexibleBool(List anim) + { + this.animations = anim; + } + + public void Add(AnimatedBool animatedBool) + { + animations.Add(animatedBool); + } + + public FlexibleReturnType UpdateFlexibleBool(float nowTime) + { + if (animations.Count == 0) return FlexibleReturnType.None; + + // 使用二分查找 + int index = FindAnimationIndex(nowTime); + + // 如果时间在第一个关键帧之前,使用首个关键帧的值,否则使用找到的关键帧的值 + value = index < 0 ? animations[0].value : animations[index].value; + + return FlexibleReturnType.MiddleExecuting; + } + + private int FindAnimationIndex(float nowTime) + { + int left = 0; + int right = animations.Count - 1; + int result = -1; + + while (left <= right) + { + int mid = left + (right - left) / 2; + // 注意对象中的属性叫 time + if (animations[mid].time <= nowTime) + { + result = mid; + left = mid + 1; + } + else + { + right = mid - 1; + } + } + return result; + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleBool.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleBool.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleBool.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleBool.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleFloat.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleFloat.cs new file mode 100644 index 00000000..8b29383a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleFloat.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + [System.Serializable] + public class AnimatedFloat : IComparable + { + public float startValue, endValue; //起止值 + public float startTime, endTime; //起止时间 + public AnimationCurveType animationCurveType; //动画曲线类型 + + public float differenceValue => endValue - startValue; //差值 + public float totalTime => endTime - startTime; //总时间 + + public AnimatedFloat(float startTime, float endTime, float startValue, float endValue, + AnimationCurveType animationCurveType) + { + this.startValue = startValue; + this.endValue = endValue; + this.startTime = startTime; + this.endTime = endTime; + this.animationCurveType = animationCurveType; + } + + public void ApplyTimeOffset(float offset) + { + startTime += offset; + endTime += offset; + } + + /// + /// 按照起始时间排序 + /// + public int CompareTo(AnimatedFloat obj) + { + return startTime.CompareTo(obj.startTime); + } + } + + [System.Serializable] + public class FlexibleFloat + { + public float value; + public List animations; + public bool isSwitchingReturnType; + public FlexibleReturnType lastReturnType; + public FlexibleReturnType returnType; + + public FlexibleFloat() + { + animations = new List(); + } + + public FlexibleFloat(List anim) + { + animations = anim; + } + + public void Add(AnimatedFloat animatedFloat) + { + animations.Add(animatedFloat); + } + + public void Sort() + { + animations.Sort(); + } + + public void UpdateFlexibleFloat(float nowTime) + { + if (animations.Count == 0) + { + value = 0; + returnType = FlexibleReturnType.None; + return; + } + if (isSwitchingReturnType) isSwitchingReturnType = false; + // 核心优化:通过二分查找快速找到最后一个 startTime <= nowTime 的动画索引 + int index = FindAnimationIndex(nowTime); + if (index < 0) + { + // nowTime 比第一个动画的开始时间还要早 + value = animations[0].startValue; + returnType = FlexibleReturnType.Before; + } + else if (index >= animations.Count) + { + // 理论上由于二分查找的写法,它不会比 Count 大,但这为了逻辑封闭可以保留 + value = animations[animations.Count - 1].endValue; + returnType = FlexibleReturnType.After; + } + else + { + AnimatedFloat anim = animations[index]; + // 检查到底是以“正在进行中”还是在动画后的“间隙” + if (nowTime >= anim.startTime && nowTime < anim.endTime) + { + float nowPercent = AnimationCurveEvaluator.Evaluate(anim.animationCurveType, + (nowTime - anim.startTime) / anim.totalTime); + value = anim.startValue + nowPercent * anim.differenceValue; + returnType = FlexibleReturnType.MiddleExecuting; + } + else + { + // 说明处在这个动画结束之后,下一个动画开始之前(或者已经完全过了最后一个动画) + value = anim.endValue; + if (index == animations.Count - 1) + returnType = FlexibleReturnType.After; + else + returnType = FlexibleReturnType.MiddleInterval; + } + } + if (lastReturnType != returnType) isSwitchingReturnType = true; + lastReturnType = returnType; + } + + private int FindAnimationIndex(float nowTime) + { + int left = 0; + int right = animations.Count - 1; + int result = -1; // -1 表示位于所有的动画开始之前 + + while (left <= right) + { + int mid = left + (right - left) / 2; + if (animations[mid].startTime <= nowTime) + { + result = mid; // 记录下满足条件的,并尝试往更后的时间找是否存在别的 + left = mid + 1; + } + else + { + right = mid - 1; + } + } + + return result; + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleFloat.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleFloat.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleFloat.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleFloat.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleInt.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleInt.cs new file mode 100644 index 00000000..6e22f042 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleInt.cs @@ -0,0 +1,89 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + [System.Serializable] + public class AnimatedInt + { + public int value; //值 + public float time; + + public AnimatedInt() + { + } + + public AnimatedInt(float time, int value) + { + this.value = value; + this.time = time; + } + + public void ApplyTimeOffset(float offset) + { + time += offset; + } + } + + [System.Serializable] + public class FlexibleInt + { + public int value; + public List animations; + + public FlexibleInt() + { + animations = new List(); + } + + public FlexibleInt(List anim) + { + this.animations = anim; + } + + public void Add(AnimatedInt animatedInt) + { + animations.Add(animatedInt); + } + + public FlexibleReturnType UpdateFlexibleInt(float nowTime) + { + if (animations.Count == 0) return FlexibleReturnType.None; + + // 使用二分查找 + int index = FindAnimationIndex(nowTime); + + // 如果时间在第一个关键帧之前,使用首个关键帧的值,否则使用找到的关键帧的值 + value = index < 0 ? animations[0].value : animations[index].value; + + return FlexibleReturnType.MiddleExecuting; + } + + private int FindAnimationIndex(float nowTime) + { + int left = 0; + int right = animations.Count - 1; + int result = -1; + + while (left <= right) + { + int mid = left + (right - left) / 2; + // 注意对象中的属性叫 time + if (animations[mid].time <= nowTime) + { + result = mid; + left = mid + 1; + } + else + { + right = mid - 1; + } + } + return result; + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleInt.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleInt.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleInt.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleInt.cs.meta diff --git a/Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleMain.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleMain.cs similarity index 96% rename from Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleMain.cs rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleMain.cs index c704ee77..cd7b935c 100644 --- a/Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleMain.cs +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleMain.cs @@ -1,15 +1,15 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace Ichni.RhythmGame -{ - public enum FlexibleReturnType - { - None = -1, //没动画 - Before = 0, //动画开始之前 - MiddleExecuting = 1, //动画中间,正在运动 - MiddleInterval = 2, //动画中间,在停顿空隙 - After = 3 //动画结束后 - } +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public enum FlexibleReturnType + { + None = -1, //没动画 + Before = 0, //动画开始之前 + MiddleExecuting = 1, //动画中间,正在运动 + MiddleInterval = 2, //动画中间,在停顿空隙 + After = 3 //动画结束后 + } } \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleMain.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleMain.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/FlexibleTypes/FlexibleMain.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/FlexibleTypes/FlexibleMain.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces.meta new file mode 100644 index 00000000..e0abfc80 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e76b2ae9d7a33ee4086a6e501801643e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces/CoreServices.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces/CoreServices.cs new file mode 100644 index 00000000..53ccde13 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces/CoreServices.cs @@ -0,0 +1,25 @@ +namespace Ichni.RhythmGame +{ + /// + /// 轻量级核心服务定位器,允许游戏与编辑器分别注册自己的的时间引擎 + /// + public static class CoreServices + { + public static ISongTimeProvider TimeProvider { get; set; } + } + + /// + /// 全局时间供应器接口 + /// 无论是在游戏本体还是在谱面编辑器,一切与时间强相关的运作(特效、生成、判定) + /// 只能依赖此接口,严禁直接调用 GameManager! + /// + public interface ISongTimeProvider + { + /// 当前音频的播放进度时间 (秒) + float SongTime { get; } + + /// 当前时间轴是否处于流转播放状态 + bool IsPlaying { get; } + } + +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces/CoreServices.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces/CoreServices.cs.meta new file mode 100644 index 00000000..e7c8abd3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/Interfaces/CoreServices.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 99b9020f699ea2441b3873c64f6e0fee \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/ProjectFiles.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/ProjectFiles.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/BeatmapContainer.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/BeatmapContainer.cs new file mode 100644 index 00000000..a88aad03 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/BeatmapContainer.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UniRx; +using UnityEngine; +using UnityEngine.Events; + +namespace Ichni.RhythmGame +{ + public class BeatmapContainer : IBaseElement + { + public List gameElementList; + + [NonSerialized] + public List lowPriorityActions; + + public BaseElement_BM matchedBM { get; set; } + + public BeatmapContainer() + { + gameElementList = new List(); + lowPriorityActions = new List(); + Observable.EveryUpdate().Subscribe(_ => ExecuteLowPriorityActions()); + } + + public void ExecuteLowPriorityActions() + { + if (lowPriorityActions.Count > 0) + { + lowPriorityActions.ForEach(low => low.Invoke()); + lowPriorityActions.Clear(); + } + } + + public void SetUpInspector() + { + throw new System.NotImplementedException(); + } + + public void Refresh() + { + throw new System.NotImplementedException(); + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/ProjectFiles/BeatmapContainer.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/BeatmapContainer.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/ProjectFiles/BeatmapContainer.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/BeatmapContainer.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/CommandScripts.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/CommandScripts.cs new file mode 100644 index 00000000..2b2f93b4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/CommandScripts.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class CommandScripts : IBaseElement + { + List commandList; + public BaseElement_BM matchedBM { get; set; } + + public CommandScripts(List commandList) + { + this.commandList = commandList; + } + + public void SetUpInspector() + { + throw new System.NotImplementedException(); + } + + public void Refresh() + { + throw new System.NotImplementedException(); + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/ProjectFiles/CommandScripts.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/CommandScripts.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/ProjectFiles/CommandScripts.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/CommandScripts.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/ProjectInformation.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/ProjectInformation.cs new file mode 100644 index 00000000..27340125 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/ProjectInformation.cs @@ -0,0 +1,47 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class ProjectInformation : IBaseElement + { + public static string EditorVersion = "0.1.0"; + + public string projectName; + public string creatorName; + public string editorVersion; + public string createTime; + public string lastSaveTime; + public List selectedThemeBundleList; + + public string projectPath; + public BaseElement_BM matchedBM { get; set; } + + public string peojectInfoPath => projectPath + "/ProjectInfo.json"; + public string songInfoPath => projectPath + "/SongInfo.json"; + public string songPath => projectPath + GameManager.Instance.songInformation.songName + ".wav"; + public string beatmapPath => projectPath + "/Beatmap.json"; + public string CommandScriptsPath => projectPath + "/CommandScripts.json"; + + public ProjectInformation(string projectName, string creatorName, string editorVersion, + string createTime, string lastSaveTime, List selectedThemeBundleList) + { + this.projectName = projectName; + this.creatorName = creatorName; + this.editorVersion = editorVersion; + this.createTime = createTime; + this.lastSaveTime = lastSaveTime; + this.selectedThemeBundleList = selectedThemeBundleList; + + projectPath = Application.streamingAssetsPath + "/Projects/" + projectName; + } + + public void SetUpInspector() + { + + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/ProjectFiles/ProjectInformation.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/ProjectInformation.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/ProjectFiles/ProjectInformation.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/ProjectInformation.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/SongInformation.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/SongInformation.cs new file mode 100644 index 00000000..8bf90121 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/SongInformation.cs @@ -0,0 +1,31 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class SongInformation : IBaseElement + { + public AudioClip song; //曲目 + public string songName; + public string songLocation; //曲名 + public float bpm; //每分钟节拍数 + public float delay; //设定音乐和谱面延迟Delay秒后开始,在延迟中,SongPosition为负数。 + + public float songLength; + public float offset = 0f; + public BaseElement_BM matchedBM { get; set; } + + public SongInformation(string songName, float bpm, float delay, float offset) + { + this.songName = songName; + this.bpm = bpm; + this.delay = delay; + this.offset = offset; + + GameManager.Instance.songPlayer.songTimeSegment = -delay; // 初始化时,歌曲时间为负 + } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/ProjectFiles/SongInformation.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/SongInformation.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/ProjectFiles/SongInformation.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Base/ProjectFiles/SongInformation.cs.meta diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components.meta diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectSubmodule.cs new file mode 100644 index 00000000..eaf0be88 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectSubmodule.cs @@ -0,0 +1,27 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Events; + +namespace Ichni.RhythmGame +{ + public class SelectSubmodule : SubmoduleBase + { + private GameElement elementToSelect; + + public SelectSubmodule(GameElement attachedGameElement, GameElement elementToSelect = null) : base(attachedGameElement) + { + if (attachedGameElement is IHaveSelectSubmodule host) + { + host.selectSubmodule = this; + attachedGameElement.gameObject.layer = LayerMask.NameToLayer("Selectable"); + this.elementToSelect = elementToSelect == null ? attachedGameElement : elementToSelect; + } + } + } + + public interface IHaveSelectSubmodule + { + public SelectSubmodule selectSubmodule { get; set; } + } +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules/SelectSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules/SelectSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectSubmodule.cs.meta diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules/SelectionConnector.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectionConnector.cs similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules/SelectionConnector.cs rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectionConnector.cs diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules/SelectionConnector.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectionConnector.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/EditorSubmodules/SelectionConnector.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/BMEditorSubmodules/SelectionConnector.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/ColorSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/ColorSubmodule.cs new file mode 100644 index 00000000..8f9c59ce --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/ColorSubmodule.cs @@ -0,0 +1,148 @@ +using System; +using System.Linq; +using Ichni.RhythmGame.Beatmap; +using UniRx; +using UniRx.Triggers; +using Unity.VisualScripting; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + /// + /// 含有颜色属性的次级模块,包括基础颜色(透明度)、发光颜色和发光强度 + /// + public partial class ColorSubmodule : SubmoduleBase + { + #region [颜色属性状态缓存] Color Cached States + public Color originalBaseColor; + public bool emissionEnabled; + public Color originalEmissionColor; + public float originalEmissionIntensity; + + public Color currentBaseColor; + public Color currentEmissionColor; + public float currentEmissionIntensity; + + public bool baseColorDirtyMark; + public bool emissionColorDirtyMark; + + public IDisposable observer; + + public Color GetCurrentEmissionColor() + { + float intensity = Mathf.Pow(2, currentEmissionIntensity); + Color emissionColor = currentEmissionColor * intensity; + emissionColor.a = 1; + return emissionColor; + } + #endregion + + #region [构造函数与初始化] Constructors & Initialization + public ColorSubmodule(GameElement attachedGameElement) : base(attachedGameElement) + { + this.originalBaseColor = Color.white; + this.emissionEnabled = false; + this.originalEmissionColor = Color.black; + this.originalEmissionIntensity = 0; + + this.currentBaseColor = Color.white; + this.currentEmissionColor = Color.black; + this.currentEmissionIntensity = 0; + + this.baseColorDirtyMark = true; + this.emissionColorDirtyMark = true; + + if (!HaveSameSubmodule && attachedGameElement is IHaveColorSubmodule host) + { + host.colorSubmodule = this; + host.SetColorObserver(); + } + } + + public ColorSubmodule(GameElement attachedGameElement, Color originalBaseColor, bool emissionEnabled, + Color originalEmissionColor, float originalEmissionIntensity) : base(attachedGameElement) + { + this.originalBaseColor = originalBaseColor; + this.emissionEnabled = emissionEnabled; + this.originalEmissionColor = originalEmissionColor; + this.originalEmissionIntensity = originalEmissionIntensity; + + this.currentBaseColor = originalBaseColor; + this.currentEmissionColor = originalEmissionColor; + this.currentEmissionIntensity = originalEmissionIntensity; + + this.baseColorDirtyMark = true; + this.emissionColorDirtyMark = true; + + if (!HaveSameSubmodule && attachedGameElement is IHaveColorSubmodule host) + { + host.colorSubmodule = this; + host.SetColorObserver(); + } + } + #endregion + + #region [生命周期与状态刷新] Lifecycle & State Refresh + + public override void Refresh() + { + currentBaseColor = originalBaseColor; + currentEmissionColor = originalEmissionColor; + currentEmissionIntensity = originalEmissionIntensity; + baseColorDirtyMark = true; + emissionColorDirtyMark = true; + } + + private bool HaveAnimation() => attachedGameElement.childElementList + .Any(element => element is BaseColorChange or EmissionColorChange); + + public override void CheckAndRemoveObservers() + { + if (!HaveAnimation()) + { + observer?.Dispose(); + } + } + #endregion + } + + #region [组件接口] Component Interface + public interface IHaveColorSubmodule + { + public ColorSubmodule colorSubmodule { get; set; } + public virtual bool haveEmission => false; + + public void SetColorObserver() + { + colorSubmodule.observer = Observable.EveryUpdate() + .Subscribe(_ => UpdateColor()) + .AddTo(colorSubmodule.attachedGameElement); + } + + public void UpdateColor(bool refreshAll = true) + { + bool willRefresh = false; + + if (colorSubmodule.baseColorDirtyMark) + { + //在动画物体中改变currentColor + colorSubmodule.baseColorDirtyMark = false; + willRefresh = true; + } + + if (colorSubmodule.emissionColorDirtyMark) + { + //在动画物体中改变currentColor + colorSubmodule.emissionColorDirtyMark = false; + willRefresh = true; + } + + if (willRefresh && refreshAll) + { + colorSubmodule.attachedGameElement.Refresh(); + } + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/ColorSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/ColorSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/ColorSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/ColorSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/EffectSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/EffectSubmodule.cs new file mode 100644 index 00000000..74ea0cf9 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/EffectSubmodule.cs @@ -0,0 +1,99 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + /// + /// 包含效果的次级模块 + /// + public partial class EffectSubmodule : SubmoduleBase + { + #region [效果字典与集合] Efffects Dictionary & Lists + public Dictionary> effectCollection; + #endregion + + #region [构造函数与初始化] Constructors & Initialization + public EffectSubmodule(GameElement attachedGameElement, EffectSubmodulePreset preset = EffectSubmodulePreset.Default) + : base(attachedGameElement) + { + effectCollection = new Dictionary>(); + + if (preset == EffectSubmodulePreset.Default) //对于默认的效果次级模块,有Prior、Default、Late三个效果集合 + { + effectCollection.Add("Prior", new List()); + effectCollection.Add("Default", new List()); + effectCollection.Add("Late", new List()); + } + else if (preset == EffectSubmodulePreset.Note) //对于Note的效果次级模块,在Note的不同状态下有独立的效果集合 + { + effectCollection.Add("Generate", new List()); + effectCollection.Add("GeneralJudge", new List()); + effectCollection.Add("StartHold", new List()); //仅用于Hold + effectCollection.Add("Holding", new List()); //仅用于Hold + effectCollection.Add("Perfect", new List()); + effectCollection.Add("Good", new List()); + effectCollection.Add("Bad", new List()); + effectCollection.Add("Miss", new List()); + effectCollection.Add("AfterJudge", new List()); + } + + if (!HaveSameSubmodule) + { + (attachedGameElement as IHaveEffectSubmodule).effectSubmodule = this; + } + } + + public EffectSubmodule(GameElement attachedGameElement, Dictionary> effectList_BM) : base(attachedGameElement) + { + effectCollection = new Dictionary>(); + + foreach (var effect in effectList_BM) + { + List effectList = new List(); + foreach (var effectBM in effect.Value) + { + if (BeatmapContainer_BM.LowPriorityDataTypes.Contains(effectBM.GetType())) // 如果是低优先级数据类型 + { + (GameManager.Instance.beatmapContainer).lowPriorityActions.Add(() => + { + effectList.Add(effectBM.ConvertToGameType(attachedGameElement)); + }); + } + else + { + effectList.Add(effectBM.ConvertToGameType(attachedGameElement)); + } + } + + effectCollection.Add(effect.Key, effectList); + } + + if (!HaveSameSubmodule && attachedGameElement is IHaveEffectSubmodule host) + { + host.effectSubmodule = this; + } + } + #endregion + } + + #region [枚举类型] Enums + public partial class EffectSubmodule + { + public enum EffectSubmodulePreset + { + Default, + Note, + } + } + #endregion + + #region [组件接口] Component Interface + public interface IHaveEffectSubmodule + { + public EffectSubmodule effectSubmodule { get; set; } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/EffectSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/EffectSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/EffectSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/EffectSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/NoteJudgeTriggerSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/NoteJudgeTriggerSubmodule.cs new file mode 100644 index 00000000..36998118 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/NoteJudgeTriggerSubmodule.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class NoteJudgeTriggerSubmodule : SubmoduleBase + { + #region [模块状态] Submodule States + public List connectedNotes; + #endregion + + #region [初始化] Initialization + public NoteJudgeTriggerSubmodule(GameElement attachedGameElement) : base(attachedGameElement) + { + connectedNotes = new List(); + + if (!HaveSameSubmodule && attachedGameElement is IHaveNoteJudgeTriggerSubmodule host) + { + host.noteJudgeTriggerSubmodule = this; + } + } + #endregion + + } + + public interface IHaveNoteJudgeTriggerSubmodule + { + public NoteJudgeTriggerSubmodule noteJudgeTriggerSubmodule { get; set; } + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/NoteJudgeTriggerSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/NoteJudgeTriggerSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/NoteJudgeTriggerSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/NoteJudgeTriggerSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/SubmoduleBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/SubmoduleBase.cs new file mode 100644 index 00000000..5343941b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/SubmoduleBase.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class SubmoduleBase : IBaseElement + { + #region [基础属性] Essential Properties + public GameElement attachedGameElement; + public BaseElement_BM matchedBM { get; set; } + + /// + /// 在生成时检测是否已经有重复的submodule + /// + public bool HaveSameSubmodule { get; set; } + #endregion + + #region [生命周期与初始化] Lifecycle & Initialization + public SubmoduleBase(GameElement attachedGameElement) + { + this.attachedGameElement = attachedGameElement; + + HaveSameSubmodule = attachedGameElement.submoduleList.Any(x => x.GetType() == this.GetType()); + + if (HaveSameSubmodule) + { + Debug.LogAssertion($"存在重复的Submodule: {GetType()},此操作无效"); + return; + } + + this.attachedGameElement.submoduleList.Add(this); + } + + public virtual void OnDelete() + { + + } + + public virtual void Delete() + { + OnDelete(); + attachedGameElement.submoduleList.Remove(this); + } + + public virtual void Refresh() + { + + } + + public virtual void CheckAndRemoveObservers() + { + + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/SubmoduleBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/SubmoduleBase.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/SubmoduleBase.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/SubmoduleBase.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TimeDurationSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TimeDurationSubmodule.cs new file mode 100644 index 00000000..a28b067c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TimeDurationSubmodule.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame.Beatmap; +using UniRx; +using Unity.Mathematics; +using UnityEngine; +using UnityEngine.Events; + +namespace Ichni.RhythmGame +{ + public partial class TimeDurationSubmodule : SubmoduleBase + { + #region [时间标记与代理] Time Flags & Actions + public bool isOverridingDuration; //是否手动设置了时间区间,开启时,子物体的时间区间将被忽略,且在自动计算区间时跳过此模块 + public float startTime, endTime; //起止时间 + private Action _enableAction, _disableAction; //启用和禁用时的额外操作 + #endregion + + #region [构造函数与初始化] Constructors & Initialization + public TimeDurationSubmodule(GameElement attachedGameElement) : base(attachedGameElement) + { + isOverridingDuration = false; + startTime = -32767; //TODO: 换为-delay + endTime = 32767; //TODO: 换为songLength + + if (!HaveSameSubmodule) + { + (attachedGameElement as IHaveTimeDurationSubmodule).timeDurationSubmodule = this; + } + } + + public TimeDurationSubmodule(GameElement attachedGameElement, bool isOverridingDuration, float startTime, float endTime) : + base(attachedGameElement) + { + this.isOverridingDuration = isOverridingDuration; + this.startTime = startTime; + this.endTime = endTime; + + if (!HaveSameSubmodule) + { + (attachedGameElement as IHaveTimeDurationSubmodule).timeDurationSubmodule = this; + } + } + #endregion + + #region [时间判断工具] Time Checking Utils + public bool CheckTimeInDuration(float time, float offset = 0.2f) + { + return time >= startTime - offset && time <= endTime + offset; + } + + public bool CheckAfterEndTime(float time, float offset = 0.2f) + { + return time > endTime + offset; + } + #endregion + + #region [设置与时长运算] Get & Set Durations + public void SetDuration(float startTime, float endTime) + { + this.startTime = startTime; + this.endTime = endTime; + this.isOverridingDuration = true; + } + + public void SetDuration(params FlexibleFloat[] flexibleFloats) + { + List startTimes = new List(); + List endTimes = new List(); + + foreach (FlexibleFloat flexibleFloat in flexibleFloats) + { + flexibleFloat.Sort(); + + if (flexibleFloat.animations.Count > 0) + { + startTimes.Add(flexibleFloat.animations[0].startTime); + endTimes.Add(flexibleFloat.animations[^1].endTime); + } + else continue; + } + if (startTimes.Count == 0 || endTimes.Count == 0) + { + return; + } + startTime = startTimes.Min(); + endTime = endTimes.Max(); + } + + public void SetDurationFromChildren(List children) + { + List durations = new List(); + + if (children.Count == 0) + { + return; + } + + foreach (var child in children) + { + durations.Add(new float2(child.startTime, child.endTime)); + } + + startTime = durations.Min(duration => duration.x); + endTime = durations.Max(duration => duration.y); + } + #endregion + + } + + #region [动态控制状态] Dynamic State Control + public partial class TimeDurationSubmodule + { + public void SetUpActions(Action enableAction, Action disableAction = null) + { + _enableAction = enableAction; + _disableAction = disableAction; + } + + public void DetectAndSwitchActiveState(float currentSongTime) + { + bool inDuration = CheckTimeInDuration(currentSongTime); + bool isActive = attachedGameElement.gameObject.activeSelf; + + if (inDuration && !isActive) + { + attachedGameElement.gameObject.SetActive(true); + _enableAction?.Invoke(); + } + else if (!inDuration && isActive) + { + attachedGameElement.gameObject.SetActive(false); + _disableAction?.Invoke(); + } + } + } + #endregion + + #region [组件接口] Component Interface + public interface IHaveTimeDurationSubmodule + { + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/TimeDurationSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TimeDurationSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/TimeDurationSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TimeDurationSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TransformSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TransformSubmodule.cs new file mode 100644 index 00000000..5750757a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TransformSubmodule.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame.Beatmap; +using UniRx; +using UniRx.Triggers; +using Unity.VisualScripting; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Ichni.RhythmGame +{ + public class TransformSubmodule : SubmoduleBase + { + #region [变换属性状态缓存] Transform Cached States + public Vector3 originalPosition; + public Vector3 originalEulerAngles; + public Vector3 originalScale; + + public Vector3 positionOffset; + public Vector3 eulerAnglesOffset; + public Vector3 scaleOffset; + + public Vector3 currentPosition; + public Vector3 currentEulerAngles; + public Vector3 currentScale; + + public bool positionDirtyMark; + public bool eulerAnglesDirtyMark; + public bool scaleDirtyMark; + + public bool eulerAnglesOffsetLock; + + public IDisposable observer; + #endregion + + #region [构造函数与初始化] Constructors & Initialization + public TransformSubmodule(GameElement attachedGameElement) : base(attachedGameElement) + { + this.originalPosition = Vector3.zero; + this.originalEulerAngles = Vector3.zero; + this.originalScale = Vector3.one; + + positionOffset = Vector3.zero; + eulerAnglesOffset = Vector3.zero; + scaleOffset = Vector3.zero; + + currentPosition = Vector3.zero; + currentEulerAngles = Vector3.zero; + currentScale = Vector3.one; + + positionDirtyMark = true; + eulerAnglesDirtyMark = true; + scaleDirtyMark = true; + + eulerAnglesOffsetLock = false; + + if (!HaveSameSubmodule && attachedGameElement is IHaveTransformSubmodule host) + { + host.transformSubmodule = this; + host.SetTransformObserver(); + } + } + + public TransformSubmodule(GameElement attachedGameElement, + Vector3 originalPosition, Vector3 originalEulerAngles, Vector3 originalScale) : base(attachedGameElement) + { + this.originalPosition = originalPosition; + this.originalEulerAngles = originalEulerAngles; + this.originalScale = originalScale; + + positionOffset = Vector3.zero; + eulerAnglesOffset = Vector3.zero; + scaleOffset = Vector3.zero; + + currentPosition = originalPosition; + currentEulerAngles = originalEulerAngles; + currentScale = originalScale; + + positionDirtyMark = true; + eulerAnglesDirtyMark = true; + scaleDirtyMark = true; + + eulerAnglesOffsetLock = false; + + if (!HaveSameSubmodule && attachedGameElement is IHaveTransformSubmodule host) + { + host.transformSubmodule = this; + host.SetTransformObserver(); + } + } + #endregion + + #region [生命周期与状态刷新] Lifecycle & State Refresh + + public override void Refresh() + { + positionDirtyMark = true; + eulerAnglesDirtyMark = true; + scaleDirtyMark = true; + } + + private bool HaveAnimation() => attachedGameElement.childElementList + .Any(element => element is Displacement or Swirl or Scale or LookAt); + + public override void CheckAndRemoveObservers() + { + if (!HaveAnimation()) + { + observer?.Dispose(); + } + } + #endregion + } + + #region [组件接口] Component Interface + public interface IHaveTransformSubmodule + { + TransformSubmodule transformSubmodule { get; set; } + + /// + /// 设置物体Transform的监听,顺序为Scale -> EulerAngles -> Position + /// 如果有一些特殊的物体(例如Camera,ElementFolder),需要自定义监听,可以重写这个方法 + /// + public void SetTransformObserver() + { + transformSubmodule.observer = Observable.EveryUpdate() + .Where(_ => GameManager.Instance.songPlayer.isUpdating) + .Subscribe(_ => UpdateTransform()) + .AddTo(transformSubmodule.attachedGameElement); + } + + public void UpdateTransform(bool refreshAll = true) + { + GameElement attachedGameElement = transformSubmodule.attachedGameElement; + bool willRefresh = false; + + if (transformSubmodule.scaleDirtyMark) + { + transformSubmodule.currentScale = transformSubmodule.originalScale + transformSubmodule.scaleOffset; + + attachedGameElement.transform.localScale = transformSubmodule.currentScale; + transformSubmodule.scaleDirtyMark = false; + willRefresh = true; + transformSubmodule.scaleOffset = Vector3.zero; + } + + if (!transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark) + { + transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset; + attachedGameElement.transform.localEulerAngles = transformSubmodule.currentEulerAngles; + transformSubmodule.eulerAnglesDirtyMark = false; + willRefresh = true; + transformSubmodule.eulerAnglesOffset = Vector3.zero; + } + + if (transformSubmodule.positionDirtyMark) + { + transformSubmodule.currentPosition = transformSubmodule.originalPosition + transformSubmodule.positionOffset; + attachedGameElement.transform.localPosition = transformSubmodule.currentPosition; + transformSubmodule.positionDirtyMark = false; + willRefresh = true; + transformSubmodule.positionOffset = Vector3.zero; + } + + if(refreshAll && willRefresh) + { + attachedGameElement.Refresh(); + } + } + + public void UpdateLookAt(LookAt lookAt) // 处理LookAt + { + Transform target = lookAt.targetGameElement.transform; + Transform self = transformSubmodule.attachedGameElement.transform; + + if (transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark) + { + Vector3 lookingDirection = (target.position - self.position).normalized; + + Vector3 eulerAnglesOffset = Quaternion.LookRotation(lookingDirection).eulerAngles; + + transformSubmodule.eulerAnglesOffset += eulerAnglesOffset; + transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset; + self.localEulerAngles = transformSubmodule.currentEulerAngles; + + transformSubmodule.eulerAnglesDirtyMark = false; + transformSubmodule.eulerAnglesOffsetLock = false; + transformSubmodule.eulerAnglesOffset = Vector3.zero; + } + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/Base/GeneralSubmodules/TransformSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TransformSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/Base/GeneralSubmodules/TransformSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/Components/TransformSubmodule.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Danmaku.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Danmaku.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Danmaku.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Danmaku.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Danmaku/Dodger.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Danmaku/Dodger.cs similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Danmaku/Dodger.cs rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Danmaku/Dodger.cs diff --git a/Assets/Scripts/EditorGame/GameElements/Danmaku/Dodger.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Danmaku/Dodger.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Danmaku/Dodger.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Danmaku/Dodger.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/EnvironmentObjects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/EnvironmentObjects.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/BackgroundSetter.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/BackgroundSetter.cs new file mode 100644 index 00000000..133c1de3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/BackgroundSetter.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class BackgroundSetter : GameElement + { + #region [暴露属性字段] Settings & Sprites + public bool useSkybox; + public string skyboxThemeBundleName; + public string skyboxMaterialName; + public Material skyboxMaterial; + public string backgroundSpriteName; + public Sprite backgroundSprite; + #endregion + + #region [关联组件] Related References + public SkyboxSubsetter skyboxSubsetter; + #endregion + + #region [生命周期] Lifecycle & Factory + public static BackgroundSetter GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, GameElement parentElement, bool useSkybox, string skyboxThemeBundleName, + string skyboxMaterialName, string backgroundSpriteName) + { + BackgroundSetter backgroundSetter = Instantiate(GameManager.Instance.basePrefabs.emptyObject) + .AddComponent(); + GameManager.Instance.backgroundSetter = backgroundSetter; + backgroundSetter.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + backgroundSetter.useSkybox = useSkybox; + backgroundSetter.skyboxThemeBundleName = skyboxThemeBundleName; + backgroundSetter.skyboxMaterialName = skyboxMaterialName; + backgroundSetter.backgroundSpriteName = backgroundSpriteName; + return backgroundSetter; + } + + public override void Refresh() + { + GameManager.Instance.backgroundController.EnableBackground(!useSkybox); + if (useSkybox && skyboxSubsetter == null) + { + SetSkybox(skyboxThemeBundleName, skyboxMaterialName); + } + else + { + SetBackgroundSprite(backgroundSpriteName); + } + } + #endregion + } + + #region [私有业务方法] Private Utilities + public partial class BackgroundSetter + { + private void SetSkybox(string themeBundleName, string materialName) + { + skyboxThemeBundleName = themeBundleName; + skyboxMaterialName = materialName; + skyboxMaterial = ThemeBundleManager.instance.GetObject(themeBundleName, materialName); + if (skyboxMaterial == null) skyboxMaterial = GameManager.Instance.basePrefabs.defaultSkyboxMaterial; + GameManager.Instance.backgroundController.SetSkybox(skyboxMaterial); + } + + private void SetBackgroundSprite(string spriteName) + { + string path = GameManager.Instance.projectInformation.projectPath + "/Sprites/" + spriteName + ".png"; + backgroundSprite = ES3.FileExists(path) ? ES3.Load(path) : GameManager.Instance.basePrefabs.defaultBackground; + GameManager.Instance.backgroundController.SetBackground(backgroundSprite); + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GlobalElements/BackgroundSetter.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/BackgroundSetter.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GlobalElements/BackgroundSetter.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/BackgroundSetter.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/EnvironmentObject.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/EnvironmentObject.cs new file mode 100644 index 00000000..78977299 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/EnvironmentObject.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class EnvironmentObject : SubstantialObject + { + #region [标记参数] Flags + public bool isStatic; + #endregion + + #region [生命周期] Lifecycle & Factory + public static EnvironmentObject GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, string themeBundleName, string objectName, GameElement parentElement, bool isStatic) + { + EnvironmentObject environmentObject = + SubstantialObject.GenerateElement(elementName, id, tags, isFirstGenerated, themeBundleName, objectName, parentElement) + .GetComponent(); + + environmentObject.isStatic = isStatic; + environmentObject.gameObject.isStatic = isStatic; + + return environmentObject; + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/EnvironmentObjects/EnvironmentObject.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/EnvironmentObject.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/EnvironmentObjects/EnvironmentObject.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/EnvironmentObject.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/SkyboxSubsetter.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/SkyboxSubsetter.cs new file mode 100644 index 00000000..ad843cbd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/SkyboxSubsetter.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class SkyboxSubsetter : GameElement + { + #region [核心组件与天空盒列表] Core Components & Skybox Lists + public SkyboxBlender skyboxBlender; + public List skyBoxThemeBundleList; + public List skyboxNameList; + public List skyboxMaterialList; + public List blendSpeedList; + public List blendTimeList; + public int currentSkyboxIndex = 0; + #endregion + + #region [编辑器UI支持] Editor Selection Tools + public List themeBundleListForSelection; + public List skyboxNameListForSelection; + public string selectedThemeBundle; + public string selectedSkybox; + #endregion + + #region [生命周期] Lifecycle & Factory + public static SkyboxSubsetter GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, GameElement parentElement, List themeBundleList,List skyboxList, + List blendTimeList, List blendSpeedList) + { + SkyboxSubsetter skyboxSubsetter = Instantiate(GameManager.Instance.basePrefabs.emptyObject) + .AddComponent(); + GameManager.Instance.backgroundSetter.skyboxSubsetter = skyboxSubsetter; + skyboxSubsetter.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + skyboxSubsetter.skyBoxThemeBundleList = themeBundleList; + skyboxSubsetter.skyboxNameList = skyboxList; + skyboxSubsetter.skyboxMaterialList = new List(); + skyboxSubsetter.blendTimeList = blendTimeList; + skyboxSubsetter.blendSpeedList = blendSpeedList; + skyboxSubsetter.SetUpBlender(); + skyboxSubsetter.themeBundleListForSelection = ThemeBundleManager.instance.loadedThemeBundleList.ConvertAll(x => x.themeBundleName); + skyboxSubsetter.skyboxNameListForSelection = new List(); + skyboxSubsetter.selectedThemeBundle = String.Empty; + skyboxSubsetter.selectedSkybox = String.Empty; + return skyboxSubsetter; + } + #endregion + + #region [内部设置与工具] Internal Configs & Utils + private void SetUpBlender() + { + skyboxBlender = gameObject.AddComponent(); + skyboxBlender.loop = false; + skyboxBlender.timeToWait = 0f; + skyboxBlender.updateLighting = false; + skyboxBlender.updateReflections = false; + skyboxBlender.skyboxMaterials = new List(); + for (int i = 0; i < skyBoxThemeBundleList.Count; i++) + { + Material skybox = ThemeBundleManager.instance.GetObject(skyBoxThemeBundleList[i], skyboxNameList[i]); + skyboxMaterialList.Add(skybox); + skyboxBlender.skyboxMaterials.Add(skybox); + } + skyboxBlender.makeFirstMaterialSkybox = true; + skyboxBlender.InspectorAndAwakeChanges(); + } + + private void AddSkybox(string skyboxThemeBundleName, string skyboxObjectName) + { + Material skybox = ThemeBundleManager.instance.GetObject(skyboxThemeBundleName, skyboxObjectName); + if (skybox != null) + { + skyBoxThemeBundleList.Add(skyboxThemeBundleName); + skyboxNameList.Add(skyboxObjectName); + skyboxMaterialList.Add(skybox); + skyboxBlender.skyboxMaterials.Add(skybox); + } + } + #endregion + + #region [轮询更新] Main Update + private void Update() + { + if (skyBoxThemeBundleList.Count > 1) + { + float songTime = CoreServices.TimeProvider.SongTime; + float delay = GameManager.Instance.songInformation.delay; + float finalTime = 32767; // 曲目长度 + + for (var index = 0; index < blendTimeList.Count + 1; index++) + { + float startTime = index == 0 ? -delay : blendTimeList[index - 1]; + float endTime = index >= blendTimeList.Count ? finalTime : blendTimeList[index]; + if(songTime >= startTime && songTime < endTime && currentSkyboxIndex != index) + { + currentSkyboxIndex = index; + if(currentSkyboxIndex != 0) skyboxBlender.blendSpeed = blendSpeedList[currentSkyboxIndex - 1]; + skyboxBlender.Blend(currentSkyboxIndex, false); + DynamicGI.UpdateEnvironment(); + } + } + } + } + #endregion + + } + +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/SkyboxSubsetter.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/SkyboxSubsetter.cs.meta new file mode 100644 index 00000000..cbc79592 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Environment/SkyboxSubsetter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 06f3fcb2652843340ad7c72b7c19ef6c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential.meta new file mode 100644 index 00000000..a381ce73 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 261f85f1881db9b42aedcdef13e06f50 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/ElementFolder.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/ElementFolder.cs new file mode 100644 index 00000000..d380cea0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/ElementFolder.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class ElementFolder : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule + { + #region [包含对象] Sub-Elements + public List trackList; + #endregion + + #region [子模块接口] Submodules + public TransformSubmodule transformSubmodule { get; set; } + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static ElementFolder GenerateElement(string name, Guid id, List tags, bool isFirstGenerated, GameElement parentElement) + { + ElementFolder elementFolder = Instantiate(GameManager.Instance.basePrefabs.elementFolder).GetComponent(); + elementFolder.Initialize(name, id, tags, isFirstGenerated, parentElement); + return elementFolder; + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + } + + #region [工具与测试] Utilities & Tests + public partial class ElementFolder + { + [Button("Test GetAllNotes")] + public List GetAllNotes() + { + List notes = new List(); + foreach (GameElement element in childElementList) + { + if (element is NoteBase note) + { + notes.Add(note); + } + } + + /*foreach (NoteBase note in notes) + { + Debug.Log(note.GetType() + " " + note.elementName + " " + note.exactJudgeTime); + }*/ + + return notes; + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/ElementFolder/ElementFolder.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/ElementFolder.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/ElementFolder/ElementFolder.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/ElementFolder.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCamera.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCamera.cs new file mode 100644 index 00000000..6562bde4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCamera.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using DG.Tweening; +using Ichni.RhythmGame.Beatmap; +using Ichni.UI; +using UniRx; +using UnityEngine; +using UnityEngine.Rendering.Universal; +using UnityEngine.Serialization; + +namespace Ichni.RhythmGame +{ + public partial class GameCamera : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule + { + #region [暴露属性字段] Camera View & Settings + public Camera cam; + public Transform rotationPoint; + public Transform positionPoint; + public Transform cameraTransform; + + public CameraViewType cameraViewType; + public float perspectiveAngle; + public float orthographicSize; + + public float perspectiveOffset; + #endregion + + #region [子模块接口与关联引用] Submodules & References + public TransformSubmodule transformSubmodule { get; set; } + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + private static CameraManager cameraManager => GameManager.Instance.cameraManager; + #endregion + + #region [生命周期] Lifecycle & Factory + public static GameCamera GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement parentElement, + CameraViewType cameraViewType, float perspectiveAngle, float orthographicSize) + { + GameCamera gameCamera = Instantiate(GameManager.Instance.basePrefabs.gameCamera).GetComponent(); + + gameCamera.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + + cameraManager.gameCamera = gameCamera; + + gameCamera.parentElement = parentElement; + gameCamera.cameraViewType = cameraViewType; + gameCamera.cam.orthographic = cameraViewType == CameraViewType.Orthographic; + gameCamera.perspectiveAngle = perspectiveAngle; + gameCamera.orthographicSize = orthographicSize; + gameCamera.cameraTransform = gameCamera.transform; + + float ratioDifference = UIManager.GetScreenRatio() - UIManager.StandardRatio; + if (ratioDifference > 0) + { + gameCamera.perspectiveOffset = -22f * ratioDifference; + } + else + { + //gameCamera.perspectiveOffset = 11f * ratioDifference; + } + + gameCamera.cam.fieldOfView = perspectiveAngle + gameCamera.perspectiveOffset; + return gameCamera; + } + + public override void AfterInitialize() + { + base.AfterInitialize(); + //gameCamera.GetComponent().cameraStack.Add(cameraManager.uiCamera); + GameManager.Instance.backgroundController.backgroundCanvas.worldCamera = cam; + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + } + #endregion + } + + #region [枚举类型] Enums + public partial class GameCamera + { + public enum CameraViewType + { + Perspective = 0, + Orthographic = 1 + } + } + #endregion + + #region [坐标变换逻辑更新] Transform Observe & Update Logics + public partial class GameCamera + { + public void SetTransformObserver() + { + transformSubmodule.observer = Observable.EveryLateUpdate() + .Where(_=>GameManager.Instance.songPlayer.isUpdating) + .Subscribe(_ => UpdateTransform()) + .AddTo(transformSubmodule.attachedGameElement); + } + + public void UpdateTransform(bool refreshAll = true) + { + bool willRefresh = false; + + if (!transformSubmodule.eulerAnglesOffsetLock && transformSubmodule.eulerAnglesDirtyMark) + { + transformSubmodule.currentEulerAngles = transformSubmodule.originalEulerAngles + transformSubmodule.eulerAnglesOffset; + transform.localEulerAngles = transformSubmodule.currentEulerAngles; + transformSubmodule.eulerAnglesDirtyMark = false; + willRefresh = true; + transformSubmodule.eulerAnglesOffset = Vector3.zero; + } + + if (transformSubmodule.positionDirtyMark) + { + transformSubmodule.currentPosition = transformSubmodule.originalPosition + transformSubmodule.positionOffset; + transform.localPosition = transformSubmodule.currentPosition; + transformSubmodule.positionDirtyMark = false; + willRefresh = true; + transformSubmodule.positionOffset = Vector3.zero; + } + + if (refreshAll && willRefresh) + { + Refresh(); + } + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GameCamera/GameCamera.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCamera.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GameCamera/GameCamera.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCamera.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCameraExtension.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCameraExtension.cs new file mode 100644 index 00000000..c7b3b70a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCameraExtension.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; + +namespace Ichni.RhythmGame +{ + public partial class GameCameraExtension : GameElement + { + #region [相机引用] Camera Reference + public GameCamera gameCamera; + public float farClipRange = 1000f; + #endregion + + #region [生命周期] Lifecycle & Factory + public static GameCameraExtension GenerateElement(string elementName, Guid id, + List tags, bool isFirstGenerated, GameElement parentElement, float farClipRange) + { + GameCameraExtension gameCameraExtension = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + gameCameraExtension.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + gameCameraExtension.gameCamera = parentElement as GameCamera; + gameCameraExtension.farClipRange = farClipRange; + gameCameraExtension.ApplyExtension(); + return gameCameraExtension; + } + #endregion + + #region [扩展功能] Extension Methods + public void ApplyExtension() + { + gameCamera.cam.farClipPlane = farClipRange; + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GameCamera/GameCameraExtension.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCameraExtension.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GameCamera/GameCameraExtension.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameCameraExtension.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameElement.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameElement.cs new file mode 100644 index 00000000..338c7b27 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameElement.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract partial class GameElement : SerializedMonoBehaviour, IBaseElement, IComparable + { + #region [属性和标记] Essential & Tracking Info + //物体名 + public string elementName; + + //标识 GUID + public Guid elementGuid; + + //标签 + public List tags; + + //物理层级优先级 + public virtual int HierarchyPriority => 0; + + //存档类 + public BaseElement_BM matchedBM { get; set; } + #endregion + + #region [层级从属结构] Hierarchy & Children + //父游戏物体 + public GameElement parentElement; + + //子物体列表 + public List childElementList = new List(); + #endregion + + #region [附着子模块集] Components List + //次级模块 + public List submoduleList = new List(); + #endregion + + #region [生命周期与构造] Initialization + /// + /// 首次初始化 + /// + /// 物体名 + public virtual void Initialize(string name, Guid elementGuid, List tags, + bool isFirstGenerated, GameElement parentElement) + { + this.elementName = name; + this.elementGuid = elementGuid; + this.tags = tags; + GameManager.Instance.beatmapContainer.gameElementList.Add(this); + submoduleList = new List(); + + if (isFirstGenerated) + { + SetDefaultSubmodules(); + } + + SetParent(parentElement); + } + + /// + /// 设置次级模块 + /// + public virtual void SetDefaultSubmodules() + { + + } + #endregion + + #region [生命周期:激活阶段] Lifecycle Events Extensions + /// + /// 在所有物体生成完毕后,执行的初始化方法 + /// + public virtual void AfterInitialize() + { + matchedBM?.AfterExecute(); + + if (this is IHaveTransformSubmodule transformSource) + { + transformSource.transformSubmodule.CheckAndRemoveObservers(); + } + + if(this is IHaveColorSubmodule colorSource) + { + colorSource.colorSubmodule.CheckAndRemoveObservers(); + } + } + + public virtual void BeforeStart() + { + if (this is IHaveTransformSubmodule transformSource) + { + transformSource.UpdateTransform(false); + } + + if (this is IHaveColorSubmodule colorSource) + { + colorSource.UpdateColor(false); + } + + Refresh(); + } + + public virtual void WhenStart() + { + + } + #endregion + + #region [工具方法] Global Methods + /// + /// 设置父物体 + /// + /// 父物体 + public void SetParent(GameElement parentElement) + { + if (parentElement != null) + { + parentElement.childElementList.Add(this); + this.parentElement = parentElement; + transform.SetParent(parentElement.transform); + } + } + + public int CompareTo(GameElement other) + { + return HierarchyPriority.CompareTo(other.HierarchyPriority); + } + #endregion + } + + #region [额外交互及存档重写预留] Editor Interaction & Interfaces Overrides + public abstract partial class GameElement //存档,删除,复制,粘贴 + { + + public virtual void Refresh() + { + + } + + /// + /// 当物体被删除时执行的方法 + /// + public virtual void OnDelete() + { + + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GameElement.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameElement.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GameElement.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/GameElement.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/SubstantialObject.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/SubstantialObject.cs new file mode 100644 index 00000000..9d65d8f6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/SubstantialObject.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Lean.Pool; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class SubstantialObject : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule, IHaveColorSubmodule + { + #region [暴露属性字段] Theme Details + public string themeBundleName, objectName; + #endregion + + #region [子模块接口] Submodules + public TransformSubmodule transformSubmodule { get; set; } + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + public ColorSubmodule colorSubmodule { get; set; } + public virtual bool haveEmission => false; + #endregion + + #region [生命周期] Lifecycle & Factory + public static SubstantialObject GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + string themeBundleName, string objectName, GameElement parentElement) + { + GameObject themeBundleObject = ThemeBundleManager.instance.GetObject(themeBundleName, objectName); + SubstantialObject substantialObject = Instantiate(themeBundleObject, parentElement.transform).GetComponent(); + substantialObject.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + substantialObject.themeBundleName = themeBundleName; + substantialObject.objectName = objectName; + substantialObject.FirstSetUpObject(isFirstGenerated); + return substantialObject; + } + + /// + /// 初次生成继承自SubstantialObject的对象时,生成方法必然使用SubstantialObject中的GenerateElement方法。 + /// 因此对于需要进行特殊处理的子类,需要重写FirstSetUpObject方法。 + /// 在读取Beatmap时,生成物体则使用子类本身的GenerateElement方法。 + /// + public virtual void FirstSetUpObject(bool isFirstGenerated) + { + + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + timeDurationSubmodule = new TimeDurationSubmodule(this); + colorSubmodule = new ColorSubmodule(this); + } + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/SubstantialObject.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/SubstantialObject.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/SubstantialObject.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/SubstantialObject.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/TemporaryObject.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/TemporaryObject.cs new file mode 100644 index 00000000..b49e873c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/TemporaryObject.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TemporaryObject : SubstantialObject + { + public static TemporaryObject GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, GameElement parentElement) + { + TemporaryObject tempObj = Instantiate(GameManager.Instance.basePrefabs.emptyObject) + .AddComponent(); + tempObj.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + tempObj.themeBundleName = string.Empty; + tempObj.objectName = string.Empty; + Debug.LogError("This TemporaryObject should not exist."); + return tempObj; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/TemporaryObject.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/TemporaryObject.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/TemporaryObject.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/TemporaryObject.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/VariablesContainer.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/VariablesContainer.cs new file mode 100644 index 00000000..1b0d48d5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/VariablesContainer.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; + +namespace Ichni.RhythmGame +{ + public partial class VariablesContainer : GameElement + { + #region [变量存储字典] Values Dictionary + public Dictionary originalVariables; + public Dictionary currentVariables; + #endregion + + #region [生命周期] Lifecycle & Factory + public static VariablesContainer GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + GameElement parentElement, Dictionary variables) + { + VariablesContainer variablesContainer = + Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + + GameManager.Instance.variablesContainer = variablesContainer; + variablesContainer.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + variablesContainer.originalVariables = new Dictionary(variables); + variablesContainer.currentVariables = new Dictionary(variables); + + return variablesContainer; + } + #endregion + + #region [变量交互接口] Variable Interactions + public void SetVariable(string variableName, int value) + { + currentVariables[variableName] = value; + } + + public int GetVariable(string variableName) + { + return currentVariables[variableName]; + } + + public void RevertVariable(string variableName) + { + currentVariables[variableName] = originalVariables[variableName]; + } + + public void RevertAllVariables() + { + currentVariables = new Dictionary(originalVariables); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GlobalElements/VariablesContainer.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/VariablesContainer.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GlobalElements/VariablesContainer.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Essential/VariablesContainer.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/BloomEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/BloomEffect.cs new file mode 100644 index 00000000..e0ca09cb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/BloomEffect.cs @@ -0,0 +1,62 @@ +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using UnityEngine.Rendering.Universal; + +namespace Ichni.RhythmGame +{ + public class BloomEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float peak; + public AnimationCurve intensityCurve; + + private Bloom _bloom; + #endregion + + #region [初始化] Initialization + public BloomEffect(float duration, float peak, AnimationCurve intensityCurve) + : base(duration) // 激活时间线 + { + this.duration = duration; + this.peak = peak; + this.intensityCurve = intensityCurve; + } + + private void PrepareEffectHandle() + { + if (_bloom == null && PostProcessingManager.GlobalVolume != null) + { + PostProcessingManager.GlobalVolume.profile.TryGet(out _bloom); + } + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + PrepareEffectHandle(); + } + + public override void Execute() + { + if (_bloom != null) + { + float currentIntensity = intensityCurve.Evaluate(effectProgressPercent) * peak; + _bloom.intensity.value = currentIntensity; + } + } + + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_bloom != null) _bloom.intensity.value = 0f; + } + #endregion + + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/BloomEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/BloomEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/BloomEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/BloomEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraOffsetEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraOffsetEffect.cs new file mode 100644 index 00000000..113f7718 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraOffsetEffect.cs @@ -0,0 +1,54 @@ +using DG.Tweening; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class CameraOffsetEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public Vector3 offsetValue; + public AnimationCurve offsetCurve; + + Transform gameCameraTransform => GameManager.Instance.cameraManager.gameCamera.cam.transform; + Tweener offsetTweener; + #endregion + + #region [初始化] Initialization + public CameraOffsetEffect(float duration, Vector3 offsetValue, AnimationCurve offsetCurve) + { + this.effectTime = this.duration; + this.duration = duration; + this.offsetValue = offsetValue; + this.offsetCurve = offsetCurve; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void Recover() + { + offsetTweener?.Kill(true); + gameCameraTransform.localPosition = Vector3.zero; + } + + public override void PreExecute() + { + offsetTweener = gameCameraTransform.DOBlendableLocalMoveBy(offsetValue, duration).SetEase(offsetCurve).Play(); + } + + public override void Adjust() + { + + } + + public override void Disrupt() + { + offsetTweener?.Kill(); + gameCameraTransform.DOLocalMove(Vector3.zero, 0.4f).Play(); + } + #endregion + + } + +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraOffsetEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraOffsetEffect.cs.meta new file mode 100644 index 00000000..163a02b1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraOffsetEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b082ee12ba2393b48b5f6bce4cf23e23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraShakeEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraShakeEffect.cs new file mode 100644 index 00000000..40d3a8cf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraShakeEffect.cs @@ -0,0 +1,75 @@ +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class CameraShakeEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float frequency; + public float amplitudeX; + public float amplitudeY; + public float amplitudeZ; + + private Transform _cameraTransform; + #endregion + + #region [初始化] Initialization + public CameraShakeEffect(float duration, float frequency, float amplitudeX, float amplitudeY, float amplitudeZ) + : base(duration) + { + this.duration = duration; + this.frequency = frequency; // 数值越大抖动越跳跃 + this.amplitudeX = amplitudeX; + this.amplitudeY = amplitudeY; + this.amplitudeZ = amplitudeZ; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + // 抓取摄像机 Transform + if (_cameraTransform == null) + { + _cameraTransform = GameManager.Instance.cameraManager.gameCamera.cam.transform; + } + } + + public override void Execute() + { + if (_cameraTransform != null) + { + // 一套基于柏林噪声和音乐进展时间的稳固伪随机位移计算 + // 因为基于 effectProgressPercent 产生,所以录像回退或者暂停时,位置百分百受控复现 + float timeFactor = effectProgressPercent * frequency; + + // 随着时间推移,让震动平滑消退 (1.0 -> 0.0) + float dampening = 1.0f - effectProgressPercent; + + float offsetX = (Mathf.PerlinNoise(timeFactor, 0) - 0.5f) * 2f * amplitudeX * dampening; + float offsetY = (Mathf.PerlinNoise(0, timeFactor) - 0.5f) * 2f * amplitudeY * dampening; + float offsetZ = (Mathf.PerlinNoise(timeFactor, timeFactor) - 0.5f) * 2f * amplitudeZ * dampening; + + // 这次应用仅在局部空间偏移,非常干净轻量 + _cameraTransform.localPosition = new Vector3(offsetX, offsetY, offsetZ); + } + } + + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_cameraTransform != null) + { + _cameraTransform.localPosition = Vector3.zero; + } + } + #endregion + + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/CameraShakeEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraShakeEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/CameraShakeEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraShakeEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraTiltEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraTiltEffect.cs new file mode 100644 index 00000000..5ba8d268 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraTiltEffect.cs @@ -0,0 +1,57 @@ +using System.Collections; +using System.Collections.Generic; +using DG.Tweening; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class CameraTiltEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public Vector3 tiltValue; + public AnimationCurve tiltCurve; + + GameCamera gameCamera=> GameManager.Instance.cameraManager.gameCamera; + Tweener tiltTweener; + Tweener tiltBackTweener; + #endregion + + #region [初始化] Initialization + public CameraTiltEffect(float duration, Vector3 tiltValue, AnimationCurve tiltCurve) + { + this.effectTime = duration; + this.duration = duration; + this.tiltValue = tiltValue; + this.tiltCurve = tiltCurve; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void Recover() + { + tiltTweener?.Kill(true); + tiltBackTweener?.Kill(true); + gameCamera.cam.transform.localEulerAngles = Vector3.zero; + } + + public override void PreExecute() + { + tiltTweener = gameCamera.cam.transform.DOBlendableLocalRotateBy(tiltValue, duration, RotateMode.FastBeyond360).SetEase(tiltCurve).Play(); + } + + public override void Adjust() + { + } + + public override void Disrupt() + { + tiltTweener?.Kill(); + tiltBackTweener = gameCamera.cam.transform.DOLocalRotate(Vector3.zero, 0.4f).SetEase(Ease.OutSine).Play(); + } + #endregion + + } + +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraTiltEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraTiltEffect.cs.meta new file mode 100644 index 00000000..6993f514 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraTiltEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 764e22166d03b564bb12196d028a7640 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraZoomEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraZoomEffect.cs new file mode 100644 index 00000000..1af66f89 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraZoomEffect.cs @@ -0,0 +1,60 @@ +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class CameraZoomEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float relativeZoom; + public AnimationCurve zoomCurve; + + private Camera _mainCamera; + private float _startFOV = 60f; // 请填入你项目的默认摄像机广角值 + #endregion + + #region [初始化] Initialization + public CameraZoomEffect(float duration, float relativeZoom, AnimationCurve zoomCurve) + : base(duration) + { + this.duration = duration; + this.relativeZoom = relativeZoom; + this.zoomCurve = zoomCurve; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + if (_mainCamera == null) + { + _mainCamera = GameManager.Instance.cameraManager.gameCamera.cam; + } + + _startFOV = _mainCamera.fieldOfView; // 记录初始 FOV,结束时恢复 + } + + public override void Execute() + { + if (_mainCamera != null) + { + // relativeZoom > 0 代表拉近视野,FOV 更小 + float offset = zoomCurve.Evaluate(effectProgressPercent) * relativeZoom; + _mainCamera.fieldOfView = _startFOV - offset; + } + } + + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_mainCamera != null) _mainCamera.fieldOfView = _startFOV; + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraZoomEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraZoomEffect.cs.meta new file mode 100644 index 00000000..3b2d7d25 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/CameraZoomEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d739305a671fa543ab8b5e1d608edc6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ChromaticAberrationEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ChromaticAberrationEffect.cs new file mode 100644 index 00000000..eacdd019 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ChromaticAberrationEffect.cs @@ -0,0 +1,69 @@ +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using UnityEngine.Rendering.Universal; // 需要引入 URP 库 + +namespace Ichni.RhythmGame +{ + public class ChromaticAberrationEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float peak; + public AnimationCurve intensityCurve; + + // 缓存后处理组件,避免运行时每帧由于反序列化而产生 GC + private ChromaticAberration _chromaticAberration; + #endregion + + #region [初始化] Initialization + public ChromaticAberrationEffect(float duration, float peak, AnimationCurve intensityCurve) + : base(duration) // 【修改1】调用基类含时构造,将 effectTime 赋为 duration,激活时长逻辑 + { + this.duration = duration; + this.peak = peak; + this.intensityCurve = intensityCurve; + } + + /// + /// 私有方法:安全获取 URP 色差后处理句柄 + /// + private void PrepareEffectHandle() + { + if (_chromaticAberration == null && PostProcessingManager.GlobalVolume != null) + { + // .profile 会自动实例化一个副本,防止修改了Editor原有的资产 + PostProcessingManager.GlobalVolume.profile.TryGet(out _chromaticAberration); + } + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + PrepareEffectHandle(); + } + + public override void Execute() + { + if (_chromaticAberration != null) + { + // 【修改2】直接借助基类已算出的 effectProgressPercent 来拉取曲线 + // 不再需要外部插件,每一帧的强度与音乐进程强绑定!哪怕音乐倒退也会原路倒退。 + float currentIntensity = intensityCurve.Evaluate(effectProgressPercent) * peak; + _chromaticAberration.intensity.value = currentIntensity; + } + } + + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_chromaticAberration != null) _chromaticAberration.intensity.value = 0f; + } + #endregion + + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/ChromaticAberrationEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ChromaticAberrationEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/ChromaticAberrationEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ChromaticAberrationEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EffectBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EffectBase.cs new file mode 100644 index 00000000..bf80169b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EffectBase.cs @@ -0,0 +1,162 @@ +using System; + +namespace Ichni.RhythmGame +{ + public abstract class EffectBase : IBaseElement + { + #region [枚举定义] Enums + public enum EffectState + { + Before = -1, + Middle = 0, + After = 1, + Error = 100 + } + #endregion + + #region [核心属性] Properties + public Beatmap.BaseElement_BM matchedBM { get; set; } + public GameElement attachedGameElement; + + /// + /// 效果的持续时间,如果为0则表示瞬间效果 + /// + public float effectTime; + + /// + /// 是否是瞬间效果 + /// + public bool isInstantEffect => effectTime <= 0; + + /// + /// 效果的进度百分比 + /// + public float effectProgressPercent; + + /// + /// 效果当前的状态 + /// + public EffectState nowEffectState; + #endregion + + #region [初始化] Initialization + protected EffectBase() + { + this.effectTime = 0; + this.nowEffectState = EffectState.Before; + this.effectProgressPercent = 0; + } + + protected EffectBase(float effectTime) + { + this.effectTime = effectTime; + this.nowEffectState = EffectState.Before; + this.effectProgressPercent = 0; + } + #endregion + + #region [基础逻辑] Main Update Logic + public virtual void UpdateEffect(float judgeTime) + { + EffectState state = CheckEffectState(judgeTime); + float songTime = CoreServices.TimeProvider.SongTime; + + if (state == EffectState.Before && nowEffectState != EffectState.Before) + { + nowEffectState = EffectState.Before; + effectProgressPercent = 0; + Recover(); + } + else if (state == EffectState.Middle) + { + if (nowEffectState == EffectState.Before) + { + PreExecute(); + } + + nowEffectState = EffectState.Middle; + effectProgressPercent = (songTime - judgeTime) / effectTime; + Execute(); + } + else if (state == EffectState.After && nowEffectState != EffectState.After) + { + nowEffectState = EffectState.After; + effectProgressPercent = 1; + Adjust(); + } + } + + protected virtual EffectState CheckEffectState(float triggerTime) + { + float songTime = CoreServices.TimeProvider.SongTime; + + if (songTime < triggerTime) + { + return EffectState.Before; + } + + if (songTime >= triggerTime && + songTime <= triggerTime + effectTime) + { + return EffectState.Middle; + } + + if (songTime > triggerTime + effectTime) + { + return EffectState.After; + } + + return EffectState.Error; + } + #endregion + + #region [效果重写] Effect Overrides + /// + /// 当从Before状态进入Middle状态时,仅在效果的开始时触发一次方法 + /// + public virtual void PreExecute() + { + + } + + /// + /// 在效果的持续时间内,触发这个方法 + /// + public virtual void Execute() + { + + } + + /// + /// 如果是非瞬间效果,在效果完成后,触发这个方法; + /// 如果是瞬间效果,则此方法即为Execute。原有的Execute方法不被调用。 + /// + public virtual void Adjust() + { + + } + + /// + /// 如果时间轴回退到效果的触发时间之前,则触发这个方法 + /// + public virtual void Recover() + { + + } + + /// + /// 如果效果被打断,则触发这个方法 + /// + public virtual void Disrupt() + { + + } + + public void Refresh() + { + + } + #endregion + + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EffectBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EffectBase.cs.meta new file mode 100644 index 00000000..481615bc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EffectBase.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 28b63dede1693624fa4d9981a90b652d \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EnableControlEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EnableControlEffect.cs new file mode 100644 index 00000000..1db241c6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EnableControlEffect.cs @@ -0,0 +1,57 @@ +using System; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class EnableControlEffect : EffectBase + { + #region [效果参数] Effect Parameters + public GameElement connectedGameElement; + public string connectedVariableName; + public int enableValue; + + public bool useExpression; + public string expression; + #endregion + + #region [初始化] Initialization + public EnableControlEffect(GameElement connectedGameElement, string connectedVariableName, + int enableValue, bool useExpression, string expression) + { + this.effectTime = 0; + this.connectedGameElement = connectedGameElement; + this.connectedVariableName = connectedVariableName; + this.enableValue = enableValue; + this.useExpression = useExpression; + this.expression = expression; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void Recover() + { + if (connectedGameElement == null) return; + + connectedGameElement.gameObject.SetActive(false); + } + + public override void Adjust() + { + if (connectedGameElement == null) return; + + if (!useExpression) + { + int value = GameManager.Instance.variablesContainer.GetVariable(connectedVariableName); + connectedGameElement.gameObject.SetActive(value == enableValue); + } + else + { + + } + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/EnableControlEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EnableControlEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/EnableControlEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/EnableControlEffect.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/MusicEffects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/MusicEffects.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs new file mode 100644 index 00000000..207bf51b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs @@ -0,0 +1,40 @@ +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class HighPassFilterEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float peak; + public AnimationCurve intensityCurve; + #endregion + + #region [初始化] Initialization + public HighPassFilterEffect(float duration, float peak, AnimationCurve intensityCurve) + { + this.effectTime = 0; + this.duration = duration; + this.peak = peak; + this.intensityCurve = intensityCurve; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void Adjust() + { + /*MMF_Player effect = LeanPool.Spawn(GameManager.Instance.basePrefabs.highPassFilterEffect).GetComponent(); + effect.GetFeedbackOfType().Duration = duration; + effect.GetFeedbackOfType().RemapHighPassZero = 10; + effect.GetFeedbackOfType().RemapHighPassOne = peak; + effect.GetFeedbackOfType().ShakeHighPass = intensityCurve; + effect.PlayFeedbacks(); + LeanPool.Despawn(effect.gameObject, duration);*/ + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/HighPassFilterEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs new file mode 100644 index 00000000..c07bd721 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs @@ -0,0 +1,42 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class LowPassFilterEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float bottom; + public AnimationCurve intensityCurve; + #endregion + + #region [初始化] Initialization + public LowPassFilterEffect(float duration, float bottom, AnimationCurve intensityCurve) + { + this.effectTime = 0; + this.duration = duration; + this.bottom = bottom; + this.intensityCurve = intensityCurve; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void Adjust() + { + /*MMF_Player effect = LeanPool.Spawn(GameManager.Instance.basePrefabs.lowPassFilterEffect).GetComponent(); + effect.GetFeedbackOfType().Duration = duration; + effect.GetFeedbackOfType().RemapLowPassZero = 22000; + effect.GetFeedbackOfType().RemapLowPassOne = bottom; + effect.GetFeedbackOfType().ShakeLowPass = intensityCurve; + effect.PlayFeedbacks(); + LeanPool.Despawn(effect.gameObject, duration);*/ + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/MusicEffects/LowPassFilterEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ParticleEmitter.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ParticleEmitter.cs new file mode 100644 index 00000000..273e963c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ParticleEmitter.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Ichni.RhythmGame +{ + + public partial class ParticleEmitter : GameElement, IHaveParticles, IHaveTimeDurationSubmodule, IHaveTransformSubmodule, IHaveColorSubmodule + { + #region [暴露属性字段] Essential Configs + public string themeBundleName; + public string materialName; + + public bool prewarm; + public float playTime; + public float stopTime; + + public ParticleSystemSimulationSpace simulationSpace; + public float density; + public float lifeTime; + public float speed; + public float radius; + + public bool isAutoOrient; + public Vector3 particleRotation; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + private List themeBundleList; + private List materialNameList; + public bool haveBaseColor => true; + public bool haveEmissionColor => true; + #endregion + + #region [子模块接口] Submodules + public ParticleSystem particle { get; set; } + private IHaveParticles particlesContainer => this; + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + public TransformSubmodule transformSubmodule { get; set; } + public ColorSubmodule colorSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static ParticleEmitter GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, GameElement parentElement, string themeBundleName, string materialName, + bool prewarm, float playTime, float stopTime, ParticleSystemSimulationSpace simulationSpace, + float density, float lifeTime, float speed, float radius, + bool isAutoOrient, Vector3 particleRotation) + { + ParticleEmitter particleEmitter = Instantiate(GameManager.Instance.basePrefabs.particleEmitter, parentElement.transform) + .GetComponent(); + particleEmitter.particle = particleEmitter.GetComponent(); + particleEmitter.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + particleEmitter.playTime = playTime; + particleEmitter.stopTime = stopTime; + particleEmitter.themeBundleList = ThemeBundleManager.instance.loadedThemeBundleList.ConvertAll(x => x.themeBundleName); + particleEmitter.materialNameList = new List(); + particleEmitter.themeBundleName = themeBundleName; + particleEmitter.materialName = materialName; + particleEmitter.particlesContainer.SetParticleMaterial(themeBundleName, materialName); + particleEmitter.SetParticleSettings(prewarm, simulationSpace, density, lifeTime, speed, radius, isAutoOrient, particleRotation, false); + return particleEmitter; + } + + public void SetParticleSettings(bool prewarm, ParticleSystemSimulationSpace simulationSpace, + float density, float lifeTime, float speed, float radius, bool isAutoOrient, Vector3 particleRotation, bool mark) + { + //这个Mark没有任何作用,只是为了让解释器把interface中的函数和这个函数区分开。否则会Stackoverflow。 + this.prewarm = prewarm; + this.simulationSpace = simulationSpace; + this.density = density; + this.lifeTime = lifeTime; + this.speed = speed; + this.radius = radius; + this.isAutoOrient = isAutoOrient; + this.particleRotation = particleRotation; + (this as IHaveParticles).SetParticleSettings(prewarm, simulationSpace, density, lifeTime, speed, radius, isAutoOrient, particleRotation); + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + transformSubmodule = new TransformSubmodule(this); + colorSubmodule = new ColorSubmodule(this, Color.white, true, Color.white, 0); + } + #endregion + } + + #region [轮询更新与交互重写] Main Update & Behavior Overrides + public partial class ParticleEmitter + { + private void Update() + { + float songTime = CoreServices.TimeProvider.SongTime; + if (playTime > songTime || stopTime < songTime) + { + particle.Stop(); + } + else + { + if (!particle.isPlaying) + { + particle.Play(); + } + } + } + + public override void Refresh() + { + base.Refresh(); + ParticleSystemRenderer particleSystemRenderer = particle.GetComponent(); + particleSystemRenderer.material.SetColor("_BaseColor", colorSubmodule.currentBaseColor); + if (colorSubmodule.emissionEnabled) + { + particleSystemRenderer.material.EnableKeyword("_EMISSION_ON"); + particleSystemRenderer.material.SetColor("_EmissionColor", colorSubmodule.GetCurrentEmissionColor()); + } + else + { + particleSystemRenderer.material.DisableKeyword("_EMISSION_ON"); + } + } + } + #endregion + + #region [接口能力] Interface + public interface IHaveParticles + { + public ParticleSystem particle { get; set; } + + public virtual void SetParticleMaterial(string themeBundleName, string materialName) + { + Material material = ThemeBundleManager.instance.GetObject(themeBundleName, materialName); + if (material == null) + { + material = ThemeBundleManager.instance.GetObject("basic", "Basic_Track_Default"); + } + + Renderer particleRenderer = particle.GetComponent(); + particleRenderer.material = Object.Instantiate(material); + particleRenderer.InitializeShader(); + } + + public virtual void SetParticleSettings(bool prewarm, ParticleSystemSimulationSpace simulationSpace, float density, float lifeTime, + float speed, float radius, bool isAutoOrient, Vector3 particleRotation) + { + SetPrewarm(prewarm); + SetSimulationSpace(simulationSpace); + SetDensity(density); + SetLifeTime(lifeTime); + SetSpeed(speed); + SetRadius(radius); + SetAlignment(isAutoOrient, particleRotation); + } + + public void SetPrewarm(bool prewarm) + { + var mainModule = particle.main; + mainModule.prewarm = prewarm; + mainModule.maxParticles = 500; + } + + public void SetSimulationSpace(ParticleSystemSimulationSpace simulationSpace) + { + var mainModule = particle.main; + mainModule.simulationSpace = simulationSpace; + } + + public void SetDensity(float density) + { + var emission = particle.emission; + emission.rateOverTime = density; + } + + public void SetLifeTime(float lifeTime) + { + var mainModule = particle.main; + mainModule.startLifetime = lifeTime; + } + + public void SetSpeed(float speed) + { + var mainModule = particle.main; + mainModule.startSpeed = speed; + } + + public void SetRadius(float radius) + { + var shapeModule = particle.shape; + shapeModule.radius = radius; + } + + public void SetAlignment(bool isAutoOrient, Vector3 particleRotation = default) + { + ParticleSystemRenderer particleSystemRenderer = particle.GetComponent(); + var mainModule = particle.main; + if (isAutoOrient) + { + particleSystemRenderer.alignment = ParticleSystemRenderSpace.View; + mainModule.startRotation3D = false; // 禁用3D旋转 + } + else + { + particleSystemRenderer.alignment = ParticleSystemRenderSpace.Local; + mainModule.startRotation3D = true; // 启用3D旋转 + SetParticleRotation(particleRotation); + } + } + + public void SetParticleRotation(Vector3 particleRotation) + { + Vector3 vector3Rotation = particleRotation * Mathf.Deg2Rad; + var mainModule = particle.main; + mainModule.startRotationX = vector3Rotation.x; + mainModule.startRotationY = vector3Rotation.y; + mainModule.startRotationZ = vector3Rotation.z; + } + } + #endregion +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ParticleEmitter.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ParticleEmitter.cs.meta new file mode 100644 index 00000000..ab2acb67 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/ParticleEmitter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0bd980b161a6b6143b2ed58e3184d499 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/PixelateEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/PixelateEffect.cs new file mode 100644 index 00000000..7d8206ca --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/PixelateEffect.cs @@ -0,0 +1,79 @@ +using Ichni.RhythmGame.Beatmap; +using SLSUtilities.Rendering.PostProcessing; // 引入刚才写的 Volume 命名空间 +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class PixelateEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float bottomX; + public float bottomY; + public AnimationCurve intensityCurve; + + private PixelateVolume _pixelateVolume; + #endregion + + #region [初始化] Initialization + public PixelateEffect(float duration, float bottomX, float bottomY, AnimationCurve intensityCurve) + : base(duration) // 激活受控时间分段 + { + this.duration = duration; + this.bottomX = bottomX; + this.bottomY = bottomY; + this.intensityCurve = intensityCurve; + } + + private void PrepareHandle() + { + // 通过 Volume 体系接管控制权 + if (_pixelateVolume == null && PostProcessingManager.GlobalVolume != null) + { + PostProcessingManager.GlobalVolume.profile.TryGet(out _pixelateVolume); + } + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + PrepareHandle(); + if (_pixelateVolume != null) + { + _pixelateVolume.forceActive.value = true; + _pixelateVolume.strengthX.value = Screen.width; + _pixelateVolume.strengthY.value = Screen.height; + } + } + + public override void Execute() + { + if (_pixelateVolume != null) + { + float x = Mathf.Lerp(Screen.width, bottomX, intensityCurve.Evaluate(effectProgressPercent)); + float y = Mathf.Lerp(Screen.height, bottomY, intensityCurve.Evaluate(effectProgressPercent)); + + _pixelateVolume.strengthX.value = x; + _pixelateVolume.strengthY.value = y; + } + } + + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_pixelateVolume != null) + { + _pixelateVolume.forceActive.value = false; + _pixelateVolume.strengthX.value = Screen.width; + _pixelateVolume.strengthY.value = Screen.height; + } + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/PixelateEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/PixelateEffect.cs.meta new file mode 100644 index 00000000..ed4e3c21 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/PixelateEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b8ffa1f96c4b9943ba150ea99ae9005 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/RadialBlurEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/RadialBlurEffect.cs new file mode 100644 index 00000000..446c3321 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/RadialBlurEffect.cs @@ -0,0 +1,84 @@ +using Ichni.RhythmGame.Beatmap; +using SLSUtilities.Rendering.PostProcessing; // 直接调取你的新版 Volume +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class RadialBlurEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public int sampleLevel; + public float position; // 注:虽然新版可能未用此值或代表意义不同,为了兼容旧读写表保持传入 + public float fadeRange; // 注:同上,保留以使 ConvertToBM 存取数据兼容 + public float peakIntensity; + public AnimationCurve intensityCurve; + + private RadialBlur _radialBlurVolume; + #endregion + + #region [初始化] Initialization + public RadialBlurEffect(float duration, int sampleLevel, float position, float fadeRange, float peakIntensity, AnimationCurve intensityCurve) + : base(duration) // 【修改】对接 EffectBase 含时控制 + { + this.duration = duration; + this.sampleLevel = sampleLevel; + this.position = position; + this.fadeRange = fadeRange; + this.peakIntensity = peakIntensity; + this.intensityCurve = intensityCurve; + } + + private void PrepareHandle() + { + if (_radialBlurVolume == null && PostProcessingManager.GlobalVolume != null) + { + // 用管家提取我们在全局体积域中注册的 RadialBlur 面板副本 + PostProcessingManager.GlobalVolume.profile.TryGet(out _radialBlurVolume); + } + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + PrepareHandle(); + if (_radialBlurVolume != null) + { + // 直接把数值映射为你的 Quality Level 枚举 (限制在 0-3 之间防越界) + _radialBlurVolume.qualityLevel.value = RadialBlurQuality.RadialBlur_8Tap_Balance; + //(RadialBlurQuality)Mathf.Clamp(sampleLevel, 0, 3); + + // 将径向中心点归位于正中(未来你可以在这里利用传入的 position 或改造一个 Vector2 支持屏幕任意位置暴气) + _radialBlurVolume.radialCenterX.value = 0.5f; + _radialBlurVolume.radialCenterY.value = 0.5f; + } + } + + public override void Execute() + { + if (_radialBlurVolume != null) + { + // 完美跟随时间推移变换强度 + float intensity = Mathf.Lerp(0, peakIntensity, intensityCurve.Evaluate(effectProgressPercent)); + _radialBlurVolume.blurRadius.value = intensity; + } + } + + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_radialBlurVolume != null) + { + // 新版里只要 radius = 0,IsActive就为 false,自动关闭!极其优雅。 + _radialBlurVolume.blurRadius.value = 0f; + } + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/RadialBlurEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/RadialBlurEffect.cs.meta new file mode 100644 index 00000000..020e74eb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/RadialBlurEffect.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b69ea496540b6f048908f9ab408c41ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/SetIntegerEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/SetIntegerEffect.cs new file mode 100644 index 00000000..dea25870 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/SetIntegerEffect.cs @@ -0,0 +1,46 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class SetIntegerEffect : EffectBase + { + #region [效果参数] Effect Parameters + public string targetVariableName; + + public int targetValue; + + public bool isRandom; + public int minValue; + public int maxValue; + #endregion + + #region [初始化] Initialization + public SetIntegerEffect(string targetVariableName, int targetValue, bool isRandom, int minValue, int maxValue) + { + this.effectTime = 0; + this.targetVariableName = targetVariableName; + this.targetValue = targetValue; + this.isRandom = isRandom; + this.minValue = minValue; + this.maxValue = maxValue; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void Recover() + { + GameManager.Instance.variablesContainer.RevertVariable(targetVariableName); + } + + public override void Adjust() + { + GameManager.Instance.variablesContainer.SetVariable(targetVariableName, isRandom ? Random.Range(minValue, maxValue + 1) : targetValue); + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/SetIntegerEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/SetIntegerEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/SetIntegerEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/SetIntegerEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/TimeEffectsCollection.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/TimeEffectsCollection.cs new file mode 100644 index 00000000..5071bae6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/TimeEffectsCollection.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class TimeEffectsCollection : GameElement, IHaveTransformSubmodule, IHaveEffectSubmodule + { + #region [暴露属性字段] Essential Configs + public float time; //触发效果的时间 + #endregion + + #region [子模块接口] Submodules + public TransformSubmodule transformSubmodule { get; set; } + public EffectSubmodule effectSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static TimeEffectsCollection GenerateElement(string name, Guid guid, List tags, + bool isFirstGenerated, GameElement parentElement, float time) + { + TimeEffectsCollection timeEffectsCollection = Instantiate(GameManager.Instance.basePrefabs.emptyObject).AddComponent(); + timeEffectsCollection.Initialize(name, guid, tags, isFirstGenerated, parentElement); + timeEffectsCollection.time = time; + return timeEffectsCollection; + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + effectSubmodule = new EffectSubmodule(this); + } + #endregion + + #region [轮询更新] Main Update + private void Update() + { + if (!GameManager.Instance.songPlayer.isUpdating) + { + return; + } + + foreach (EffectBase effect in effectSubmodule.effectCollection["Prior"]) + { + effect.UpdateEffect(time); + } + + foreach (EffectBase effect in effectSubmodule.effectCollection["Default"]) + { + effect.UpdateEffect(time); + } + + foreach (EffectBase effect in effectSubmodule.effectCollection["Late"]) + { + effect.UpdateEffect(time); + } + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/TimeEffectsCollection.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/TimeEffectsCollection.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/TimeEffectsCollection.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/TimeEffectsCollection.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/VignetteEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/VignetteEffect.cs new file mode 100644 index 00000000..9fd399d3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/VignetteEffect.cs @@ -0,0 +1,72 @@ +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using UnityEngine.Rendering.Universal; + +namespace Ichni.RhythmGame +{ + public class VignetteEffect : EffectBase + { + #region [效果参数] Effect Parameters + public float duration; + public float peak; + public float smoothness; + public Color color; + public AnimationCurve intensityCurve; + + private Vignette _vignette; + #endregion + + #region [初始化] Initialization + public VignetteEffect(float duration, float peak, float smoothness, Color color, AnimationCurve intensityCurve) + : base(duration) + { + this.duration = duration; + this.peak = peak; + this.smoothness = smoothness; + this.color = color; + this.intensityCurve = intensityCurve; + } + + private void PrepareEffectHandle() + { + if (_vignette == null && PostProcessingManager.GlobalVolume != null) + { + PostProcessingManager.GlobalVolume.profile.TryGet(out _vignette); + } + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void PreExecute() + { + PrepareEffectHandle(); + if (_vignette != null) + { + _vignette.smoothness.value = smoothness; + _vignette.color.value = color; + } + } + + public override void Execute() + { + if (_vignette != null) + { + float currentIntensity = intensityCurve.Evaluate(effectProgressPercent) * peak; + _vignette.intensity.value = currentIntensity; + } + } + + // 注意暗角和其它的不同,它的默认值通常不是0而是类似0.2左右 + public override void Adjust() { ResetEffect(); } + public override void Recover() { ResetEffect(); } + public override void Disrupt() { ResetEffect(); } + + private void ResetEffect() + { + if (_vignette != null) _vignette.intensity.value = 0f; // 你可以修改为符合你游戏默认画风的默认暗角值 + } + #endregion + + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/GeneralEffects/VignetteEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/VignetteEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/GeneralEffects/VignetteEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/GeneralEffects/VignetteEffect.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Notes.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset new file mode 100644 index 00000000..a9327cff --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset @@ -0,0 +1,69 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2d8a87c5b71c49f4997d1b98e9dea18b, type: 3} + m_Name: NoteAudioCollection + m_EditorClassIdentifier: + serializationData: + SerializedFormat: 2 + SerializedBytes: + ReferencedUnityObjects: + - {fileID: 8300000, guid: 659cb798f955c8f4bb530bf89f54e2cc, type: 3} + - {fileID: 8300000, guid: 617b904af4f9aa4479b11e83947d02fd, type: 3} + SerializedBytesString: + Prefab: {fileID: 0} + PrefabModificationsReferencedUnityObjects: [] + PrefabModifications: [] + SerializationNodes: + - Name: audioClips + Entry: 7 + Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[UnityEngine.AudioClip, + UnityEngine.AudioModule]], mscorlib + - Name: comparer + Entry: 7 + Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String, + mscorlib]], mscorlib + - Name: + Entry: 8 + Data: + - Name: + Entry: 12 + Data: 2 + - Name: + Entry: 7 + Data: + - Name: $k + Entry: 1 + Data: DefaultTap + - Name: $v + Entry: 10 + Data: 0 + - Name: + Entry: 8 + Data: + - Name: + Entry: 7 + Data: + - Name: $k + Entry: 1 + Data: DefaultStay + - Name: $v + Entry: 10 + Data: 1 + - Name: + Entry: 8 + Data: + - Name: + Entry: 13 + Data: + - Name: + Entry: 8 + Data: diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.asset.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs new file mode 100644 index 00000000..85a4201d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs @@ -0,0 +1,34 @@ +using System.Collections; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + [CreateAssetMenu(fileName = "NoteAudioCollection", menuName = "Ichni/Note/NoteAudioCollection")] + public class NoteAudioCollection : SerializedScriptableObject + { + public Dictionary audioClips = new Dictionary(); + } + + public static class AudioExtension + { + /// + /// 在全局(不受位置影响)播放一段 AudioClip。 + /// + public static void PlayClipAtPoint2D(AudioClip clip, float volume = 1f) + { + // 新建一个临时的 GameObject + GameObject go = new GameObject("TempAudio2D"); + var src = go.AddComponent(); + src.outputAudioMixerGroup = null; //EditorManager.instance.audioManager.noteSoundFXGroup; + src.clip = clip; + src.volume = volume; + src.spatialBlend = 0f; // 0 = 完全 2D + src.playOnAwake = false; + src.Play(); + // 自动销毁,避免内存泄漏 + Object.Destroy(go, clip.length + 0.1f); + } + } +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioCollection.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs new file mode 100644 index 00000000..6f69c8e5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Ichni.RhythmGame.Beatmap; +using SLSUtilities.WwiseAssistance; + +namespace Ichni.RhythmGame +{ + public partial class NoteAudioSubmodule : SubmoduleBase + { + #region [暴露属性字段] Audio Settings + public List generalJudgeAudioList; + public List perfectAudioList; + public List goodAudioList; + public List badAudioList; + public List missAudioList; + public List holdStartAudioList; + #endregion + + #region [运行时缓存变量] GC-Free Wwise IDs + // --- 高性能无 GC 的 Wwise 预存 ID 数组 --- + // 我们利用基础类型数组而不是 List 再次挤压最后一点遍历性能。 + private uint[] _generalJudgeAudioIds; + private uint[] _perfectAudioIds; + private uint[] _goodAudioIds; + private uint[] _badAudioIds; + private uint[] _missAudioIds; + private uint[] _holdStartAudioIds; + + private NoteBase note => attachedGameElement as NoteBase; + #endregion + + #region [生命周期] Initialization + public NoteAudioSubmodule(NoteBase attachedGameElement, string defaultAudio) : base(attachedGameElement) + { + generalJudgeAudioList = new List() { defaultAudio }; + perfectAudioList = new List(); + goodAudioList = new List(); + badAudioList = new List(); + missAudioList = new List(); + holdStartAudioList = new List(); + + if (!HaveSameSubmodule) + { + this.note.NoteAudioSubmodule = this; + } + + InitializeAudio(); + } + + public NoteAudioSubmodule(NoteBase attachedGameElement, List generalJudgeAudioList, + List perfectAudioList, List goodAudioList, List badAudioList, + List missAudioList, List holdStartAudioList) : base(attachedGameElement) + { + this.generalJudgeAudioList = generalJudgeAudioList ?? new List(); + this.perfectAudioList = perfectAudioList ?? new List(); + this.goodAudioList = goodAudioList ?? new List(); + this.badAudioList = badAudioList ?? new List(); + this.missAudioList = missAudioList ?? new List(); + this.holdStartAudioList = holdStartAudioList ?? new List(); + + if (!HaveSameSubmodule) + { + this.note.NoteAudioSubmodule = this; + } + + InitializeAudio(); + } + + public void InitializeAudio() + { + // 通过反射,只在生成(或池化取出)时进行一次大写转换与常数查询,彻底摒弃字符串带来的运行时哈希。 + _generalJudgeAudioIds = ParseWwiseEventIDs(generalJudgeAudioList); + _perfectAudioIds = ParseWwiseEventIDs(perfectAudioList); + _goodAudioIds = ParseWwiseEventIDs(goodAudioList); + _badAudioIds = ParseWwiseEventIDs(badAudioList); + _missAudioIds = ParseWwiseEventIDs(missAudioList); + _holdStartAudioIds = ParseWwiseEventIDs(holdStartAudioList); + } + + private uint[] ParseWwiseEventIDs(List audioNames) + { + if (audioNames == null || audioNames.Count == 0) return new uint[0]; + + List ids = new List(audioNames.Count); + Type eventsType = typeof(AK.EVENTS); + + for (int i = 0; i < audioNames.Count; i++) + { + if (string.IsNullOrEmpty(audioNames[i])) continue; + + string upperName = audioNames[i].ToUpper(); + FieldInfo field = eventsType.GetField(upperName, BindingFlags.Public | BindingFlags.Static); + + if (field != null) + { + ids.Add((uint)field.GetValue(null)); + } + else + { + UnityEngine.Debug.LogWarning($"[Wwise 注意] 在 AK.EVENTS 中找不到对应的大写事件名称: {upperName}"); + } + } + return ids.ToArray(); + } + #endregion + } + + #region [播放控制] Audio Playback Methods + public partial class NoteAudioSubmodule + { + public void PlayHoldStartAudio() + { + PlayAudioInternal(_holdStartAudioIds); + } + + public void PlayGeneralJudgeAudios() + { + PlayAudioInternal(_generalJudgeAudioIds); + } + + public void PlayNoteJudgeAudios(NoteBase.NoteJudgeType judgeType) + { + switch (judgeType) + { + case NoteBase.NoteJudgeType.Perfect: PlayAudioInternal(_perfectAudioIds); break; + case NoteBase.NoteJudgeType.Good: PlayAudioInternal(_goodAudioIds); break; + case NoteBase.NoteJudgeType.Bad: PlayAudioInternal(_badAudioIds); break; + case NoteBase.NoteJudgeType.Miss: PlayAudioInternal(_missAudioIds); break; + } + } + + private void PlayAudioInternal(uint[] audioIds) + { + if (audioIds == null) return; + for (int i = 0; i < audioIds.Length; i++) + { + // 最高性能播放:绕过 AudioManager 那些空物体创建,直接挂载到 AudioManager 的单例底层进行原生播放。 + AkSoundEngine.PostEvent(audioIds[i], AudioManager.Instance.gameObject); + } + } + } + #endregion + + #region [Beatmap] Executing and Reversing + + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/AudioSubmodule/NoteAudioSubmodule.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs new file mode 100644 index 00000000..32bd25ec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs @@ -0,0 +1,24 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class FullScreenBalancedJudgeUnit : NoteJudgeUnit + { + #region [初始化] Initialization + public FullScreenBalancedJudgeUnit(NoteBase note) : base(note) + { + } + #endregion + + #region [判定逻辑覆盖] Judge Overrides + protected override GameObject GetHintImagePrefab() + { + throw new System.NotImplementedException(); + } + #endregion + + } +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenBalancedJudgeUnit.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs new file mode 100644 index 00000000..216427cb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs @@ -0,0 +1,43 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class FullScreenNearTimeJudgeUnit : NoteJudgeUnit + { + #region [初始化] Initialization + public FullScreenNearTimeJudgeUnit(NoteBase note) : base(note) + { + + } + #endregion + + #region [判定逻辑覆盖] Judge Overrides + protected override GameObject GetHintImagePrefab() => GameManager.Instance.basePrefabs.fullscreenNearTimeHint; + + public override void UpdateJudge() + { + Vector2 noteScreenPosition = note.noteScreenPosition; + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, noteScreenPosition, null, out Vector2 uiPosition)) + { + judgeHintImage.anchoredPosition = uiPosition; + } + } + + public override bool CheckJudgeAvailability(InputUnit inputUnit) + { + if (inputUnit is InputUnitSwipe swipe && note is Flick flick) + { + return flick.CheckSwipeDirection(swipe); + } + + return true; + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/FullScreenNearTimeJudgeUnit.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs new file mode 100644 index 00000000..7bdee7e5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs @@ -0,0 +1,60 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class NoteJudgeSubmodule : SubmoduleBase + { + #region [暴露属性字段] Judge Units + public List judgeUnitList; + private NoteBase note => attachedGameElement as NoteBase; + #endregion + + #region [生命周期] Initialization + public NoteJudgeSubmodule(NoteBase attachedGameElement) : base(attachedGameElement) + { + judgeUnitList = new List(); + + if (!HaveSameSubmodule) + { + this.note.NoteJudgeSubmodule = this; + } + } + + public NoteJudgeSubmodule(NoteBase attachedGameElement, List judgeUnitList_BM) : base(attachedGameElement) + { + judgeUnitList = new List(); + + foreach (NoteJudgeUnit_BM judgeUnitBM in judgeUnitList_BM) + { + judgeUnitList.Add(judgeUnitBM.ConvertToGameType(attachedGameElement)); + } + + if (!HaveSameSubmodule) + { + this.note.NoteJudgeSubmodule = this; + } + } + #endregion + } + + #region [扩展工厂] Collection Factory + public partial class NoteJudgeSubmodule + { + private static Dictionary JudgeUnitCollection(NoteBase note) => + new Dictionary() + { + { "TouchArea", new TouchAreaJudgeUnit(note, 1000) }, + { "FullScreenNearTime", new FullScreenNearTimeJudgeUnit(note) }, + { "TriggerConnect", new TriggerConnectJudgeUnit(note, null) } + }; + } + #endregion + + #region [Beatmap] Executing and Reversing + + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs new file mode 100644 index 00000000..6031367a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Ichni.RhythmGame +{ + public abstract class NoteJudgeUnit : IBaseElement + { + #region [核心属性] Properties + public NoteBase note; + public bool isShowingJudge; + + public RectTransform judgeHintImage; + public BaseElement_BM matchedBM { get; set; } + #endregion + + #region [子类重写] Virtual & Abstract Methods + protected abstract GameObject GetHintImagePrefab(); + + protected NoteJudgeUnit(NoteBase note) + { + this.note = note; + } + + public virtual void SetShowingJudge(bool isShowing) + { + isShowingJudge = isShowing; + if (isShowing) + { + judgeHintImage = Object.Instantiate(GetHintImagePrefab(), + GameManager.Instance.judgeHintCanvas.transform).GetComponent(); + } + else if (judgeHintImage is not null) + { + Object.Destroy(judgeHintImage.gameObject); + } + } + + public virtual void UpdateJudge() + { + + } + + public virtual bool CheckJudgeAvailability(InputUnit inputUnit) + { + throw new NotImplementedException(); + } + #endregion + + #region [转换与存储] Data Mapping + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/NoteJudgeUnit.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs new file mode 100644 index 00000000..3ad01d86 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs @@ -0,0 +1,66 @@ +using System.Collections; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TouchAreaJudgeUnit : NoteJudgeUnit + { + #region [暴露属性字段] Properties + public float ellipseXMultiplier = 1.25f; + public float ellipseYMultiplier = 1.0f; + public float areaRadius; + + private static float CurrentScreenRatio => Screen.height / 1080f; // 以1080p为基准的屏幕缩放比例 + #endregion + + #region [初始化] Initialization + public TouchAreaJudgeUnit(NoteBase note, float areaRadius) : base(note) + { + this.areaRadius = areaRadius; + } + #endregion + + #region [判定逻辑覆盖] Judge Overrides + protected override GameObject GetHintImagePrefab() + { + return GameManager.Instance.basePrefabs.areaHint; + } + + public override void UpdateJudge() + { + if ((note is not Hold && note.isFirstJudged)||(note is Hold && note.isFinalJudged)) return; + Vector2 noteScreenPosition = note.noteScreenPosition; + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, noteScreenPosition, null, out Vector2 uiPosition)) + { + judgeHintImage.anchoredPosition = uiPosition; + judgeHintImage.sizeDelta = new Vector2(areaRadius * 2 * ellipseXMultiplier, areaRadius * 2 * ellipseYMultiplier) * CurrentScreenRatio; + } + } + + public override bool CheckJudgeAvailability(InputUnit inputUnit) + { + Vector2 inputScreenPosition = inputUnit.inputPosition; + Vector2 noteScreenPosition = note.GetScreenPosition(); + float scaledBaseRadius = areaRadius * CurrentScreenRatio; + + float dx = Mathf.Abs(inputScreenPosition.x - noteScreenPosition.x) / ellipseXMultiplier; + float dy = Mathf.Abs(inputScreenPosition.y - noteScreenPosition.y) / ellipseYMultiplier; + + if ((dx * dx) + (dy * dy) <= (scaledBaseRadius * scaledBaseRadius)) + { + if (inputUnit is InputUnitSwipe swipe && note is Flick flick) + { + return flick.CheckSwipeDirection(swipe); + } + return true; + } + + return false; + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TouchAreaJudgeUnit.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs new file mode 100644 index 00000000..8bbb1fb3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs @@ -0,0 +1,50 @@ +using System; +using Ichni.RhythmGame.Beatmap; +using TMPro; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TriggerConnectJudgeUnit : NoteJudgeUnit + { + #region [暴露属性字段] Properties + public GameElement connectedJudgeTrigger; + #endregion + + #region [初始化] Initialization + public TriggerConnectJudgeUnit(NoteBase note, GameElement judgeTrigger) : base(note) + { + this.connectedJudgeTrigger = judgeTrigger; + } + #endregion + + #region [判定逻辑覆盖] Judge Overrides + protected override GameObject GetHintImagePrefab() => GameManager.Instance.basePrefabs.triggerHint; + + public override void SetShowingJudge(bool isShowing) + { + if(connectedJudgeTrigger == null) return; + + base.SetShowingJudge(isShowing); + + if (judgeHintImage != null) + { + judgeHintImage.GetComponent().text = connectedJudgeTrigger.elementName; + } + } + + public override void UpdateJudge() + { + if(note.isFirstJudged || connectedJudgeTrigger == null) return; + Vector2 noteScreenPosition = note.noteScreenPosition; + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, noteScreenPosition, null, out Vector2 uiPosition)) + { + judgeHintImage.anchoredPosition = uiPosition; + } + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/JudgeSubmodules/TriggerConnectJudgeUnit.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteBadEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteBadEffect.cs new file mode 100644 index 00000000..ed951552 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteBadEffect.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteBadEffect : NoteEffectBase + { + + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteBadEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteBadEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteBadEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteBadEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteEffectBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteEffectBase.cs new file mode 100644 index 00000000..ac4aef8c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteEffectBase.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteEffectBase : EffectBase + { + #region [基础关联] Core References + public NoteBase note; + public NoteVisualBase noteVisual; + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteEffectBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteEffectBase.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteEffectBase.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteEffectBase.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs new file mode 100644 index 00000000..860418d0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteGeneralJudgeEffect : NoteEffectBase + { + + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGeneralJudgeEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs new file mode 100644 index 00000000..cff90d47 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteGenerateEffect : NoteEffectBase + { + #region [效果参数] Effect Parameters + public float generateTime; + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + protected override EffectState CheckEffectState(float triggerTime) + { + float songTime = CoreServices.TimeProvider.SongTime; + triggerTime -= generateTime; + + if (songTime < triggerTime) + { + return EffectState.Before; + } + + if (songTime >= triggerTime && + songTime <= triggerTime + effectTime) + { + return EffectState.Middle; + } + + if (songTime > triggerTime + effectTime) + { + return EffectState.After; + } + + return EffectState.Error; + } + + public override void UpdateEffect(float judgeTime) + { + EffectState state = CheckEffectState(judgeTime); + float songTime = CoreServices.TimeProvider.SongTime; + judgeTime -= generateTime; + + if (state == EffectState.Before && nowEffectState != EffectState.Before) + { + nowEffectState = EffectState.Before; + effectProgressPercent = 0; + Recover(); + } + else if (state == EffectState.Middle) + { + if (nowEffectState == EffectState.Before) + { + PreExecute(); + } + + nowEffectState = EffectState.Middle; + effectProgressPercent = (songTime - judgeTime) / effectTime; + Execute(); + } + else if (state == EffectState.After && nowEffectState != EffectState.After) + { + nowEffectState = EffectState.After; + effectProgressPercent = 1; + Adjust(); + } + } + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGenerateEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGoodEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGoodEffect.cs new file mode 100644 index 00000000..467a4775 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGoodEffect.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteGoodEffect : NoteEffectBase + { + + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteGoodEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGoodEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteGoodEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteGoodEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs new file mode 100644 index 00000000..d7d21948 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs @@ -0,0 +1,50 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteHoldingEffect : NoteEffectBase + { + #region [子类特化数据] Hold Specific Data + public float GetHoldingTime() + { + return (note as Hold).holdEndTime - note.exactJudgeTime; + } + #endregion + + #region [效果逻辑覆盖] Effect Pattern Overrides + public override void UpdateEffect(float judgeTime) + { + EffectState state = CheckEffectState(judgeTime); + float songTime = CoreServices.TimeProvider.SongTime; + + if (state == EffectState.Before && nowEffectState != EffectState.Before) + { + nowEffectState = EffectState.Before; + effectProgressPercent = 0; + Recover(); + } + else if (state == EffectState.Middle && (note as Hold).preJudgeType != NoteBase.NoteJudgeType.NotJudged) + { + if (nowEffectState == EffectState.Before) + { + PreExecute(); + } + + nowEffectState = EffectState.Middle; + effectProgressPercent = (songTime - judgeTime) / effectTime; + Execute(); + } + else if (state == EffectState.After && nowEffectState != EffectState.After) + { + nowEffectState = EffectState.After; + effectProgressPercent = 1; + Adjust(); + } + } + #endregion + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteHoldingEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteMissEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteMissEffect.cs new file mode 100644 index 00000000..e0e1ec6b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteMissEffect.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NoteMissEffect : NoteEffectBase + { + + } + + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteMissEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteMissEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NoteMissEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NoteMissEffect.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NotePerfectEffect.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NotePerfectEffect.cs new file mode 100644 index 00000000..d5946567 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NotePerfectEffect.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class NotePerfectEffect : NoteEffectBase + { + //public override bool IsPost => true; + } + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NotePerfectEffect.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NotePerfectEffect.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteEffects/NotePerfectEffect.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteEffects/NotePerfectEffect.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Flick.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Flick.cs new file mode 100644 index 00000000..017a456e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Flick.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Unity.VisualScripting; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class Flick : NoteBase + { + #region [特有属性字段] Special Fields + public NoteJudgeType preJudgeType; + public List availableFlickDirections; + public float flickBuffer = 0.5f; + #endregion + + #region [生成与初始化] Generation & Initialization + public static Flick GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + GameElement parentElement, float exactJudgeTime, List directions) + { + Flick flick = LeanPool.Spawn(GameManager.Instance.basePrefabs.flickNote, parentElement.transform).GetComponent(); + + flick.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + flick.exactJudgeTime = exactJudgeTime; + flick.judgeIntervals = NoteJudgeIntervals.FlickDefault; + + // 注意这里暂未赋值传入的 directions, 请根据情况补充。 + if (parentElement.TryGetComponent(out Track track) && track.trackTimeSubmodule != null) + { + flick.track = track; + flick.trackPositioner.enabled = true; + flick.trackPositioner.spline = track.trackPathSubmodule.path; + flick.isOnTrack = true; + flick.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); + } + else + { + flick.track = null; + flick.isOnTrack = false; + } + return flick; + } + + public override void SetDefaultSubmodules() + { + base.SetDefaultSubmodules(); + NoteAudioSubmodule = new NoteAudioSubmodule(this, "DefaultStay"); + } + #endregion + + #region [主循环阶段] Main Update + public override bool ManualUpdate(float currentSongTime) + { + if (!isFirstJudged && !isDuringJudging && + currentSongTime >= exactJudgeTime + judgeIntervals.beforeMiss.intervalStart && + !GameManager.Instance.noteJudgeManager.checkingFlickList.Contains(this)) + { + isDuringJudging = true; + GameManager.Instance.noteJudgeManager.checkingFlickList.Add(this); + } + + ExecuteFinalJudge(currentSongTime); + return base.ManualUpdate(currentSongTime); + } + + protected override void RemoveFromCheckingList() + { + if (GameManager.Instance.noteJudgeManager.checkingFlickList.Contains(this)) + GameManager.Instance.noteJudgeManager.checkingFlickList.Remove(this); + } + #endregion + + #region [核心判定逻辑] Judgement Logic + public bool CheckJudgeAvailability(InputUnitSwipe inputUnitSwipe) + { + foreach (var judgeUnit in NoteJudgeSubmodule.judgeUnitList) + { + if (!judgeUnit.CheckJudgeAvailability(inputUnitSwipe)) return false; + } + return true; + } + + public bool CheckSwipeDirection(InputUnitSwipe inputUnitSwipe) + { + if (inputUnitSwipe.isGeneric) return true; + + Camera gameCamera = GameManager.Instance.cameraManager.gameCamera.cam; + foreach (Vector2 localDir in availableFlickDirections) + { + Vector3 worldDirection = noteVisual.transform.TransformDirection(localDir.normalized); + Vector3 noteOriginWorld = noteVisual.transform.position; + Vector3 noteTargetWorld = noteOriginWorld + worldDirection; + + Vector3 screenOrigin = gameCamera.WorldToScreenPoint(noteOriginWorld); + Vector3 screenTarget = gameCamera.WorldToScreenPoint(noteTargetWorld); + Vector2 noteScreenDirection = new Vector2(screenTarget.x - screenOrigin.x, screenTarget.y - screenOrigin.y).normalized; + + if (noteScreenDirection.sqrMagnitude < 0.01f) continue; + + float dotProduct = Vector2.Dot(inputUnitSwipe.swipeDirection, noteScreenDirection); + if (dotProduct >= flickBuffer) return true; + } + return false; + } + + public override void ExecuteStartJudge(float triggerTime) + { + float timeDifference = triggerTime - exactJudgeTime; + + RemoveFromCheckingList(); + preJudgeType = GetStartJudgeType(timeDifference); + isFirstJudged = true; + } + + public void ExecuteTapJudge(float triggerTime) + { + RemoveFromCheckingList(); + NoteJudgeType startJudgeType = GetStartJudgeType(triggerTime - exactJudgeTime); + + switch (startJudgeType) + { + case NoteJudgeType.Perfect: Perfect(triggerTime); GameManager.Instance.playingRecorder.resultData.Add(0); break; + case NoteJudgeType.Good: Good(triggerTime); break; + case NoteJudgeType.Bad: Bad(triggerTime); break; + case NoteJudgeType.Miss: Miss(triggerTime); break; + } + + if (startJudgeType != NoteJudgeType.Miss) NoteAudioSubmodule.PlayGeneralJudgeAudios(); + + isFirstJudged = true; + isFinalJudged = true; + } + + public void ExecuteFinalJudge(float triggerTime) + { + if (isFirstJudged && !isFinalJudged && preJudgeType != NoteJudgeType.NotJudged && triggerTime >= exactJudgeTime) + { + switch (preJudgeType) + { + case NoteJudgeType.Perfect: Perfect(triggerTime); GameManager.Instance.playingRecorder.resultData.Add(0); break; + case NoteJudgeType.Good: Good(triggerTime); break; + case NoteJudgeType.Bad: Bad(triggerTime); break; + case NoteJudgeType.Miss: Miss(triggerTime); break; + } + + if (preJudgeType != NoteJudgeType.Miss) NoteAudioSubmodule.PlayGeneralJudgeAudios(); + isFinalJudged = true; + } + } + #endregion + + #region [谱面功能] Beatmap Method + #endregion + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Flick.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Flick.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Flick.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Flick.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Hold.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Hold.cs new file mode 100644 index 00000000..42f4530a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Hold.cs @@ -0,0 +1,305 @@ +using System; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Unity.VisualScripting; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class Hold : NoteBase + { + #region [特有属性字段] Special Fields + public float holdEndTime; + public float holdingTime; + public bool isHolding; + public float holdingBufferTime; + public float bufferTimer; + + public float preTimeDifference; + public NoteJudgeType preJudgeType; + public float postTimeDifference; + public NoteJudgeType postJudgeType; + + protected List startHoldEffects; + protected List holdingEffects; + #endregion + + #region [生成与初始化] Generation & Initialization + public static Hold GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + GameElement parentElement, float exactJudgeTime, float holdEndTime) + { + Hold hold = LeanPool.Spawn(GameManager.Instance.basePrefabs.holdNote, parentElement.transform).GetComponent(); + + hold.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + hold.exactJudgeTime = exactJudgeTime; + hold.holdEndTime = holdEndTime; + hold.holdingTime = 0; + hold.holdingBufferTime = 0.04f; + hold.judgeIntervals = NoteJudgeIntervals.HoldDefault; + hold.preJudgeType = NoteJudgeType.NotJudged; + hold.postJudgeType = NoteJudgeType.NotJudged; + + if (parentElement.TryGetComponent(out Track track) && track.trackTimeSubmodule != null) + { + hold.track = track; + hold.trackPositioner.enabled = true; + hold.trackPositioner.spline = track.trackPathSubmodule.path; + hold.trackPositioner.updateMethod = SplineUser.UpdateMethod.LateUpdate; + hold.isOnTrack = true; + hold.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); + } + else + { + hold.track = null; + hold.isOnTrack = false; + } + return hold; + } + + public override void AfterInitialize() + { + base.AfterInitialize(); + startHoldEffects = GetEffectListSafe("StartHold"); + holdingEffects = GetEffectListSafe("Holding"); + } + + public override void SetDefaultSubmodules() + { + base.SetDefaultSubmodules(); + NoteAudioSubmodule = new NoteAudioSubmodule(this, "DefaultTap"); // 注意以前是你写死的 DefaultTap,依据需求核对 + } + #endregion + + #region [主循环阶段] Main Update + public override bool ManualUpdate(float currentSongTime) + { + if (!base.ManualUpdate(currentSongTime)) return false; + + if (!isFirstJudged && !isDuringJudging && + currentSongTime >= exactJudgeTime + judgeIntervals.beforeMiss.intervalStart && + !GameManager.Instance.noteJudgeManager.checkingHoldList.Contains(this)) + { + isDuringJudging = true; + GameManager.Instance.noteJudgeManager.checkingHoldList.Add(this); + } + + if (!GameManager.Instance.songPlayer.isUpdating || isFinalJudged) return true; + + if (SettingsManager.instance.gameSettings.autoPlay && isFirstJudged) ExecuteProcessJudge(); + + if (isHolding) + { + holdingTime = currentSongTime - exactJudgeTime; + bufferTimer = holdingBufferTime; + } + else if (isFirstJudged) + { + bufferTimer -= Time.deltaTime; + } + + if (isOnTrack) UpdateNoteInTrack(currentSongTime); + + if (isDuringJudging) noteScreenPosition = GetScreenPosition(); + + SetJudgeArea(); + + if (!isFirstJudged && exactJudgeTime < currentSongTime) SlowOffsetAfterExactJudgeTime(); + + UpdateHoldEffects(); + + if (SettingsManager.instance.gameSettings.autoPlay && !isFirstJudged && currentSongTime >= exactJudgeTime) + { + ExecuteStartJudge(currentSongTime); + } + + CheckHoldLifeCycle(currentSongTime); + + return true; + } + + private void CheckHoldLifeCycle(float currentSongTime) + { + if (isFirstJudged && currentSongTime > holdEndTime) + { + isHolding = false; + isFinalJudged = true; + ExecuteFinalJudge(currentSongTime); + RemoveFromCheckingList(); + } + else if (isFirstJudged && bufferTimer < 0f) + { + isHolding = false; + isFinalJudged = true; + DisruptHoldEffects(); + ExecuteFinalJudge(currentSongTime); + RemoveFromCheckingList(); + } + else if (!isFirstJudged && currentSongTime > exactJudgeTime + judgeIntervals.afterMiss) + { + isFirstJudged = true; + isFinalJudged = true; + DisruptHoldEffects(); + Miss(exactJudgeTime + judgeIntervals.afterMiss); + RemoveFromCheckingList(); + } + } + + protected override void RemoveFromCheckingList() + { + if (GameManager.Instance.noteJudgeManager.checkingHoldList.Contains(this)) + GameManager.Instance.noteJudgeManager.checkingHoldList.Remove(this); + } + #endregion + + #region [核心判定逻辑] Judgement Logic + public bool CheckJudgeAvailability(InputUnitTap inputUnitTap) + { + if (isFirstJudged) return false; + foreach (var judgeUnit in NoteJudgeSubmodule.judgeUnitList) + { + if (!judgeUnit.CheckJudgeAvailability(inputUnitTap)) return false; + } + return true; + } + + public bool CheckJudgeAvailability(InputUnitTouch inputUnitTouch) + { + if (!isFirstJudged) return false; + foreach (var judgeUnit in NoteJudgeSubmodule.judgeUnitList) + { + if (!judgeUnit.CheckJudgeAvailability(inputUnitTouch)) return false; + } + return true; + } + + public override void ExecuteStartJudge(float triggerTime) + { + preTimeDifference = triggerTime - exactJudgeTime; + preJudgeType = GetStartJudgeType(preTimeDifference); + + isFirstJudged = true; + isHolding = true; + NoteAudioSubmodule.PlayHoldStartAudio(); + } + + public void ExecuteProcessJudge() + { + isHolding = true; + } + + public void ExecuteFinalJudge(float triggerTime) + { + if (startHoldEffects != null) + { + for (int i = 0; i < startHoldEffects.Count; i++) + { + if (startHoldEffects[i].nowEffectState == EffectBase.EffectState.Middle) + startHoldEffects[i].Adjust(); + } + } + if (holdingEffects != null) + { + for (int i = 0; i < holdingEffects.Count; i++) holdingEffects[i].Adjust(); + } + + postTimeDifference = holdEndTime - triggerTime; + + if (postTimeDifference <= 0.1f) postJudgeType = NoteJudgeType.Perfect; + else if (postTimeDifference <= 0.125f) postJudgeType = NoteJudgeType.Good; + else postJudgeType = NoteJudgeType.Bad; + + NoteJudgeType finalJudge = GetLowerType(preJudgeType, postJudgeType); + float finalTimeDifference = Mathf.Min(preTimeDifference, postTimeDifference); + GameManager.Instance.playingRecorder.resultData.Add(finalTimeDifference); + + switch (finalJudge) + { + case NoteJudgeType.Perfect: Perfect(triggerTime); break; + case NoteJudgeType.Good: Good(triggerTime); break; + case NoteJudgeType.Bad: Bad(triggerTime); break; + case NoteJudgeType.Miss: Miss(triggerTime); break; + } + + if (finalJudge != NoteJudgeType.Miss) NoteAudioSubmodule.PlayGeneralJudgeAudios(); + } + + protected override void SetJudgeArea() + { + if (!SettingsManager.instance.gameSettings.debugMode || NoteJudgeSubmodule == null) return; + + if (isDuringJudging && !isFirstJudged) + { + foreach (NoteJudgeUnit unit in NoteJudgeSubmodule.judgeUnitList) + { + if (!unit.isShowingJudge) unit.SetShowingJudge(true); + } + } + foreach (NoteJudgeUnit unit in NoteJudgeSubmodule.judgeUnitList) + { + if (unit.isShowingJudge) unit.UpdateJudge(); + } + + if (!isDuringJudging && CoreServices.TimeProvider.SongTime > holdEndTime - 2 * Time.deltaTime) + { + foreach (NoteJudgeUnit unit in NoteJudgeSubmodule.judgeUnitList) + { + if (unit.isShowingJudge) unit.SetShowingJudge(false); + } + } + } + #endregion + + #region [特效与物理偏移] Special Hold Logic + public override void UpdateNoteInMovableTrack(float songTime) + { + if (!isHolding && !isFinalJudged) base.UpdateNoteInMovableTrack(songTime); + if (noteVisual is INoteVisualHold noteVisualHold) noteVisualHold.UpdateHoldInMovableTrack(); + } + + public override void UpdateNoteInStaticTrack(float songTime) + { + base.UpdateNoteInStaticTrack(songTime); + if (noteVisual is INoteVisualHold noteVisualHold) noteVisualHold.UpdateHoldInStaticTrack(); + } + + public override void SetPerfectPosition() + { + if (isOnTrack && track.trackTimeSubmodule is TrackTimeSubmoduleMovable movable) + { + holdingTime = holdEndTime - exactJudgeTime; + (noteVisual as INoteVisualHold)?.UpdateHoldInMovableTrack(); + } + } + + protected override void SlowOffsetAfterExactJudgeTime() + { + if (isOnTrack && track.trackTimeSubmodule is TrackTimeSubmoduleMovable movable) + { + holdingTime = CoreServices.TimeProvider.SongTime - exactJudgeTime; + (noteVisual as INoteVisualHold)?.UpdateHoldInMovableTrack(); + } + } + + private void UpdateHoldEffects() + { + if (startHoldEffects != null) + for (int i = 0; i < startHoldEffects.Count; i++) startHoldEffects[i].UpdateEffect(exactJudgeTime); + + if (holdingEffects != null) + for (int i = 0; i < holdingEffects.Count; i++) holdingEffects[i].UpdateEffect(exactJudgeTime); + } + + private void DisruptHoldEffects() + { + if (startHoldEffects != null) + for (int i = 0; i < startHoldEffects.Count; i++) startHoldEffects[i].Disrupt(); + } + #endregion + + #region [谱面功能] Beatmap Method + #endregion + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Hold.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Hold.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Hold.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Hold.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/NoteBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/NoteBase.cs new file mode 100644 index 00000000..06791b2f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/NoteBase.cs @@ -0,0 +1,483 @@ +using System; +using System.Collections.Generic; +using Dreamteck.Splines; +using Sirenix.OdinInspector; +using TMPro; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract partial class NoteBase : GameElement, IHaveTimeDurationSubmodule, IComparable + { + #region [暴露属性字段] Basic & Info + [Title("Basic Info")] + public float exactJudgeTime; + public NoteJudgeIntervals judgeIntervals; + + [Title("Track Info")] + public bool isOnTrack; + public Track track; + public SplinePositioner trackPositioner; + + [Title("NoteVisual")] + public NoteVisualBase noteVisual; + + [Title("Submodules")] + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + public NoteJudgeSubmodule NoteJudgeSubmodule { get; set; } + public NoteAudioSubmodule NoteAudioSubmodule { get; set; } + + [Title("In-Game Info")] + public bool isDuringJudging; + public Vector2 noteScreenPosition; + public Vector2 perfectNoteScreenPosition; + public bool isFirstJudged; + public bool isFinalJudged; + public override int HierarchyPriority => -10; + + [Title("Debug")] + public TMP_Text judgeRankHint; + #endregion + + #region [运行时缓存变量] GC-Free 核心状态层 + protected List generateEffects; + protected List generalJudgeEffects; + protected List perfectEffects; + protected List goodEffects; + protected List badEffects; + protected List missEffects; + protected List afterJudgeEffects; + + protected float judgedTriggerTime; + protected NoteJudgeType judgedType; + protected bool isJudgedAndDestroying; + protected float destroyTimer; + + // 用于记录是否离开屏幕的最后一帧有效坐标 + protected Vector2 lastValidScreenPosition = -Vector2.one; + #endregion + + #region [生命周期] Initialization + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + NoteJudgeSubmodule = new NoteJudgeSubmodule(this); + } + + public override void AfterInitialize() + { + // 安全读取字典防报错 + generateEffects = GetEffectListSafe("Generate"); + generalJudgeEffects = GetEffectListSafe("GeneralJudge"); + perfectEffects = GetEffectListSafe("Perfect"); + goodEffects = GetEffectListSafe("Good"); + badEffects = GetEffectListSafe("Bad"); + missEffects = GetEffectListSafe("Miss"); + afterJudgeEffects = GetEffectListSafe("AfterJudge"); + + perfectNoteScreenPosition = -Vector2.one; + lastValidScreenPosition = -Vector2.one; // 初始化 + + float beyondTime = 0f; + + // 【Review修改】:使用无 GC 预存列表操作替代 String 调用 + if (generateEffects != null) + { + for (int i = 0; i < generateEffects.Count; i++) + { + EffectBase effectBase = generateEffects[i]; + if (effectBase is NoteGenerateEffect ge) + { + ge.Recover(); + beyondTime = Mathf.Max(beyondTime, ge.generateTime); + } + else + { + effectBase.Recover(); + } + } + } + + if (exactJudgeTime - beyondTime - 0.5f > -GameManager.Instance.songInformation.delay) + { + gameObject.SetActive(false); + GameManager.Instance.noteManager.RegisterNote(this, exactJudgeTime - beyondTime - 0.5f); + } + } + #endregion + + #region [轮询更新] Main Update & Visual Calculate + public virtual bool ManualUpdate(float currentSongTime) + { + // 若被 Judge 判定击中进入到特效摧毁倒计时 + if (isJudgedAndDestroying) + { + UpdatePostJudgeEffects(); + + destroyTimer -= Time.deltaTime; + if (destroyTimer <= 0) + { + isJudgedAndDestroying = false; + + // 【池化核心】调用 NoteManager 向 LeanPool 归还自己,向外抛出 false 要求将自己从更新名单剔除 + GameManager.Instance.noteManager.DespawnNote(this); + return false; + } + return true; + } + + if (isFinalJudged) return true; + + // 1. 轨迹更新 + UpdateNoteInTrack(currentSongTime); + // 2. 屏幕坐标系计算缓存 + if (perfectNoteScreenPosition == -Vector2.one) + { + if (isDuringJudging) + { + noteScreenPosition = GetScreenPosition(); + } + if (exactJudgeTime <= currentSongTime) // 代替 GameManager.Instance.songTime + { + perfectNoteScreenPosition = noteScreenPosition; + } + } + SetJudgeArea(); + if (generateEffects != null) + { + for (int i = 0; i < generateEffects.Count; i++) + { + generateEffects[i].UpdateEffect(exactJudgeTime); + } + } + // 自然 Miss 判定 + if (!isFirstJudged && currentSongTime > exactJudgeTime + judgeIntervals.afterMiss) + { + Miss(exactJudgeTime + judgeIntervals.afterMiss); + GameManager.Instance.playingRecorder.resultData.Add(judgeIntervals.afterMiss); + isFirstJudged = true; + isFinalJudged = true; + RemoveFromCheckingList(); + } + + // AutoPlay + if (SettingsManager.instance.gameSettings.autoPlay && !isFirstJudged && currentSongTime >= exactJudgeTime) + { + ExecuteStartJudge(currentSongTime); + } + return true; // 仍然存活 + } + + public Vector2 GetScreenPosition() + { + Camera cam = GameManager.Instance.cameraManager.gameCamera.cam; + Vector3 screenPoint = cam.WorldToScreenPoint(noteVisual.noteVisualPosition); + + // 判断是否在屏幕内并且在相机前方 (z>0) + bool isInsideScreen = screenPoint.z > 0 && + screenPoint.x >= 0 && screenPoint.x <= Screen.width && + screenPoint.y >= 0 && screenPoint.y <= Screen.height; + + if (isInsideScreen) + { + // 如果在视野中,则缓存坐标并返回 + lastValidScreenPosition = new Vector2(screenPoint.x, screenPoint.y); + return lastValidScreenPosition; + } + else + { + // 如果已经离开屏幕,但是曾经存在缓存,就返回离开前最后一帧坐标 + if (lastValidScreenPosition != -Vector2.one) + { + return lastValidScreenPosition; + } + + // 极端情况(生成时直接在屏幕外),直接返回换算的外界坐标兜底 + return new Vector2(screenPoint.x, screenPoint.y); + } + } + + protected virtual void SetJudgeArea() + { + if (!SettingsManager.instance.gameSettings.debugMode || NoteJudgeSubmodule?.judgeUnitList == null) return; + + if (isDuringJudging && !isFirstJudged) + { + for (int i = 0; i < NoteJudgeSubmodule.judgeUnitList.Count; i++) + { + var unit = NoteJudgeSubmodule.judgeUnitList[i]; + if (!unit.isShowingJudge) unit.SetShowingJudge(true); + } + } + + for (int i = 0; i < NoteJudgeSubmodule.judgeUnitList.Count; i++) + { + var unit = NoteJudgeSubmodule.judgeUnitList[i]; + if (unit.isShowingJudge) unit.UpdateJudge(); + } + + if (!isDuringJudging && isFinalJudged) + { + for (int i = 0; i < NoteJudgeSubmodule.judgeUnitList.Count; i++) + { + var unit = NoteJudgeSubmodule.judgeUnitList[i]; + if (unit.isShowingJudge) unit.SetShowingJudge(false); + } + } + } + #endregion + + #region [轨道运动] Track Logic + public void UpdateNoteInTrack(float songTime) + { + if (!isOnTrack || track.trackTimeSubmodule == null) return; + + if (track.trackTimeSubmodule is TrackTimeSubmoduleMovable) + { + UpdateNoteInMovableTrack(songTime); + } + else if (track.trackTimeSubmodule is TrackTimeSubmoduleStatic) + { + UpdateNoteInStaticTrack(songTime); + } + } + + public virtual void UpdateNoteInMovableTrack(float songTime) + { + TrackTimeSubmoduleMovable trackTimeSubmoduleMovable = track.trackTimeSubmodule as TrackTimeSubmoduleMovable; + trackPositioner.SetPercent(trackTimeSubmoduleMovable.GetTrackPercent(exactJudgeTime)); + } + + public virtual void UpdateNoteInStaticTrack(float songTime) + { + TrackTimeSubmoduleStatic trackTimeSubmoduleStatic = track.trackTimeSubmodule as TrackTimeSubmoduleStatic; + + float startMove = exactJudgeTime - trackTimeSubmoduleStatic.trackTotalTime; + float percent = AnimationCurveEvaluator.Evaluate(trackTimeSubmoduleStatic.animationCurveType, (songTime - startMove) / trackTimeSubmoduleStatic.trackTotalTime); + + percent = Mathf.Clamp01(percent); // 替代 Max 和 Min 系统调用 + trackPositioner.SetPercent(1 - percent); + } + + public virtual void SetPerfectPosition() + { + if (isOnTrack && track.trackTimeSubmodule is TrackTimeSubmoduleMovable movable) + { + float notePercent = movable.GetTrackPercent(CoreServices.TimeProvider.SongTime); + trackPositioner.SetPercent(notePercent); + } + } + + protected virtual void SlowOffsetAfterExactJudgeTime() + { + if (isOnTrack && track.trackTimeSubmodule is TrackTimeSubmoduleMovable movable) + { + float slowedTime = (CoreServices.TimeProvider.SongTime - exactJudgeTime) * 0.8f; + float notePercent = movable.GetTrackPercent(exactJudgeTime + slowedTime); + trackPositioner.SetPercent(notePercent); + } + } + #endregion + + #region [打击判定分发] Judge Dispatcher + public virtual void Perfect(float triggerTime) { ExecuteJudge(NoteJudgeType.Perfect, triggerTime); } + public virtual void Good(float triggerTime) { ExecuteJudge(NoteJudgeType.Good, triggerTime); } + public virtual void Bad(float triggerTime){ ExecuteJudge(NoteJudgeType.Bad, triggerTime); } + public virtual void Miss(float triggerTime) { ExecuteJudge(NoteJudgeType.Miss, triggerTime); } + public virtual void ExecuteStartJudge(float triggerTime) {} + protected virtual void RemoveFromCheckingList() => throw new NotImplementedException(); + + protected virtual NoteJudgeType GetStartJudgeType(float timeDifference) + { + return judgeIntervals.GetNoteJudgeType(timeDifference); + } + + protected virtual void ExecuteJudge(NoteJudgeType judgeType, float triggerTime) + { + isDuringJudging = false; + judgedTriggerTime = triggerTime; + judgedType = judgeType; + + switch (judgeType) + { + case NoteJudgeType.Perfect: + GameManager.Instance.playingRecorder.AddPerfect(); + break; + case NoteJudgeType.Good: + GameManager.Instance.playingRecorder.AddGood(); + break; + case NoteJudgeType.Bad: + GameManager.Instance.playingRecorder.AddBad(); + break; + case NoteJudgeType.Miss: + GameManager.Instance.playingRecorder.AddMiss(); + break; + } + + NoteAudioSubmodule.PlayNoteJudgeAudios(judgeType); + + if (isOnTrack) track.childElementList.Remove(this); + + if (NoteJudgeSubmodule?.judgeUnitList != null) + { + for (int i = 0; i < NoteJudgeSubmodule.judgeUnitList.Count; i++) + { + var unit = NoteJudgeSubmodule.judgeUnitList[i]; + if (unit.isShowingJudge) + { + unit.SetShowingJudge(false); + } + } + } + + // 启动销毁计时器接管 + isJudgedAndDestroying = true; + destroyTimer = 1.2f; + } + + protected virtual void UpdatePostJudgeEffects() + { + UpdateEffectListInternal(generalJudgeEffects, judgedTriggerTime); + switch (judgedType) + { + case NoteJudgeType.Perfect: + UpdateEffectListInternal(perfectEffects, judgedTriggerTime); + break; + case NoteJudgeType.Good: + UpdateEffectListInternal(goodEffects, judgedTriggerTime); + break; + case NoteJudgeType.Bad: + UpdateEffectListInternal(badEffects, judgedTriggerTime); + break; + case NoteJudgeType.Miss: + UpdateEffectListInternal(missEffects, judgedTriggerTime); + break; + } + UpdateEffectListInternal(afterJudgeEffects, exactJudgeTime); + } + + // 【Review修改】:改用强制 for 循环,彻底禁用带局部参数传递的 Lambda 方法与扩展 + private void UpdateEffectListInternal(List effects, float time) + { + if (effects == null) return; + for (int i = 0; i < effects.Count; i++) + { + effects[i].UpdateEffect(time); + } + } + + protected List GetEffectListSafe(string key) + { + if (noteVisual?.effectSubmodule?.effectCollection != null && + noteVisual.effectSubmodule.effectCollection.TryGetValue(key, out var list)) + { + return list; + } + return null; + } + + public int CompareTo(NoteBase other) + { + return exactJudgeTime.CompareTo(other.exactJudgeTime); + } + #endregion + } + + #region [类附属依赖系统] Judge Type & Intervals + public abstract partial class NoteBase + { + public enum NoteJudgeType + { + Perfect = 0, + Good = 1, + Bad = 2, + Miss = 3, + NotJudged = -999 + } + + public static NoteJudgeType GetLowerType(NoteJudgeType typeA, NoteJudgeType typeB) + { + if (typeA == NoteJudgeType.NotJudged) return typeB; + if (typeB == NoteJudgeType.NotJudged) return typeA; + return typeA > typeB ? typeA : typeB; + } + + public class NoteJudgeIntervals + { + public TimeInterval beforeMiss, beforeBad, beforeGood, perfect, afterGood, afterBad; + public float afterMiss; + + public static readonly NoteJudgeIntervals TapDefault = new NoteJudgeIntervals( + new TimeInterval(-0.15f, -0.15f), new TimeInterval(-0.15f, -0.125f), + new TimeInterval(-0.125f, -0.1f), new TimeInterval(-0.1f, 0.1f), + new TimeInterval(0.1f, 0.125f), new TimeInterval(0.125f, 0.15f), 0.15f); + + public static readonly NoteJudgeIntervals StayDefault = new NoteJudgeIntervals( + new TimeInterval(-0.15f, -0.15f), new TimeInterval(-0.15f, -0.15f), + new TimeInterval(-0.15f, -0.15f), new TimeInterval(-0.15f, 0.15f), + new TimeInterval(0.15f, 0.15f), new TimeInterval(0.15f, 0.15f), 0.15f); + + public static readonly NoteJudgeIntervals HoldDefault = new NoteJudgeIntervals( + new TimeInterval(-0.15f, -0.15f), new TimeInterval(-0.15f, -0.125f), + new TimeInterval(-0.125f, -0.1f), new TimeInterval(-0.1f, 0.1f), + new TimeInterval(0.1f, 0.125f), new TimeInterval(0.125f, 0.15f), 0.15f); + + public static readonly NoteJudgeIntervals FlickDefault = new NoteJudgeIntervals( + new TimeInterval(-0.15f, -0.15f), new TimeInterval(-0.15f, -0.15f), + new TimeInterval(-0.15f, -0.15f), new TimeInterval(-0.15f, 0.15f), + new TimeInterval(0.15f, 0.15f), new TimeInterval(0.15f, 0.15f), 0.15f); + + public NoteJudgeIntervals(TimeInterval beforeMiss, TimeInterval beforeBad, TimeInterval beforeGood, + TimeInterval perfect, TimeInterval afterGood, TimeInterval afterBad, float afterMiss) + { + this.beforeMiss = beforeMiss; this.beforeBad = beforeBad; + this.beforeGood = beforeGood; this.perfect = perfect; + this.afterGood = afterGood; this.afterBad = afterBad; + this.afterMiss = afterMiss; + } + + public NoteJudgeType GetNoteJudgeType(float timeDifference) + { + if (beforeMiss.IsInInterval(timeDifference)) return NoteJudgeType.Miss; + if (beforeBad.IsInInterval(timeDifference)) return NoteJudgeType.Bad; + if (beforeGood.IsInInterval(timeDifference)) return NoteJudgeType.Good; + if (perfect.IsInInterval(timeDifference)) return NoteJudgeType.Perfect; + if (afterGood.IsInInterval(timeDifference)) return NoteJudgeType.Good; + if (afterBad.IsInInterval(timeDifference)) return NoteJudgeType.Bad; + + return NoteJudgeType.Miss; + } + } + + public class TimeInterval + { + public float intervalStart; + public float intervalEnd; + + public TimeInterval(float start, float end) + { + intervalStart = start; + intervalEnd = end; + } + + public bool IsInInterval(float time) + { + if (Mathf.Approximately(intervalStart, intervalEnd)) return false; + return time >= intervalStart && time <= intervalEnd; + } + } + + public static string GetNoteTypeName(NoteBase note) + { + return note switch + { + Tap => "Tap", + Stay => "Stay", + Hold => "Hold", + Flick => "Flick", + _ => throw new NotImplementedException("Note type not recognized") + }; + } + } + #endregion +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/NoteBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/NoteBase.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/NoteBase.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/NoteBase.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Stay.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Stay.cs new file mode 100644 index 00000000..7e8e515e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Stay.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Unity.VisualScripting; + +namespace Ichni.RhythmGame +{ + public class Stay : NoteBase + { + #region [特有属性字段] Special Fields + public NoteJudgeType preJudgeType; + #endregion + + #region [生成与初始化] Generation & Initialization + public static Stay GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + GameElement parentElement, float exactJudgeTime) + { + Stay stay = LeanPool.Spawn(GameManager.Instance.basePrefabs.stayNote, parentElement.transform).GetComponent(); + + stay.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + stay.exactJudgeTime = exactJudgeTime; + stay.preJudgeType = NoteJudgeType.NotJudged; + stay.judgeIntervals = NoteJudgeIntervals.StayDefault; + + if (parentElement.TryGetComponent(out Track track) && track.trackTimeSubmodule != null) + { + stay.track = track; + stay.trackPositioner.enabled = true; + stay.trackPositioner.spline = track.trackPathSubmodule.path; + stay.isOnTrack = true; + stay.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); + } + else + { + stay.track = null; + stay.isOnTrack = false; + } + return stay; + } + + public override void SetDefaultSubmodules() + { + base.SetDefaultSubmodules(); + NoteAudioSubmodule = new NoteAudioSubmodule(this, "DefaultStay"); + } + #endregion + + #region [主循环阶段] Main Update + public override bool ManualUpdate(float currentSongTime) + { + if (!isFirstJudged && !isDuringJudging && + currentSongTime >= exactJudgeTime + judgeIntervals.beforeMiss.intervalStart && + !GameManager.Instance.noteJudgeManager.checkingStayList.Contains(this)) + { + isDuringJudging = true; + GameManager.Instance.noteJudgeManager.checkingStayList.Add(this); + } + + ExecuteFinalJudge(currentSongTime); + return base.ManualUpdate(currentSongTime); + } + + protected override void RemoveFromCheckingList() + { + if (GameManager.Instance.noteJudgeManager.checkingStayList.Contains(this)) + GameManager.Instance.noteJudgeManager.checkingStayList.Remove(this); + } + #endregion + + #region [核心判定逻辑] Judgement Logic + public bool CheckJudgeAvailability(InputUnitTouch inputUnitTouch) + { + foreach (var judgeUnit in NoteJudgeSubmodule.judgeUnitList) + { + if (!judgeUnit.CheckJudgeAvailability(inputUnitTouch)) return false; + } + return true; + } + + public override void ExecuteStartJudge(float triggerTime) + { + NoteJudgeType startJudgeType = GetStartJudgeType(triggerTime - exactJudgeTime); + + if (startJudgeType != NoteJudgeType.Perfect) return; + + RemoveFromCheckingList(); + preJudgeType = startJudgeType; + isFirstJudged = true; + } + + public void ExecuteFinalJudge(float triggerTime) + { + if (isFirstJudged && !isFinalJudged && preJudgeType != NoteJudgeType.NotJudged && triggerTime >= exactJudgeTime) + { + switch (preJudgeType) + { + case NoteJudgeType.Perfect: Perfect(triggerTime); GameManager.Instance.playingRecorder.resultData.Add(0); break; + case NoteJudgeType.Good: Good(triggerTime); break; + case NoteJudgeType.Bad: Bad(triggerTime); break; + case NoteJudgeType.Miss: Miss(triggerTime); break; + } + + if (preJudgeType != NoteJudgeType.Miss) NoteAudioSubmodule.PlayGeneralJudgeAudios(); + isFinalJudged = true; + } + } + #endregion + + #region [谱面功能] Beatmap Method + #endregion + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Stay.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Stay.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Stay.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Stay.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Tap.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Tap.cs new file mode 100644 index 00000000..bee68e67 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Tap.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Unity.VisualScripting; + +namespace Ichni.RhythmGame +{ + public class Tap : NoteBase + { + #region [生成与初始化] Generation & Initialization + public static Tap GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + GameElement parentElement, float exactJudgeTime) + { + Tap tap = LeanPool.Spawn(GameManager.Instance.basePrefabs.tapNote, parentElement.transform).GetComponent(); + + tap.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + tap.exactJudgeTime = exactJudgeTime; + tap.judgeIntervals = NoteJudgeIntervals.TapDefault; // 接入了你改好的享元单例 + + if (parentElement.TryGetComponent(out Track track) && track.trackTimeSubmodule != null) + { + tap.track = track; + tap.trackPositioner.enabled = true; + tap.trackPositioner.spline = track.trackPathSubmodule.path; + tap.isOnTrack = true; + tap.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); + } + else + { + tap.track = null; + tap.isOnTrack = false; + } + return tap; + } + + public override void SetDefaultSubmodules() + { + base.SetDefaultSubmodules(); + NoteAudioSubmodule = new NoteAudioSubmodule(this, "DefaultTap"); + } + #endregion + + #region [主循环阶段] Main Update + public override bool ManualUpdate(float currentSongTime) + { + if (!isFirstJudged && !isDuringJudging && + currentSongTime >= exactJudgeTime + judgeIntervals.beforeMiss.intervalStart && + !GameManager.Instance.noteJudgeManager.checkingTapList.Contains(this)) + { + isDuringJudging = true; + GameManager.Instance.noteJudgeManager.checkingTapList.Add(this); + } + return base.ManualUpdate(currentSongTime); + } + + protected override void RemoveFromCheckingList() + { + if (GameManager.Instance.noteJudgeManager.checkingTapList.Contains(this)) + GameManager.Instance.noteJudgeManager.checkingTapList.Remove(this); + } + #endregion + + #region [核心判定逻辑] Judgement Logic + public bool CheckJudgeAvailability(InputUnitTap inputUnitTap) + { + foreach (var judgeUnit in NoteJudgeSubmodule.judgeUnitList) + { + if (!judgeUnit.CheckJudgeAvailability(inputUnitTap)) return false; + } + return true; + } + + protected override NoteJudgeType GetStartJudgeType(float timeDifference) => judgeIntervals.GetNoteJudgeType(timeDifference); + + public override void ExecuteStartJudge(float triggerTime) + { + RemoveFromCheckingList(); + + float timeDifference = triggerTime - exactJudgeTime; + NoteJudgeType startJudgeType = GetStartJudgeType(timeDifference); + + GameManager.Instance.playingRecorder.resultData.Add(timeDifference); + + switch (startJudgeType) + { + case NoteJudgeType.Perfect: Perfect(triggerTime); break; + case NoteJudgeType.Good: Good(triggerTime); break; + case NoteJudgeType.Bad: Bad(triggerTime); break; + case NoteJudgeType.Miss: Miss(triggerTime); break; + } + + if (startJudgeType != NoteJudgeType.Miss) + NoteAudioSubmodule.PlayGeneralJudgeAudios(); + + isFirstJudged = true; + isFinalJudged = true; + } + #endregion + + #region [谱面功能] Beatmap Method + #endregion + } + +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Tap.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Tap.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteObjects/Tap.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteObjects/Tap.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteVisual.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteVisual.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBase.cs new file mode 100644 index 00000000..29064bcf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBase.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.RhythmGame +{ + public abstract class NoteVisualBase : SubstantialObject, IHaveEffectSubmodule, IHaveSelectSubmodule + { + #region [暴露属性字段] Essential Configs + public NoteBase note; + + public bool isHighlighted; + + public GameObject noteMain; + public GameObject judgeEffect; + + public GameObject perfectPoint; + + public List notePartList; + public List extraPartList; + public List effectPrefabList; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public virtual Vector3 noteVisualPosition => noteMain.transform.position; + public EffectSubmodule effectSubmodule { get; set; } + public SelectSubmodule selectSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public new static NoteVisualBase GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, string themeBundleName, string objectName, GameElement parentElement, bool isHighlighted) + { + NoteVisualBase noteVisual = SubstantialObject.GenerateElement(elementName, id, tags, + isFirstGenerated, themeBundleName, objectName, parentElement).GetComponent(); + + noteVisual.isHighlighted = isHighlighted; + noteVisual.SetHighlight(); + + return noteVisual; + } + + public override void SetDefaultSubmodules() + { + base.SetDefaultSubmodules(); + effectSubmodule = new EffectSubmodule(this, EffectSubmodule.EffectSubmodulePreset.Note); + } + #endregion + + #region [行为重写] Behavior Overrides + public virtual void Recover() + { + + } + + public virtual void SetHighlight() + { + + } + #endregion + } + + + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteVisual/NoteVisualBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBase.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteVisual/NoteVisualBase.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBase.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs new file mode 100644 index 00000000..9275b23d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs @@ -0,0 +1,33 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class NoteVisualBaseHold : NoteVisualBase + { + #region [暴露属性字段] Essential Configs + public Hold hold; + #endregion + + #region [行为重写] Behavior Overrides + public virtual void UpdateHoldInMovableTrack() + { + + } + + public virtual void UpdateHoldInStaticTrack() + { + + } + #endregion + } + + public interface INoteVisualHold + { + public Hold hold { get; set; } + public void UpdateHoldInMovableTrack(); + public void UpdateHoldInStaticTrack(); + } +} diff --git a/Assets/Scripts/EditorGame/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Notes/NoteVisual/NoteVisualBaseHold.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Track.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/PathNode.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/PathNode.cs new file mode 100644 index 00000000..67ae2e37 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/PathNode.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class PathNode : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule, IHaveColorSubmodule + { + #region [暴露属性字段] Essential Configs + public Track track; + public SplinePoint node; + public bool isShowingSphere; + public override int HierarchyPriority => -100; + public bool haveEmission => false; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public int index => track.trackPathSubmodule.pathNodeList.IndexOf(this); + #endregion + + #region [子模块接口] Submodules + public TransformSubmodule transformSubmodule { get; set; } + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + public ColorSubmodule colorSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static PathNode GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, + Track track, bool isShowingSphere) + { + PathNode pathNode = LeanPool.Spawn(GameManager.Instance.basePrefabs.pathNode, track.transform).GetComponent(); + + pathNode.Initialize(elementName, id, tags, isFirstGenerated, track); + + pathNode.track = track; + + pathNode.isShowingSphere = isShowingSphere; + track.trackPathSubmodule.pathNodeList.Add(pathNode); + track.trackPathSubmodule.SetPathNode(pathNode); + return pathNode; + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + timeDurationSubmodule = new TimeDurationSubmodule(this); + colorSubmodule = new ColorSubmodule(this); + } + #endregion + } + + #region [行为重写] Behavior Overrides + public partial class PathNode + { + public override void Refresh() + { + base.Refresh(); + Vector3 position = transformSubmodule.currentPosition; + Vector3 normal = Quaternion.Euler(transformSubmodule.currentEulerAngles) * Vector3.up; + float size = transformSubmodule.currentScale.x; + Color color = colorSubmodule.currentBaseColor; + + transform.localPosition = position; + transform.localRotation = Quaternion.LookRotation(normal); + transform.localScale = Vector3.one * size; + + node = new SplinePoint(position, Vector3.up, normal, size, color); + track.trackPathSubmodule.SetPathNode(this); + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/PathNode.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/PathNode.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/PathNode.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/PathNode.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Track.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Track.cs new file mode 100644 index 00000000..72e142dc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Track.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.RhythmGame +{ + public partial class Track : GameElement, IHaveTransformSubmodule, IHaveTimeDurationSubmodule + { + #region [暴露属性字段] Essential Configs + public GameObject trackRenderer; + #endregion + + #region [子模块接口] Submodules + public TransformSubmodule transformSubmodule { get; set; } + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + public TrackPathSubmodule trackPathSubmodule { get; set; } + public TrackTimeSubmodule trackTimeSubmodule { get; set; } + public TrackRendererSubmodule trackRendererSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static Track GenerateElement(string elementName, Guid id, List tags, bool isFirstGenerated, GameElement parentElement) + { + Track track = LeanPool.Spawn(GameManager.Instance.basePrefabs.track, parentElement.transform).GetComponent(); + track.Initialize(elementName, id, tags, isFirstGenerated, parentElement); + if (parentElement is ElementFolder folder) + { + folder.trackList.Add(track); + } + + return track; + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + timeDurationSubmodule = new TimeDurationSubmodule(this); + trackPathSubmodule = new TrackPathSubmodule(this, TrackSpaceType.CatmullRom, TrackSamplingType.TimeDistributed, false, false); + trackTimeSubmodule = null; + trackRendererSubmodule = null; + } + + public override void AfterInitialize() + { + base.AfterInitialize(); + + GameManager.Instance.trackManager.RegisterTrack(this); + + if (trackPathSubmodule != null && trackPathSubmodule.pathNodeList.Count > 3) + { + trackPathSubmodule.ClosePath(); + } + + if(trackRendererSubmodule != null) + { + trackRendererSubmodule.meshGenerator.autoUpdate = false; + } + } + #endregion + + #region [轮询更新] Main Update + public void ManualUpdate(float currentSongTime) + { + if (timeDurationSubmodule.CheckTimeInDuration(currentSongTime)) + { + (trackTimeSubmodule as TrackTimeSubmoduleMovable)?.UpdateTrackPart(currentSongTime); + } + } + + public void ManualLateUpdate() + { + if(trackPathSubmodule != null) trackPathSubmodule.refreshedThisFrame = false; + } + #endregion + + #region [行为重写] Behavior Overrides + public override void Refresh() + { + base.Refresh(); + trackPathSubmodule?.Refresh(); + trackTimeSubmodule?.Refresh(); + trackRendererSubmodule?.Refresh(); + } + + public override void OnDelete() + { + GameManager.Instance.trackManager.UnregisterTrack(this); + if (parentElement is ElementFolder folder) folder.trackList.Remove(this); + } + #endregion + } + + #region [枚举定义] Enums + public partial class Track + { + public enum TrackSpaceType + { + CatmullRom = 0, + BSpline = 1, + Linear = 3 + } + + public enum TrackSamplingType + { + TimeDistributed = 0, + DistanceDistributed = 1 + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/Track.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Track.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/Track.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Track.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackPoints.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackPoints.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/CrossTrackPoint.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/CrossTrackPoint.cs new file mode 100644 index 00000000..769db22e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/CrossTrackPoint.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class CrossTrackPoint : GameElement, IHaveTimeDurationSubmodule + { + #region [暴露属性字段] Essential Configs + public ElementFolder trackListFolder; + public FlexibleInt trackSwitch; + public FlexibleFloat trackPercent; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public Track nowAttachedTrack; + private int nowAttachedTrackIndex; + public SplinePositioner trackPositioner; + #endregion + + #region [子模块接口] Submodules + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static CrossTrackPoint GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, ElementFolder elementFolder, FlexibleInt trackSwitch, FlexibleFloat trackPercent) + { + CrossTrackPoint point = + LeanPool.Spawn(GameManager.Instance.basePrefabs.crossTrackPoint, elementFolder.transform).GetComponent(); + point.Initialize(elementName, id, tags, isFirstGenerated, elementFolder); + point.trackPositioner = point.gameObject.GetComponent(); + point.nowAttachedTrackIndex = -1; + point.trackListFolder = elementFolder; + point.trackSwitch = trackSwitch; + point.trackPercent = trackPercent; + point.trackPositioner.motion.applyRotation = false; + return point; + } + + public override void AfterInitialize() + { + GameManager.Instance.trackManager.RegisterCrossPoint(this); + base.AfterInitialize(); + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + #endregion + + #region [轮询更新] Main Update + public void ManualUpdate(float currentSongTime) + { + if (trackPercent.animations.Count > 0) + { + trackSwitch.UpdateFlexibleInt(currentSongTime); + trackPercent.UpdateFlexibleFloat(currentSongTime); + SetPoint(); + + if(nowAttachedTrackIndex >= trackSwitch.animations.Count - 1 && + trackPercent.returnType == FlexibleReturnType.After) + { + trackPositioner.SetPercent(1); + GameManager.Instance.trackManager.UnregisterCrossPoint(this); + } + } + } + + private void SetPoint() + { + if (nowAttachedTrackIndex != trackSwitch.value && + trackSwitch.value >= 0 && + trackSwitch.value < trackListFolder.trackList.Count) + { + nowAttachedTrack = trackListFolder.trackList[trackSwitch.value]; + nowAttachedTrackIndex = trackSwitch.value; + trackPositioner.spline = trackListFolder.trackList[trackSwitch.value].trackPathSubmodule.path; + trackPositioner.Rebuild(); + } + + trackPositioner.SetPercent(trackPercent.value); + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackPoints/CrossTrackPoint.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/CrossTrackPoint.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackPoints/CrossTrackPoint.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/CrossTrackPoint.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackHeadPoint.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackHeadPoint.cs new file mode 100644 index 00000000..56a38a5c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackHeadPoint.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class TrackHeadPoint : GameElement, IHaveTimeDurationSubmodule + { + #region [暴露属性字段] Essential Configs + public Track track; + public bool motionApplyRotation; + public Vector3 motionEulerAngles; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public TrackTimeSubmoduleMovable trackTimeSubmoduleMovable; + public SplinePositioner trackPositioner; + #endregion + + #region [子模块接口] Submodules + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static TrackHeadPoint GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, Track track, bool motionApplyRotation, Vector3 motionEulerAngles) + { + TrackHeadPoint head = + LeanPool.Spawn(GameManager.Instance.basePrefabs.trackHeadPoint, track.transform).GetComponent(); + + head.Initialize(elementName, id, tags, isFirstGenerated, track); + head.track = track; + head.trackPositioner = head.gameObject.GetComponent(); + head.trackPositioner.spline = track.trackPathSubmodule.path; + head.trackTimeSubmoduleMovable = track.trackTimeSubmodule as TrackTimeSubmoduleMovable; + + head.motionApplyRotation = motionApplyRotation; + head.trackPositioner.motion.applyRotation = motionApplyRotation; + head.motionEulerAngles = motionEulerAngles; + head.trackPositioner.motion.rotationOffset = motionEulerAngles; + + return head; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + + public override void AfterInitialize() + { + GameManager.Instance.trackManager.RegisterHeadPoint(this); + base.AfterInitialize(); + } + #endregion + + #region [轮询更新] Main Update + public void ManualUpdate(float currentSongTime) + { + if (track.timeDurationSubmodule.CheckTimeInDuration(currentSongTime)) + { + trackPositioner.SetPercent(trackTimeSubmoduleMovable.headPercent); + } + + if(track.timeDurationSubmodule.CheckAfterEndTime(currentSongTime)) + { + GameManager.Instance.trackManager.UnregisterHeadPoint(this); + } + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackPoints/TrackHeadPoint.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackHeadPoint.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackPoints/TrackHeadPoint.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackHeadPoint.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackPercentPoint.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackPercentPoint.cs new file mode 100644 index 00000000..49ad8939 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackPercentPoint.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using Lean.Pool; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + /// + /// 在轨道上根据百分比进行运动的点 + /// + public partial class TrackPercentPoint : GameElement, IHaveTimeDurationSubmodule + { + #region [暴露属性字段] Essential Configs + public Track track; + public FlexibleFloat trackPercent; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public SplinePositioner trackPositioner; + #endregion + + #region [子模块接口] Submodules + public TimeDurationSubmodule timeDurationSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static TrackPercentPoint GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, Track track, FlexibleFloat trackPercent) + { + TrackPercentPoint point = + LeanPool.Spawn(GameManager.Instance.basePrefabs.trackPercentPoint, track.transform).GetComponent(); + + point.Initialize(elementName, id, tags, isFirstGenerated, track); + point.track = track; + point.trackPositioner = point.gameObject.GetComponent(); + point.trackPositioner.spline = track.trackPathSubmodule.path; + point.trackPercent = trackPercent; + + point.trackPositioner.motion.applyRotation = false; + return point; + } + + public override void SetDefaultSubmodules() + { + timeDurationSubmodule = new TimeDurationSubmodule(this); + } + + public override void AfterInitialize() + { + GameManager.Instance.trackManager.RegisterPercentPoint(this); + base.AfterInitialize(); + } + #endregion + + #region [轮询更新] Main Update + public void ManualUpdate(float currentSongTime) + { + if (trackPercent.animations.Count > 0) + { + trackPercent.UpdateFlexibleFloat(currentSongTime); + if (trackPercent.returnType == FlexibleReturnType.MiddleExecuting) + { + float finalValue = trackPercent.value; + if (finalValue > 1 && finalValue > Mathf.Floor(finalValue)) finalValue -= Mathf.Floor(finalValue); + trackPositioner.SetPercent(finalValue); + } + + if (trackPercent.returnType == FlexibleReturnType.After) + { + trackPositioner.SetPercent(1); + GameManager.Instance.trackManager.UnregisterPercentPoint(this); + } + } + } + #endregion + } + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackPoints/TrackPercentPoint.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackPercentPoint.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackPoints/TrackPercentPoint.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackPoints/TrackPercentPoint.cs.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules.meta diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackExtraModifier.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs new file mode 100644 index 00000000..bafb4414 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; + +namespace Ichni.RhythmGame +{ + public partial class TrackPathSubmodule : TrackSubmodule + { + #region [暴露属性字段] Essential Configs + public SplineComputer path; + public List pathNodeList; + + public Track.TrackSpaceType trackSpaceType; + public Track.TrackSamplingType trackSamplingType; + public bool isClosed; + public bool isShowingDisplay; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public bool refreshedThisFrame = false; + #endregion + + #region [生命周期] Initialization + public TrackPathSubmodule(Track track, Track.TrackSpaceType trackSpaceType, + Track.TrackSamplingType trackSamplingType, bool isClosed, bool isShowingDisplay) : base(track) + { + this.path = track.GetComponent(); + + this.pathNodeList = new List(); + this.trackSpaceType = trackSpaceType; + this.trackSamplingType = trackSamplingType; + this.isClosed = isClosed; + + this.path.sampleRate = 8; + this.path.updateMode = SplineComputer.UpdateMode.LateUpdate; + SetUpSplineComputer(this.trackSpaceType, this.trackSamplingType); + //闭合路径在PathNode生成时执行,在初始化的情况下,PathNode数量为0,不会执行闭合操作 + + this.isShowingDisplay = isShowingDisplay; + + if (!HaveSameSubmodule) + { + this.track.trackPathSubmodule = this; + } + } + #endregion + } + + #region [路径操作逻辑] Path Setting Logic + public partial class TrackPathSubmodule + { + private void SetUpSplineComputer(Track.TrackSpaceType trackSpaceType, Track.TrackSamplingType trackSamplingType) + { + path.type = (Spline.Type)trackSpaceType; + path.sampleMode = (SplineComputer.SampleMode)(int)trackSamplingType; + path.space = SplineComputer.Space.Local; + } + + public void ClosePath() + { + if (isClosed) + { + path.Close(); + } + else + { + path.Break(); + } + } + + public void SetTrackSpaceType(int spaceType) + { + int SpaceType = spaceType; + if (spaceType == 2) SpaceType++; + trackSpaceType = (Track.TrackSpaceType)SpaceType; + path.type = (Spline.Type)SpaceType; + } + + public void SetPathNode(PathNode point) + { + path.SetPoint(point.index, point.node, SplineComputer.Space.Local); + } + + public override void Refresh() + { + if(refreshedThisFrame) return; + refreshedThisFrame = true; + + SetTrackSpaceType((int)trackSpaceType); + SetUpSplineComputer(trackSpaceType, trackSamplingType); + foreach (var pathNode in pathNodeList) + { + SetPathNode(pathNode); + } + ClosePath(); + path.RebuildImmediate(true, true); + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackPathSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule.meta new file mode 100644 index 00000000..a078c016 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b7b08fcd7a4efa54cb8a7b1eaec50d7a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs new file mode 100644 index 00000000..a17e76af --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs @@ -0,0 +1,113 @@ +using System; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class TrackRendererSubmodule : TrackSubmodule + { + #region [暴露属性字段] Fields + public MeshGenerator meshGenerator; + public MeshRenderer meshRenderer; + public Material renderMaterial; + + public string materialThemeBundleName; + public string materialName; + public bool enableEmission; + public float emissionIntensity; + public bool zWrite; + public Vector2 uvScale; + public Vector2 uvOffset; + #endregion + + #region [初始化与防御机制] Initialization & Defense + public TrackRendererSubmodule(Track track, bool enableEmission, float emissionIntensity, bool zWrite, + Vector2 uvScale, Vector2 uvOffset) : base(track) + { + this.enableEmission = enableEmission; + this.emissionIntensity = emissionIntensity; + this.materialThemeBundleName = String.Empty; + this.materialName = String.Empty; + this.zWrite = zWrite; + this.uvScale = uvScale; + this.uvOffset = uvOffset; + + if (!HaveSameSubmodule) + { + this.track.trackRendererSubmodule = this; + } + + // 【盲区修复】: 在启用所需的轨道生成器前,强行将对象池残留里的其他轨道生成器打入休眠状态,以防止管网和曲线同屏出现重叠! + DisableAllGenerators(); + } + + private void DisableAllGenerators() + { + if (track.trackRenderer == null) return; + + if (track.trackRenderer.TryGetComponent(out SplineRenderer sr)) sr.enabled = false; + if (track.trackRenderer.TryGetComponent(out PathGenerator pg)) pg.enabled = false; + if (track.trackRenderer.TryGetComponent(out TubeGenerator tg)) tg.enabled = false; + if (track.trackRenderer.TryGetComponent(out SurfaceGenerator sg)) sg.enabled = false; + if (track.trackRenderer.TryGetComponent(out MeshRenderer mr)) mr.enabled = false; + } + #endregion + + #region [材质与网格控制] Material & Mesh Control + public void ApplyMaterial(string materialThemeBundleName, string materialName) + { + this.materialThemeBundleName = materialThemeBundleName; + this.materialName = materialName; + Material mat = ThemeBundleManager.instance.GetObject(materialThemeBundleName, materialName); + if(mat != null) + { + renderMaterial = mat; + meshRenderer.material = renderMaterial; + } + + meshRenderer.InitializeShader(); + } + + public override void Refresh() + { + SetEnableZWrite(); + SetEnableEmission(); + SetEmissionIntensity(); + SetUV(); + } + + protected void SetMesh() + { + this.meshGenerator.enabled = true; + + if (track.trackTimeSubmodule is TrackTimeSubmoduleMovable trackTimeSubmoduleMovable) + { + meshGenerator.clipFrom = trackTimeSubmoduleMovable.tailPercent; + meshGenerator.clipTo = trackTimeSubmoduleMovable.headPercent; + } + else + { + meshGenerator.clipFrom = 0; + meshGenerator.clipTo = 1; + } + + this.meshRenderer.enabled = true; + // 短暂激活生成网格后,为了性能再度关闭自身的实时刷新。 + this.meshGenerator.enabled = false; + } + + protected void SetEnableEmission() => meshRenderer.material.SetInt("_Emission", enableEmission ? 1 : 0); + protected void SetEnableZWrite() => meshRenderer.material.SetInt("_ZWrite", zWrite ? 1 : 0); + protected void SetEmissionIntensity() => meshRenderer.material.SetColor("_EmissionColor", Color.white * Mathf.Pow(2, emissionIntensity)); + protected void SetUV() + { + if (meshGenerator != null) + { + meshGenerator.uvScale = uvScale; + meshGenerator.uvOffset = uvOffset; + } + } + #endregion + } +} diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackRendererSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackRendererSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleAutoOrient.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleAutoOrient.cs new file mode 100644 index 00000000..2e00e963 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleAutoOrient.cs @@ -0,0 +1,43 @@ +using System; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackRendererSubmoduleAutoOrient : TrackRendererSubmodule + { + #region [引擎组件] Components + public SplineRenderer splineRenderer; + #endregion + + #region [初始化] Initialization + public TrackRendererSubmoduleAutoOrient(Track track, bool enableEmission, float emissionIntensity, + bool zWrite, Vector2 uvScale, Vector2 uvOffset, Material material = null) : + base(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset) + { + this.splineRenderer = track.trackRenderer.GetComponent(); + this.meshRenderer = splineRenderer.GetComponent(); + this.meshGenerator = splineRenderer; + + // 真正安全地仅激活该模块需要用到的引擎 + this.splineRenderer.enabled = true; + this.meshGenerator.enabled = true; + + this.renderMaterial = material == null ? GameManager.Instance.basePrefabs.defaultTrackMaterial : material; + this.splineRenderer.spline = track.trackPathSubmodule.path; + this.splineRenderer.clipFrom = 0; + this.splineRenderer.clipTo = 1; + this.splineRenderer.updateMethod = SplineUser.UpdateMethod.Update; + this.meshRenderer.material = renderMaterial; + this.splineRenderer.color = Color.white; + this.splineRenderer.uvRotation = 90; + this.splineRenderer.uvMode = MeshGenerator.UVMode.UniformClip; + + SetMesh(); + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleAutoOrient.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleAutoOrient.cs.meta new file mode 100644 index 00000000..01df10ec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleAutoOrient.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8759211b0c81a09428d1a2d666e30bc2 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodulePathGenerator.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodulePathGenerator.cs new file mode 100644 index 00000000..afed4b87 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodulePathGenerator.cs @@ -0,0 +1,42 @@ +using System; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackRendererSubmodulePathGenerator : TrackRendererSubmodule + { + #region [引擎组件] Components + public PathGenerator pathGenerator; + #endregion + + #region [初始化] Initialization + public TrackRendererSubmodulePathGenerator(Track track, bool enableEmission, float emissionIntensity, + bool zWrite, Vector2 uvScale, Vector2 uvOffset, Material material = null) : + base(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset) + { + this.pathGenerator = track.trackRenderer.GetComponent(); + this.meshRenderer = pathGenerator.GetComponent(); + this.meshGenerator = pathGenerator; + + this.pathGenerator.enabled = true; + this.meshGenerator.enabled = true; + + this.renderMaterial = material == null ? GameManager.Instance.basePrefabs.defaultTrackMaterial : material; + this.pathGenerator.spline = track.trackPathSubmodule.path; + this.pathGenerator.clipFrom = 0; + this.pathGenerator.clipTo = 1; + this.pathGenerator.updateMethod = SplineUser.UpdateMethod.Update; + this.meshRenderer.material = renderMaterial; + this.pathGenerator.color = Color.white; + this.pathGenerator.uvRotation = 90; + this.pathGenerator.uvMode = MeshGenerator.UVMode.UniformClip; + + SetMesh(); + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodulePathGenerator.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodulePathGenerator.cs.meta new file mode 100644 index 00000000..141e6196 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodulePathGenerator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cb4f75ad71bc84542b2d0c5f3e4ac2e9 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleSurface.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleSurface.cs new file mode 100644 index 00000000..9147c3c9 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleSurface.cs @@ -0,0 +1,42 @@ +using System; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackRendererSubmoduleSurface : TrackRendererSubmodule + { + #region [引擎组件] Components + public SurfaceGenerator surface; + #endregion + + #region [初始化] Initialization + public TrackRendererSubmoduleSurface(Track track, bool enableEmission, float emissionIntensity, + bool zWrite, Vector2 uvScale, Vector2 uvOffset, Material material = null) : + base(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset) + { + this.surface = track.trackRenderer.GetComponent(); + this.meshRenderer = surface.GetComponent(); + this.meshGenerator = surface; + + this.surface.enabled = true; + this.meshGenerator.enabled = true; + + this.renderMaterial = material == null ? GameManager.Instance.basePrefabs.defaultTrackMaterial : material; + this.surface.spline = track.trackPathSubmodule.path; + this.surface.clipFrom = 0; + this.surface.clipTo = 1; + this.surface.updateMethod = SplineUser.UpdateMethod.Update; + this.meshRenderer.material = renderMaterial; + this.surface.color = Color.white; + this.surface.uvRotation = 90; + this.surface.uvMode = MeshGenerator.UVMode.UniformClip; + + SetMesh(); + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleSurface.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleSurface.cs.meta new file mode 100644 index 00000000..2c8d24a9 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleSurface.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cd94823a9d144bf4fbce2a2e3be7c52b \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleTubeGenerator.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleTubeGenerator.cs new file mode 100644 index 00000000..c1cdfd60 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleTubeGenerator.cs @@ -0,0 +1,46 @@ +using System; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackRendererSubmoduleTubeGenerator : TrackRendererSubmodule + { + #region [引擎组件] Components + public TubeGenerator tubeGenerator; + public int sideCount; + #endregion + + #region [初始化] Initialization + public TrackRendererSubmoduleTubeGenerator(Track track, bool enableEmission, float emissionIntensity, + bool zWrite, int sideCount, Vector2 uvScale, Vector2 uvOffset, Material material = null) : + base(track, enableEmission, emissionIntensity, zWrite, uvScale, uvOffset) + { + this.sideCount = sideCount; + + this.tubeGenerator = track.trackRenderer.GetComponent(); + this.meshRenderer = tubeGenerator.GetComponent(); + this.meshGenerator = tubeGenerator; + + this.tubeGenerator.enabled = true; + this.meshGenerator.enabled = true; + + this.renderMaterial = material == null ? GameManager.Instance.basePrefabs.defaultTrackMaterial : material; + this.tubeGenerator.spline = track.trackPathSubmodule.path; + this.tubeGenerator.clipFrom = 0; + this.tubeGenerator.clipTo = 1; + this.tubeGenerator.updateMethod = SplineUser.UpdateMethod.Update; + this.meshRenderer.material = renderMaterial; + this.tubeGenerator.color = Color.white; + this.tubeGenerator.uvRotation = 90; + this.tubeGenerator.sides = sideCount; + this.tubeGenerator.uvMode = MeshGenerator.UVMode.UniformClip; + + SetMesh(); + } + #endregion + + } + +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleTubeGenerator.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleTubeGenerator.cs.meta new file mode 100644 index 00000000..7237cc23 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmoduleTubeGenerator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9454ca2f05755b94eab1ebc16973c038 \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackSubmodule.cs similarity index 95% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackSubmodule.cs rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackSubmodule.cs index d8bc15be..70f67b0a 100644 --- a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackSubmodule.cs +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackSubmodule.cs @@ -1,16 +1,16 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace Ichni.RhythmGame -{ - public abstract class TrackSubmodule : SubmoduleBase - { - public Track track; - - public TrackSubmodule(Track track) : base(track) - { - this.track = track; - } - } +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public abstract class TrackSubmodule : SubmoduleBase + { + public Track track; + + public TrackSubmodule(Track track) : base(track) + { + this.track = track; + } + } } \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs new file mode 100644 index 00000000..a714cdf0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs @@ -0,0 +1,123 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackTimeSubmodule : TrackSubmodule + { + #region [暴露属性字段] Config + public float headPercent, tailPercent; + #endregion + + #region [生命周期] Initialization + public TrackTimeSubmodule(Track track) : base(track) + { + if (!HaveSameSubmodule) + { + this.track.trackTimeSubmodule = this; + } + } + #endregion + + } + + #region [派生类] Movable Submodule + public class TrackTimeSubmoduleMovable : TrackTimeSubmodule + { + #region [暴露属性字段] Movable Configs + public float trackStartTime; + public float trackEndTime; + public float trackTotalTime; + public float visibleTrackTimeLength; + public AnimationCurveType animationCurveType; + #endregion + + #region [生命周期] Initialization + public TrackTimeSubmoduleMovable(Track track, float trackStartTime, float trackEndTime, + float visibleTrackTimeLength, AnimationCurveType animationCurveType) : base(track) + { + this.trackStartTime = trackStartTime; + this.trackEndTime = trackEndTime; + this.trackTotalTime = trackEndTime - trackStartTime; + this.visibleTrackTimeLength = visibleTrackTimeLength; + this.animationCurveType = animationCurveType; + //timeDurationSubmodule 根据下辖Note的时间来设置 + } + #endregion + + #region [时间运算逻辑] Timing Calculation + public void UpdateTrackPart(float songTime) + { + headPercent = GetTrackPercent(songTime); + tailPercent = GetTrackPercent(songTime - visibleTrackTimeLength); + + if (track.trackRendererSubmodule != null) + { + track.trackRendererSubmodule.meshGenerator.clipFrom = tailPercent; + track.trackRendererSubmodule.meshGenerator.clipTo = headPercent; + } + } + + public float GetTrackPercent(float songTimeInTime) + { + float per = AnimationCurveEvaluator.Evaluate(animationCurveType, (songTimeInTime - trackStartTime) / trackTotalTime); + return Mathf.Clamp01(per); + } + #endregion + + #region [行为重写] Behavior Overrides + public override void Refresh() + { + trackTotalTime = trackEndTime - trackStartTime; + UpdateTrackPart(CoreServices.TimeProvider.SongTime); + + track.childElementList.ForEach(child => + { + if (child is NoteBase note) + { + note.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); + } + }); + } + #endregion + + } + #endregion + + #region [派生类] Static Submodule + public class TrackTimeSubmoduleStatic : TrackTimeSubmodule + { + #region [暴露属性字段] Static Configs + public float trackTotalTime; + public AnimationCurveType animationCurveType; + #endregion + + #region [生命周期] Initialization + public TrackTimeSubmoduleStatic(Track track, float trackTotalTime, AnimationCurveType animationCurveType) : + base(track) + { + this.trackTotalTime = trackTotalTime; + this.animationCurveType = animationCurveType; + this.headPercent = 0; + this.tailPercent = 1; + //timeDurationSubmodule 根据下辖Note的时间来设置 + } + #endregion + + #region [行为重写] Behavior Overrides + public override void Refresh() + { + track.childElementList.ForEach(child => + { + if (child is NoteBase note) + { + note.UpdateNoteInTrack(CoreServices.TimeProvider.SongTime); + } + }); + } + #endregion + + } + #endregion +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/TrackSubmodules/TrackTimeSubmodule.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers.meta new file mode 100644 index 00000000..cc8a57ce --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ee0ac5310b2ba504fab9267f9ad81a9b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ObjectTracker.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ObjectTracker.cs new file mode 100644 index 00000000..55286693 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ObjectTracker.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ObjectTracker : MonoBehaviour +{ + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void Update() + { + + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ObjectTracker.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ObjectTracker.cs.meta new file mode 100644 index 00000000..16d40338 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ObjectTracker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb900d6a5df01384481372f1fdeca79f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ParticleTracker.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ParticleTracker.cs new file mode 100644 index 00000000..30ee44ac --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ParticleTracker.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Dreamteck.Splines; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.RhythmGame +{ + public partial class ParticleTracker : GameElement, IHaveParticles, IHaveColorSubmodule + { + #region [暴露属性字段] Essential Configs + public Track track; + public string themeBundleName; + public string materialName; + + public bool prewarm; + public float playTime; + public float stopTime; + + public bool is3D; + public float width; + public Vector3 extendDirection; + public float density; + public float lifeTime; + public bool isAutoOrient; + public Vector3 particleRotation; + #endregion + + #region [计算与状态缓存] Calculated & Cached States + public ParticleController particleController; + public bool haveBaseColor => true; + public bool haveEmissionColor => true; + #endregion + + #region [子模块接口] Submodules + public ParticleSystem particle { get; set; } + public ColorSubmodule colorSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static ParticleTracker GenerateElement(string elementName, Guid id, List tags, + bool isFirstGenerated, Track track, string themeBundleName, string materialName, + bool prewarm, float playTime, float stopTime, + bool is3D, float width, Vector3 extendDirection, + float density, float lifeTime, + bool isAutoOrient, Vector3 particleRotation) + { + ParticleTracker particleTracker = Instantiate(GameManager.Instance.basePrefabs.particleTracker, track.transform) + .GetComponent(); + particleTracker.particle = particleTracker.GetComponent(); + particleTracker.Initialize(elementName, id, tags, isFirstGenerated, track); + particleTracker.track = track; + particleTracker.particleController.spline = track.trackPathSubmodule.path; + particleTracker.playTime = playTime; + particleTracker.stopTime = stopTime; + particleTracker.themeBundleName = themeBundleName; + particleTracker.materialName = materialName; + (particleTracker as IHaveParticles).SetParticleMaterial(themeBundleName, materialName); + particleTracker.SetParticleSettings(prewarm, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation); + return particleTracker; + } + + public override void SetDefaultSubmodules() + { + colorSubmodule = new ColorSubmodule(this, Color.white, true, Color.white, 0); + } + #endregion + + #region [运行时设置] Runtime Settings + public void SetParticleSettings(bool prewarm, bool is3D, float width, Vector3 extendDirection, + float density, float lifeTime, bool isAutoOrient, Vector3 particleRotation) + { + this.prewarm = prewarm; + this.is3D = is3D; + this.width = width; + this.extendDirection = extendDirection; + this.density = density; + this.lifeTime = lifeTime; + this.prewarm = prewarm; + this.isAutoOrient = isAutoOrient; + this.particleRotation = particleRotation; + (this as IHaveParticles).SetParticleSettings(prewarm, ParticleSystemSimulationSpace.Local, density, + lifeTime, 0, 1, isAutoOrient, particleRotation); + SetShape(); + } + #endregion + } + + #region [轮询更新] Main Update + public partial class ParticleTracker + { + private void Update() + { + float songTime = CoreServices.TimeProvider.SongTime; + if (playTime > songTime || stopTime < songTime) + { + particle.Stop(); + } + else + { + if (!particle.isPlaying) + { + particle.Play(); + } + } + } + } + #endregion + + #region [行为重写] Behavior Overrides + public partial class ParticleTracker + { + private void SetShape() + { + particleController.is3D = is3D; + particleController.width = width; + particleController.extendDirection = extendDirection; + particleController.Rebuild(); + } + + public override void Refresh() + { + base.Refresh(); + ParticleSystemRenderer particleSystemRenderer = particle.GetComponent(); + particleSystemRenderer.material.SetColor("_BaseColor", colorSubmodule.currentBaseColor); + if (colorSubmodule.emissionEnabled) + { + particleSystemRenderer.material.EnableKeyword("_EMISSION_ON"); + particleSystemRenderer.material.SetColor("_EmissionColor", colorSubmodule.GetCurrentEmissionColor()); + } + else + { + particleSystemRenderer.material.DisableKeyword("_EMISSION_ON"); + } + } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/Trackers/ParticleTracker.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ParticleTracker.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/Trackers/ParticleTracker.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trackers/ParticleTracker.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trail.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trail.cs new file mode 100644 index 00000000..1e9dd253 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trail.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame.Beatmap; +using UniRx; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public partial class Trail : GameElement, IHaveTransformSubmodule, IHaveTrail + { + #region [暴露属性字段] Essential Configs + public Material renderMaterial; + public float visibleTimeLength; + public bool isAutoOrient; + public float widthMultiplier; + public AnimationCurve widthCurve; + public Gradient gradient; + #endregion + + #region [子模块接口] Submodules + public TrailRenderer trailRenderer { get; set; } + public TransformSubmodule transformSubmodule { get; set; } + #endregion + + #region [生命周期] Lifecycle & Factory + public static Trail GenerateElement(string name, Guid id, List tags, bool isFirstGenerated, + GameElement parentElement, float visibleTimeLength, bool isAutoOrient, float widthMultiplier, + AnimationCurve widthCurve, Gradient gradient, Material material = null) + { + Trail trail = Instantiate(GameManager.Instance.basePrefabs.trail, parentElement.transform).GetComponent(); + trail.trailRenderer = trail.GetComponent(); + + trail.Initialize(name, id, tags, isFirstGenerated, parentElement); + + trail.renderMaterial = material == null ? GameManager.Instance.basePrefabs.defaultTrailMaterial : material; + trail.visibleTimeLength = visibleTimeLength; + trail.isAutoOrient = isAutoOrient; + trail.widthMultiplier = widthMultiplier; + trail.widthCurve = widthCurve; + trail.gradient = gradient; + + trail.trailRenderer.material = trail.renderMaterial; + trail.trailRenderer.time = visibleTimeLength; + trail.trailRenderer.alignment = isAutoOrient ? LineAlignment.View : LineAlignment.TransformZ; + trail.trailRenderer.widthMultiplier = widthMultiplier; + trail.trailRenderer.widthCurve = widthCurve; + trail.trailRenderer.colorGradient = gradient; + trail.trailRenderer.emitting = false; + return trail; + } + + public override void SetDefaultSubmodules() + { + transformSubmodule = new TransformSubmodule(this); + } + + public override void WhenStart() + { + base.WhenStart(); + trailRenderer.emitting = true; + trailRenderer.Clear(); + } + #endregion + } + + #region [接口定义] Interfaces + public interface IHaveTrail + { + TrailRenderer trailRenderer { get; set; } + } + #endregion + +} \ No newline at end of file diff --git a/Assets/Scripts/EditorGame/GameElements/Track/Trail.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trail.cs.meta similarity index 100% rename from Assets/Scripts/EditorGame/GameElements/Track/Trail.cs.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/GameElements/Track/Trail.cs.meta diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/clean_bm.py b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/clean_bm.py new file mode 100644 index 00000000..c28a54b7 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/clean_bm.py @@ -0,0 +1,101 @@ +import os +import re + +directory = r"d:\Projects\ichni Official\Assets\Scripts\Game" +skip_dir = r"d:\Projects\ichni Official\Assets\Scripts\Game\BeatmapEditor" # Skip if there's any Editor specific directory. But user said "Game文件夹". + +def clean_file(filepath): + # Do not process BM format classes if they contain definitions of SaveBM or something, but realistically BM classes use ExecuteBM. + # The user instruction: "把没有实际作用的SaveBM和ConvertToBM的函数剔除" + + try: + with open(filepath, 'r', encoding='utf-8') as f: + lines = f.readlines() + except Exception as e: + print("Error reading", filepath, e) + return + + out_lines = [] + i = 0 + changed = False + deleting_method = False + brace_depth = 0 + has_seen_open_brace = False + + while i < len(lines): + line = lines[i] + + if not deleting_method: + # We look for SaveBM or ConvertToBM declarations. + # E.g. `public override void SaveBM()` or `public FlexibleFloat_BM ConvertToBM()` + if re.search(r'\bvoid\s+SaveBM\s*\(', line) or re.search(r'\b\w+_BM\b\s+ConvertToBM\s*\(', line) or re.search(r'\bBaseElement_BM\b\s+ConvertToBM\s*\(', line) or re.search(r'\bFlexible[a-zA-Z]*_BM\b\s+ConvertToBM\s*\(', line) or "public void SaveBM()" in line or "public override void SaveBM()" in line or "ConvertToBM()" in line: + + # if there is a doc comment before this line, we'd ideally remove it too. + # Let's remove the previous lines if they are '///' or '[Button]' + idx = len(out_lines) - 1 + while idx >= 0: + prev_line = out_lines[idx].strip() + if prev_line.startswith('///') or prev_line.startswith('[') or prev_line == "": + out_lines.pop(idx) + idx -= 1 + else: + break + + deleting_method = True + brace_depth = 0 + has_seen_open_brace = False + + if '{' in line: + brace_depth += line.count('{') + has_seen_open_brace = True + if '}' in line: + brace_depth -= line.count('}') + if brace_depth == 0 and has_seen_open_brace: + deleting_method = False + + # Check for one-liner interface without braces + if ';' in line and not has_seen_open_brace: + deleting_method = False + + changed = True + i += 1 + continue + else: + if '{' in line: + brace_depth += line.count('{') + has_seen_open_brace = True + if '}' in line: + brace_depth -= line.count('}') + if brace_depth <= 0 and has_seen_open_brace: + deleting_method = False + i += 1 + continue + + out_lines.append(line) + i += 1 + + if not changed: + return + + text = "".join(out_lines) + + # Remove empty partial classes + # e.g. public partial class TrackTotalTimeChange { } + text = re.sub(r'(public|protected|private)?\s*partial\s+class\s+\w+\s*\{[\s\n]*\}', '', text) + + # Remove empty Region blocks + text = re.sub(r'#region\s+\[存档转换\]\s*Beatmap Data Conversion[\s\n]*#endregion', '', text) + + # Clean up excess newlines + text = re.sub(r'\n\s*\n\s*\n', '\n\n', text) + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(text) + print("Cleaned:", filepath) + +for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith('.cs'): + filepath = os.path.join(root, file) + # Do not touch files in Base/Beatmap if they are definitions of the BM format themselves, unless they have ConvertToBM which they dont + clean_file(filepath) diff --git a/Assets/Assets.sln.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/clean_bm.py.meta similarity index 74% rename from Assets/Assets.sln.meta rename to .agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/clean_bm.py.meta index 388c5224..f9ca89e6 100644 --- a/Assets/Assets.sln.meta +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Game/clean_bm.py.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 459a7ea4ef65c664482b6377848ac5c3 +guid: 344b38ed0c0632b4388166300bc74b35 DefaultImporter: externalObjects: {} userData: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI.meta new file mode 100644 index 00000000..2e12cb27 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6ce44a629b74e264aae29bb37bb825d8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameLoadingCanvas.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameLoadingCanvas.cs new file mode 100644 index 00000000..be7ece5d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameLoadingCanvas.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using DG.Tweening; +using Ichni.UI; +using Michsky.MUIP; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.RhythmGame.UI +{ + public class GameLoadingCanvas : UIPageBase + { + public bool allPartsSet; + public RectTransform leftParts; + public RectTransform rightParts; + + public TMP_Text progressText; + public RectTransform progressMark; + public ProgressBar progressBar; + + public TMP_Text songNameText; + public Image illustrationImage; + public TMP_Text difficultyText; + public TMP_Text levelText; + + public TMP_Text composerText; + public TMP_Text illustratorText; + public TMP_Text chartDesignerText; + + private void Start() + { + Initialize(); + } + + private void Initialize() + { + InformationTransistor info = InformationTransistor.instance; + + songNameText.text = info.song.songName; + illustrationImage.sprite = info.song.illustration; + difficultyText.text = info.difficulty.GetDifficultyName(); + levelText.text = info.difficulty.difficultyValue.ToString(); + composerText.text = info.song.composer; + illustratorText.text = info.song.illustratorName; + chartDesignerText.text = info.difficulty.charterName; + } + + public void SetProgress(float value) + { + progressBar.currentPercent = value; + progressText.text = Mathf.RoundToInt(value) + "%"; + float markXPosition = 1800 * (value / 100f); + progressMark.anchoredPosition = new Vector2(markXPosition, progressMark.anchoredPosition.y); + progressBar.UpdateUI(); + } + + public void MoveParts() + { + leftParts.anchoredPosition = new Vector2(-1030f, 0f); + rightParts.anchoredPosition = new Vector2(250, 0f); + allPartsSet = false; + + leftParts.DOAnchorPosX(1030f, 0.5f).OnComplete(() => allPartsSet = true).Play(); + rightParts.DOAnchorPosX(-250f, 0.5f).Play(); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameLoadingCanvas.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameLoadingCanvas.cs.meta new file mode 100644 index 00000000..539e47ea --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameLoadingCanvas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 863ea549d1832c3439a4bde5cfb8829a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GamePauseInterface.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GamePauseInterface.cs new file mode 100644 index 00000000..a48fca93 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GamePauseInterface.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.UI; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.UI; + +namespace Ichni.RhythmGame.UI +{ + public class GamePauseInterface : UIPageBase + { + public Button resumeButton; + public Button restartButton; + public Button returnButton; + + private void Start() + { + resumeButton.onClick.AddListener(()=> + { + FadeOut(0.5f, true, ()=> + { + GameManager.Instance.songPlayer.ResumeSong(); + GameManager.Instance.gameUICanvas.pauseButton.interactable = true; + }); + }); + + restartButton.onClick.AddListener(GameManager.RestartGame); + + returnButton.onClick.AddListener(GameManager.ReturnToMenu); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GamePauseInterface.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GamePauseInterface.cs.meta new file mode 100644 index 00000000..fd297651 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GamePauseInterface.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 741eb2ad037947a4eb1d85aea247d432 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameUICanvas.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameUICanvas.cs new file mode 100644 index 00000000..93a856bf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameUICanvas.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.UI; +using Sirenix.OdinInspector; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.RhythmGame.UI +{ + public class GameUICanvas : UIPageBase + { + public Button pauseButton; + + public TMP_Text difficultyNameText; + public TMP_Text difficultyValueText; + + public TMP_Text readyText; + public TMP_Text accuracyText; + public TMP_Text comboText; + + public Image progressBar; + + public GamePauseInterface pauseInterface; + + [Title("Debug")] + public TMP_Text fpsText; + + private void Start() + { + pauseButton.onClick.AddListener(()=> + { + if (GameManager.Instance.songPlayer.isPlaying) + { + GameManager.Instance.songPlayer.PauseSong(); + pauseButton.interactable = false; + pauseInterface.FadeIn(0.5f, true); + } + }); + + difficultyNameText.text = InformationTransistor.instance.difficulty.difficultyName; + difficultyNameText.color = InformationTransistor.instance.difficulty.color / 2f; + difficultyValueText.text = InformationTransistor.instance.difficulty.difficultyValue.ToString(); + } + + private void Update() + { + fpsText.text = (1.0f / Time.unscaledDeltaTime).ToString("F2"); + if (GameManager.Instance.songPlayer.isPlaying) + { + float songLength = GameManager.Instance.songInformation.songLength; + progressBar.fillAmount = songLength > 0 ? CoreServices.TimeProvider.SongTime / songLength : 0f; // 如果歌曲长度为0,填充量为0 + } + } + + public void UpdateAccuracy(float accuracy) + { + accuracyText.text = accuracy.ToString("F2") + "%"; + } + + public void UpdateCombo(int currentCombo) + { + comboText.text = currentCombo.ToString("D"); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameUICanvas.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameUICanvas.cs.meta new file mode 100644 index 00000000..2f3439bb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/GameUICanvas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 642d31b4e1b74ca439fd7605cdad9300 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/ResultGraph.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/ResultGraph.cs new file mode 100644 index 00000000..a47595b2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/ResultGraph.cs @@ -0,0 +1,93 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.RhythmGame +{ + public class ResultGraph : MonoBehaviour + { + public List pillars; + public TMP_Text averageText; + public float maxPillarHeight = 150f; + public static List<(int, int)> ranges = new List<(int, int)>() + { + (int.MinValue, -150), //Miss + (-150, -125), //Bad + (-125, -100), //Good + (-100, -75), (-75, -50), (-50, 50), (50, 75), (75, 100), //Perfect + (100, 125), //Good + (125, 150), //Bad + (150, int.MaxValue) //Miss + }; + + public void Initialize(List noteHitTimes) + { + SetAverageText(noteHitTimes); + SetPillarHeights(GetNoteCounts(noteHitTimes)); + } + + private void SetAverageText(List noteHitTimes) + { + if (noteHitTimes.Count == 0) + { + averageText.text = "N/A"; + return; + } + + float average = noteHitTimes.Average(); + float averageInMs = average * 1000f; + if (averageInMs > 0) + { + averageText.text = $"+{averageInMs:F2} ms"; + } + else if (averageInMs < 0) + { + averageText.text = $"{averageInMs:F2} ms"; + } + else + { + averageText.text = "0 ms"; + } + } + + private List GetNoteCounts(List noteHitTimes) + { + List noteCounts = new List(new int[ranges.Count]); + + foreach (float hitTime in noteHitTimes) + { + for (int i = 0; i < ranges.Count; i++) + { + float hitTimeInMs = hitTime * 1000f; + if (hitTimeInMs >= ranges[i].Item1 && hitTimeInMs < ranges[i].Item2) + { + //Debug.Log($"Hit time in ms: {hitTimeInMs}, Range: {ranges[i].Item1} to {ranges[i].Item2}"); + noteCounts[i]++; + break; + } + } + } + + return noteCounts; + } + + private void SetPillarHeights(List noteCounts) + { + int maxCount = noteCounts.Max(); + + if(maxCount == 0) throw new System.Exception("There is no note count to display."); + + for (int i = 0; i < noteCounts.Count; i++) + { + //Debug.Log($"Pillar {i} count: {noteCounts[i]}, Max count: {maxCount}"); + + float percentage = noteCounts[i] / (float)maxCount; + float height = Mathf.Sqrt(percentage) * maxPillarHeight; + pillars[i].rectTransform.sizeDelta = new Vector2(50, height); + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/ResultGraph.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/ResultGraph.cs.meta new file mode 100644 index 00000000..ce0b0efa --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/ResultGraph.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 10ae959865d96eb498dbc6c9ebd9774b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/SummaryPageCanvas.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/SummaryPageCanvas.cs new file mode 100644 index 00000000..e90a57e2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/SummaryPageCanvas.cs @@ -0,0 +1,92 @@ +using System.Collections; +using System.Collections.Generic; +using I2.Loc; +using UnityEngine; +using Ichni.UI; +using Michsky.MUIP; +using Sirenix.OdinInspector; +using TMPro; +using UnityEngine.UI; + +namespace Ichni.RhythmGame +{ + public class SummaryPageCanvas : UIPageBase + { + [Title("Song Information")] + public TMP_Text songNameText; + public TMP_Text composerText; + + [Title("Illustration")] + public Image illustrationImage; + + [Title("Character")] + public Image characterImage; + public TMP_Text characterNameText; + public TMP_Text sentenceText; + + [Title("Difficulty")] + public TMP_Text difficultyText; + public TMP_Text levelText; + + [Title("ChartInfo")] + public TMP_Text illustratorText; + public TMP_Text chartDesignerText; + public TMP_Text songLengthText; + public TMP_Text bpmText; + + [Title("Accuracy")] + public TMP_Text accuracyText; + + [Title("Game Records")] + public TMP_Text perfectCountText; + public TMP_Text goodCountText; + public TMP_Text badCountText; + public TMP_Text missCountText; + public TMP_Text maxComboText; + + [Title("ResultGraph")] + public ResultGraph resultGraph; + + [Title("Return")] + public Button shareButton; + public Button restartButton; + public Button backButton; + + public void SetUpSummary() + { + InformationTransistor info = InformationTransistor.instance; + + songNameText.text = info.song.songName; + composerText.text = info.song.composer; + + illustrationImage.sprite = info.song.illustration; + + characterNameText.GetComponent().SetTerm("Characters/Soullies"); + sentenceText.GetComponent().SetTerm("Sentences/Soullies_Summary_Sentence_0"); + + difficultyText.text = info.difficulty.GetDifficultyName(); + levelText.text = info.difficulty.difficultyValue.ToString(); + + chartDesignerText.text = info.difficulty.charterName; + illustratorText.text = info.song.illustratorName; + songLengthText.text = System.TimeSpan.FromSeconds(info.songLength).ToString(@"m\:ss"); + bpmText.text = info.bpm.ToString("F1"); + + PlayingRecorder recorder = GameManager.Instance.playingRecorder; + + perfectCountText.text = recorder.perfectCount.ToString(); + goodCountText.text = recorder.goodCount.ToString(); + badCountText.text = recorder.badCount.ToString(); + missCountText.text = recorder.missCount.ToString(); + accuracyText.text = recorder.accuracy.ToString("F2") + "%"; + maxComboText.text = recorder.maxCombo.ToString(); + + resultGraph.Initialize(recorder.resultData); + + restartButton.onClick.RemoveAllListeners(); + restartButton.onClick.AddListener(GameManager.RestartGame); + backButton.onClick.RemoveAllListeners(); + backButton.onClick.AddListener(GameManager.ReturnToMenu); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/SummaryPageCanvas.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/SummaryPageCanvas.cs.meta new file mode 100644 index 00000000..9d8d1c36 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/GameUI/SummaryPageCanvas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03a93fcd083c47c4b91981092fc03210 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated.meta new file mode 100644 index 00000000..82303c4f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba9abcc807a40284ea5e3bbec751787d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated/Wwise_IDs.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated/Wwise_IDs.cs new file mode 100644 index 00000000..f2cbad60 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated/Wwise_IDs.cs @@ -0,0 +1,113 @@ +namespace AK +{ + public class EVENTS + { + public static uint CONFIRM = 1517537939U; + public static uint DEFAULTENDHOLD = 2505666480U; + public static uint DEFAULTFLICK = 3627244205U; + public static uint DEFAULTSTARTHOLD = 2388618003U; + public static uint DEFAULTSTAY = 1372602579U; + public static uint DEFAULTTAP = 2262739839U; + public static uint ENTERTOGAME = 3220617700U; + public static uint OPENSTORYBLOCK = 1584519635U; + public static uint PAUSESONG = 1650034634U; + public static uint PLAYOFFSETMUSIC = 3566916299U; + public static uint PLAYPREVIEW = 2727732517U; + public static uint PLAYSONG = 290606676U; + public static uint RESUMESONG = 3332337159U; + public static uint SELECTDIFFICULTY = 3093683834U; + public static uint STOPSONG = 3341952226U; + public static uint SWITCHTAB = 3869171442U; + public static uint TOUCHTOSTART = 3357374771U; + } // public class EVENTS + + public class SWITCHES + { + public class CHAPTER + { + public static uint GROUP = 3370370056U; + + public class SWITCH + { + public static uint CHAPTER0 = 1183684776U; + public static uint CHAPTER1 = 1183684777U; + } // public class SWITCH + } // public class CHAPTER + + public class CHAPTER0 + { + public static uint GROUP = 1183684776U; + + public class SWITCH + { + public static uint ALUNITE_CAS = 2507417087U; + public static uint CHAOS_ZONE = 2596658740U; + public static uint CHAPTER_0_TUTORIAL = 2730052214U; + public static uint DROWNING_IN_A_MOMENT = 177815898U; + public static uint FATE_OF_FEAR = 2521149362U; + public static uint LEAVE_WORLDS_WITH_SMILE = 3613796964U; + public static uint LUNAR_SHADE = 2841092029U; + public static uint PALINGENESIS_OF_FFFFFF = 3841829234U; + public static uint REGISTER = 554627078U; + public static uint SOLITUDES = 2426055465U; + public static uint SPACE_RAIN = 3094374598U; + public static uint WORLD_FOR_WHITE_LIES = 4104344593U; + public static uint YOUR_SHADOW = 3924398335U; + } // public class SWITCH + } // public class CHAPTER0 + + public class CHAPTER1 + { + public static uint GROUP = 1183684777U; + + public class SWITCH + { + public static uint ABSOLUTELY_0 = 2478735458U; + public static uint ASYMPTOTICAL_STABILITY = 2049741433U; + public static uint FREEZING_POINT = 1748099318U; + public static uint HEAVENLY_DYSTOPIA = 1376130227U; + public static uint LAST_REMAINS = 2113868303U; + public static uint LEVIATHAN = 3862550981U; + public static uint SERENE = 1815191009U; + public static uint SONATA_OF_DREAM = 54936379U; + } // public class SWITCH + } // public class CHAPTER1 + + } // public class SWITCHES + + public class GAME_PARAMETERS + { + public static uint MASTERVOLUME = 2918011349U; + public static uint MUSICVOLUME = 2346531308U; + public static uint PREVIEW_HIGHPASS = 4043093737U; + public static uint PREVIEW_LOWPASS = 363065643U; + public static uint PREVIEW_VOLUME = 2183069388U; + public static uint SONG_HIGHPASS = 2191510614U; + public static uint SONG_LOWPASS = 3534982690U; + public static uint SONG_VOLUME = 2936015903U; + public static uint SOUNDFXVOLUME = 336603982U; + public static uint UIVOLUME = 3415057477U; + } // public class GAME_PARAMETERS + + public class BANKS + { + public static uint INIT = 1355168291U; + public static uint ICHNI = 2458106708U; + } // public class BANKS + + public class BUSSES + { + public static uint MASTER = 4056684167U; + public static uint MUSIC = 3991942870U; + public static uint SOUNDFX = 2810670744U; + public static uint UI = 1551306167U; + } // public class BUSSES + + public class AUDIO_DEVICES + { + public static uint NO_OUTPUT = 2317455096U; + public static uint SYSTEM = 3859886410U; + } // public class AUDIO_DEVICES + +}// namespace AK + diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated/Wwise_IDs.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated/Wwise_IDs.cs.meta new file mode 100644 index 00000000..8eb18a75 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Generated/Wwise_IDs.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 321be5f8db6d2934d8d1eb6271b364ca \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager.meta new file mode 100644 index 00000000..40c466e9 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1d381b6d808395841bb1ad73c5755f27 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/AnimationManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/AnimationManager.cs new file mode 100644 index 00000000..da04a7be --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/AnimationManager.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class AnimationManager : MonoBehaviour + { + // 如果你的动画拆分为了独立的类别,也可以分开成多个 List + private List _activeAnimations = new List(200); + + public void RegisterAnimation(AnimationBase anim) => _activeAnimations.Add(anim); + public void UnregisterAnimation(AnimationBase anim) => _activeAnimations.Remove(anim); + + public void ManualUpdate(float currentSongTime) + { + // 倒序遍历以防在更新途中某个动画自行销毁 + for (int i = _activeAnimations.Count - 1; i >= 0; i--) + { + if(_activeAnimations[i].isActiveAndEnabled) _activeAnimations[i].ManualUpdate(currentSongTime); + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/AnimationManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/AnimationManager.cs.meta new file mode 100644 index 00000000..1d06fc18 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/AnimationManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1c491de7b6b1b3e4686ffd034993fc39 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BackgroundController.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BackgroundController.cs new file mode 100644 index 00000000..1a4b357e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BackgroundController.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni +{ + public class BackgroundController : MonoBehaviour + { + public Canvas backgroundCanvas; + public Image backgroundImage; + public Material skyboxMaterial; + + public void EnableBackground(bool enable) + { + backgroundCanvas.gameObject.SetActive(enable); + } + + public void SetBackground(Sprite sprite) + { + backgroundImage.sprite = sprite; + } + + public void SetSkybox(Material material) + { + skyboxMaterial = material; + RenderSettings.skybox = material; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BackgroundController.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BackgroundController.cs.meta new file mode 100644 index 00000000..f9780923 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BackgroundController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d11bf14c4a47948449b265290bde821f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BasePrefabsCollection.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BasePrefabsCollection.cs new file mode 100644 index 00000000..a92bb49a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BasePrefabsCollection.cs @@ -0,0 +1,75 @@ +using System.Collections; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using TMPro; +using UnityEngine; +using UnityEngine.Serialization; +using Event = AK.Wwise.Event; + +[CreateAssetMenu(fileName = "BasePrefabsCollection", menuName = "Ichni/BasePrefabsCollection", order = 0)] +public partial class BasePrefabsCollection : SerializedScriptableObject +{ + [Title("基础预制体")] public GameObject emptyObject; + public GameObject elementFolder; + public GameObject gameCamera; + + [Title("Track相关")] public GameObject track; + public GameObject trackDisplay; + public GameObject pathNode; + public Material defaultTrackMaterial; + public GameObject particleTracker; + public GameObject crossTrackPoint; + public GameObject trackPercentPoint; + public GameObject trackHeadPoint; + + [Title("Trail相关")] public GameObject trail; + public Material defaultTrailMaterial; + + [Title("Note 相关")] public GameObject tapNote; + public GameObject stayNote; + public GameObject holdNote; + public GameObject flickNote; + + [Title("Note 判定UI")] public GameObject fullscreenNearTimeHint; + public GameObject areaHint; + public GameObject triggerHint; + + [Title("Effect相关")] public Material defaultParticleMaterial; + public GameObject particleEmitter; + public GameObject bloomEffect; + public GameObject cameraShakeEffect; + public GameObject cameraZoomEffect; + public GameObject chromaticAberrationEffect; + public GameObject vignetteEffect; + public GameObject lowPassFilterEffect; + public GameObject highPassFilterEffect; + + [Title("Background相关")] public Sprite defaultBackground; + public Material defaultSkyboxMaterial; + + [Title("音频相关")] public GameObject audioEventObject; + public Dictionary noteSounds; + + [Title("字体")] public Dictionary fonts; + + [Title("Debug")] public GameObject judgeRankHint; + public GameObject tapInputMark; + public GameObject touchInputMark; + + public GameObject directionalSwipeInputMark; + public GameObject genericSwipeInputMark; + + public GameObject inputEndMark; + public GameObject inputCanceledMark; +} + +public partial class BasePrefabsCollection +{ + public void PrepareAudios() + { + foreach (KeyValuePair sound in noteSounds) + { + AkSoundEngine.PrepareEvent(AkPreparationType.Preparation_Load, new uint[] { sound.Value.PlayingId }, 1); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BasePrefabsCollection.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BasePrefabsCollection.cs.meta new file mode 100644 index 00000000..2554a5e1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/BasePrefabsCollection.cs.meta @@ -0,0 +1,36 @@ +fileFormatVersion: 2 +guid: 9bfe18cabd8814ad0b27f5969180c1d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: + - emptyObject: {instanceID: 0} + - elementFolder: {instanceID: 0} + - gameCamera: {instanceID: 0} + - track: {instanceID: 0} + - pathNode: {instanceID: 0} + - defaultTrackMaterial: {instanceID: 0} + - trail: {instanceID: 0} + - defaultTrailMaterial: {instanceID: 0} + - tapNote: {instanceID: 0} + - stayNote: {instanceID: 0} + - holdNote: {instanceID: 0} + - flickNote: {instanceID: 0} + - tapNoteSound: {instanceID: 0} + - stayNoteSound: {instanceID: 0} + - holdNoteStartSound: {instanceID: 0} + - holdNoteLoopSound: {instanceID: 0} + - holdNoteEndSound: {instanceID: 0} + - flickNoteSound: {instanceID: 0} + - bloomShake: {instanceID: 0} + - dynamicUIContainer: {instanceID: 0} + - inputField: {fileID: 8936320662031972394, guid: 79e257fb81d2b0a4dade94526886cf0e, + type: 3} + - Vec3inputField: {fileID: 8936320662031972394, guid: 86a1f66d7e43c7e4a8ab14c84b86af8c, + type: 3} + - text: {fileID: 7331471404896706636, guid: 0420d23f7ef40d74183ba313464088cc, type: 3} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CameraManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CameraManager.cs new file mode 100644 index 00000000..bd791c4e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CameraManager.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.RhythmGame; +using Ichni.RhythmGame.Beatmap; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni +{ + public class CameraManager : MonoBehaviour, IBaseElement + { + public Camera backgroundCamera; + public Camera uiCamera; + public GameCamera gameCamera; + + public BaseElement_BM matchedBM { get; set; } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CameraManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CameraManager.cs.meta new file mode 100644 index 00000000..cbcbd9c4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CameraManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 604e2c80e1fb64c2ba9608c11fb2f040 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CustomPrefabsCollection.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CustomPrefabsCollection.cs new file mode 100644 index 00000000..e89efe2a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CustomPrefabsCollection.cs @@ -0,0 +1,27 @@ +using System.Collections; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + [CreateAssetMenu(fileName = "BasePrefabsCollection", menuName = "Ichni/CustomPrefabsCollection", order = 0)] + public class CustomPrefabsCollection : SerializedScriptableObject + { + public string themeBundleName = "theme_bundle_name_here"; + public Dictionary Prefabs = new Dictionary(); + + public GameObject GetPrefab(string prefabName) + { + if (Prefabs.TryGetValue(prefabName, out GameObject prefab)) + { + return prefab; + } + else + { + Debug.LogError($"Prefab '{prefabName}' not found in {themeBundleName} collection."); + return null; + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CustomPrefabsCollection.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CustomPrefabsCollection.cs.meta new file mode 100644 index 00000000..7d7a4fea --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/CustomPrefabsCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f21a52ae2934677448c0b4addcbcd9da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameInputManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameInputManager.cs new file mode 100644 index 00000000..e153061d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameInputManager.cs @@ -0,0 +1,543 @@ +using UnityEngine; +using UnityEngine.InputSystem; +using System; +using System.Collections.Generic; +using DG.Tweening; +using Ichni; +using Lean.Common; +using Lean.Pool; +using Sirenix.OdinInspector; +using TMPro; +using UnityEngine.InputSystem.Controls; +using UnityEngine.InputSystem.EnhancedTouch; +using UnityEngine.UI; +using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch; +using TouchPhase = UnityEngine.InputSystem.TouchPhase; + +namespace Ichni.RhythmGame +{ + /// + /// 为节奏游戏设计的输入管理器,处理多点触控并分发三种主要事件。 + /// 【重要】此版本内置了编辑器内的鼠标模拟功能,无需手机即可测试。 + /// + public class GameInputManager : MonoBehaviour + { + // ===================================================================== + // 可配置参数 (Configurable Parameters) + // ===================================================================== + + [Header("划动设置 (Swipe Settings)")] [Tooltip("识别为划动的最小移动距离(像素)")] [SerializeField] + private float minSwipeDistance = 100f; + + [SerializeField] private float swipeAngleThreshold = 1f; + + public GameInput gameInput; + public PlayerInput playerInput; + private string rebindsFilePath => Application.persistentDataPath + "/GameData/Rebindings.json"; + + // ===================================================================== + // 内部状态 (Internal State) + // ===================================================================== + private class TouchState + { + public int TouchId; + public Vector2 StartPosition; + public float StartTime; + public Vector2 LastSwipeDirection = Vector2.zero; + public bool isFirstSwipe = true; + } + + private readonly Dictionary _activeTouches = new Dictionary(); + + // 为鼠标模拟专门设置一个固定的Touch ID + private const int MOUSE_TOUCH_ID = 999; + + // ===================================================================== + // MonoBehaviour 生命周期方法 (Lifecycle Methods) + // ===================================================================== + + private void Awake() + { +#if UNITY_EDITOR || UNITY_STANDALONE + DOTween.SetTweensCapacity(200, 200); + gameInput = new GameInput(); + gameInput.Game.Enable(); + if (ES3.FileExists(rebindsFilePath) && ES3.KeyExists("Rebinds", rebindsFilePath)) + { + gameInput.LoadBindingOverridesFromJson(ES3.Load("Rebinds", rebindsFilePath)); + Debug.Log("已加载自定义按键绑定"); + } + + RegisterActionsInputs(); +#else + Debug.Log("已启用真实触摸输入"); +#endif + } + + private void Update() + { + if (!GameManager.Instance.songPlayer.isUpdating) + { + return; + } + + // 使用预处理指令区分平台 +#if UNITY_EDITOR || UNITY_STANDALONE + HandleHolding(); +#else + ProcessRealTouchInput(); +#endif + } + + private void OnDisable() + { +#if UNITY_EDITOR || UNITY_STANDALONE + +#else + +#endif + } + + private void OnTap(int id, Vector2 position) + { + if (SettingsManager.instance.gameSettings.debugMode) + { + GenerateTapMark(id, position); + } + + GameManager.Instance.noteJudgeManager.SetNewInputUnitTap(id, position); + } + + private void OnTouch(int id, Vector2 position) + { + if (SettingsManager.instance.gameSettings.debugMode) + { + GenerateTouchMark(id, position); + } + + GameManager.Instance.noteJudgeManager.SetNewInputUnitTouch(id, position); + } + + private void OnSwipe(int id, Vector2 position, bool isGeneric, bool isFirst, Vector2 direction) + { + if (SettingsManager.instance.gameSettings.debugMode) + { + GenerateSwipeMark(id, position, isGeneric, isFirst, direction); + + if (isFirst) Debug.Log($"划动开始 - ID: {id}, 位置: {position}, 方向: {direction}"); + else Debug.Log($"划动更新 - ID: {id}, 位置: {position}, 方向: {direction}"); + } + + GameManager.Instance.noteJudgeManager.SetNewInputUnitSwipe(id, position, isGeneric, isFirst, direction); + } + +#if UNITY_EDITOR || UNITY_STANDALONE + /// + /// 【仅在编辑器中运行】处理鼠标输入并模拟触摸事件。 + /// + private void ProcessMouseInput() + { + if (Mouse.current == null) return; + + Vector2 position = Mouse.current.position.ReadValue(); + + if (Mouse.current.leftButton.wasPressedThisFrame) + { + ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Began, position); + } + else if (Mouse.current.leftButton.isPressed) + { + // 如果鼠标位置有变化,则为Moved,否则为Stationary + if (Mouse.current.delta.ReadValue().sqrMagnitude > 0.1f) + { + ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Moved, position); + } + else + { + ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Stationary, position); + } + } + else if (Mouse.current.leftButton.wasReleasedThisFrame) + { + ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Ended, position); + } + + if (Mouse.current.rightButton.wasPressedThisFrame) + { + ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Began, position); + } + else if (Mouse.current.rightButton.isPressed) + { + // 如果鼠标位置有变化,则为Moved,否则为Stationary + if (Mouse.current.delta.ReadValue().sqrMagnitude > 0.1f) + { + ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Moved, position); + } + else + { + ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Stationary, position); + } + } + else if (Mouse.current.rightButton.wasReleasedThisFrame) + { + ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Ended, position); + } + } +#endif + +#if UNITY_EDITOR || UNITY_STANDALONE + + public bool holdingTouch0; + public bool holdingTouch1; + public bool holdingTouch2; + public bool holdingTouch3; + public bool holdingSwipe0; + public bool holdingSwipe1; + + private void RegisterActionsInputs() + { + gameInput.Game.Tap0.performed += ctx => + { + if (ctx.performed) + { + Vector2 inputPosition = new Vector2(-600 + Screen.width * 0.5f, 200f); + OnTap(0, inputPosition); + holdingTouch0 = true; + } + }; + + gameInput.Game.Tap1.performed += ctx => + { + if (ctx.performed) + { + Vector2 inputPosition = new Vector2(-200 + Screen.width * 0.5f, 200f); + OnTap(1, inputPosition); + holdingTouch1 = true; + } + }; + + gameInput.Game.Tap2.performed += ctx => + { + if (ctx.performed) + { + Vector2 inputPosition = new Vector2(200 + Screen.width * 0.5f, 200f); + OnTap(2, inputPosition); + holdingTouch2 = true; + } + }; + + gameInput.Game.Tap3.performed += ctx => + { + if (ctx.performed) + { + Vector2 inputPosition = new Vector2(600 + Screen.width * 0.5f, 200f); + OnTap(3, inputPosition); + holdingTouch3 = true; + } + }; + + gameInput.Game.Tap0.canceled += ctx => + { + if (ctx.canceled) + { + holdingTouch0 = false; + } + }; + + gameInput.Game.Tap1.canceled += ctx => + { + if (ctx.canceled) + { + holdingTouch1 = false; + } + }; + + gameInput.Game.Tap2.canceled += ctx => + { + if (ctx.canceled) + { + holdingTouch2 = false; + } + }; + + gameInput.Game.Tap3.canceled += ctx => + { + if (ctx.canceled) + { + holdingTouch3 = false; + } + }; + + gameInput.Game.Swipe0.performed += ctx => + { + if (ctx.performed) + { + holdingSwipe0 = true; + } + }; + + gameInput.Game.Swipe0.canceled += ctx => + { + if (ctx.canceled) + { + holdingSwipe0 = false; + } + }; + } + + private void HandleHolding() + { + if (holdingTouch0) + { + Vector2 inputPosition = new Vector2(-600 + Screen.width * 0.5f, 200f); + OnTouch(0, inputPosition); + } + + if (holdingTouch1) + { + Vector2 inputPosition = new Vector2(-200 + Screen.width * 0.5f, 200f); + OnTouch(1, inputPosition); + } + + if (holdingTouch2) + { + Vector2 inputPosition = new Vector2(200 + Screen.width * 0.5f, 200f); + OnTouch(2, inputPosition); + } + + if (holdingTouch3) + { + Vector2 inputPosition = new Vector2(600 + Screen.width * 0.5f, 200f); + OnTouch(3, inputPosition); + } + + if (holdingSwipe0) + { + Vector2 inputPosition = new Vector2(Screen.width * 0.5f, 200f); + OnSwipe(0, inputPosition, true, false, Vector2.zero); + } + } +#endif + + /// + /// 【仅在真机上运行】处理真实的触摸屏输入。 + /// + private void ProcessRealTouchInput() + { + if (Touchscreen.current == null) return; + + foreach (Touch touch in Touch.activeTouches) + { + ProcessInputEvent( + touch.touchId, + touch.phase, + touch.screenPosition + ); + } + } + + /// + /// 所有输入事件的核心处理函数,无论是真实触摸还是鼠标模拟都会调用它。 + /// + private void ProcessInputEvent(int touchId, TouchPhase phase, Vector2 position) + { + switch (phase) + { + case TouchPhase.Began: + var newState = new TouchState + { + TouchId = touchId, + StartPosition = position, + StartTime = CoreServices.TimeProvider.SongTime, + LastSwipeDirection = Vector2.zero, + isFirstSwipe = true + }; + _activeTouches[touchId] = newState; + OnTap(touchId, position); + OnTouch(touchId, position); + break; + + case TouchPhase.Moved: + if (_activeTouches.TryGetValue(touchId, out TouchState movedState)) + { + OnTouch(touchId, position); + DetectSwipe(movedState, position); + } + + break; + + case TouchPhase.Stationary: + if (_activeTouches.TryGetValue(touchId, out TouchState stationaryState)) + { + OnTouch(touchId, position); + } + + break; + + case TouchPhase.Ended: + if (_activeTouches.ContainsKey(touchId)) + { + _activeTouches.Remove(touchId); + if (SettingsManager.instance.gameSettings.debugMode) + { + GenerateEndMark(position); + } + } + + break; + + case TouchPhase.Canceled: + if (_activeTouches.ContainsKey(touchId)) + { + _activeTouches.Remove(touchId); + if (SettingsManager.instance.gameSettings.debugMode) + { + GenerateCanceledMark(position); + } + } + + break; + } + } + + /// + /// 检测划动逻辑 (无需修改) + /// + private void DetectSwipe(TouchState state, Vector2 currentPosition) + { + Vector2 swipeVector = currentPosition - state.StartPosition; + if (swipeVector.magnitude < minSwipeDistance) return; + + Vector2 direction = swipeVector.normalized; + + // 检查是否是新的划动方向 + if (Vector2.Dot(direction, state.LastSwipeDirection) <= swipeAngleThreshold) + { + OnSwipe(state.TouchId, state.StartPosition, false, state.isFirstSwipe, direction); + state.LastSwipeDirection = direction; + state.StartPosition = currentPosition; + state.StartTime = CoreServices.TimeProvider.SongTime; + state.isFirstSwipe = false; + } + } + + private void GenerateTapMark(int id, Vector2 pos) + { + RectTransform mark = LeanPool.Spawn(GameManager.Instance.basePrefabs.tapInputMark, + GameManager.Instance.judgeHintCanvas.transform).GetComponent(); + + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, pos, null, out Vector2 uiPosition)) + { + mark.anchoredPosition = uiPosition; + mark.GetComponentInChildren().text = GameManager.Instance.noteJudgeManager.checkingTapList.Count.ToString(); + } + + Sequence ss = DOTween.Sequence(); + ss.OnStart(() => + { + mark.GetComponent().color = Color.white; + mark.localScale = Vector3.zero; + }); + ss.Join(mark.GetComponent().DOFade(0, 0.25f)); + ss.Join(mark.DOScale(5, 0.25f)); + ss.OnComplete(() => LeanPool.Despawn(mark.gameObject)); + ss.SetUpdate(true); + ss.Play(); + } + + private void GenerateTouchMark(int id, Vector2 pos) + { + RectTransform mark = LeanPool.Spawn(GameManager.Instance.basePrefabs.touchInputMark, + GameManager.Instance.judgeHintCanvas.transform).GetComponent(); + + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, pos, null, out Vector2 uiPosition)) + { + mark.anchoredPosition = uiPosition; + } + + Sequence ss = DOTween.Sequence(); + ss.OnStart(() => { mark.GetComponent().color = Color.white; }); + ss.Join(mark.GetComponent().DOFade(0, 0.1f)); + ss.OnComplete(() => LeanPool.Despawn(mark.gameObject)); + ss.SetUpdate(true); + ss.Play(); + } + + private void GenerateSwipeMark(int id, Vector2 pos, bool isGeneric, bool isFirst, Vector2 direction) + { + GameObject markPrefab = isGeneric + ? GameManager.Instance.basePrefabs.genericSwipeInputMark + : GameManager.Instance.basePrefabs.directionalSwipeInputMark; + + RectTransform mark = LeanPool.Spawn(markPrefab, GameManager.Instance.judgeHintCanvas.transform).GetComponent(); + + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, pos, null, out Vector2 uiPosition)) + { + mark.anchoredPosition = uiPosition; + } + + mark.localEulerAngles = new Vector3(0, 0, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg - 90f); + + Sequence ss = DOTween.Sequence(); + ss.OnStart(() => + { + mark.GetComponent().color = isFirst ? Color.red : Color.white; + mark.localScale = Vector3.zero; + }); + ss.Join(mark.GetComponent().DOFade(0, 0.25f)); + ss.Join(mark.DOScale(5, 0.25f)); + ss.OnComplete(() => LeanPool.Despawn(mark.gameObject)); + ss.SetUpdate(true); + ss.Play(); + } + + private void GenerateEndMark(Vector2 pos) + { + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + RectTransform mark = LeanPool.Spawn(GameManager.Instance.basePrefabs.inputEndMark, canvasRect).GetComponent(); + + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, pos, null, out Vector2 uiPosition)) + { + mark.anchoredPosition = uiPosition; + } + + Sequence ss = DOTween.Sequence(); + ss.OnStart(() => + { + mark.GetComponent().color = Color.white; + mark.localScale = Vector3.one * 5f; + }); + ss.Join(mark.GetComponent().DOFade(0, 0.25f)); + ss.Join(mark.DOScale(0, 0.25f)); + ss.OnComplete(() => LeanPool.Despawn(mark.gameObject)); + ss.SetUpdate(true); + ss.Play(); + } + + private void GenerateCanceledMark(Vector2 pos) + { + RectTransform canvasRect = GameManager.Instance.judgeHintCanvas.GetComponent(); + RectTransform mark = LeanPool.Spawn(GameManager.Instance.basePrefabs.inputCanceledMark, canvasRect).GetComponent(); + + if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, pos, null, out Vector2 uiPosition)) + { + mark.anchoredPosition = uiPosition; + } + + Sequence ss = DOTween.Sequence(); + ss.OnStart(() => + { + mark.GetComponent().color = Color.white; + mark.localScale = Vector3.one * 5f; + }); + ss.Join(mark.GetComponent().DOFade(0, 0.25f)); + ss.Join(mark.DOScale(0, 0.25f)); + ss.OnComplete(() => LeanPool.Despawn(mark.gameObject)); + ss.SetUpdate(true); + ss.Play(); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameInputManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameInputManager.cs.meta new file mode 100644 index 00000000..9460bc44 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameInputManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99971dd6462223c4596d435e8acdcfb8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: -13 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameManager.cs new file mode 100644 index 00000000..1e5bb1a6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameManager.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame; +using Ichni.RhythmGame.UI; +using Sirenix.OdinInspector; +using SLSUtilities.General; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.Serialization; + +namespace Ichni +{ + public partial class GameManager : Singleton, ISongTimeProvider + { + [FormerlySerializedAs("audioManager")] public SongPlayer songPlayer; + + public CameraManager cameraManager; + + [FormerlySerializedAs("inputManager")] public NoteJudgeManager noteJudgeManager; + + public BackgroundSetter backgroundSetter; + public BackgroundController backgroundController; + + public VariablesContainer variablesContainer; + + public ProjectLoader projectLoader; + public PlayingRecorder playingRecorder; + + public BeatmapContainer beatmapContainer; + public CommandScripts commandScripts; + public ProjectInformation projectInformation; + public SongInformation songInformation; + + public List timeDurations; + public AnimationManager animationManager; + public TrackManager trackManager; + public NoteManager noteManager; + + public BasePrefabsCollection basePrefabs; + public Dictionary customPrefabs; + + [Title("UI")] + public Canvas judgeHintCanvas; + public GameUICanvas gameUICanvas; + public GameLoadingCanvas gameLoadingCanvas; + public SummaryPageCanvas summaryPageCanvas; + + public float SongTime => songPlayer.isStarting ? 0 : + songPlayer.songTimeSegment - songInformation.offset - (SettingsManager.instance.gameSettings.beatmapOffset / 1000f); + public bool IsPlaying => songPlayer != null && songPlayer.isPlaying; + + + public bool isDebugging; + + protected override void Awake() + { + base.Awake(); + CoreServices.TimeProvider = this; + timeDurations = new List(); + playingRecorder = new PlayingRecorder(); + } + + private void Start() + { + basePrefabs.PrepareAudios(); + projectLoader.TestLoad(); + } + + private void Update() + { + if (!songPlayer.isUpdating) return; + + foreach (var timeDuration in timeDurations) + { + timeDuration?.DetectAndSwitchActiveState(SongTime); + } + + animationManager.ManualUpdate(SongTime); + trackManager.ManualUpdate(SongTime); + noteManager.ManualUpdate(SongTime); + } + + private void LateUpdate() + { + if (!songPlayer.isUpdating) return; + + trackManager.ManualLateUpdate(SongTime); + noteManager.ManualLateUpdate(SongTime); + } + } + + public partial class GameManager + { + public static void RestartGame() + { + SceneManager.LoadScene("GameScene"); + Time.timeScale = 1f; // 确保重启时时间缩放恢复正常 + } + + public static void ReturnToMenu() + { + //InformationTransistor.instance.isReturnedFromGame = true; + SceneManager.LoadScene("MenuScene"); + Time.timeScale = 1f; // 确保返回时时间缩放恢复正常 + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameManager.cs.meta new file mode 100644 index 00000000..2903058d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/GameManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d3db32468da3024cb565d23fa250ab9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: -10 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteJudgeManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteJudgeManager.cs new file mode 100644 index 00000000..80e446d5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteJudgeManager.cs @@ -0,0 +1,375 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.RhythmGame; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni +{ + public partial class NoteJudgeManager : SerializedMonoBehaviour + { + public List checkingTapList; + public List checkingStayList; + public List checkingHoldList; + public List checkingFlickList; + + public List inputUnitTapList; + public List inputUnitTouchList; + public List inputUnitSwipeList; + + private List _availableTapsBuffer = new List(30); + private List _availableHoldsBuffer = new List(10); + private List _availableFlicksBuffer = new List(10); + private List _availableStaysBuffer = new List(30); + + private List _minTapsBuffer = new List(5); + private List _minHoldsBuffer = new List(5); + private List _minFlicksBuffer = new List(5); + + private InputUnitSwipe _cachedAssumedSwipe = new InputUnitSwipe(0, Vector2.zero, true, true, Vector2.zero); + + private void Start() + { + checkingTapList = new List(); + checkingStayList = new List(); + checkingHoldList = new List(); + checkingFlickList = new List(); + inputUnitTapList = new List(); + inputUnitTouchList = new List(); + inputUnitSwipeList = new List(); + } + + public void Update() + { + if (!GameManager.Instance.songPlayer.isPlaying) + { + return; + } + + float songTime = CoreServices.TimeProvider.SongTime; + + foreach (var swipe in inputUnitSwipeList) + { + _availableFlicksBuffer.Clear(); + + foreach (Flick flick in checkingFlickList) + { + if (flick.CheckJudgeAvailability(swipe)) + { + _availableFlicksBuffer.Add(flick); + } + } + + if (_availableFlicksBuffer.Count > 0) + { + if (swipe.isFirst) // 原 tapSwipeList 逻辑 + { + GetAllMinNotes(_availableFlicksBuffer, _minFlicksBuffer); // 提醒:内部也需防 new List + if (_minFlicksBuffer.Count == 1) _minFlicksBuffer[0].ExecuteStartJudge(songTime); + else if (_minFlicksBuffer.Count > 1) GetNearestNote(_minFlicksBuffer, swipe.inputPosition).ExecuteTapJudge(songTime); + } + else // 原 holdSwipeList 逻辑 + { + _availableFlicksBuffer.Sort(); + _availableFlicksBuffer[0].ExecuteStartJudge(songTime); + } + } + } + + /*foreach (InputUnitSwipe tapSwipe in tapSwipeList) + { + List availableFlicks = new List(); + + foreach (Flick flick in checkingFlickList) + { + if (flick.CheckJudgeAvailability(tapSwipe)) + { + availableFlicks.Add(flick); + } + } + + List minFlicks = GetAllMinNotes(availableFlicks); + + if (minFlicks.Count == 1) + { + minFlicks[0].ExecuteStartJudge(); + } + else if (minFlicks.Count > 1) + { + GetNearestNote(minFlicks, tapSwipe.inputPosition).ExecuteTapJudge(); + } + } + + foreach (InputUnitSwipe inputUnitSwipe in holdSwipeList) + { + List availableFlicks = new List(); + + foreach (Flick flick in checkingFlickList) + { + if (flick.CheckJudgeAvailability(inputUnitSwipe)) + { + availableFlicks.Add(flick); + } + } + + if (availableFlicks.Count > 0) + { + availableFlicks.Sort(); + Flick closestFlick = availableFlicks[0]; + closestFlick.ExecuteStartJudge(); + } + }*/ + + foreach (InputUnitTap inputUnitTap in inputUnitTapList) + { + _availableTapsBuffer.Clear(); + _availableHoldsBuffer.Clear(); + + foreach (Tap tap in checkingTapList) + { + if (tap.CheckJudgeAvailability(inputUnitTap)) _availableTapsBuffer.Add(tap); + } + foreach (Hold hold in checkingHoldList) + { + if (hold.CheckJudgeAvailability(inputUnitTap)) _availableHoldsBuffer.Add(hold); + } + + bool haveTap = _availableTapsBuffer.Count > 0; + bool haveHold = _availableHoldsBuffer.Count > 0; + + Flick closestFlick = null; + InputUnitSwipe assumedSwipe = new InputUnitSwipe(inputUnitTap.fingerId, inputUnitTap.inputPosition, true, true, Vector2.zero); + foreach (Flick flick in checkingFlickList) + { + if (flick.CheckJudgeAvailability(assumedSwipe)) + { + if (closestFlick == null || flick.exactJudgeTime < closestFlick.exactJudgeTime) + { + closestFlick = flick; + } + } + } + + if (haveHold && haveTap) + { + GetAllMinNotes(_availableHoldsBuffer, _minHoldsBuffer); + GetAllMinNotes(_availableTapsBuffer, _minTapsBuffer); + Hold closestHold = _minHoldsBuffer[0]; + Tap closestTap = _minTapsBuffer[0]; + bool holdBlockedByFlick = false; + bool tapBlockedByFlick = false; + + if (closestFlick != null) + { + if (closestFlick.exactJudgeTime < closestHold.exactJudgeTime) + { + holdBlockedByFlick = true; + } + + if (closestFlick.exactJudgeTime < closestTap.exactJudgeTime) + { + tapBlockedByFlick = true; + } + } + + if (!holdBlockedByFlick && closestHold.exactJudgeTime < closestTap.exactJudgeTime) + { + if (_minHoldsBuffer.Count == 1) + { + _minHoldsBuffer[0].ExecuteStartJudge(songTime); + } + else + { + GetNearestNote(_minHoldsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime); + } + } + else if (!tapBlockedByFlick) + { + if (_minTapsBuffer.Count == 1) + { + _minTapsBuffer[0].ExecuteStartJudge(songTime); + } + else + { + GetNearestNote(_minTapsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime); + } + } + } + else if (haveHold) + { + GetAllMinNotes(_availableHoldsBuffer, _minHoldsBuffer); + bool holdBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minHoldsBuffer[0].exactJudgeTime; + if (!holdBlockedByFlick) + { + if (_minHoldsBuffer.Count == 1) + { + _minHoldsBuffer[0].ExecuteStartJudge(songTime); + } + else + { + GetNearestNote(_minHoldsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime); + } + } + } + else if (haveTap) + { + GetAllMinNotes(_availableTapsBuffer, _minTapsBuffer); + bool tapBlockedByFlick = closestFlick != null && closestFlick.exactJudgeTime < _minTapsBuffer[0].exactJudgeTime; + if (!tapBlockedByFlick) + { + if (_minTapsBuffer.Count == 1) + { + _minTapsBuffer[0].ExecuteStartJudge(songTime); + } + else + { + GetNearestNote(_minTapsBuffer, inputUnitTap.inputPosition).ExecuteStartJudge(songTime); + } + } + } + } + + foreach (InputUnitTouch inputUnitTouch in inputUnitTouchList) + { + _availableStaysBuffer.Clear(); + foreach (Stay stay in checkingStayList) + { + if (stay.CheckJudgeAvailability(inputUnitTouch)) + { + _availableStaysBuffer.Add(stay); + } + } + + _availableHoldsBuffer.Clear(); + foreach (Hold hold in checkingHoldList) + { + if (hold.CheckJudgeAvailability(inputUnitTouch)) + { + _availableHoldsBuffer.Add(hold); + } + } + + foreach (Stay stay in _availableStaysBuffer) + { + stay.ExecuteStartJudge(songTime); + } + + foreach (Hold hold in _availableHoldsBuffer) + { + hold.ExecuteProcessJudge(); + } + } + + inputUnitTapList.Clear(); + inputUnitTouchList.Clear(); + inputUnitSwipeList.Clear(); + } + + public void SetNewInputUnitTap(int fingerId, Vector2 inputPosition) + { + InputUnitTap inputUnitTap = new InputUnitTap(fingerId, inputPosition); + if(!inputUnitTapList.Exists(x => x.fingerId == fingerId)) + { + inputUnitTapList.Add(inputUnitTap); + } + } + + public void SetNewInputUnitTouch(int fingerId, Vector2 inputPosition) + { + InputUnitTouch inputUnitTouch = new InputUnitTouch(fingerId, inputPosition); + if(!inputUnitTouchList.Exists(x => x.fingerId == fingerId)) + { + inputUnitTouchList.Add(inputUnitTouch); + } + } + + public void SetNewInputUnitSwipe(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 delta) + { + InputUnitSwipe inputUnitSwipe = new InputUnitSwipe(fingerId, inputPosition, isGeneric, isFirst, delta); + if(!inputUnitSwipeList.Exists(x => x.fingerId == fingerId)) + { + inputUnitSwipeList.Add(inputUnitSwipe); + } + } + } + + public partial class NoteJudgeManager + { + private void GetAllMinNotes(List availableFlicks, List destination) where T : NoteBase + { + destination.Clear(); + float minTime = float.MaxValue; + foreach (T note in availableFlicks) + { + if (note.exactJudgeTime < minTime) + { + minTime = note.exactJudgeTime; + destination.Clear(); + destination.Add(note); + } + else if (Mathf.Approximately(note.exactJudgeTime, minTime)) + { + destination.Add(note); + } + } + } + + + private T GetNearestNote(List notes, Vector2 inputPosition) where T : NoteBase + { + float minDistance = float.MaxValue; + T closestNote = null; + foreach (T note in notes) + { + float distance = Vector2.Distance(inputPosition, note.noteScreenPosition); + if (distance < minDistance) + { + minDistance = distance; + closestNote = note; + } + } + + return closestNote; + } + } + + public class InputUnit + { + public int fingerId; + public Vector2 inputPosition; + } + + public class InputUnitTap : InputUnit + { + public InputUnitTap(int fingerId, Vector2 inputPosition) + { + this.fingerId = fingerId; + this.inputPosition = inputPosition; + } + } + + public class InputUnitTouch : InputUnit + { + public InputUnitTouch(int fingerId, Vector2 inputPosition) + { + this.fingerId = fingerId; + this.inputPosition = inputPosition; + } + } + + public class InputUnitSwipe : InputUnit + { + public Vector2 swipeDirection; + public bool isGeneric; + public bool isFirst; + public InputUnitSwipe(int fingerId, Vector2 inputPosition, bool isGeneric, bool isFirst, Vector2 swipeDirection) + { + this.fingerId = fingerId; + this.inputPosition = inputPosition; + this.isGeneric = isGeneric; + this.isFirst = isFirst; + this.swipeDirection = swipeDirection.normalized; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteJudgeManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteJudgeManager.cs.meta new file mode 100644 index 00000000..d9b71271 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteJudgeManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c09a058df03a6bd4a89c637faeeb21aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: -12 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteManager.cs new file mode 100644 index 00000000..d093cb72 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteManager.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using Lean.Pool; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class NoteManager : MonoBehaviour + { + #region [核心管家字段] Fields + // 挂起队列:存放还未进入判定时间序列休眠的预设 + private List<(float activationTime, NoteBase note)> _pendingNotes = new List<(float, NoteBase)>(); + // 活跃队列:存活在场上的有效音符,统一进行中央更新 + private List _activeNotes = new List(500); + private int _nextNoteIndex = 0; + #endregion + + #region [队列注册与控制] Registration + public void RegisterNote(NoteBase note, float activationTime) + { + _pendingNotes.Add((activationTime, note)); + } + + public void AllNotesRegistered() + { + _pendingNotes.Sort((a, b) => a.activationTime.CompareTo(b.activationTime)); + _nextNoteIndex = 0; + } + #endregion + + #region [中央集权主循环] Manager-Driven Tick + public void ManualUpdate(float currentSongTime) + { + if (!GameManager.Instance.songPlayer.isUpdating) return; + + // 1. 唤醒 pending 并推进入场 + while (_nextNoteIndex < _pendingNotes.Count && + currentSongTime >= _pendingNotes[_nextNoteIndex].activationTime) + { + NoteBase noteToActivate = _pendingNotes[_nextNoteIndex].note; + noteToActivate.gameObject.SetActive(true); + _activeNotes.Add(noteToActivate); + _nextNoteIndex++; + } + + // 2. 集中处理场内活跃 Note (倒序遍历以防越界) + for (int i = _activeNotes.Count - 1; i >= 0; i--) + { + if (!_activeNotes[i].ManualUpdate(currentSongTime)) + { + _activeNotes.RemoveAt(i); + } + } + } + + public void ManualLateUpdate(float currentSongTime) + { + if (!GameManager.Instance.songPlayer.isUpdating) return; + + for (int i = _activeNotes.Count - 1; i >= 0; i--) + { + if (_activeNotes[i] is Hold hold) + { + hold.isHolding = false; + } + } + } + + #endregion + + #region [生命回收] Pool Management + public void DespawnNote(NoteBase note) + { + if (note.gameObject.activeSelf) + { + LeanPool.Despawn(note.gameObject); + } + } + #endregion + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteManager.cs.meta new file mode 100644 index 00000000..0843f094 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/NoteManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e50ec54a2c71f744bfced70191dc2d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PlayingRecorder.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PlayingRecorder.cs new file mode 100644 index 00000000..07a360c5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PlayingRecorder.cs @@ -0,0 +1,108 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class PlayingRecorder + { + public int perfectCount; + public int goodCount; + public int badCount; + public int missCount; + public int totalCount; + + public float accuracy; + + public int currentCombo; + public int maxCombo; + + public List resultData = new List(); + + public bool isFullCombo; + public bool isAllPerfect; + + public void Initialize() + { + perfectCount = 0; + goodCount = 0; + badCount = 0; + missCount = 0; + totalCount = 0; + accuracy = 100f; + currentCombo = 0; + maxCombo = 0; + isFullCombo = true; + isAllPerfect = true; + } + + private void UpdateAccuracy() + { + float baseValue = perfectCount + goodCount * 0.7f + badCount * 0.3f; + if (totalCount > 0) + { + accuracy = baseValue / totalCount * 100f; + } + else + { + accuracy = 100f; // 如果没有任何击打,准确率为100% + } + } + + private void AddCombo() + { + currentCombo++; + maxCombo = Mathf.Max(maxCombo, currentCombo); + } + + private void ResetCombo() + { + currentCombo = 0; + } + + public void AddPerfect() + { + perfectCount++; + totalCount++; + AddCombo(); + UpdateAccuracy(); + GameManager.Instance.gameUICanvas.UpdateAccuracy(accuracy); + GameManager.Instance.gameUICanvas.UpdateCombo(currentCombo); + } + + public void AddGood() + { + goodCount++; + totalCount++; + AddCombo(); + UpdateAccuracy(); + isAllPerfect = false; + GameManager.Instance.gameUICanvas.UpdateAccuracy(accuracy); + GameManager.Instance.gameUICanvas.UpdateCombo(currentCombo); + } + + public void AddBad() + { + badCount++; + totalCount++; + ResetCombo(); + UpdateAccuracy(); + isFullCombo = false; + isAllPerfect = false; + GameManager.Instance.gameUICanvas.UpdateAccuracy(accuracy); + GameManager.Instance.gameUICanvas.UpdateCombo(currentCombo); + } + + public void AddMiss() + { + missCount++; + totalCount++; + ResetCombo(); + UpdateAccuracy(); + isFullCombo = false; + isAllPerfect = false; + GameManager.Instance.gameUICanvas.UpdateAccuracy(accuracy); + GameManager.Instance.gameUICanvas.UpdateCombo(currentCombo); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PlayingRecorder.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PlayingRecorder.cs.meta new file mode 100644 index 00000000..657d3646 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PlayingRecorder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61f9523f83dc79f499a64a4bb8679572 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PostProcessingManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PostProcessingManager.cs new file mode 100644 index 00000000..49d712d4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PostProcessingManager.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Sirenix.OdinInspector; +using SLSUtilities.General; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.Universal; + +namespace Ichni +{ + public class PostProcessingManager : Singleton + { + public static Volume GlobalVolume => instance._globalVolume; + + [ShowInInspector] + private Volume _globalVolume; + + protected override void Awake() + { + base.Awake(); + //FindAndCacheFeatureWithReflection(); + } + + private void OnDisable() + { + //FindAndCacheFeatureWithReflection(); + } + + private void FindAndCacheFeatureWithReflection() + { + var pipelineAsset = GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset; + if (pipelineAsset == null) + { + Debug.LogError("当前渲染管线不是 UniversalRenderPipelineAsset。"); + return; + } + + // 2. 使用反射来获取内部的 m_RendererDataList 字段 + FieldInfo rendererDataListField = + typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList", BindingFlags.NonPublic | BindingFlags.Instance); + if (rendererDataListField == null) + { + Debug.LogError("在 UniversalRenderPipelineAsset 中无法通过反射找到 'm_RendererDataList' 字段。API可能已在你的URP版本中更改。"); + return; + } + + var rendererDataList = rendererDataListField.GetValue(pipelineAsset) as ScriptableRendererData[]; + if (rendererDataList == null) + { + Debug.LogError("获取渲染器数据列表失败。"); + return; + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PostProcessingManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PostProcessingManager.cs.meta new file mode 100644 index 00000000..6d33b054 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/PostProcessingManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8b242683d247c4ee59ef50fd8ef06701 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ProjectLoader.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ProjectLoader.cs new file mode 100644 index 00000000..6c38c3ce --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ProjectLoader.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using DG.Tweening; +using Ichni.RhythmGame; +using Ichni.RhythmGame.Beatmap; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; + +namespace Ichni +{ + public class ProjectLoader + { + public float fakeLoadPercent; + public float realLoadPercent; + public float displayLoadPercent => Mathf.Min(realLoadPercent, fakeLoadPercent); + + public static readonly ES3Settings SaveSettings = new ES3Settings + { + compressionType = ES3.CompressionType.None, + encryptionType = ES3.EncryptionType.None, + format = ES3.Format.JSON, + location = ES3.Location.Resources + }; + + private static readonly ES3Settings LoadSettings = new ES3Settings + { + compressionType = ES3.CompressionType.Gzip, + encryptionType = ES3.EncryptionType.AES, + encryptionPassword = "Soullies515", + format = ES3.Format.JSON, + location = ES3.Location.Resources + }; + + public void TestLoad() + { + string beatMapFolderPath = "Beatmaps/" + InformationTransistor.instance.chapter.chapterIndex + + "/" + InformationTransistor.instance.song.songName + + "/" + InformationTransistor.instance.difficulty.difficultyName; + + LoadProjectInfo(beatMapFolderPath); + LoadSongInfo(beatMapFolderPath); + LoadCommandScripts(beatMapFolderPath); + + GameManager.Instance.gameLoadingCanvas.MoveParts(); + + ThemeBundleManager.instance.LoadThemeBundles(GameManager.Instance.projectInformation.selectedThemeBundleList); + realLoadPercent = 0f; + fakeLoadPercent = 0f; + + Observable.EveryUpdate() + .Where(_ => ThemeBundleManager.instance.waitingBundleAmount.Value == 0 && + GameManager.Instance.gameLoadingCanvas.allPartsSet) + .First() + .Subscribe(_ => + { + LoadBeatMap(beatMapFolderPath); + + IDisposable fakeLoadObserver = Observable.EveryUpdate().Subscribe(_ => + { + fakeLoadPercent += Time.deltaTime * 0.2f; + GameManager.Instance.gameLoadingCanvas.SetProgress(displayLoadPercent * 100f); + }); + + Observable.EveryUpdate() + .Where(_ => ((BeatmapContainer_BM)GameManager.Instance.beatmapContainer.matchedBM).remainingElementAmount.Value == 0 && + fakeLoadPercent >= 1f) + .First() + .Subscribe(_ => + { + fakeLoadObserver.Dispose(); + + Observable.Timer(TimeSpan.FromSeconds(1f)).First().Subscribe(_ => + { + GameManager.Instance.gameLoadingCanvas.FadeOut(); + GameManager.Instance.songPlayer.isLoading = false; + + GameManager.Instance.beatmapContainer.gameElementList.ForEach(element => element.BeforeStart()); + + GameManager.Instance.gameUICanvas.readyText.DOFade(1, 0.5f) + .SetLoops(4, LoopType.Yoyo).SetEase(Ease.InOutFlash).Play(); + + Observable.Timer(TimeSpan.FromSeconds(2)).First().Subscribe(_ => + { + GameManager.Instance.songPlayer.isDelaying = true; + GameManager.Instance.songPlayer.isStarting = false; + Observable.NextFrame().Subscribe(_ => + { + GameManager.Instance.timeDurations = GameManager.Instance.beatmapContainer.gameElementList + .Select(x=> (x as IHaveTimeDurationSubmodule)?.timeDurationSubmodule) + .Where(x => x != null && x.startTime > 0 && x.endTime < GameManager.Instance.songInformation.songLength).ToList(); + GameManager.Instance.beatmapContainer.gameElementList.ForEach(element => element.WhenStart()); + }); + }); + }); + }); + }); + + /*Observable.EveryUpdate() + .Where(_ => !GameManager.Instance.audioManager.isStarting) + .First() + .Subscribe(_ => + { + + });*/ + } + + public void Load(string chapterName, string musicName, string difficultyName) + { + string beatMapFolderPath = "Beatmaps/" + chapterName + "/" + musicName + "/" + difficultyName; + + LoadProjectInfo(beatMapFolderPath); + LoadSongInfo(beatMapFolderPath); + LoadCommandScripts(beatMapFolderPath); + LoadBeatMap(beatMapFolderPath); + } + + private void LoadProjectInfo(string beatMapFolderPath) + { + string projectInfoPath = beatMapFolderPath + "/ProjectInfo.bytes"; + ES3.Load("ProjectInformation", projectInfoPath, LoadSettings).ExecuteBM(); + } + + private void LoadSongInfo(string beatMapFolderPath) + { + string songInfoPath = beatMapFolderPath + "/SongInfo.bytes"; + ES3.Load("SongInformation",songInfoPath, LoadSettings).ExecuteBM(); + } + + private void LoadBeatMap(string beatMapFolderPath) + { + string beatmapPath = beatMapFolderPath + "/Beatmap.bytes"; + ES3.Load("Beatmap", beatmapPath, LoadSettings).ExecuteBM(); + } + + private void LoadCommandScripts(string beatMapFolderPath) + { + string commandScriptsPath = beatMapFolderPath + "/CommandScripts.bytes"; + ES3.Load("CommandScripts", commandScriptsPath, LoadSettings).ExecuteBM(); + } + + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ProjectLoader.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ProjectLoader.cs.meta new file mode 100644 index 00000000..d6a21262 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ProjectLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5ef8c67a6d965e247b18f89311136408 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/SongPlayer.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/SongPlayer.cs new file mode 100644 index 00000000..af0f4d5f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/SongPlayer.cs @@ -0,0 +1,163 @@ +using System.Collections; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using SLSUtilities.WwiseAssistance; +using UnityEngine; +using Event = AK.Wwise.Event; + +namespace Ichni +{ + public class SongPlayer : SerializedMonoBehaviour + { + public bool isLoading = true; + public bool isStarting = true; + public bool isDelaying = false; + public bool isPlaying = false; // 是否正在播放音乐 + public bool isPausing = false; // 是否正在暂停音乐 + public bool isFinished = false; + public bool isUpdating => isDelaying || isPlaying; + + public Event PlayMusicEvent; // 播放背景音乐的事件 + public Event ResumeMusicEvent; // 恢复播放背景音乐的事件 + public Event PauseMusicEvent; // 暂停播放背景音乐的事件 + public Event StopMusicEvent; // 停止播放背景音乐的事件 + + private uint _playingId; + public float songTimeSegment = 0; + public float pauseTimeSegment; + private float duration; + private float recordedSongSeg; + + public float judgeOffset = 0; + + private void Start() + { + InformationTransistor.instance.chapterSwitch.SetValue(gameObject); + InformationTransistor.instance.songSwitch.SetValue(gameObject); + isLoading = true; + isStarting = true; + } + + private void Update() + { + if (isLoading) + { + return; + } + + if (isDelaying) + { + songTimeSegment += Time.deltaTime; + //songTimeSegment = Mathf.Max(songTimeSegment, 0); // 确保时间段不为负数 + + if (songTimeSegment >= 0) + { + isDelaying = false; + songTimeSegment = 0; // 延迟结束后,时间段归零 + PlaySong(); + } + } + else + { + if (GameManager.Instance.isDebugging) + { + return; + } + + if (isFinished) + { + songTimeSegment = recordedSongSeg; + + return; + } + + if (isPlaying) + { + float currentSongSegment = PlaySegment() / 1000f - (judgeOffset / 1000f); + + if (recordedSongSeg < currentSongSegment) + { + songTimeSegment = currentSongSegment; + recordedSongSeg = currentSongSegment; + } + } + else if (isPausing) + { + songTimeSegment = pauseTimeSegment; + } + } + } + + + [Button] + public void PlaySong() + { + _playingId = PlayMusicEvent.Post(gameObject, + (uint)AkCallbackType.AK_EnableGetMusicPlayPosition | + (uint)AkCallbackType.AK_MusicSyncEntry | + (uint)AkCallbackType.AK_MusicSyncExit, + OnMusicEvent, null); + + isPlaying = true; + isPausing = false; + isFinished = false; + } + + [Button] + public void PauseSong() + { + pauseTimeSegment = songTimeSegment; + isPlaying = false; + isPausing = true; + PauseMusicEvent.Post(gameObject); + Time.timeScale = 0; + } + + [Button] + public void ResumeSong() + { + Time.timeScale = 1; + isPlaying = true; + isPausing = false; + ResumeMusicEvent.Post(gameObject); + } + + [Button] + public void StopSong() + { + isPlaying = false; + isPausing = false; + StopMusicEvent.Post(gameObject); + } + + private void OnMusicEvent(object in_cookie, AkCallbackType in_type, AkCallbackInfo in_info) + { + Debug.Log(in_type + " " + in_info); + + if (in_type == AkCallbackType.AK_MusicSyncEntry) + { + if (in_info is AkMusicSyncCallbackInfo musicInfo) + { + GameManager.Instance.songInformation.songLength = musicInfo.segmentInfo_iActiveDuration / 1000f; + InformationTransistor.instance.songLength = musicInfo.segmentInfo_iActiveDuration / 1000f; + InformationTransistor.instance.bpm = GameManager.Instance.songInformation.bpm; + } + } + + if (in_type == AkCallbackType.AK_MusicSyncExit) + { + isFinished = true; + GameManager.Instance.summaryPageCanvas.SetUpSummary(); + GameManager.Instance.summaryPageCanvas.FadeIn(); + } + } + + int PlaySegment() + { + AkSegmentInfo segmentInfo = new AkSegmentInfo(); + AkSoundEngine.GetPlayingSegmentInfo(_playingId, segmentInfo,true); + + return segmentInfo.iCurrentPosition; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/SongPlayer.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/SongPlayer.cs.meta new file mode 100644 index 00000000..77a810ae --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/SongPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb5869a7f15cc7f489c8e6aaf7ae5fff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ThemeBundleManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ThemeBundleManager.cs new file mode 100644 index 00000000..717986d5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ThemeBundleManager.cs @@ -0,0 +1,256 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using TMPro; +using UniRx; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.Serialization; + +namespace Ichni +{ + public class ThemeBundleManager : MonoBehaviour + { + public static ThemeBundleManager instance; + + public List themeBundleAbstractList; + public List selectedThemeBundleList; + public List loadedThemeBundleList; + + public IntReactiveProperty waitingBundleAmount; + + private void Awake() + { + instance = this; + + loadedThemeBundleList = new List(); + AssetBundle.UnloadAllAssetBundles(true); + LoadAllThemeBundlesAbstract(); + //LoadThemeBundle("basic"); + //LoadThemeBundle("departure_to_multiverse"); + } + + public bool TryGetThemeBundle(string themeBundleName, out ThemeBundle themeBundle) + { + themeBundle = loadedThemeBundleList.Find(bundle => bundle.themeBundleName == themeBundleName); + return themeBundle != null; + } + + public T GetObject(string themeBundleName, string objectName) where T : class + { + return loadedThemeBundleList.Find(bundle => bundle.themeBundleName == themeBundleName)?.GetObject(objectName); + } + + public void LoadThemeBundles(List list) + { + waitingBundleAmount = new IntReactiveProperty(list.Count); + Debug.Log("Waiting for " + list.Count + " AssetBundles to load."); + foreach (var bundle in list) + { + LoadThemeBundle(bundle); + } + + selectedThemeBundleList = list; + } + + public void LoadAllThemeBundlesAbstract() + { + string uri = Application.streamingAssetsPath + "/ThemeBundles/"; + + if (ES3.DirectoryExists(uri)) + { + List allFileList = ES3.GetFiles(uri).ToList(); + List absList = new List(); + foreach (var abs in allFileList) + { + if (abs.EndsWith(".abs")) + { + absList.Add(abs); + } + } + + foreach (var abs in absList) + { + themeBundleAbstractList.Add(ES3.Load("ThemeBundleAbstract", uri + abs)); + } + } + } + + public void LoadThemeBundle(string themeBundleName) + { + StartCoroutine(LoadThemeBundleCoroutine(themeBundleName)); + } + + IEnumerator LoadThemeBundleCoroutine(string themeBundleName) + { + string uri = ""; + + if (Application.platform == RuntimePlatform.WindowsEditor || + Application.platform == RuntimePlatform.WindowsPlayer) + { + uri = Application.streamingAssetsPath + "/ThemeBundles/Windows64/" + themeBundleName; + } + else if (Application.platform == RuntimePlatform.OSXEditor || + Application.platform == RuntimePlatform.OSXPlayer) + { + uri = Application.streamingAssetsPath + "/ThemeBundles/OSX/" + themeBundleName; + } + else if (Application.platform == RuntimePlatform.Android) + { + uri = Application.streamingAssetsPath + "/ThemeBundles/Android/" + themeBundleName; + } + else if (Application.platform == RuntimePlatform.IPhonePlayer) + { + uri = Application.streamingAssetsPath + "/ThemeBundles/IOS/" + themeBundleName; + } + else + { + Debug.LogError("Unsupported platform for loading theme bundles."); + yield break; + } + + AssetBundleCreateRequest createRequest = AssetBundle.LoadFromFileAsync(uri); + yield return createRequest; + AssetBundle bundle = createRequest.assetBundle; + Object[] ob = bundle.LoadAllAssets(); + loadedThemeBundleList.Add(new ThemeBundle(themeBundleName)); + + for (int i = 0; i < ob.Length; i++) + { + if (ob[i].GetType() == typeof(GameObject)) + { + loadedThemeBundleList[^1].assetList_GameObject.Add(ob[i] as GameObject); + } + else if (ob[i].GetType() == typeof(Material)) + { + loadedThemeBundleList[^1].assetList_Material.Add(ob[i] as Material); + } + else if (ob[i].GetType() == typeof(Texture2D)) + { + loadedThemeBundleList[^1].assetList_Texture.Add(ob[i] as Texture2D); + } + else + { + loadedThemeBundleList[^1].assetList_Other.Add(ob[i]); + } + } + + yield return new WaitForEndOfFrame(); + print(themeBundleName + " Done!"); + waitingBundleAmount.Value--; + } + } + + [System.Serializable] + public class ThemeBundleAbstract + { + public string fileName; + public string displayName; + public string description; + public List tags; + public string iconPath; + + public ThemeBundleAbstract() + { + } + + public ThemeBundleAbstract(string fileName) + { + this.fileName = fileName; + this.displayName = fileName; + this.description = "Default Description"; + this.tags = new List(); + this.iconPath = "Icons/Default.png"; + } + + public Texture2D GetIcon() + { + string uri = Application.streamingAssetsPath + "/" + iconPath; + if (ES3.FileExists(uri)) + { + Texture2D sp = ES3.LoadImage(uri); + return sp; + } + return null; + } + } + + [System.Serializable] + public class ThemeBundle + { + public string themeBundleName; + public List assetList_GameObject; + public List assetList_Material; + public List assetList_Texture; + public List assetList_Other; + + public ThemeBundle(string name) + { + themeBundleName = name; + assetList_GameObject = new List(); + assetList_Material = new List(); + assetList_Texture = new List(); + assetList_Other = new List(); + } + + public T GetObject(string name) + { + if (name == "None") + { + return default(T); + } + + T[] assetList; + + if (typeof(T) == typeof(GameObject)) + { + assetList = assetList_GameObject.ToArray() as T[]; + + for (int i = 0; i < assetList_GameObject.Count; i++) + { + if (name == assetList_GameObject[i].name) + { + return assetList[i]; + } + } + } + else if (typeof(T) == typeof(Material)) + { + assetList = assetList_Material.ToArray() as T[]; + + for (int i = 0; i < assetList_Material.Count; i++) + { + if (name == assetList_Material[i].name) + { + return assetList[i]; + } + } + } + else if (typeof(T) == typeof(Texture2D)) + { + assetList = assetList_Texture.ToArray() as T[]; + + for (int i = 0; i < assetList_Texture.Count; i++) + { + if (name == assetList_Texture[i].name) + { + return assetList[i]; + } + } + } + else + { + assetList = assetList_Other.ToArray() as T[]; + for (int i = 0; i < assetList_Other.Count; i++) + { + if (name == assetList_Other[i].name) + { + return assetList[i]; + } + } + } + + return default(T); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ThemeBundleManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ThemeBundleManager.cs.meta new file mode 100644 index 00000000..dd69cfdc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/ThemeBundleManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d27859adda1341aaa0db8a117d5431c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/TrackManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/TrackManager.cs new file mode 100644 index 00000000..c94077a6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/TrackManager.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class TrackManager : MonoBehaviour + { + #region [核心管家字段] Fields + // 这里可以分类存储场上的各类轨道组件 + private List _activeTracks = new List(50); + private List _activeHeadPoints = new List(50); + private List _activePercentPoints = new List(50); + private List _activeCrossPoints = new List(50); + #endregion + + #region [注册与注销] Registration + public void RegisterTrack(Track track) => _activeTracks.Add(track); + public void UnregisterTrack(Track track) => _activeTracks.Remove(track); + + public void RegisterHeadPoint(TrackHeadPoint point) => _activeHeadPoints.Add(point); + public void UnregisterHeadPoint(TrackHeadPoint point) => _activeHeadPoints.Remove(point); + + public void RegisterPercentPoint(TrackPercentPoint point) => _activePercentPoints.Add(point); + public void UnregisterPercentPoint(TrackPercentPoint point) => _activePercentPoints.Remove(point); + + public void RegisterCrossPoint(CrossTrackPoint point) => _activeCrossPoints.Add(point); + public void UnregisterCrossPoint(CrossTrackPoint point) => _activeCrossPoints.Remove(point); + #endregion + + #region [中央集权主循环] Manager-Driven Tick + // 注意:如果你希望将所有的 Manager 都由 GameManager.Update 统一调度, + // 可以将此处的 Update() 和 LateUpdate() 改为公开的 ManualUpdate(float songTime)。 + public void ManualUpdate(float currentSongTime) + { + if (!GameManager.Instance.songPlayer.isUpdating) return; + + // 1. 更新所有主轨道的滑动区间 + for (var index = 0; index < _activeTracks.Count; index++) + { + var track = _activeTracks[index]; + if (track.isActiveAndEnabled) track.ManualUpdate(currentSongTime); + } + + // 2. 更新所有的轨道头部引导点 + for (var index = 0; index < _activeHeadPoints.Count; index++) + { + var headPoint = _activeHeadPoints[index]; + if (headPoint.isActiveAndEnabled) headPoint.ManualUpdate(currentSongTime); + } + + // 3. 更新基于百分比的相对点 + for (var index = 0; index < _activePercentPoints.Count; index++) + { + var percentPoint = _activePercentPoints[index]; + if (percentPoint.isActiveAndEnabled) percentPoint.ManualUpdate(currentSongTime); + } + + // 4. 更新跨轨切分点 + for (var index = 0; index < _activeCrossPoints.Count; index++) + { + var crossPoint = _activeCrossPoints[index]; + if (crossPoint.isActiveAndEnabled) crossPoint.ManualUpdate(currentSongTime); + } + } + + public void ManualLateUpdate(float currentSongTime) + { + if (!GameManager.Instance.songPlayer.isUpdating) return; + + // 执行所有轨道的重置与缓冲清理操作 + foreach (var track in _activeTracks) + { + if(track.isActiveAndEnabled) track.ManualLateUpdate(); + } + } + #endregion + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/TrackManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/TrackManager.cs.meta new file mode 100644 index 00000000..d3cd178d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Manager/TrackManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: da561687e36397a46a7e241694ba3c8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu.meta new file mode 100644 index 00000000..98d4612b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 509fb7ef7b830e14b9d6568767c0562a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection.meta new file mode 100644 index 00000000..e0f2c02f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 03778af9388dbd545b4c1d03776d3ae7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionManager.cs new file mode 100644 index 00000000..80319bfa --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionManager.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.Menu; +using Ichni.RhythmGame; +using Ichni.UI; +using Sirenix.OdinInspector; + +namespace Ichni +{ + public partial class ChapterSelectionManager : SerializedMonoBehaviour + { + public static ChapterSelectionManager instance; + + public ChapterSelectionUIPage chapterSelectionUIPage; + public List chapters; + public TutorialCollection tutorialCollection; + public ChapterSelectionUnit currentChapter; + } + + public partial class ChapterSelectionManager + { + private void Awake() + { + instance = this; + } + + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionManager.cs.meta new file mode 100644 index 00000000..099ddcd7 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8aa09572a86d1ab47af92aa372e1f90d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs new file mode 100644 index 00000000..d2f6ea1f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using AK.Wwise; +using Ichni.RhythmGame; +using Sirenix.OdinInspector; +using SLSUtilities.WwiseAssistance; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.Menu +{ + [CreateAssetMenu(fileName = "DefaultChapter", menuName = "Ichni/UI/ChapterSelectionUnit", order = 0)] + public partial class ChapterSelectionUnit : SerializedScriptableObject + { + public string chapterIndex; + public string chapterName; + public string chapterSubtitle; + public Color themeColor; + public Sprite avatar; + public Switch chapterSwitch; + + [Searchable] + public List songs = new List(); + + [Button] + public void SetUpDefaultDifficulties() + { + foreach (SongItemData song in songs) + { + if(song.difficultyDataList.All(d => d.difficultyName != "Easy")) + { + song.difficultyDataList.Add(new DifficultyData( + 0, "Easy","Easy", 0, "", + new Color(0f, 0.7f, 0.2f, 1f))); + } + + if (song.difficultyDataList.All(d => d.difficultyName != "Hard")) + { + song.difficultyDataList.Add(new DifficultyData( + 1,"Hard", "Hard", 0, "", + new Color(1f, 0.2f, 0.2f, 1f))); + } + } + } + + [Button] + public void SelectSwitch() + { + AudioManager.SetSwitch(chapterSwitch); + } + } + + public partial class ChapterSelectionUnit + { + public List GetRelatedSongNamesOfUnlockKey(string key) + { + return (from song in songs where song.storyUnlockKey == key select song.songName).ToList(); + } + + public (int, int, int, int, int) GetChapterSaveInfo() + { + int beatmapCount = 0; + int finishedSongCount = 0; + int finishedBeatmapCount = 0; + int fullComboCount = 0; + int allPerfectCount = 0; + + foreach (SongItemData song in songs) + { + if (GameSaveManager.instance.SongSaveModule.songStatusSaves.TryGetValue(song.songName, out var songStatus)) + { + bool thisSongFinished = false; + foreach (BeatmapSave beatmapSave in songStatus.beatmapSaves) + { + beatmapCount++; + if (!beatmapSave.IsEmpty()) + { + thisSongFinished = true; + finishedBeatmapCount++; + + if (beatmapSave.isFullCombo) + { + fullComboCount++; + } + + if (beatmapSave.isAllPerfect) + { + allPerfectCount++; + } + } + } + + if (thisSongFinished) + { + finishedSongCount++; + } + } + } + + return (beatmapCount, finishedSongCount, finishedBeatmapCount, fullComboCount, allPerfectCount); + } + } + + [InlineProperty] + [Serializable] + public class SongItemData + { + [FoldoutGroup("$songName", false)] + public string songName; + + [FoldoutGroup("$songName")] + public string displaySongName; + + [FoldoutGroup("$songName")] + public string composer; + + [FoldoutGroup("$songName")] + public Switch songSwitch; + + [FoldoutGroup("$songName")] + public Sprite illustration; + + [FoldoutGroup("$songName")] + public string illustratorName; + + [FoldoutGroup("$songName")] + public string additionalInformation; + + [FoldoutGroup("$songName")] + public List difficultyDataList; + + [FoldoutGroup("$songName")] + public string storyUnlockKey; + + [FoldoutGroup("$songName")] + public string paymentUnlockKey; + } + + [Serializable] + public class DifficultyData + { + public int difficultyIndex; + public string difficultyName; + public string displayDifficultyName; + public int difficultyValue; + public string charterName; + public Color color; + public bool isAvailable; + + public DifficultyData() + { + + } + + public DifficultyData(int difficultyIndex, string difficultyName, string displayDifficultyName, int difficultyValue, string charterName, Color color) + { + this.difficultyIndex = difficultyIndex; + this.difficultyName = difficultyName; + this.displayDifficultyName = displayDifficultyName; + this.charterName = charterName; + this.difficultyValue = difficultyValue; + this.color = color; + this.isAvailable = true; + } + + public string GetDifficultyName() + { + return string.IsNullOrEmpty(displayDifficultyName) ? difficultyName : displayDifficultyName; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs.meta new file mode 100644 index 00000000..d13cf1eb --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/ChapterSelection/ChapterSelectionUnit.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bf4450f8404fb5d4991d2b5a7973ba31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/InformationTransistor.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/InformationTransistor.cs new file mode 100644 index 00000000..cd61b8b2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/InformationTransistor.cs @@ -0,0 +1,52 @@ +using System.Collections; +using System.Collections.Generic; +using AK.Wwise; +using Ichni.Menu; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni +{ + public class InformationTransistor : MonoBehaviour + { + public static InformationTransistor instance; + + public bool isReturnedFromGame; + public bool isReturnedFromTutorial; + + public ChapterSelectionUnit chapter; + public SongItemData song; + public DifficultyData difficulty; + + public float songLength; + public float bpm; + + public Switch chapterSwitch; + public Switch songSwitch; + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + isReturnedFromGame = false; + isReturnedFromTutorial = false; + } + else + { + Destroy(gameObject); + } + } + + public void SetInformation(ChapterSelectionUnit chapter, + SongItemData song, DifficultyData difficulty) + { + this.chapter = chapter; + this.song = song; + this.difficulty = difficulty; + this.chapterSwitch = chapter.chapterSwitch; + this.songSwitch = song.songSwitch; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/InformationTransistor.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/InformationTransistor.cs.meta new file mode 100644 index 00000000..701c80fd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/InformationTransistor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c6497985923eab43a4c3dd28ba0b9d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInformationRecorder.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInformationRecorder.cs new file mode 100644 index 00000000..0bebdce9 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInformationRecorder.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Menu +{ + public class MenuInformationRecorder : SerializedMonoBehaviour + { + public static MenuInformationRecorder instance; + public Dictionary songSelectionRecords; + + private void Awake() + { + if (instance == null) + { + instance = this; + songSelectionRecords = new Dictionary(); + } + else + { + Destroy(gameObject); + } + } + + public SongSelectionRecord GetRecordOfThisChapter() + { + ChapterSelectionUnit currentChapter = ChapterSelectionManager.instance.currentChapter; + if (songSelectionRecords.TryGetValue(currentChapter, out SongSelectionRecord record)) + { + return record; + } + else + { + return new SongSelectionRecord(currentChapter.songs[0], currentChapter.songs[0].difficultyDataList[0]); + } + } + } + + public class SongSelectionRecord + { + public SongItemData song; + public int difficultyIndex; + + public SongSelectionRecord(SongItemData song, DifficultyData difficulty) + { + this.song = song; + difficultyIndex = song.difficultyDataList.IndexOf(difficulty); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInformationRecorder.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInformationRecorder.cs.meta new file mode 100644 index 00000000..87e8d8c5 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInformationRecorder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fea7f0403d91a0748adaa9946f58d1f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInputManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInputManager.cs new file mode 100644 index 00000000..523b2eb0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInputManager.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.InputSystem; + +namespace Ichni.Menu +{ + public class MenuInputManager : MonoBehaviour + { + public static MenuInputManager instance; + + public GameInput gameInput; + public PlayerInput playerInput; + + public static event UnityAction OnMoveUp; + public static event UnityAction OnMoveDown; + + private void Awake() + { + instance = this; + gameInput = new GameInput(); + gameInput.Menu.Enable(); + RegisterActionsInputs(); + } + + private void RegisterActionsInputs() + { + gameInput.Menu.Up.performed += ctx => + { + if (ctx.performed) + { + OnMoveUp?.Invoke(); + } + }; + + gameInput.Menu.Down.performed += ctx => + { + if (ctx.performed) + { + OnMoveDown?.Invoke(); + } + }; + + gameInput.Menu.Scroll.performed += ctx => + { + if (ctx.performed) + { + float scrollValue = ctx.ReadValue(); + if (scrollValue > 0) + { + OnMoveUp?.Invoke(); + } + else if (scrollValue < 0) + { + OnMoveDown?.Invoke(); + } + } + }; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInputManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInputManager.cs.meta new file mode 100644 index 00000000..c8a093cc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuInputManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95c5a7741e873fd44a1236b4dcedb1d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuManager.cs new file mode 100644 index 00000000..b333c86a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuManager.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using AK.Wwise; +using Ichni.Menu; +using Ichni.Story.UI; +using Ichni.UI; +using Sirenix.OdinInspector; +using SLSUtilities.WwiseAssistance; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.Serialization; + +namespace Ichni +{ + public partial class MenuManager : MonoBehaviour + { + public static MenuManager instance; + + public StartUIPage startUIPage; + public ChapterSelectionUIPage chapterSelectionUIPage; + public StoryUIPage storyUIPage; + public DialogUIPage dialogUIPage; + public SongSelectionUIPage songSelectionUIPage; + public TransitionUIPage transitionUIPage; + public SettingsUIPage settingsUIPage; + + public List languageList; + public List displayLanguageList; + } + + public partial class MenuManager + { + AsyncOperation asyncOperation; + public bool isEnteringGame; + + private void Awake() + { + instance = this; + } + + private void Start() + { + isEnteringGame = false; + + if (InformationTransistor.instance.isReturnedFromGame) + { + startUIPage.mainCanvasGroup.gameObject.SetActive(false); + ChapterSelectionManager.instance.currentChapter = + InformationTransistor.instance.chapter; + AudioManager.SetSwitch(ChapterSelectionManager.instance.currentChapter.chapterSwitch); + MenuInformationRecorder.instance.songSelectionRecords.Add( + ChapterSelectionManager.instance.currentChapter, + new SongSelectionRecord(InformationTransistor.instance.song, InformationTransistor.instance.difficulty)); + songSelectionUIPage.FadeIn(); + } + else if (InformationTransistor.instance.isReturnedFromTutorial) + { + startUIPage.mainCanvasGroup.gameObject.SetActive(false); + ChapterSelectionManager.instance.currentChapter = InformationTransistor.instance.chapter; + AudioManager.SetSwitch(ChapterSelectionManager.instance.currentChapter.chapterSwitch); + storyUIPage.FadeIn(); + } + + Application.targetFrameRate = SettingsManager.instance.gameSettings.targetFrame; + asyncOperation = SceneManager.LoadSceneAsync("GameScene"); + asyncOperation.allowSceneActivation = false; + } + } + + public partial class MenuManager + { + public void EnterGame() + { + InformationTransistor.instance.isReturnedFromTutorial = false; + InformationTransistor.instance.isReturnedFromGame = true; + EnterGameScene(); + } + + public void EnterGameScene() + { + MenuInputManager.instance.gameInput.Menu.Disable(); + asyncOperation.allowSceneActivation = true; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuManager.cs.meta new file mode 100644 index 00000000..5cbe0e3a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/MenuManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f493d04990806d4ea9ca1e0f7733048 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection.meta new file mode 100644 index 00000000..a49c76f8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b7a92293064a12b44b29a26166706d0a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection/SongSelectionManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection/SongSelectionManager.cs new file mode 100644 index 00000000..37ea48b6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection/SongSelectionManager.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using DG.Tweening; +using Ichni.Menu.UI; +using Ichni.RhythmGame; +using Ichni.UI; +using Sirenix.OdinInspector; +using SLSUtilities.WwiseAssistance; +using UnityEngine; + +namespace Ichni.Menu +{ + public partial class SongSelectionManager : SerializedMonoBehaviour + { + public static SongSelectionManager instance; + + public SongSelectionUIPage songSelectionUIPage; + + public float currentFilterValue; + public Tweener filterTweener; + + private uint _playPreviewPlayingID; + + private void Awake() + { + instance = this; + currentFilterValue = 100f; + _playPreviewPlayingID = AkUnitySoundEngine.AK_INVALID_PLAYING_ID; + } + + } + + public partial class SongSelectionManager + { + public void SetPreview(SongItemData connectedSong, bool isLocked) + { + if(_playPreviewPlayingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + AudioManager.Stop(_playPreviewPlayingID); + } + AudioManager.SetSwitch(connectedSong.songSwitch); + _playPreviewPlayingID = AudioManager.Post(AK.EVENTS.PLAYPREVIEW); + + float targetFilterValue = isLocked ? 50 : 100; + if (!Mathf.Approximately(currentFilterValue, targetFilterValue)) + { + filterTweener.Kill(true); + filterTweener = + DOTween.To(() => currentFilterValue, x => currentFilterValue = x, targetFilterValue, 1f) + .SetEase(Ease.OutQuad) + .OnUpdate(() => + { + AudioManager.SetRTPC("PreviewLowPassFilter", currentFilterValue); + }).Play(); + } + } + + public void StopPreviewSong() + { + if(_playPreviewPlayingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + AudioManager.Stop(_playPreviewPlayingID); + _playPreviewPlayingID = AkUnitySoundEngine.AK_INVALID_PLAYING_ID; + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection/SongSelectionManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection/SongSelectionManager.cs.meta new file mode 100644 index 00000000..9bbc40a0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SongSelection/SongSelectionManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 726b9313110aee54fbba1c1891bdaa1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SoundBankInitializer.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SoundBankInitializer.cs new file mode 100644 index 00000000..14bad463 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SoundBankInitializer.cs @@ -0,0 +1,22 @@ +using System.Collections; +using System.Collections.Generic; +using AK.Wwise; +using UnityEngine; + +namespace Ichni.Menu +{ + public class SoundBankInitializer : MonoBehaviour + { + public bool initialized = false; + public List soundBanks; + + private void Awake() + { + if (initialized) return; + + initialized = true; + soundBanks.ForEach(bank => bank.Load()); + Debug.Log("AudioManager initialized with " + soundBanks.Count + " sound banks loaded."); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SoundBankInitializer.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SoundBankInitializer.cs.meta new file mode 100644 index 00000000..072341af --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/SoundBankInitializer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 42a430e2cbe01744b80892b0f74b9317 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial.meta new file mode 100644 index 00000000..52398d20 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7016ea9e7660ad347839677dfdc276f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial/TutorialCollection.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial/TutorialCollection.cs new file mode 100644 index 00000000..b3aed34f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial/TutorialCollection.cs @@ -0,0 +1,14 @@ +using System.Collections; +using System.Collections.Generic; +using AK.Wwise; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Menu +{ + [CreateAssetMenu(fileName = "TutorialCollection", menuName = "Ichni/TutorialCollection")] + public class TutorialCollection : SerializedScriptableObject + { + public Dictionary songs = new Dictionary(); + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial/TutorialCollection.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial/TutorialCollection.cs.meta new file mode 100644 index 00000000..b9ab5396 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Menu/Tutorial/TutorialCollection.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8561f35c982f8ea4b9d5ea726028ff7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities.meta new file mode 100644 index 00000000..aa030d20 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0569c6ecd8d125b428fe4ca993e668bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General.meta new file mode 100644 index 00000000..fd796f01 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7cbe1888dddc87246aa5867a7622f319 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ColliderExtensions.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ColliderExtensions.cs new file mode 100644 index 00000000..4fdbaefc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ColliderExtensions.cs @@ -0,0 +1,85 @@ +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class ColliderExtensions + { + /// + /// 判断一个世界坐标点是否在 BoxCollider 内部(支持旋转和缩放) + /// + public static bool IsPointInside(this BoxCollider box, Vector3 worldPoint) + { + // 1. 将世界坐标转为该 Collider 的本地坐标(处理了旋转和位置) + Vector3 localPoint = box.transform.InverseTransformPoint(worldPoint); + + // 2. 减去中心偏移 + localPoint -= box.center; + + // 3. 计算实际的半长宽高(考虑 LossyScale 缩放) + // 注意:BoxCollider 的 size 已经包含了本地缩放,InverseTransformPoint 已经处理了缩放的影响 + Vector3 halfSize = box.size * 0.5f; + + // 4. AABB 判定 + return Mathf.Abs(localPoint.x) <= halfSize.x && + Mathf.Abs(localPoint.y) <= halfSize.y && + Mathf.Abs(localPoint.z) <= halfSize.z; + } + + /// + /// 判断一个世界坐标点是否在 SphereCollider 内部 + /// + public static bool IsPointInside(this SphereCollider sphere, Vector3 worldPoint) + { + // 考虑中心偏移的世界坐标中心点 + Vector3 center = sphere.transform.TransformPoint(sphere.center); + + // 计算缩放后的实际半径(Unity 以三个轴中缩放最大的为准) + Vector3 lossyScale = sphere.transform.lossyScale; + float maxScale = Mathf.Max(lossyScale.x, Mathf.Max(lossyScale.y, lossyScale.z)); + float scaledRadius = sphere.radius * maxScale; + + // 距离平方判定,性能最高 + float sqrDistance = (worldPoint - center).sqrMagnitude; + return sqrDistance <= (scaledRadius * scaledRadius); + } + + /// + /// 判断一个世界坐标点是否在 CapsuleCollider 内部 + /// + public static bool IsPointInside(this CapsuleCollider capsule, Vector3 worldPoint) + { + // 转为本地坐标 + Vector3 localPoint = capsule.transform.InverseTransformPoint(worldPoint); + localPoint -= capsule.center; + + // 计算胶囊体内部中心轴线的半高度(不含两头的半圆) + float radius = capsule.radius; + float halfHeight = Mathf.Max(0f, (capsule.height * 0.5f) - radius); + + // 根据方向计算点到中心线段的最短距离 + // direction: 0 = X, 1 = Y, 2 = Z + Vector3 closestPointOnAxis = Vector3.zero; + switch (capsule.direction) + { + case 0: closestPointOnAxis.x = Mathf.Clamp(localPoint.x, -halfHeight, halfHeight); break; + case 1: closestPointOnAxis.y = Mathf.Clamp(localPoint.y, -halfHeight, halfHeight); break; + case 2: closestPointOnAxis.z = Mathf.Clamp(localPoint.z, -halfHeight, halfHeight); break; + } + + return (localPoint - closestPointOnAxis).sqrMagnitude <= (radius * radius); + } + + /// + /// 通用入口:根据 Collider 类型自动选择检测方法 + /// + public static bool IsPointInside(this Collider collider, Vector3 worldPoint) + { + if (collider is BoxCollider box) return box.IsPointInside(worldPoint); + if (collider is SphereCollider sphere) return sphere.IsPointInside(worldPoint); + if (collider is CapsuleCollider capsule) return capsule.IsPointInside(worldPoint); + + // 如果是 MeshCollider 等复杂形状,回退到 Bounds 粗略检测 + return collider.bounds.Contains(worldPoint); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ColliderExtensions.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ColliderExtensions.cs.meta new file mode 100644 index 00000000..77d33f38 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ColliderExtensions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bbfbb5726f87a7241b8f73b6201de57d \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/CompoundBool.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/CompoundBool.cs new file mode 100644 index 00000000..c524ce58 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/CompoundBool.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace SLSUtilities.General +{ + /// + /// 一个复合布尔值类,支持多来源修改和优先级系统。 + /// 可以将多个来源对布尔值的修改综合起来,由高优先级的修改决定最终结果。 + /// + [InlineProperty] + [HideReferenceObjectPicker] + public class CompoundBool + { + /// + /// 返回当前生效优先级层级的计数。 + /// + [ShowInInspector] + [LabelText("True Count"), LabelWidth(70)] + [HorizontalGroup("Stats", Width = 100)] + public int TrueCount => GetEffectiveCount(); + + private SortedList counts = new SortedList(); + private bool isDirty = true; + private bool cachedValue; + + /// + /// 获取最终计算出的布尔值。 + /// + [ShowInInspector] + [LabelText("Value"), LabelWidth(50)] + [HorizontalGroup("Stats", Width = 100)] + public bool Value + { + get + { + if (isDirty) RecalculateValue(); + return cachedValue; + } + } + + /// + /// 初始化 CompoundBool。 + /// + /// 初始值。如果为 true,将在优先级 0 初始化计数为 1。 + public CompoundBool(bool initialValue = false) + { + if (initialValue) Modify(true, 0); + } + + /// + /// 在指定的优先级层级修改布尔值。 + /// 高优先级的值会覆盖低优先级的值。 + /// 如果某个非零优先级的计数变为零,将自动移除该优先级层级。 + /// + /// 修改意图(true 增加计数,false 减少计数)。 + /// 优先级,默认为 0。数字越大优先级越高。 + public void Modify(bool marker, int priority = 0) + { + if (counts.TryGetValue(priority, out int currentCount)) + { + counts[priority] = marker ? currentCount + 1 : currentCount - 1; + } + else + { + counts.Add(priority, marker ? 1 : -1); + } + + if(priority != 0 && counts[priority] == 0) + { + counts.Remove(priority); + } + + isDirty = true; + } + + /// + /// 重置所有修改,恢复到默认状态(false)。 + /// + public void Reset() + { + counts.Clear(); + isDirty = true; + } + + private void RecalculateValue() + { + // 从最高优先级(列表末尾)向最低优先级迭代 + for (int i = counts.Count - 1; i >= 0; i--) + { + int count = counts.Values[i]; + if (count != 0) + { + cachedValue = count > 0; + isDirty = false; + return; + } + } + + // 如果所有计数都为零或列表为空,则默认为 false + cachedValue = false; + isDirty = false; + } + + private int GetEffectiveCount() + { + // 从最高优先级向最低优先级迭代 + for (int i = counts.Count - 1; i >= 0; i--) + { + int count = counts.Values[i]; + if (count != 0) return count; + } + return 0; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/CompoundBool.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/CompoundBool.cs.meta new file mode 100644 index 00000000..c831b28f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/CompoundBool.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e0ce182426546484b89de4813493f8f0 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/DictionaryExtension.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/DictionaryExtension.cs new file mode 100644 index 00000000..e82f77a6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/DictionaryExtension.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class DictionaryExtension + { + /// + /// 删除所有满足 predicate 的键值对。 + /// + public static void RemoveWhere(this IDictionary dict, Func predicate) + { + dict.Where(pair => predicate(pair.Key, pair.Value)).Select(pair => pair.Key).ToList().ForEach(key => dict.Remove(key)); + } + + /// + /// 修改字典中指定键的值,如果键不存在则添加该键值对。 + /// + public static void ModifyOrAdd(this IDictionary dictionary, string key, float delta, float defaultValue = 0) + { + if (!dictionary.TryAdd(key, defaultValue + delta)) + { + dictionary[key] += delta; + } + } + + /// + /// 从字典中获取指定键的原始值。 + /// + public static float GetRawValue(this Dictionary dictionary, string key, float defaultValue = 0) + { + return dictionary.GetValueOrDefault(key, defaultValue); + } + + /// + /// 从字典中获取指定键的值,并将其四舍五入为整数返回。 + /// + public static int GetRoundValue(this Dictionary dictionary, string key, int defaultValue = 0) + { + return dictionary.TryGetValue(key, out float value) ? Mathf.RoundToInt(value) : defaultValue; + } + + /// + /// 从字典中获取指定键的值,并将其向下取整为整数返回。 + /// + public static int GetFloorValue(this Dictionary dictionary, string key, int defaultValue = 0) + { + return dictionary.TryGetValue(key, out float value) ? Mathf.FloorToInt(value) : defaultValue; + } + + /// + /// 将源字典中的所有键值对粘贴到目标字典中,避免重复键。 + /// + public static void Paste(this IDictionary source, IDictionary target) + { + foreach (var pair in source) + { + if (!target.ContainsKey(pair.Key)) + { + target[pair.Key] = pair.Value; + } + else + { + Debug.LogWarning($"Attribute \"{pair.Key}\" already exists. Skipping duplicate."); + } + } + } + } + + public static class DictionaryExtensionForEvents + { + #region 不含返回值的事件 + public static void Invoke(this IDictionary dictionary) + { + foreach (string key in dictionary.Keys.ToList()) + { + var pAction = dictionary[key]; + pAction.Invoke(); + + if (pAction.ShouldRemove) + { + dictionary.Remove(key); + } + } + } + + public static void Invoke(this IDictionary> dictionary, T arg) + { + foreach (string key in dictionary.Keys.ToList()) + { + var pAction = dictionary[key]; + pAction.Invoke(arg); + + if (pAction.ShouldRemove) + { + dictionary.Remove(key); + } + } + } + + public static void Invoke(this IDictionary> dictionary, T0 arg0, T1 arg1) + { + foreach (string key in dictionary.Keys.ToList()) + { + var pAction = dictionary[key]; + pAction.Invoke(arg0, arg1); + + if (pAction.ShouldRemove) + { + dictionary.Remove(key); + } + } + } + + + public static void Invoke(this IDictionary> dictionary, T0 arg0, T1 arg1, T2 arg2) + { + foreach (string key in dictionary.Keys.ToList()) + { + var pAction = dictionary[key]; + pAction.Invoke(arg0, arg1, arg2); + + if (pAction.ShouldRemove) + { + dictionary.Remove(key); + } + } + } + #endregion + + #region 含有返回值的事件 + + public static Dictionary Invoke(this IDictionary> dictionary) + { + Dictionary results = new Dictionary(); + + foreach (string key in dictionary.Keys.ToList()) + { + var pFunc = dictionary[key]; + results[key] = pFunc.Invoke(); + + if (pFunc.ShouldRemove) + { + dictionary.Remove(key); + } + } + + return results; + } + + public static Dictionary Invoke(this IDictionary> dictionary, T arg) + { + Dictionary results = new Dictionary(); + + foreach (string key in dictionary.Keys.ToList()) + { + var pFunc = dictionary[key]; + results[key] = pFunc.Invoke(arg); + + if (pFunc.ShouldRemove) + { + dictionary.Remove(key); + } + } + + return results; + } + + public static Dictionary Invoke(this IDictionary> dictionary, T0 arg0, T1 arg1) + { + Dictionary results = new Dictionary(); + + foreach (string key in dictionary.Keys.ToList()) + { + var pFunc = dictionary[key]; + results[key] = pFunc.Invoke(arg0, arg1); + + if (pFunc.ShouldRemove) + { + dictionary.Remove(key); + } + } + + return results; + } + + public static Dictionary Invoke(this IDictionary> dictionary, T0 arg0, T1 arg1, T2 arg2) + { + Dictionary results = new Dictionary(); + + foreach (string key in dictionary.Keys.ToList()) + { + var pFunc = dictionary[key]; + results[key] = pFunc.Invoke(arg0, arg1, arg2); + + if (pFunc.ShouldRemove) + { + dictionary.Remove(key); + } + } + + return results; + } + #endregion + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/DictionaryExtension.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/DictionaryExtension.cs.meta new file mode 100644 index 00000000..c30254d0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/DictionaryExtension.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c2791d3b316c2614394010404ee2ccc4 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Ease.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Ease.cs new file mode 100644 index 00000000..7b83a490 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Ease.cs @@ -0,0 +1,157 @@ +using UnityEngine; +using System; +using System.Collections.Generic; + +namespace SLSUtilities.General +{ + public enum EaseType + { + Linear, + InSine, OutSine, InOutSine, + InQuad, OutQuad, InOutQuad, + InCubic, OutCubic, InOutCubic, + InQuart, OutQuart, InOutQuart, + InQuint, OutQuint, InOutQuint, + InExpo, OutExpo, InOutExpo, + InCirc, OutCirc, InOutCirc, + InElastic, OutElastic, InOutElastic, + InBack, OutBack, InOutBack, + InBounce, OutBounce, InOutBounce, + Flash, InFlash, OutFlash, InOutFlash + } + + public static class Ease + { + // 缓存生成的曲线,避免重复计算 + private static readonly Dictionary CurveCache = new Dictionary(); + + /// + /// 获取指定的缓动曲线 + /// + public static AnimationCurve GetCurve(EaseType easeType) + { + if (CurveCache.TryGetValue(easeType, out AnimationCurve cachedCurve)) + { + return cachedCurve; + } + + AnimationCurve newCurve = CreateCurve(easeType); + CurveCache[easeType] = newCurve; + return newCurve; + } + + private static AnimationCurve CreateCurve(EaseType easeType) + { + Keyframe[] keys; + // 采样精细度:对于复杂的曲线(如 Elastic, Bounce),采样点需要多一些 + int samples = IsComplex(easeType) ? 50 : 20; + keys = new Keyframe[samples + 1]; + + for (int i = 0; i <= samples; i++) + { + float t = (float)i / samples; + float v = Evaluate(easeType, t); + keys[i] = new Keyframe(t, v); + } + + AnimationCurve curve = new AnimationCurve(keys); + + // 平滑曲线切线(除线性外) + if (easeType != EaseType.Linear) + { + for (int i = 0; i < keys.Length; i++) + { + curve.SmoothTangents(i, 0f); + } + } + + return curve; + } + + private static bool IsComplex(EaseType type) + { + return type.ToString().Contains("Elastic") || + type.ToString().Contains("Bounce") || + type.ToString().Contains("Flash"); + } + + // 数学计算核心:标准的 Robert Penner 缓动公式 + private static float Evaluate(EaseType type, float t) + { + switch (type) + { + case EaseType.Linear: return t; + case EaseType.InSine: return 1f - Mathf.Cos(t * Mathf.PI * 0.5f); + case EaseType.OutSine: return Mathf.Sin(t * Mathf.PI * 0.5f); + case EaseType.InOutSine: return -(Mathf.Cos(Mathf.PI * t) - 1f) * 0.5f; + case EaseType.InQuad: return t * t; + case EaseType.OutQuad: return 1f - (1f - t) * (1f - t); + case EaseType.InOutQuad: return t < 0.5f ? 2f * t * t : 1f - Mathf.Pow(-2f * t + 2f, 2f) * 0.5f; + case EaseType.InCubic: return t * t * t; + case EaseType.OutCubic: return 1f - Mathf.Pow(1f - t, 3f); + case EaseType.InOutCubic: return t < 0.5f ? 4f * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 3f) * 0.5f; + case EaseType.InQuart: return t * t * t * t; + case EaseType.OutQuart: return 1f - Mathf.Pow(1f - t, 4f); + case EaseType.InOutQuart: return t < 0.5f ? 8f * t * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 4f) * 0.5f; + case EaseType.InQuint: return t * t * t * t * t; + case EaseType.OutQuint: return 1f - Mathf.Pow(1f - t, 5f); + case EaseType.InOutQuint: return t < 0.5f ? 16f * t * t * t * t * t : 1f - Mathf.Pow(-2f * t + 2f, 5f) * 0.5f; + case EaseType.InExpo: return t == 0f ? 0f : Mathf.Pow(2f, 10f * t - 10f); + case EaseType.OutExpo: return t == 1f ? 1f : 1f - Mathf.Pow(2f, -10f * t); + case EaseType.InOutExpo: + return t == 0f ? 0f : t == 1f ? 1f : t < 0.5f ? Mathf.Pow(2f, 20f * t - 10f) * 0.5f : (2f - Mathf.Pow(2f, -20f * t + 10f)) * 0.5f; + case EaseType.InCirc: return 1f - Mathf.Sqrt(1f - Mathf.Pow(t, 2f)); + case EaseType.OutCirc: return Mathf.Sqrt(1f - Mathf.Pow(t - 1f, 2f)); + case EaseType.InOutCirc: + return t < 0.5f + ? (1f - Mathf.Sqrt(1f - Mathf.Pow(2f * t, 2f))) * 0.5f + : (Mathf.Sqrt(1f - Mathf.Pow(-2f * t + 2f, 2f)) + 1f) * 0.5f; + case EaseType.InBack: + { + const float s = 1.70158f; + return (s + 1f) * t * t * t - s * t * t; + } + case EaseType.OutBack: + { + const float s = 1.70158f; + return 1f + (s + 1f) * Mathf.Pow(t - 1f, 3f) + s * Mathf.Pow(t - 1f, 2f); + } + case EaseType.InOutBack: + { + const float s = 1.70158f * 1.525f; + return t < 0.5f + ? (Mathf.Pow(2f * t, 2f) * ((s + 1f) * 2f * t - s)) * 0.5f + : (Mathf.Pow(2f * t - 2f, 2f) * ((s + 1f) * (t * 2f - 2f) + s) + 2f) * 0.5f; + } + case EaseType.InElastic: + return t == 0f ? 0f : t == 1f ? 1f : -Mathf.Pow(2f, 10f * t - 10f) * Mathf.Sin((t * 10f - 10.75f) * ((2f * Mathf.PI) / 3f)); + case EaseType.OutElastic: + return t == 0f ? 0f : t == 1f ? 1f : Mathf.Pow(2f, -10f * t) * Mathf.Sin((t * 10f - 0.75f) * ((2f * Mathf.PI) / 3f)) + 1f; + case EaseType.InOutElastic: + const float c5 = (2f * Mathf.PI) / 4.5f; + return t == 0f ? 0f : + t == 1f ? 1f : + t < 0.5f ? -(Mathf.Pow(2f, 20f * t - 10f) * Mathf.Sin((20f * t - 11.125f) * c5)) * 0.5f : + (Mathf.Pow(2f, -20f * t + 10f) * Mathf.Sin((20f * t - 11.125f) * c5)) * 0.5f + 1f; + case EaseType.InBounce: return 1f - Evaluate(EaseType.OutBounce, 1f - t); + case EaseType.OutBounce: + if (t < 1f / 2.75f) return 7.5625f * t * t; + else if (t < 2f / 2.75f) return 7.5625f * (t -= 1.5f / 2.75f) * t + 0.75f; + else if (t < 2.5f / 2.75f) return 7.5625f * (t -= 2.25f / 2.75f) * t + 0.9375f; + else return 7.5625f * (t -= 2.625f / 2.75f) * t + 0.984375f; + case EaseType.InOutBounce: + return t < 0.5f + ? (1f - Evaluate(EaseType.OutBounce, 1f - 2f * t)) * 0.5f + : (1f + Evaluate(EaseType.OutBounce, 2f * t - 1f)) * 0.5f; + + // Flash 效果通常指类似锯齿波的闪烁,这里模仿 DOTween 的 Flash 逻辑(快速往复) + case EaseType.Flash: return Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f)); // 简单的往复采样 + case EaseType.InFlash: return t * Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f)); + case EaseType.OutFlash: return (1f - t) * Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f)); + case EaseType.InOutFlash: return Mathf.SmoothStep(0, 1, t) * Mathf.Abs(Mathf.Cos(t * Mathf.PI * 4f)); + + default: return t; + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Ease.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Ease.cs.meta new file mode 100644 index 00000000..dce8a77b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Ease.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 99e626ae0cb173a40b150f7428791857 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/GameEvent.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/GameEvent.cs new file mode 100644 index 00000000..a082fc49 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/GameEvent.cs @@ -0,0 +1,178 @@ +using System; +using UnityEngine; +using UnityEngine.Events; +namespace SLSUtilities.General +{ + public class PrioritizedFunctionBase : IPrioritized + { + public int Priority { get; set; } + + /// + /// 执行计数。 + /// -1 代表无限次执行。 + /// >0 代表剩余执行次数,每次 Invoke 后递减,减至 0 时应被移除。 + /// + public int ExecutionCount { get; set; } = -1; + + public bool ShouldRemove => ExecutionCount == 0; + + protected void UpdateCount() + { + if (ExecutionCount > 0) ExecutionCount--; + } + } + + #region PrioritizedAction + public class PrioritizedAction : PrioritizedFunctionBase + { + private readonly Action action; + + public PrioritizedAction(Action action, int priority = 0, int count = -1) + { + this.action = action; + Priority = priority; + ExecutionCount = count; + } + + public void Invoke() + { + action.Invoke(); + UpdateCount(); + } + } + + public class PrioritizedAction : PrioritizedFunctionBase + { + private readonly Action action; + + public PrioritizedAction(Action action, int priority = 0, int count = -1) + { + this.action = action; + Priority = priority; + ExecutionCount = count; + } + + public void Invoke(T arg) + { + action.Invoke(arg); + UpdateCount(); + } + } + + public class PrioritizedAction : PrioritizedFunctionBase + { + private readonly Action action; + + public PrioritizedAction(Action action, int priority = 0, int count = -1) + { + this.action = action; + Priority = priority; + ExecutionCount = count; + } + + public void Invoke(T0 arg0, T1 arg1) + { + action.Invoke(arg0, arg1); + UpdateCount(); + } + } + + public class PrioritizedAction : PrioritizedFunctionBase + { + private readonly Action action; + + public PrioritizedAction(Action action, int priority = 0, int count = -1) + { + this.action = action; + Priority = priority; + ExecutionCount = count; + } + + public void Invoke(T0 arg0, T1 arg1, T2 arg2) + { + action.Invoke(arg0, arg1, arg2); + UpdateCount(); + } + } + #endregion + + #region PrioritizedFunc + + public class PrioritizedFunc : PrioritizedFunctionBase + { + private readonly Func func; + + public PrioritizedFunc(Func func, int priority = 0, int count = -1) + { + this.func = func; + this.Priority = priority; + this.ExecutionCount = count; + } + + public TR Invoke() + { + TR result = func.Invoke(); + UpdateCount(); + return result; + } + } + + public class PrioritizedFunc : PrioritizedFunctionBase + { + private readonly Func func; + + public PrioritizedFunc(Func func, int priority = 0, int count = -1) + { + this.func = func; + this.Priority = priority; + this.ExecutionCount = count; + } + + public TR Invoke(T arg) + { + TR result = func.Invoke(arg); + UpdateCount(); + return result; + } + } + + public class PrioritizedFunc : PrioritizedFunctionBase + { + private readonly Func func; + + public PrioritizedFunc(Func func, int priority = 0, int count = -1) + { + this.func = func; + this.Priority = priority; + this.ExecutionCount = count; + } + + public TR Invoke(T0 arg0, T1 arg1) + { + TR result = func.Invoke(arg0, arg1); + UpdateCount(); + return result; + } + } + + public class PrioritizedFunc : PrioritizedFunctionBase + { + private readonly Func func; + + public PrioritizedFunc(Func func, int priority = 0, int count = -1) + { + this.func = func; + this.Priority = priority; + this.ExecutionCount = count; + } + + public TR Invoke(T0 arg0, T1 arg1, T2 arg2) + { + TR result = func.Invoke(arg0, arg1, arg2); + UpdateCount(); + return result; + } + } + #endregion +} + diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/GameEvent.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/GameEvent.cs.meta new file mode 100644 index 00000000..4a86fb60 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/GameEvent.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d4a739f10fb7d8d49a75be3de4f95ac8 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ICloneable.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ICloneable.cs new file mode 100644 index 00000000..db7bd334 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ICloneable.cs @@ -0,0 +1,9 @@ +using UnityEngine; + +namespace SLSUtilities.General +{ + public interface ICloneable + { + T Clone(); + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ICloneable.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ICloneable.cs.meta new file mode 100644 index 00000000..bc0edcbc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ICloneable.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7b42b93abdc7c084fa29770bf5e5f66b \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/IPrioritized.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/IPrioritized.cs new file mode 100644 index 00000000..55ba18a3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/IPrioritized.cs @@ -0,0 +1,61 @@ +using System; + +namespace SLSUtilities.General +{ + /// + /// 实现该接口的类可以根据优先级进行比较和排序。 + /// 数字越大优先级越高。 + /// + public interface IPrioritized : IComparable + { + int Priority { get; } + + int IComparable.CompareTo(IPrioritized other) + { + return other.Priority.CompareTo(Priority); + } + } + + public static class IPrioritizedExtensions + { + public static PrioritizedAction ToPrioritized(this Action action, int priority = 0) + { + return new PrioritizedAction(action, priority); + } + + public static PrioritizedAction ToPrioritized(this Action action, int priority = 0) + { + return new PrioritizedAction(action, priority); + } + + public static PrioritizedAction ToPrioritized(this Action action, int priority = 0) + { + return new PrioritizedAction(action, priority); + } + + public static PrioritizedAction ToPrioritized(this Action action, int priority = 0) + { + return new PrioritizedAction(action, priority); + } + + public static PrioritizedFunc ToPrioritized(this Func func, int priority = 0) + { + return new PrioritizedFunc(func, priority); + } + + public static PrioritizedFunc ToPrioritized(this Func func, int priority = 0) + { + return new PrioritizedFunc(func, priority); + } + + public static PrioritizedFunc ToPrioritized(this Func func, int priority = 0) + { + return new PrioritizedFunc(func, priority); + } + + public static PrioritizedFunc ToPrioritized(this Func func, int priority = 0) + { + return new PrioritizedFunc(func, priority); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/IPrioritized.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/IPrioritized.cs.meta new file mode 100644 index 00000000..83c70bef --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/IPrioritized.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 84b1c40e60420ad47b31aef43bc98901 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ListExtension.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ListExtension.cs new file mode 100644 index 00000000..38f5f69f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ListExtension.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Random = UnityEngine.Random; + +namespace SLSUtilities.General +{ + public static partial class ListExtension + { + /// + /// 对列表中的每个元素执行指定的操作 + /// + public static void For(this List list, Action action) + { + for (var index = 0; index < list.Count; index++) + { + action(list[index]); + } + } + + /// + /// 将列表中的元素从此列表的原始位置转移到指定位置 + /// + public static void Transfer(this List list, T item, int index) + { + if (list.Contains(item)) + { + list.Remove(item); + list.Insert(index, item); + } + else + { + Debug.LogError($"Item {item} not found in this List."); + } + } + + /// + /// 将列表中的元素从此列表转移到另一个列表,可选择插入位置,默认添加到末尾 + /// + public static void Transfer(this List fromList, List toList, T item, int index = -1) + { + if (fromList.Contains(item)) + { + fromList.Remove(item); + + if (index < 0) + { + toList.Add(item); + } + else + { + toList.Insert(index, item); + } + } + else + { + Debug.LogError($"Item {item} not found in this List."); + } + } + + /// + /// 从列表中移除另一个列表中的所有元素 + /// + public static void Remove(this List list, List listToRemove) + { + foreach (T item in listToRemove) + { + if (list.Contains(item)) + { + list.Remove(item); + } + } + } + + /// + /// 在列表中交换两个元素 + /// + public static void Swap(this List list, int i, int j) + { + (list[i], list[j]) = (list[j], list[i]); + } + + /// + /// 随机打乱列表中的元素 + /// + public static void Shuffle(this List list) + { + for (int i = 0; i < list.Count; i++) + { + list.Swap(i, Random.Range(i, list.Count)); + } + } + + public static bool TryGet(this List list, Func predicate, out T element) + { + foreach (T item in list) + { + if (predicate(item)) + { + element = item; + return true; + } + } + + element = default; + return false; + } + + /// + /// 尝试从列表中随机获取一个元素 + /// + public static bool TryGetRandom(this List list, out T element) + { + if (list.Count == 0) + { + element = default; + return false; + } + + element = list[Random.Range(0, list.Count)]; + return true; + } + + /// + /// 返回一个新的列表,包含原列表中排除指定元素后的所有元素 + /// + public static List Exclude(this List list, T element) + { + List newList = new List(list); + newList.Remove(element); + return newList; + } + + /// + /// 返回一个新的列表,包含原列表中排除指定元素后的所有元素 + /// + public static List Exclude(this List list, params T[] elements) + { + List newList = new List(list); + foreach (T element in elements) + { + if (newList.Contains(element)) + { + newList.Remove(element); + } + } + return newList; + } + + /// + /// 返回一个新的列表,包含原列表中排除指定元素后的所有元素 + /// + public static List Exclude(this List list, List elements) + { + return list.Exclude(elements.ToArray()); + } + + /// + /// 根据指定的过滤器函数,返回一个新的列表,包含符合条件的元素 + /// + public static List Filtered(this List list, Func filter, bool include = true) + { + return list.Where(item => filter(item) == include).ToList(); + } + + /// + /// 检查所有过滤器函数是否都全都返回true + /// + public static bool All(this List> filterList, T item) + { + return filterList.All(filter => filter(item)); + } + + /// + /// 检查是否有任一过滤器函数返回true + /// + public static bool Any(this List> filterList, T item) + { + return filterList.Any(filter => filter(item)); + } + + /// + /// 检查从lastList到thisList是否有元素根据condition条件切换出列表 + /// + public static bool SwitchOut(this List lastList, List thisList, Func condition) + { + return lastList.Any(condition) && thisList.All(element => !condition(element)); + } + + /// + /// 检查从lastList到thisList是否有指定元素切换出列表 + /// + public static bool SwitchOut(this List lastList, List thisList, T element) + { + return lastList.Contains(element) && !thisList.Contains(element); + } + } + + public static partial class ListExtension + { + /// + /// 根据优先级将新项插入到已排序的列表中,保持列表按优先级从高到低排序(二分查找)。 + /// 数字越大优先级越高。 + /// + public static void AddByPriority(this List list, T newItem) where T : IPrioritized + { + // 优化:检查是否可以直接添加到末尾 + if (list.Count == 0 || newItem.Priority <= list[list.Count - 1].Priority) + { + list.Add(newItem); + return; + } + + int low = 0; + int high = list.Count; + + while (low < high) + { + int mid = low + (high - low) / 2; + + // 规则:数字越大,优先级越高,越靠前 + // 如果中间项的优先级 >= 新项的优先级, + // 说明新项应该在它后面 + if (list[mid].Priority >= newItem.Priority) + { + low = mid + 1; + } + else + { + // 中间项的优先级 < 新项,说明新项应该在它前面(或就是这个位置) + high = mid; + } + } + + list.Insert(low, newItem); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ListExtension.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ListExtension.cs.meta new file mode 100644 index 00000000..ba3739a8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/ListExtension.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3651f5e91140cb944b2ee602da6e5c48 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/MathExtensions.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/MathExtensions.cs new file mode 100644 index 00000000..5fa60fc2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/MathExtensions.cs @@ -0,0 +1,92 @@ +using System; +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class MathExtensions + { + public static float ClampAngle(float lfAngle, float lfMin, float lfMax) + { + if (lfAngle < -360f) lfAngle += 360f; + if (lfAngle > 360f) lfAngle -= 360f; + return Mathf.Clamp(lfAngle, lfMin, lfMax); + } + + public static Vector3 Flatten(this Vector3 vector) + { + return new Vector3(vector.x, 0, vector.z); + } + } + + public class LerpFloat + { + public float currentValue; + public float targetValue; + public float lerpSpeed; + + public LerpFloat(float initialValue, float lerpSpeed) + { + this.currentValue = initialValue; + this.targetValue = initialValue; + this.lerpSpeed = lerpSpeed; + } + + public void Update(float deltaTime) + { + currentValue = Mathf.Lerp(currentValue, targetValue, lerpSpeed * deltaTime); + } + + public void Update(float customSpeed, float deltaTime) + { + currentValue = Mathf.Lerp(currentValue, targetValue, customSpeed * deltaTime); + } + } + + public class LerpVector3 + { + public Vector3 currentValue; + public Vector3 targetValue; + public float lerpSpeed; + + public LerpVector3(Vector3 initialValue, float lerpSpeed) + { + this.currentValue = initialValue; + this.targetValue = initialValue; + this.lerpSpeed = lerpSpeed; + } + + public void Update(float deltaTime) + { + currentValue = Vector3.Lerp(currentValue, targetValue, lerpSpeed * deltaTime); + } + + public void Update(float customSpeed, float deltaTime) + { + currentValue = Vector3.Lerp(currentValue, targetValue, customSpeed * deltaTime); + } + } + + public class LerpColor + { + public Color currentValue; + public Color targetValue; + public float lerpSpeed; + + public LerpColor(Color initialValue, float lerpSpeed) + { + this.currentValue = initialValue; + this.targetValue = initialValue; + this.lerpSpeed = lerpSpeed; + } + + public void Update(float deltaTime) + { + currentValue = Color.Lerp(currentValue, targetValue, lerpSpeed * deltaTime); + } + + public void Update(float customSpeed, float deltaTime) + { + currentValue = Color.Lerp(currentValue, targetValue, customSpeed * deltaTime); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/MathExtensions.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/MathExtensions.cs.meta new file mode 100644 index 00000000..27040800 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/MathExtensions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a153bf87166c48543b97d7e69d5f6c0e \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Singleton.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Singleton.cs new file mode 100644 index 00000000..d869cb10 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Singleton.cs @@ -0,0 +1,36 @@ +using Sirenix.OdinInspector; +using UnityEngine; + +namespace SLSUtilities.General +{ + public class Singleton : SerializedMonoBehaviour where T : MonoBehaviour + { + protected static T instance; + public static T Instance => instance == null ? FindFirstObjectByType() : instance; + + protected virtual void Awake() + { + Initialize(false); + } + + protected virtual void Initialize(bool dontDestroy) + { + if (dontDestroy) + { + if (instance == null) + { + instance = this as T; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + else + { + instance = this as T; + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Singleton.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Singleton.cs.meta new file mode 100644 index 00000000..2ad4b4bf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/Singleton.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 69d5cc5c03690434ebfd1473226fe410 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpaceConverter.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpaceConverter.cs new file mode 100644 index 00000000..9925bc24 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpaceConverter.cs @@ -0,0 +1,118 @@ +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class SpaceConverter + { + /// + /// 世界坐标转换为屏幕坐标 + /// + /// 屏幕坐标 + /// 用于转换的摄像机 + /// + public static Vector3 WorldPointToScreenPoint(Vector3 worldPoint, Camera worldCamera) + { + // Camera.main 世界摄像机 + Vector3 screenPoint = worldCamera.WorldToScreenPoint(worldPoint); + return screenPoint; + } + + /// + /// 世界坐标转换为归一化屏幕坐标 (0-1) + /// + /// + /// + /// + public static Vector2 WorldPointToNormalizedScreenPoint(Vector3 worldPoint, Camera worldCamera) + { + Vector2 screenPoint = WorldPointToScreenPoint(worldPoint, worldCamera); + Vector2 normalizedScreenPoint = new Vector2(screenPoint.x / Screen.width, screenPoint.y / Screen.height); + return normalizedScreenPoint; + } + + /// + /// 屏幕坐标转换为世界坐标 + /// + /// 屏幕坐标 + /// 距离摄像机 Z 平面的距离 + /// 用于转换的摄像机 + /// + public static Vector3 ScreenPointToWorldPoint(Vector2 screenPoint, float planeZ, Camera worldCamera) + { + // Camera.main 世界摄像机 + Vector3 position = new Vector3(screenPoint.x, screenPoint.y, planeZ); + Vector3 worldPoint = worldCamera.ScreenToWorldPoint(position); + return worldPoint; + } + + + public static Vector2 WorldPointToUILocalPoint(RectTransform rt, Vector3 worldPosition, Camera worldCamera, Camera uiCamera) + { + // 将世界坐标转换为屏幕坐标 + var screenPosition = WorldPointToScreenPoint(worldPosition, worldCamera); + // 将屏幕坐标转换为 UGUI 坐标 + Vector2 localPoint = ScreenPointToUILocalPoint(rt, screenPosition, uiCamera); + + return localPoint; + } + + public static Vector3 WorldPointToUIPoint(RectTransform rt, Vector3 worldPosition, Camera worldCamera, Camera uiCamera) + { + // 将世界坐标转换为屏幕坐标 + var screenPosition = WorldPointToScreenPoint(worldPosition, worldCamera); + // 将屏幕坐标转换为 UGUI 坐标 + Vector3 uiPoint = ScreenPointToUIPoint(rt, screenPosition, uiCamera); + + return uiPoint; + } + + // RectTransformUtility.WorldToScreenPoint + // RectTransformUtility.ScreenPointToWorldPointInRectangle + // RectTransformUtility.ScreenPointToLocalPointInRectangle + // 上面三个坐标转换的方法使用 Camera 的地方 + // 当 Canvas renderMode 为 RenderMode.ScreenSpaceCamera、RenderMode.WorldSpace 时 传递参数 canvas.worldCamera + // 当 Canvas renderMode 为 RenderMode.ScreenSpaceOverlay 时 传递参数 null + + // UI 坐标转换为屏幕坐标 + public static Vector2 UIPointToScreenPoint(Vector3 worldPoint, Camera uiCamera) + { + Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, worldPoint); + return screenPoint; + } + + // 屏幕坐标转换为 UGUI 坐标 + public static Vector3 ScreenPointToUIPoint(RectTransform rt, Vector2 screenPoint, Camera uiCamera) + { + //UI屏幕坐标转换为世界坐标 + // 当 Canvas renderMode 为 RenderMode.ScreenSpaceCamera、RenderMode.WorldSpace 时 uiCamera 不能为空 + // 当 Canvas renderMode 为 RenderMode.ScreenSpaceOverlay 时 uiCamera 可以为空 + RectTransformUtility.ScreenPointToWorldPointInRectangle(rt, screenPoint, uiCamera, out Vector3 globalMousePos); + // 转换后的 globalMousePos 使用下面方法赋值 + // target 为需要使用的 UI RectTransform + // rt 可以是 target.GetComponent(), 也可以是 target.parent.GetComponent() + // target.transform.position = globalMousePos; + return globalMousePos; + } + + // 屏幕坐标转换为 UGUI RectTransform 的 anchoredPosition + public static Vector2 ScreenPointToUILocalPoint(RectTransform parentRT, Vector2 screenPoint, Camera uiCamera) + { + RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRT, screenPoint, uiCamera, out Vector2 localPos); + // 转换后的 localPos 使用下面方法赋值 + // target 为需要使用的 UI RectTransform + // parentRT 是 target.parent.GetComponent() + // 最后赋值 target.anchoredPosition = localPos; + return localPos; + } + + /// + /// 获取目标 RectTransform 在目标空间 RectTransform 中的本地位置 + /// + public static Vector2 GetLocalUIPosition(RectTransform targetRect, RectTransform targetSpace) + { + Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(null, targetRect.position); + RectTransformUtility.ScreenPointToLocalPointInRectangle(targetSpace, screenPos, null, out Vector2 localPos); + return localPos; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpaceConverter.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpaceConverter.cs.meta new file mode 100644 index 00000000..005daf16 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpaceConverter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4315a0d9d4589a243b33d79da55b47a9 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpriteExtension.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpriteExtension.cs new file mode 100644 index 00000000..e81118e4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpriteExtension.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class SpriteExtension + { + public static Sprite Create(Texture2D texture) + { + return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpriteExtension.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpriteExtension.cs.meta new file mode 100644 index 00000000..d2f3f77e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/SpriteExtension.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a3531403c3553ec4e9db865958d48ef4 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/StringExtension.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/StringExtension.cs new file mode 100644 index 00000000..a32b9731 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/StringExtension.cs @@ -0,0 +1,18 @@ +using I2.Loc; +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class StringExtension + { + public static string Localize(this string original) + { + if (LocalizationManager.TryGetTranslation(original, out string translated)) + { + return translated; + } + + return original; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/StringExtension.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/StringExtension.cs.meta new file mode 100644 index 00000000..342543bd --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/StringExtension.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fd4a697d946242343a065437c29f0cf2 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/TransformExtension.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/TransformExtension.cs new file mode 100644 index 00000000..67b164ab --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/TransformExtension.cs @@ -0,0 +1,24 @@ +using Lean.Pool; +using UnityEngine; + +namespace SLSUtilities.General +{ + public static class TransformExtension + { + public static void DestroyAllChildren(this Transform transform) + { + for (int i = transform.childCount - 1; i >= 0; i--) + { + Object.Destroy(transform.GetChild(i).gameObject); + } + } + + public static void DespawnAllChildren(this Transform transform) + { + for (int i = transform.childCount - 1; i >= 0; i--) + { + LeanPool.Despawn(transform.GetChild(i).gameObject); + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/TransformExtension.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/TransformExtension.cs.meta new file mode 100644 index 00000000..e38b07b0 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/General/TransformExtension.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 81bebd0fa57793c4bbe5369f3205e71b \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance.meta new file mode 100644 index 00000000..53fe2f67 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2cc876a6374ae414a8f65261647ed756 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance/PooledObject.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance/PooledObject.cs new file mode 100644 index 00000000..11c0a92c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance/PooledObject.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Lean.Pool; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace SLSUtilities.LeanPoolAssistance +{ + public class PooledObject : SerializedMonoBehaviour, IPoolable + { + [Tooltip("是否在生成后定时自动回收")] + public bool isAutoDespawn = true; + + [ShowIf("isAutoDespawn")][Tooltip("自动回收时间")] + public float autoDespawnTime = 1; + [ShowIf("isAutoDespawn")][Tooltip("自动回收计时器")][ReadOnly][HideInEditorMode] + public float despawnTimer; + + private List children; + + protected bool spawnExecuted = false; + + [HideInInspector] + public Action onSpawnAction; + [HideInInspector] + public Action onDespawnAction; + + public virtual void OnSpawn() + { + if (spawnExecuted) + { + return; + } + + spawnExecuted = true; + despawnTimer = 0; + onSpawnAction?.Invoke(); + + children = GetComponentsInChildren().ToList(); + children.Remove(this); + children.ForEach(child => child.OnSpawn()); + } + + protected virtual void Update() + { + UpdateTimer(Time.deltaTime); + } + + protected virtual void UpdateTimer(float deltaTime) + { + if (!isAutoDespawn) + { + return; + } + + despawnTimer += deltaTime; + + if (despawnTimer >= autoDespawnTime) + { + LeanPool.Despawn(gameObject); + } + } + + public virtual void OnDespawn() + { + spawnExecuted = false; + onDespawnAction?.Invoke(); + children.ForEach(child => child.OnDespawn()); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance/PooledObject.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance/PooledObject.cs.meta new file mode 100644 index 00000000..d5441891 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/LeanPoolAssistance/PooledObject.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: cfc79d04c0439624b848efbb0e52b465 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance.meta new file mode 100644 index 00000000..9f33c1b8 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5f92777719cc6094288e071be9cab585 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioContainer.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioContainer.cs new file mode 100644 index 00000000..42382d2b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioContainer.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Lean.Pool; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; +using Event = AK.Wwise.Event; + +namespace SLSUtilities.WwiseAssistance +{ + public class AudioContainer : SerializedMonoBehaviour + { + public Dictionary soundEventDictionary = new Dictionary(); + + private HashSet normalAudioPlayingIDs; + + private void Awake() + { + soundEventDictionary ??= new Dictionary(); + normalAudioPlayingIDs = new HashSet(); + } + + /// + /// 触发自定义的Wwise事件 + /// + public void PostEvent(string eventName, GameObject attachedObject = null) + { + Event evt = soundEventDictionary[eventName]; + + if (evt == null) return; + + attachedObject = attachedObject == null ? gameObject : attachedObject; + evt.Post(attachedObject); + } + + /// + /// 依附于GameObject播放3D音频,attachedObject为null时默认为当前GameObject + /// + public uint PlaySoundFX(string soundName, GameObject attachedObject = null, bool spawnTempObject = false) + { + if (!soundEventDictionary.TryGetValue(soundName, out Event sound)) + { + Debug.LogWarning($"Sound event '{soundName}' not found in dictionary."); + return 0; + } + + attachedObject = attachedObject == null ? gameObject : attachedObject; + + uint playingID; // *** 修正 ***: 在这里声明 playingID + + if (spawnTempObject) + { + GameObject soundObj = LeanPool.Spawn(AudioManager.Instance.audioPoint, attachedObject.transform); + + // *** 修正 ***: + // 1. 将 soundObj 本身作为 cookie 传递。 + // 2. 捕获 Post() 返回的 + playingID = sound.Post(soundObj, (uint)AkCallbackType.AK_EndOfEvent, AutoDespawnCallback, soundObj); + soundObj.name = $"{soundName}-WwiseEvent{playingID}"; // 使用正确的 playingID + } + else + { + // *** 修正 ***: + // 1. cookie 传递 null,因为我们不需要它。 + // 2. 捕获 Post() 返回的 playingID。 + playingID = sound.Post(attachedObject,(uint)AkCallbackType.AK_EndOfEvent, OnEventCallback, null); + } + + // *** 修正 ***: + // 将捕获到的、正确的 playingID 添加到集合中 + // (还应检查 Post 是否成功,不为0) + if (playingID != 0) + { + normalAudioPlayingIDs.Add(playingID); + } + + return playingID; + } + + /// + /// 在指定位置播放3D音频 + /// + public uint PlaySoundFX(string soundName, Vector3 position, bool isLoop = false) + { + Event sound = soundEventDictionary[soundName]; + if (sound == null) return 0; + + GameObject soundObj = LeanPool.Spawn(AudioManager.Instance.audioPoint, position, Quaternion.identity); + + // *** 修正 ***: + // 1. 将 soundObj 作为 cookie 传递。 + // 2. 捕获 Post() 返回的 playingID。 + uint playingID = sound.Post(soundObj, (uint)AkCallbackType.AK_EndOfEvent, AutoDespawnCallback, soundObj); + soundObj.name = $"{soundName}-WwiseEvent{playingID}"; // 使用正确的 playingID + + if (playingID != 0) + { + normalAudioPlayingIDs.Add(playingID); + } + + return playingID; + } + + /// + /// 停止播放音效 + /// + public void StopSoundFX(uint playingID) + { + if (normalAudioPlayingIDs.Contains(playingID)) + { + AkUnitySoundEngine.StopPlayingID(playingID); + } + else + { + Debug.LogWarning($"No sound is playing with PlayingID: {playingID}"); + } + } + + /// + /// 以淡出效果停止播放音效 + /// + public void StopSoundWithFadeOut(uint playingID, float fadeOutDuration) + { + if (normalAudioPlayingIDs.Contains(playingID)) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Stop, + playingID, + (int)(fadeOutDuration * 1000), // 将秒数转为毫秒 + AkCurveInterpolation.AkCurveInterpolation_Sine // 选择淡出曲线 + ); + } + else + { + Debug.LogWarning($"No sound is playing with PlayingID: {playingID}"); + } + } + + /// + /// 自动回收临时音频对象 + /// + private void AutoDespawnCallback(object in_cookie, AkCallbackType in_type, AkCallbackInfo in_info) + { + if (in_type == AkCallbackType.AK_EndOfEvent) + { + // *** 修正 ***: cookie 现在是 GameObject + GameObject soundObj = (GameObject)in_cookie; + + // *** 修正 ***: 从 AkCallbackInfo (in_info) 中获取 playingID + uint playingID = 0; + if (in_info is AkEventCallbackInfo eventInfo) + { + playingID = eventInfo.playingID; + } + + if (playingID != 0 && normalAudioPlayingIDs.Contains(playingID)) + { + normalAudioPlayingIDs.Remove(playingID); + if (soundObj != null) // 最好检查一下对象是否仍然存在 + { + LeanPool.Despawn(soundObj); + } + } + } + } + + private void OnEventCallback(object in_cookie, AkCallbackType in_type, AkCallbackInfo in_info) + { + if (in_type == AkCallbackType.AK_EndOfEvent) + { + // *** 修正 ***: 从 AkCallbackInfo (in_info) 中获取 playingID + uint playingID = 0; + if (in_info is AkEventCallbackInfo eventInfo) + { + playingID = eventInfo.playingID; + } + + if (playingID != 0 && normalAudioPlayingIDs.Contains(playingID)) + { + normalAudioPlayingIDs.Remove(playingID); + } + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioContainer.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioContainer.cs.meta new file mode 100644 index 00000000..1c04fc39 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioContainer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02ab9c91fe7438e47b00faa9866da79e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs new file mode 100644 index 00000000..58cdc420 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; +using UnityEngine; +using AK.Wwise; +using Ichni; +using Lean.Pool; +using Sirenix.OdinInspector; +using SLSUtilities.General; + +namespace SLSUtilities.WwiseAssistance +{ + public partial class AudioManager : Singleton + { + [Title("Settings")] + [Required] + public GameObject audioPoint; + public List soundBanks; + + [Title("Subsystems")] + public SongPlayer backgroundMusicManager; + + private static Dictionary TrackedPlayingIDs = new Dictionary(); + + protected override void Awake() + { + Initialize(true); + soundBanks.ForEach(bank => bank.Load()); + } + } + + public partial class AudioManager + { + /// + /// 【核心方法】播放音效 + /// 使用方法: AudioManager.Instance.PlayEvent(AK.EVENTS.EXPLOSION, position); + /// + /// AK.EVENTS 中的静态 ID + public static uint Post(uint eventID) + { + if (eventID == 0) return AkUnitySoundEngine.AK_INVALID_PLAYING_ID; + + uint playingID = AkUnitySoundEngine.PostEvent(eventID, Instance.gameObject); + + if (playingID == AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + Debug.LogWarning($"Wwise Event ID [{eventID}] 播放失败"); + } + + return playingID; + } + + public static AudioPoint Post(uint eventID, Vector3 position) + { + if (eventID == 0) return null; + + AudioPoint point = LeanPool.Spawn(instance.audioPoint, position, Quaternion.identity).GetComponent(); + point.Play(eventID); + + return point; + } + + public static uint Post(uint eventID, GameObject attachedObject) + { + if (eventID == 0 || attachedObject == null) return AkUnitySoundEngine.AK_INVALID_PLAYING_ID; + return AkUnitySoundEngine.PostEvent(eventID, attachedObject); + } + + public static AudioPoint Post(string eventName, Vector3 position) + { + return Post(AkUnitySoundEngine.GetIDFromString(eventName), position); + } + + public static void Post(string eventName, GameObject attachedObject) + { + Post(AkUnitySoundEngine.GetIDFromString(eventName), attachedObject); + } + + public static void Stop(uint playingID, int fadeOutMs = 0) + { + if (playingID == AkUnitySoundEngine.AK_INVALID_PLAYING_ID) return; + + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Stop, + playingID, fadeOutMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + + public void StopAll() + { + AkUnitySoundEngine.StopAll(); + TrackedPlayingIDs.Clear(); + } + } + + public partial class AudioManager + { + private static void RegisterTracking(string trackingKey, uint playingID) + { + if (playingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + TrackedPlayingIDs[trackingKey] = playingID; + } + } + + public static void Post(string trackingKey, uint eventID, Vector3 position = default) + { + Stop(trackingKey); + AudioPoint point = Post(eventID, position); + uint playingID = point.eventID; + RegisterTracking(trackingKey, playingID); + } + + public static void Post(string trackingKey, string eventName, Vector3 position = default) + { + Post(trackingKey, AkUnitySoundEngine.GetIDFromString(eventName), position); + } + + public static void Post(string trackingKey, uint eventID, GameObject attachedObject) + { + Stop(trackingKey); + uint playingID = Post(eventID, attachedObject); + RegisterTracking(trackingKey, playingID); + } + + public static void Post(string trackingKey, string eventName, GameObject attachedObject) + { + Post(trackingKey, AkUnitySoundEngine.GetIDFromString(eventName), attachedObject); + } + + public static void Pause(string trackingKey, int fadeOutMs = 0) + { + if (TrackedPlayingIDs.TryGetValue(trackingKey, out uint playingID)) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Pause, + playingID, fadeOutMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + } + + public static void PauseAllTrackedEvents(int fadeOutMs = 0) + { + foreach (var playingID in TrackedPlayingIDs.Values) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Pause, + playingID, fadeOutMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + } + + // --- 【新增】核心控制:继续 --- + public static void Resume(string trackingKey, int fadeInMs = 0) + { + if (TrackedPlayingIDs.TryGetValue(trackingKey, out uint playingID)) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Resume, + playingID, fadeInMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + } + + public static void ResumeAllTrackedEvents(int fadeInMs = 0) + { + foreach (var playingID in TrackedPlayingIDs.Values) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Resume, + playingID, fadeInMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + } + + /// + /// 【新增】通过 Key 停止特定音效 + /// + public static void Stop(string trackingKey, int fadeOutMs = 0) + { + if (TrackedPlayingIDs.TryGetValue(trackingKey, out uint playingID)) + { + Stop(playingID, fadeOutMs); + TrackedPlayingIDs.Remove(trackingKey); + } + } + + /// + /// 【新增】停止所有被追踪的循环音效 (常用于切换场景前) + /// + public static void StopAllTrackedEvents(int fadeOutMs = 0) + { + foreach (var playingID in TrackedPlayingIDs.Values) + { + Stop(playingID, fadeOutMs); + } + TrackedPlayingIDs.Clear(); + } + } + + public partial class AudioManager + { + /// + /// 设置全局参数 (RTPC) + /// 例如: "MasterVolume", "MusicVolume", "PlayerHealth" + /// + public static void SetRTPC(string rtpcName, float value) + { + AkUnitySoundEngine.SetRTPCValue(rtpcName, value); + } + + /// + /// 设置状态 (State) + /// 例如: Group="MusicState", State="Combat" / "Explore" + /// + public static void SetState(string stateGroup, string stateName) + { + AkUnitySoundEngine.SetState(stateGroup, stateName); + } + + public static void SetState(State targetState) + { + targetState?.SetValue(); + } + + /// + /// 设置全局开关 (Switch) - 通常用于特定的全局对象 + /// + public static void SetSwitch(string switchGroup, string switchName) + { + // 对于全局Switch,通常传一个全局GameObject,或者使用 Wwise 的 Global Scope + AkUnitySoundEngine.SetSwitch(switchGroup, switchName, Instance.gameObject); + } + + public static void SetSwitch(Switch targetSwitch) + { + targetSwitch?.SetValue(Instance.gameObject); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs.meta new file mode 100644 index 00000000..b5f3056a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86e86add9ce9c434e8022154ae0c00e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioPoint.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioPoint.cs new file mode 100644 index 00000000..aaf0dd55 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioPoint.cs @@ -0,0 +1,107 @@ +using UnityEngine; +using Lean.Pool; +using AK.Wwise; +using SLSUtilities.LeanPoolAssistance; // 引用 Wwise + +namespace SLSUtilities.WwiseAssistance +{ + /// + /// 挂载在 AudioPoint 预制体上,负责管理单个音频实例的生命周期 + /// + public partial class AudioPoint : PooledObject + { + public uint playingID = AkUnitySoundEngine.AK_INVALID_PLAYING_ID; + public uint eventID = uint.MaxValue; + + /// + /// 播放指定 ID 的事件 + /// + public uint Play(uint eventID) + { + this.eventID = eventID; + + // PostEvent 并注册 EndOfEvent 回调 + playingID = AkUnitySoundEngine.PostEvent(eventID, gameObject, (uint)AkCallbackType.AK_EndOfEvent, OnEventFinished, null); + + // 安全检查:如果 ID 无效(比如Bank没加载),立即回收,防止对象泄漏 + if (playingID == AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + Debug.LogWarning($"Wwise Event ID [{eventID}] 播放失败,正在回收对象。"); + LeanPool.Despawn(gameObject); + } + + return playingID; + } + + /// + /// 手动停止播放(支持淡出),用于 Loop 音效 + /// + /// 淡出时间(毫秒) + public void Stop(int fadeOutMs = 0) + { + if (playingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + // 执行 Stop Action + AkUnitySoundEngine.ExecuteActionOnPlayingID(AkActionOnEventType.AkActionOnEventType_Stop, playingID, fadeOutMs, AkCurveInterpolation.AkCurveInterpolation_Sine); + // 注意:这里不调用 DespawnSelf。 + // Wwise 停止并淡出后,会触发 AK_EndOfEvent,由 OnEventFinished 负责回收。 + } + else + { + // 如果没有在播放,直接回收 + LeanPool.Despawn(gameObject); + } + } + + public void Pause(int fadeOutMs = 0) + { + if (playingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Pause, + playingID, fadeOutMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + } + + // 【新增】继续 + public void Resume(int fadeInMs = 0) + { + if (playingID != AkUnitySoundEngine.AK_INVALID_PLAYING_ID) + { + AkUnitySoundEngine.ExecuteActionOnPlayingID( + AkActionOnEventType.AkActionOnEventType_Resume, + playingID, fadeInMs, + AkCurveInterpolation.AkCurveInterpolation_Linear + ); + } + } + } + + public partial class AudioPoint + { + /// + /// Wwise 内部回调 + /// + private void OnEventFinished(object in_cookie, AkCallbackType in_type, object in_info) + { + if (in_type == AkCallbackType.AK_EndOfEvent) + { + // 确保在 Unity 主线程且对象有效时回收 + // (Wwise 回调通常在主线程,但为了防止 Destroy 竞争条件,加个检查) + if (this != null && gameObject.activeInHierarchy) + { + LeanPool.Despawn(gameObject); + } + } + } + + public override void OnDespawn() + { + playingID = AkUnitySoundEngine.AK_INVALID_PLAYING_ID; + eventID = 0; + base.OnDespawn(); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioPoint.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioPoint.cs.meta new file mode 100644 index 00000000..117b3845 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/AudioPoint.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 64c98413cba4866419bb2de67c4b0caa \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/BackgroundMusicManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/BackgroundMusicManager.cs new file mode 100644 index 00000000..12c3dbbf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/BackgroundMusicManager.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using AK.Wwise; +using Sirenix.OdinInspector; +using UnityEngine.Serialization; + +namespace SLSUtilities.WwiseAssistance +{ + public class BackgroundMusicManager : SerializedMonoBehaviour + { + public Dictionary baseMusicDictionary; // 背景音乐事件字典 + public Event playMusicEvent; // 播放背景音乐的事件 + public Event stopMusicEvent; // 停止播放背景音乐的事件 + + private void Start() + { + PlayMusic("NormalMusic"); + } + + public void PlayMusic(string musicStateName) + { + //if (baseMusicDictionary.ContainsKey(musicStateName)) + { + stopMusicEvent.Post(gameObject); + //baseMusicDictionary[musicStateName].SetValue(); + playMusicEvent.Post(gameObject); + } + } + + public void StopMusic() + { + stopMusicEvent.Post(gameObject); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/BackgroundMusicManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/BackgroundMusicManager.cs.meta new file mode 100644 index 00000000..924969cf --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/BackgroundMusicManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9eaf3d8d8a9f40a4ab52ce261804f4f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor.meta new file mode 100644 index 00000000..4bf49afe --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 783cec019e92e554a96cce27fc92cd17 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEditorUtils.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEditorUtils.cs new file mode 100644 index 00000000..b0dba550 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEditorUtils.cs @@ -0,0 +1,75 @@ +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace SLSUtilities.WwiseAssistance.Editor +{ + // 用于管理 Wwise 数据缓存的静态类 + public static class WwiseDataCache + { + private static Dictionary _nameToId = new Dictionary(); + private static Dictionary _idToName = new Dictionary(); + private static bool _isDirty = true; // 标记数据是否需要刷新 + + // 当脚本重新编译时,标记为 dirty + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnScriptsReloaded() => _isDirty = true; + + public static void RefreshData() + { + if (!_isDirty && _nameToId.Count > 0) return; + + _nameToId.Clear(); + _idToName.Clear(); + + Type eventsType = Type.GetType("AK+EVENTS, Assembly-CSharp"); + + if (eventsType == null) + { + eventsType = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .FirstOrDefault(t => t.FullName == "AK.EVENTS" || (t.Name == "EVENTS" && t.DeclaringType?.Name == "AK")); + } + + if (eventsType != null) + { + FieldInfo[] fields = eventsType.GetFields(BindingFlags.Public | BindingFlags.Static); + foreach (var field in fields) + { + if (field.FieldType == typeof(uint)) + { + string name = field.Name; + uint id = (uint)field.GetValue(null); + + if (!_nameToId.ContainsKey(name)) _nameToId.Add(name, id); + if (!_idToName.ContainsKey(id)) _idToName.Add(id, name); + } + } + } + _isDirty = false; + } + + public static string GetName(uint id) + { + RefreshData(); + return _idToName.TryGetValue(id, out string name) ? name : "Invalid ID"; + } + + public static uint GetID(string name) + { + RefreshData(); + return _nameToId.TryGetValue(name, out uint id) ? id : 0; + } + + public static IEnumerable GetAllNames() + { + RefreshData(); + return _nameToId.Keys; + } + } +} +#endif \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEditorUtils.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEditorUtils.cs.meta new file mode 100644 index 00000000..7fffbb1c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEditorUtils.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d996ce2f80fdbb14792cc6d5e0406a83 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventDrawer.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventDrawer.cs new file mode 100644 index 00000000..e033a305 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventDrawer.cs @@ -0,0 +1,56 @@ +#if UNITY_EDITOR +using System.Linq; +using Sirenix.OdinInspector.Editor; +using UnityEditor; +using UnityEngine; + +namespace SLSUtilities.WwiseAssistance.Editor +{ + public class WwiseEventDrawer : OdinAttributeDrawer + { + protected override void DrawPropertyLayout(GUIContent label) + { + uint currentID = this.ValueEntry.SmartValue; + + // 获取显示名 + string displayName = WwiseDataCache.GetName(currentID); + if (currentID == 0) displayName = "None (Click to Search)"; + + Rect rect = EditorGUILayout.GetControlRect(label != null); + if (label != null) + { + rect = EditorGUI.PrefixLabel(rect, label); + } + + // 绘制触发按钮 + if (GUI.Button(rect, displayName, EditorStyles.popup)) + { + ShowSelector(rect); + } + } + + private void ShowSelector(Rect rect) + { + var allNames = WwiseDataCache.GetAllNames(); + WwiseEventSelector selector = new WwiseEventSelector(allNames); + + // 设置窗口大小 + // 宽度:自适应,最小 250 + float width = Mathf.Max(rect.width, 250); + + // 高度:限制为 300 + // 这既能保证空状态下不显得太长,也能在列表展开时显示足够多的条目(约10-12条) + selector.ShowInPopup(rect, new Vector2(width, 300)); + + selector.SelectionConfirmed += (selectedData) => + { + var selectedName = selectedData.FirstOrDefault(); + uint newID = WwiseDataCache.GetID(selectedName); + + this.ValueEntry.SmartValue = newID; + this.ValueEntry.ApplyChanges(); + }; + } + } +} +#endif \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventDrawer.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventDrawer.cs.meta new file mode 100644 index 00000000..e39542d6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventDrawer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6f0201ab40a00504bb1e8430c6cc41a8 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventSelector.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventSelector.cs new file mode 100644 index 00000000..0c45e8e3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventSelector.cs @@ -0,0 +1,58 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using Sirenix.OdinInspector.Editor; +using UnityEditor; +using UnityEngine; + +namespace SLSUtilities.WwiseAssistance.Editor +{ + public class WwiseEventSelector : GenericSelector + { + public WwiseEventSelector(IEnumerable items) + : base("Wwise Events", false, x => x, items) + { + this.EnableSingleClickToSelect(); + + // 【核心修改】禁用 Odin 自动绘制逻辑,防止它自作多情画第二个框 + this.SelectionTree.Config.DrawSearchToolbar = false; + } + + protected override void DrawSelectionTree() + { + // 1. 【手动绘制搜索框】 + // 我们自己调用 SearchField 的绘制方法,确保只出现一次 + // 放在最上面,符合默认习惯 + this.SelectionTree.DrawSearchToolbar(); + + // 获取当前的搜索词 + string currentTerm = this.SelectionTree.Config.SearchTerm; + + // 2. 控制列表显示逻辑 + if (string.IsNullOrEmpty(currentTerm)) + { + // --- 空状态:显示提示 --- + GUILayout.FlexibleSpace(); + + var style = new GUIStyle(EditorStyles.centeredGreyMiniLabel) + { + fontSize = 12, + wordWrap = true + }; + GUILayout.Label("Type to search Wwise Event...", style); + + GUILayout.FlexibleSpace(); + } + else + { + // --- 有内容:绘制列表 --- + // 绘制一根细线做分割(可选,为了美观) + Sirenix.Utilities.Editor.SirenixEditorGUI.HorizontalLineSeparator(); + + // 绘制列表树 + // 因为 DrawSearchToolbar 已经是 false 了,这里只会画纯粹的列表 + this.SelectionTree.DrawMenuTree(); + } + } + } +} +#endif \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventSelector.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventSelector.cs.meta new file mode 100644 index 00000000..5db907a4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/Editor/WwiseEventSelector.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 791d65f112e572f4781b1a973bdad096 \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/WwiseAttributes.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/WwiseAttributes.cs new file mode 100644 index 00000000..1f14950f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/WwiseAttributes.cs @@ -0,0 +1,12 @@ +using System; +using UnityEngine; + +namespace SLSUtilities.WwiseAssistance +{ + // 这个标签可以挂在 uint 字段上 + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class WwiseEventAttribute : Attribute + { + + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/WwiseAttributes.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/WwiseAttributes.cs.meta new file mode 100644 index 00000000..b15c5ef4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/SLSUtilities/WwiseAssistance/WwiseAttributes.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8dd9775523a950a4eaabbebea04ae6ea \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving.meta new file mode 100644 index 00000000..419a2657 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81811d9d9c15b884592f7264eb06fad2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/BeatmapSave.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/BeatmapSave.cs new file mode 100644 index 00000000..bc25ed9f --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/BeatmapSave.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class BeatmapSave + { + public float accuracy; + public bool isFullCombo; + public bool isAllPerfect; + + public BeatmapSave() + { + + } + + public BeatmapSave(float accuracy, bool isFullCombo, bool isAllPerfect) + { + this.accuracy = accuracy; + this.isFullCombo = isFullCombo; + this.isAllPerfect = isAllPerfect; + } + + public bool IsEmpty() + { + return accuracy == 0f && !isFullCombo && !isAllPerfect; + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/BeatmapSave.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/BeatmapSave.cs.meta new file mode 100644 index 00000000..f126b7a3 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/BeatmapSave.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6b751bb37660d844780656591830afff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/GameSaveManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/GameSaveManager.cs new file mode 100644 index 00000000..c086abfc --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/GameSaveManager.cs @@ -0,0 +1,338 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Ichni.Menu; +using Ichni.RhythmGame; +using Ichni.Story; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni +{ + public class GameSaveManager : SerializedMonoBehaviour + { + public static GameSaveManager instance; + + public SongSaveModule SongSaveModule; + public StorySaveModule StorySaveModule; + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + + private void Start() + { + SongSaveModule = new SongSaveModule(); + SongSaveModule.LoadSongStatuses(); + SongSaveModule.LoadStoryUnlockKeys(); + + StorySaveModule = new StorySaveModule(); + StorySaveModule.LoadStoryVariables(); + StorySaveModule.LoadChoices(); + } + } + + public partial class SongSaveModule + { + public HashSet storyUnlockKeys; + public HashSet paymentUnlockKeys; + public Dictionary songStatusSaves; + private string songSavePath => Application.persistentDataPath + "/GameSaves/SongSaves.json"; + private string storyUnlockKeysPath => Application.persistentDataPath + "/GameSaves/UnlockKeys.json"; + private string paymentUnlockKeysPath => Application.persistentDataPath + "/GameSaves/PaymentUnlockKeys.json"; + public SongSaveModule() + { + songStatusSaves = new Dictionary(); + storyUnlockKeys = new HashSet(); + paymentUnlockKeys = new HashSet(); + //Debug.Log("Song save path: " + songSavePath); + } + } + + public partial class SongSaveModule + { + public void SaveStoryUnlockKeys() + { + ES3.Save("UnlockKeys", storyUnlockKeys, storyUnlockKeysPath); + } + + public void LoadStoryUnlockKeys() + { + if (ES3.FileExists(storyUnlockKeysPath)) + { + storyUnlockKeys = ES3.Load>("UnlockKeys", storyUnlockKeysPath); + } + else + { + storyUnlockKeys = new HashSet(); + SaveStoryUnlockKeys(); + } + } + + public bool CheckStoryKey(string key) + { + return key == string.Empty || storyUnlockKeys.Contains(key); + } + + public void ClearStoryKeys() + { + storyUnlockKeys.Clear(); + SaveStoryUnlockKeys(); + } + } + + public partial class SongSaveModule + { + public void SavePaymentUnlockKeys() + { + ES3.Save("UnlockKeys", paymentUnlockKeys, paymentUnlockKeysPath); + } + + public void LoadPaymentUnlockKeys() + { + if (ES3.FileExists(paymentUnlockKeysPath)) + { + paymentUnlockKeys = ES3.Load>("UnlockKeys", paymentUnlockKeysPath); + } + else + { + paymentUnlockKeys = new HashSet(); + SavePaymentUnlockKeys(); + } + } + + public bool CheckPaymentKey(string key) + { + return key == string.Empty || paymentUnlockKeys.Contains(key); + } + + public void ClearPaymentKeys() + { + paymentUnlockKeys.Clear(); + SavePaymentUnlockKeys(); + } + } + + public partial class SongSaveModule + { + private void InitializeSongStatuses() + { + foreach (ChapterSelectionUnit chapter in ChapterSelectionManager.instance.chapters) + { + foreach (SongItemData song in chapter.songs) + { + SongStatusSave songStatus = new SongStatusSave(false, string.Empty, new List()); + foreach (DifficultyData difficulty in song.difficultyDataList) + { + songStatus.beatmapSaves.Add(new BeatmapSave(0f,false, false)); + } + + songStatusSaves.Add(song.songName, songStatus); + } + } + + SaveSongStatuses(); + } + + private void CheckSongStatuses() + { + foreach (ChapterSelectionUnit chapter in ChapterSelectionManager.instance.chapters) + { + foreach (SongItemData song in chapter.songs) + { + if (songStatusSaves.TryGetValue(song.songName, out SongStatusSave songStatus)) + { + int difficultiesCount = song.difficultyDataList.Count; + if (songStatus.beatmapSaves.Count < difficultiesCount) + { + for (int i = songStatus.beatmapSaves.Count; i < difficultiesCount; i++) + { + songStatus.beatmapSaves.Add(new BeatmapSave(0f, false, false)); + } + } + else if (songStatus.beatmapSaves.Count > difficultiesCount) + { + songStatus.beatmapSaves.RemoveRange(difficultiesCount, songStatus.beatmapSaves.Count - difficultiesCount); + } + } + else + { + songStatus = new SongStatusSave(false, string.Empty, new List()); + foreach (DifficultyData difficulty in song.difficultyDataList) + { + songStatus.beatmapSaves.Add(new BeatmapSave(0f, false, false)); + } + songStatusSaves.Add(song.songName, songStatus); + } + } + } + + SaveSongStatuses(); + } + + [Button] + public void SaveSongStatuses() + { + ES3.Save("SongSaves", songStatusSaves, songSavePath); + } + + public void LoadSongStatuses() + { + if (ES3.FileExists(songSavePath)) + { + songStatusSaves = ES3.Load>("SongSaves", songSavePath); + CheckSongStatuses(); + } + else + { + InitializeSongStatuses(); + } + } + + [Button] + public void ClearBeatmapRecords() + { + foreach (var songStatus in songStatusSaves.Values) + { + foreach (var beatmapSave in songStatus.beatmapSaves) + { + beatmapSave.accuracy = 0.0f; + beatmapSave.isFullCombo = false; + beatmapSave.isAllPerfect = false; + } + } + + SaveSongStatuses(); + } + + public SongStatusSave GetSongStatusSave(string songName) + { + if (songStatusSaves.TryGetValue(songName, out SongStatusSave save)) + { + return save; + } + + Debug.LogWarning("Song status save for " + songName + " does not exist."); + return null; + } + } + + public partial class StorySaveModule + { + public Dictionary> tutorialBlockSaves; + public Dictionary> songBlockSaves; + public Dictionary> dialogBlockSaves; + public Dictionary> connectorSaves; + + public Dictionary storyVariables; + public Dictionary selectedChoices; + + private string GetStorySavePath(string chapterName) => Application.persistentDataPath + "/StorySaves/" + chapterName + ".json"; + private string StoryVariablesPath => Application.persistentDataPath + "/StorySaves/StoryVariables.json"; + private string ChoicesPath => Application.persistentDataPath + "/StorySaves/Choices.json"; + + public StorySaveModule() + { + tutorialBlockSaves = new Dictionary>(); + songBlockSaves = new Dictionary>(); + dialogBlockSaves = new Dictionary>(); + connectorSaves = new Dictionary>(); + storyVariables = new Dictionary(); + selectedChoices = new Dictionary(); + //Debug.Log("Story Variables path: " + StoryVariablesPath); + } + } + + public partial class StorySaveModule + { + public bool IsNewStoryline(string chapterName) + { + return !ES3.FileExists(GetStorySavePath(chapterName)); + } + + public void LoadStoryline(string chapterName) + { + tutorialBlockSaves[chapterName] = ES3.Load>("TutorialBlockSaves", GetStorySavePath(chapterName)); + songBlockSaves[chapterName] = ES3.Load>("SongBlockSaves", GetStorySavePath(chapterName)); + dialogBlockSaves[chapterName] = ES3.Load>("TextBlockSaves", GetStorySavePath(chapterName)); + connectorSaves[chapterName] = ES3.Load>("BlockConnectorSaves", GetStorySavePath(chapterName)); + } + + public void SaveStoryline(string chapterName, List tutorialBlocks, + List songBlocks, List dialogBlocks, + List connectors) + { + ES3.Save("TutorialBlockSaves", tutorialBlocks, GetStorySavePath(chapterName)); + ES3.Save("SongBlockSaves", songBlocks, GetStorySavePath(chapterName)); + ES3.Save("TextBlockSaves", dialogBlocks, GetStorySavePath(chapterName)); + ES3.Save("BlockConnectorSaves", connectors, GetStorySavePath(chapterName)); + + SaveStoryVariables(); + SaveChoices(); + } + + public void ClearAllStoryline() + { + string path = GetStorySavePath("Chapter0"); + if (ES3.FileExists(path)) + { + ES3.DeleteFile(path); + Debug.Log("Cleared all story saves at: " + path); + } + else + { + Debug.LogWarning("No story saves found at: " + path); + } + } + } + + public partial class StorySaveModule + { + public void SaveStoryVariables() + { + string path = Application.persistentDataPath + "/StorySaves/" + "StoryVariables.json"; + ES3.Save("StoryVariables", storyVariables, path); + } + + public void LoadStoryVariables() + { + string path = Application.persistentDataPath + "/StorySaves/" + "StoryVariables.json"; + if (ES3.FileExists(path)) + { + storyVariables = ES3.Load>("StoryVariables", path); + } + else + { + storyVariables = new Dictionary(); + } + } + + public void SaveChoices() + { + string path = Application.persistentDataPath + "/StorySaves/" + "Choices.json"; + ES3.Save("Choices", selectedChoices, path); + } + + public void LoadChoices() + { + string path = Application.persistentDataPath + "/StorySaves/" + "Choices.json"; + if (ES3.FileExists(path)) + { + selectedChoices = ES3.Load>("Choices", path); + } + else + { + selectedChoices = new Dictionary(); + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/GameSaveManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/GameSaveManager.cs.meta new file mode 100644 index 00000000..34928ad1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/GameSaveManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cffb558157bfb9a49a2a624f025f8958 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/SongStatusSave.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/SongStatusSave.cs new file mode 100644 index 00000000..791fe54c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/SongStatusSave.cs @@ -0,0 +1,25 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.RhythmGame +{ + public class SongStatusSave + { + public bool isCompleted; + public string additionalInfo; + public List beatmapSaves; + + public SongStatusSave() + { + + } + + public SongStatusSave(bool isCompleted, string additionalInfo, List beatmapSaves) + { + this.isCompleted = isCompleted; + this.additionalInfo = additionalInfo; + this.beatmapSaves = beatmapSaves; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/SongStatusSave.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/SongStatusSave.cs.meta new file mode 100644 index 00000000..356a63a1 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/SongStatusSave.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0e15d295a0ae77041ad0269ab3801bc3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/StorySave.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/StorySave.cs new file mode 100644 index 00000000..a2150fea --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/StorySave.cs @@ -0,0 +1,53 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.Story +{ + public class StoryBlockSave + { + public string blockName; + public Vector2 position; + public StoryBlockState state; + + public StoryBlockSave(string blockName, Vector2 position, StoryBlockState state) + { + this.blockName = blockName; + this.state = state; + this.position = position; + } + } + + public class TutorialBlockSave : StoryBlockSave + { + public TutorialBlockSave(string blockName, Vector2 position, StoryBlockState state) : base(blockName, position, state) + { + } + } + + public class DialogBlockSave : StoryBlockSave + { + public DialogBlockSave(string blockName, Vector2 position, StoryBlockState state) : base(blockName, position, state) + { + } + } + + public class SongBlockSave : StoryBlockSave + { + public SongBlockSave(string blockName, Vector2 position, StoryBlockState state) : base(blockName, position, state) + { + } + } + + public class BlockConnectorSave + { + public string startBlockName; + public string endBlockName; + + public BlockConnectorSave(string startBlockName, string endBlockName) + { + this.startBlockName = startBlockName; + this.endBlockName = endBlockName; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/StorySave.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/StorySave.cs.meta new file mode 100644 index 00000000..4ae1d50b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Saving/StorySave.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ff8f51d49d96524fab4c38c7f846368 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings.meta new file mode 100644 index 00000000..8cac1630 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f7a3d96ccce4d840bd4911722a981b5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/GameSettings.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/GameSettings.cs new file mode 100644 index 00000000..9f954c5b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/GameSettings.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Rendering.Universal; + +namespace Ichni +{ + public class GameSettings + { + public int masterVolume = 50; + public int musicVolume = 50; + public int soundEffectVolume = 50; + public int uiVolume = 50; + + public int targetFrame = 60; + public int resolutionLevel = 3; + + public int beatmapOffset = 0; + + public int languageIndex = 0; + + public bool debugMode = false; + public bool judgeType = false; + public bool autoPlay = false; + + public GameSettings() + { + + } + + public GameSettings(int masterVolume, int musicVolume, int soundEffectVolume, int uiVolume, + int beatmapOffset, int targetFrame, int resolutionLevel, int languageIndex, bool debugMode, + bool judgeType, bool autoPlay) + { + this.masterVolume = masterVolume; + this.musicVolume = musicVolume; + this.soundEffectVolume = soundEffectVolume; + this.uiVolume = uiVolume; + this.beatmapOffset = beatmapOffset; + this.targetFrame = targetFrame; + this.resolutionLevel = resolutionLevel; + this.languageIndex = languageIndex; + this.debugMode = debugMode; + this.judgeType = judgeType; + this.autoPlay = autoPlay; + } + + public void ApplyVolume() + { + AkSoundEngine.SetRTPCValue("MasterVolume", masterVolume); + AkSoundEngine.SetRTPCValue("MusicVolume", musicVolume); + AkSoundEngine.SetRTPCValue("SoundFXVolume", soundEffectVolume); + AkSoundEngine.SetRTPCValue("UIVolume", uiVolume); + } + + public void ApplyGraphic() + { + Application.targetFrameRate = targetFrame; + UniversalRenderPipelineAsset currentUrpAsset = GraphicsSettings.defaultRenderPipeline as UniversalRenderPipelineAsset; + currentUrpAsset.renderScale = 0.5f + 0.1f * resolutionLevel; + } + + public void ApplyLanguage() + { + I2.Loc.LocalizationManager.CurrentLanguage = MenuManager.instance.languageList[languageIndex]; + I2.Loc.LocalizationManager.UpdateSources(); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/GameSettings.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/GameSettings.cs.meta new file mode 100644 index 00000000..328a63ee --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/GameSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c2ca67619927cac4a91d5b0612d6f7eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/SettingsManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/SettingsManager.cs new file mode 100644 index 00000000..39f4d7fe --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/SettingsManager.cs @@ -0,0 +1,60 @@ +using Ichni.Menu; +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni +{ + public class SettingsManager : SerializedMonoBehaviour + { + public static SettingsManager instance; + + [FormerlySerializedAs("settingsPage")] + public SettingsUIPage settingsUIPage; + public GameSettings gameSettings; + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + + private void Start() + { + LoadGameSettings(); + } + + [Button] + public void SaveGameSettings() + { + string savePath = Application.persistentDataPath + "/GameData/GameSettings.json"; + ES3.Save("GameSettings", gameSettings, savePath); + } + + [Button] + public void LoadGameSettings() + { + string savePath = Application.persistentDataPath + "/GameData/GameSettings.json"; + if (ES3.FileExists(savePath)) + { + gameSettings = ES3.Load("GameSettings", savePath); + } + else + { + gameSettings = new GameSettings( + 50, 50, 50, 50, + 0, 60, 3, 0, false, false, false); + } + + gameSettings.ApplyVolume(); + gameSettings.ApplyGraphic(); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/SettingsManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/SettingsManager.cs.meta new file mode 100644 index 00000000..37221bca --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Settings/SettingsManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 49501a5fe129ecf4ca83fd1c5d03411e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story.meta new file mode 100644 index 00000000..944faee4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0486971babc04a1419e16413dfce980c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog.meta new file mode 100644 index 00000000..c78470d6 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2de1e277bcf059c42826353d1c086ccc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogManager.cs new file mode 100644 index 00000000..172a0f41 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogManager.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Ichni.Story.UI; +using Sirenix.OdinInspector; +using SLSUtilities.General; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Serialization; + +namespace Ichni.Story +{ + public partial class DialogManager : SerializedMonoBehaviour + { + public static DialogManager instance; + + public List dialogTextAssets; + + public bool isPlayingDialog; + public bool isPlayingChoice; + + public string currentDialog; + public int currentDialogSentenceIndex; + public string currentFinalType; + + public Dictionary> functionDictionary; + public Dictionary> dialogDictionary; + public Dictionary choiceDictionary; + public Dictionary> conditionDictionary; + public List dialogEndActions; + + private string currentLoadingDialog; + + public DialogUIPage dialogUIPage; + + private void Awake() + { + instance = this; + } + + public void SetDialog(string dialogName) + { + string chapter = ChapterSelectionManager.instance.currentChapter.chapterIndex; + TextAsset dialog = Resources.Load("Story/" + chapter + "/Dialogs/" + dialogName); + dialogUIPage.dialogContentFrame.ClearAllSentences(); + SetDialog(new List { dialog }); + } + + public void SetDialog(List dialogFiles, string dialogParagraphName = "") + { + dialogUIPage.FadeIn(); + + isPlayingDialog = true; + + currentDialog = "NULL"; + + dialogEndActions = new List(); + + LoadDialog(dialogFiles, out string firstHeader); + + currentDialog = dialogParagraphName == "" ? firstHeader : dialogParagraphName; + } + + public void PlayNextDialogParagraph(string nextDialog, bool invokeFunctions = true) + { + currentDialog = nextDialog; + currentDialogSentenceIndex = 0; + + if (invokeFunctions && functionDictionary.TryGetValue(currentDialog, out List functionList)) + { + functionList.ForEach(x => StoryInterpreters.FunctionInterpreter.Eval(x)); + } + + if (choiceDictionary.ContainsKey(currentDialog)) + { + currentFinalType = "Choice"; + } + else if (conditionDictionary.ContainsKey(currentDialog)) + { + currentFinalType = "Condition"; + } + else + { + currentFinalType = "None"; + } + } + + public void PlayDialog() + { + if(currentDialog == "NULL") + { + throw new Exception("Current dialog is NULL"); + } + + if (isPlayingChoice) + { + return; + } + + if (dialogDictionary[currentDialog].Count > 0 && currentDialogSentenceIndex < dialogDictionary[currentDialog].Count) + { + DialogSentence currentSentence = dialogDictionary[currentDialog][currentDialogSentenceIndex]; + + string interpretedContent = currentSentence.GetInterpretedContent(); + + dialogUIPage.dialogContentFrame.PlaySentence(currentSentence.characterName, interpretedContent); + currentDialogSentenceIndex++; + + if (currentDialogSentenceIndex <= dialogDictionary[currentDialog].Count) + { + return; + } + } + + if(currentDialogSentenceIndex >= dialogDictionary[currentDialog].Count) + { + if (currentFinalType == "Choice") + { + isPlayingChoice = true; + dialogUIPage.dialogContentFrame.PlayChoice(choiceDictionary[currentDialog]); + return; + } + + if (currentFinalType == "Condition") + { + foreach (var condition in conditionDictionary[currentDialog]) + { + if (condition.GetConditionResult()) + { + PlayNextDialogParagraph(condition.nextDialogName); + return; + } + } + } + } + + if (currentFinalType == "None" && currentDialogSentenceIndex >= dialogDictionary[currentDialog].Count) + { + isPlayingDialog = false; + dialogUIPage.FadeOut(); + + if (StoryManager.instance.storyline.currentBlock.state == StoryBlockState.Current) + { + StoryManager.instance.storyline.currentBlock.state = StoryBlockState.Completed; + StoryManager.instance.storyUIPage.messageBox.Clear(); + dialogEndActions.ForEach(action => action.Invoke()); + StoryManager.instance.storyUIPage.messageBox.SetUp(); + StoryManager.instance.storyline.SaveStoryline(ChapterSelectionManager.instance.currentChapter.chapterIndex); + Debug.Log("Dialog completed, setting block state to Completed."); + } + } + } + + public void RevealDialog() + { + string finalType; + int max = 0; + Debug.Log($"Revealing dialog: {currentDialog}, currentFinalType: {currentFinalType}"); + do + { + finalType = currentFinalType; + currentDialogSentenceIndex = 0; + + foreach (DialogSentence sentence in dialogDictionary[currentDialog]) + { + string interpretedContent = sentence.GetInterpretedContent(); + dialogUIPage.dialogContentFrame.PlaySentence(sentence.characterName, interpretedContent); + currentDialogSentenceIndex++; + } + + if (finalType == "Choice") + { + ChoiceGroup choiceGroup = choiceDictionary[currentDialog]; + int choiceIndex = GameSaveManager.instance.StorySaveModule.selectedChoices[choiceGroup.choiceName]; + dialogUIPage.dialogContentFrame.SelectChoice(choiceGroup, choiceIndex); + } + + if (finalType == "Condition") + { + foreach (var condition in conditionDictionary[currentDialog]) + { + if (condition.GetConditionResult()) + { + PlayNextDialogParagraph(condition.nextDialogName, false); + } + } + } + + max++; + + if (max > 1024) + { + throw new Exception("An infinite loop may detected in dialog parsing. Please check the dialog structure."); + } + + } while (finalType != "None"); + } + } + + public partial class DialogManager + { + public void LoadDialog(List dialogFiles, out string firstHeader) + { + ClearDictionaries(); + + firstHeader = string.Empty; + + dialogTextAssets = dialogFiles; + List dialogLines = new List(); + + foreach (TextAsset textAsset in dialogTextAssets) + { + dialogLines.AddRange(ExtractValidFragments(textAsset.text)); + } + + dialogLines.RemoveAll(line => line.Trim() == ""); + + //dialogLines.ForEach(Debug.Log); + + foreach (string line in dialogLines) + { + if (!ParseHeader(line)) + { + if (!ParseChoiceModule(line)) + { + if (!ParseConditionModule(line)) + { + if (!ParseDialogSentence(line)) + { + throw new Exception($"Invalid dialog line: {line}"); // 抛出异常,提示不合法的对话行 + } + } + } + } + else + { + if (firstHeader == string.Empty) + { + firstHeader = currentDialog; + } + } + } + + //dialogDictionary.RemoveWhere((header, sentences) => sentences == null || sentences.Count == 0); + choiceDictionary.RemoveWhere((header, choices) => choices == null || choices.choices.Count == 0); + conditionDictionary.RemoveWhere((header, conditions) => conditions == null || conditions.Count == 0); + } + + /// + /// 从原始大文本中提取所有以 '$' 开头的有效片段,忽略以 '#' 开头的注释片段。 + /// 拆分依据:每当遇到 '$' 或 '#' 字符,即视为一个新片段的起始。 + /// + /// 未分割的完整文本(可能包含任意换行或连续内容)。 + /// 剥离首 '$' 后的有效文本列表。 + public static List ExtractValidFragments(string inputText) + { + if (inputText == null) + { + throw new ArgumentNullException(nameof(inputText)); + } + + // 正则:(?[$#]) // 片段起始前缀 + // (?.*? ) // 非贪婪捕获所有内容 + // (?=(?:[$#])|\z) // 直到下一个 '$'、'#' 或文末 + + const string pattern = @"(?[$#])(?.*?)(?=(?:[$#])|\z)"; + MatchCollection matches = Regex.Matches(inputText, pattern, RegexOptions.Singleline); + + var result = new List(matches.Count); + foreach (Match m in matches) + { + char prefix = m.Groups["prefix"].Value[0]; + string content = m.Groups["content"].Value; + + if (prefix == '$') + { + result.Add(content.Trim()); + } + // prefix == '#' 时自动忽略 + } + + return result; + } + + } + + public partial class DialogManager + { + public void ClearDictionaries() + { + dialogDictionary.Clear(); + choiceDictionary.Clear(); + conditionDictionary.Clear(); + functionDictionary.Clear(); + } + + public bool ParseHeader(string line) + { + //格式:[currentLoadingDialog]{Function0();Function1();Function2();} + line = line.Trim(); + + string dialogTitle = line.Split("{")[0]; + if (dialogTitle[0] != '[' || dialogTitle[^1] != ']') + { + return false; + } + + currentLoadingDialog = dialogTitle.Replace("[", "").Replace("]", ""); + + dialogDictionary.Add(currentLoadingDialog, new List()); + //choiceDictionary.Add(currentLoadingDialog, new ChoiceGroup("Error")); + conditionDictionary.Add(currentLoadingDialog, new List()); + + if (currentDialog == "NULL") + { + currentDialog = currentLoadingDialog; + } + + if (!line.Contains("{")) // 这个Header没有函数需要执行 + { + return true; + } + + string functions = line.Split("{")[1]; + if (functions.Contains("}")) + { + functions = functions.Split("}")[0]; + } + else + { + throw new System.Exception("Dialog header's function list must be enclosed in {}."); + } + + functions = functions.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); //忽略空格,换行 + + List functionList = functions.Split(';').ToList(); //分割函数 + functionList = functionList.Where(x => !string.IsNullOrEmpty(x)).ToList(); //去除空函数 + functionDictionary.Add(currentLoadingDialog, functionList); + + return true; + } + + public bool ParseDialogSentence(string line) + { + //speakerName:sentence + + string[] sentenceData; + if (line.Contains(":")) + { + sentenceData = line.Split(":", 2); + } + else + { + return false; + } + + string character = sentenceData[0]; + string speakerName = character; + + DialogSentence dialogSentence = new DialogSentence + { + characterName = speakerName.Trim(), + content = sentenceData[1].Trim() + }; + + dialogDictionary[currentLoadingDialog].Add(dialogSentence); + + return true; + } + + public bool ParseChoiceModule(string line) + { + //$Choice(ChoiceName){ + //choiceText0->[nextDialogName0]; + //choiceText1->[nextDialogName1]; + //} + + line = line.Trim(); + + if (line.Contains("Choice")) + { + string[] choiceModuleData = line.Split('{'); + + string choiceName = choiceModuleData[0].Split('(')[1].Replace(")", "").Trim(); + ChoiceGroup choiceGroup = new ChoiceGroup(choiceName); + + string[] choiceData = choiceModuleData[1].Split(';'); + for (var index = 0; index < choiceData.Length - 1; index++) + { + choiceData[index] = choiceData[index].Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); + + string choiceText = choiceData[index].Split("->[")[0].Trim(); + string nextDialogName = choiceData[index].Split("->[")[1].Replace("]", "").Trim(); + + choiceGroup.choices.Add(new Choice(choiceText, nextDialogName)); + } + + choiceDictionary[currentLoadingDialog] = choiceGroup; + + return true; + } + + return false; + } + + /// + /// 解析条件模块 + /// + /// + /// + private bool ParseConditionModule(string line) + { + //$Condition{ + //conditionSentence0->[nextDialogName0]; + //conditionSentence1->[nextDialogName1]; + //} + + if (line.Contains("Condition")) + { + string[] conditionModuleData = line.Split('{'); + + List conditions = new List(); + + string[] conditionData = conditionModuleData[1].Split(';'); + + for (var index = 0; index < conditionData.Length - 1; index++) + { + conditionData[index] = conditionData[index].Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); + Condition condition = new Condition + { + conditionSentence = conditionData[index].Split("->[")[0].Trim(), + nextDialogName = conditionData[index].Split("->[")[1].Replace("]", "").Trim(), + }; + + conditions.Add(condition); + } + + conditionDictionary[currentLoadingDialog] = conditions; + + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogManager.cs.meta new file mode 100644 index 00000000..0e062938 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e89b9d7eea97734baa166072b050239 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogModule.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogModule.cs new file mode 100644 index 00000000..11eebc85 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogModule.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; + +namespace Ichni.Story +{ + public class DialogSentence + { + public string content; + public string audioEventName; + + public string characterName; + + public DialogSentence() + { + + } + + public DialogSentence(string content, string audioEventName, string characterName) + { + this.content = content; + this.audioEventName = audioEventName; + this.characterName = characterName; + } + + /// + /// 匹配{@FUNCTION},解析函数并返回解析后的语句内容。 + /// + public string GetInterpretedContent() + { + List parts = new List(); + Regex regex = new Regex(@"\{\@.*?\}"); + int lastIndex = 0; + + foreach (Match match in regex.Matches(content)) + { + if (match.Index > lastIndex) + { + parts.Add(content.Substring(lastIndex, match.Index - lastIndex)); + } + parts.Add(match.Value); + lastIndex = match.Index + match.Length; + } + + if (lastIndex < content.Length) + { + parts.Add(content.Substring(lastIndex)); + } + + for (int i = 0; i < parts.Count; i++) + { + if (parts[i].StartsWith("{@") && parts[i].EndsWith("}")) + { + string expression = parts[i].Substring(2, parts[i].Length - 3); + object result = StoryInterpreters.FunctionInterpreter.Eval(expression); + parts[i] = result.ToString(); + } + } + + return string.Join("", parts); + } + } + + public class ChoiceGroup + { + public string choiceName; + public List choices; + + public ChoiceGroup(string choiceName) + { + this.choiceName = choiceName; + this.choices = new List(); + } + } + + public class Choice + { + public string choiceText; + public string nextDialogName; + + public Choice(string choiceText, string nextDialogName) + { + this.choiceText = choiceText; + this.nextDialogName = nextDialogName; + } + } + + public class Condition + { + public string conditionSentence; + public string nextDialogName; + + public bool GetConditionResult() + { + bool result = StoryInterpreters.ConditionInterpreter.Eval(conditionSentence); + return result; + } + } + + public class DialogCharacter + { + public string name; + public string title; + public Dictionary emotions; + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogModule.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogModule.cs.meta new file mode 100644 index 00000000..98fc3ee7 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/DialogModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d32abf19c3d0f4546b2b86f9fcc4e117 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryInterpreters.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryInterpreters.cs new file mode 100644 index 00000000..06fcc832 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryInterpreters.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using DynamicExpresso; +using Ichni.Menu; +using Ichni.Story; +using Ichni.Story.UI; +using UnityEngine; + +namespace Ichni.Story +{ + public static partial class StoryInterpreters + { + public static readonly Interpreter FunctionInterpreter; + public static readonly Interpreter ConditionInterpreter; + + static StoryInterpreters() + { + FunctionInterpreter = new Interpreter(); + ConditionInterpreter = new Interpreter(); + + SetFunctionInterpreter(); + SetConditionInterpreter(); + } + + static void SetFunctionInterpreter() + { + FunctionInterpreter.SetFunction("SetVariable", new Action(SetStoryVariable)); + FunctionInterpreter.SetFunction("GetVariable", new Func(GetStoryVariable)); + FunctionInterpreter.SetFunction("GenerateDialogBlock", new Action(GenerateDialogBlock)); + FunctionInterpreter.SetFunction("GenerateSongBlock", new Action(GenerateSongBlock)); + FunctionInterpreter.SetFunction("SetUnlockKey", new Action(SetUnlockKey)); + } + + static void SetConditionInterpreter() + { + ConditionInterpreter.SetFunction("GetVariable", new Func(GetStoryVariable)); + } + } + + public static partial class StoryInterpreters + { + /// + /// 设置全局变量的值 + /// + static void SetStoryVariable(string variableName, int value) + { + GameSaveManager.instance.StorySaveModule.storyVariables[variableName] = value; + } + + /// + /// 获取全局变量的值 + /// + static int GetStoryVariable(string variableName) + { + if (GameSaveManager.instance.StorySaveModule.storyVariables.TryGetValue(variableName, out int value)) + { + return value; + } + + throw new ArgumentException($"Global variable '{variableName}' not found."); + } + } + + public static partial class StoryInterpreters + { + static void GenerateDialogBlock(string blockName) + { + StoryBlockUIBase currentBlock = StoryManager.instance.storyline.currentBlock; + Vector2 positionOffset = new Vector2(500, 0); + DialogBlockUI newBlock = StoryManager.instance.storyline.GenerateDialogBlock(blockName, currentBlock.blockPosition + positionOffset, StoryBlockState.Current); + StoryManager.instance.storyline.GenerateConnector(currentBlock, newBlock); + StoryManager.instance.storyline.SetUpBackground(); + StoryManager.instance.storyline.connectors.ForEach(c => c.SetCurve()); + } + + static void GenerateSongBlock(string blockName) + { + StoryBlockUIBase currentBlock = StoryManager.instance.storyline.currentBlock; + Vector2 positionOffset = new Vector2(500, 0); + SongBlockUI newBlock = StoryManager.instance.storyline.GenerateSongBlock(blockName, currentBlock.blockPosition + positionOffset, StoryBlockState.Current); + StoryManager.instance.storyline.GenerateConnector(currentBlock, newBlock); + } + + static void SetUnlockKey(string key) + { + if (GameSaveManager.instance.SongSaveModule.storyUnlockKeys.Add(key)) + { + GameSaveManager.instance.SongSaveModule.SaveStoryUnlockKeys(); + + DialogManager.instance.dialogEndActions.Add(() => + { + ChapterSelectionUnit currentChapter = ChapterSelectionManager.instance.currentChapter; + List unlockedSongs = currentChapter.GetRelatedSongNamesOfUnlockKey(key); + foreach (string songName in unlockedSongs) + { + StoryManager.instance.storyUIPage.messageBox.AddInfo( + "Message/Unlock_Song_Title", "Message/Unlock_Song", + () => StoryManager.instance.storyUIPage.messageBox.SetParameter("SongName", songName)); + } + }); + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryInterpreters.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryInterpreters.cs.meta new file mode 100644 index 00000000..9b551691 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryInterpreters.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dd119fb631aebb548a637407d15c5eea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryManager.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryManager.cs new file mode 100644 index 00000000..ace9244a --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryManager.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using Ichni.Story.UI; +using Ichni.UI; +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.Story +{ + public partial class StoryManager : SerializedMonoBehaviour + { + public static StoryManager instance; + + [FormerlySerializedAs("storylineDisplay")] public Storyline storyline; + public StoryUIPage storyUIPage; + + public Dictionary storyDatas; + + + void Awake() + { + instance = this; + } + } + + public partial class StoryManager + { + [Button] + public void ClearAllStorySave() + { + GameSaveManager.instance.StorySaveModule.ClearAllStoryline(); + GameSaveManager.instance.SongSaveModule.ClearStoryKeys(); + } + } + + public enum StoryBlockState + { + Locked, + Current, + Completed + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryManager.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryManager.cs.meta new file mode 100644 index 00000000..2f93f10e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/Dialog/StoryManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e58008720832d5045ada32d1161faa69 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData.meta new file mode 100644 index 00000000..d4c0850c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31a6a9bf0919951489cacfb86fb715c9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData/StoryData.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData/StoryData.cs new file mode 100644 index 00000000..f946d651 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData/StoryData.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.Story +{ + [CreateAssetMenu(fileName = "StoryData", menuName = "Ichni/Story/StoryData")] + public class StoryData : SerializedScriptableObject + { + public List dialogBlockDatas; // 剧情单元格名称列表 + public List songBlockDatas; // 音乐单元格名称列表 + public List tutorialBlockDatas; // 教程单元格名称列表 + public List initialBlocks; // 初始剧情单元格列表,包含所有初始剧情单元格的名称 + + public StoryBlockData GetDataByName(string blockName, out Type dataType) + { + foreach (var block in tutorialBlockDatas.Where(block => block.blockName == blockName)) + { + dataType = typeof(TutorialBlockData); + return block; + } + + foreach (var block in songBlockDatas.Where(block => block.blockName == blockName)) + { + dataType = typeof(SongBlockData); + return block; + } + + foreach (var block in dialogBlockDatas.Where(block => block.blockName == blockName)) + { + dataType = typeof(DialogBlockData); + return block; + } + + throw new ArgumentException($"No block found with name: {blockName}"); + } + } + + [InlineProperty] + [Serializable] + public class InitialBlockData + { + public string blockName; + public StoryBlockState initialState; // 初始状态 + public Vector2 blockPosition; // 初始位置 + public List nextBlocks; // 下一步可选的剧情单元格名称列表 + } + + [InlineProperty] + [Serializable] + public class StoryBlockData + { + [FoldoutGroup("$blockName", true)] + public string blockName; + [FoldoutGroup("$blockName")] + public string blockID; + [FoldoutGroup("$blockName")] + public Vector2 blockSize; + } + + [InlineProperty] + [Serializable] + public class TutorialBlockData : StoryBlockData + { + [FoldoutGroup("$blockName")] + public string tutorialName; + + + public TutorialBlockData() + { + this.blockSize = new Vector2(400, 200); + } + } + + [InlineProperty] + [Serializable] + public class DialogBlockData : StoryBlockData + { + [FoldoutGroup("$blockName")] + public string dialogTitle; + + + public DialogBlockData() + { + this.blockSize = new Vector2(400, 200); + } + } + + [InlineProperty] + [Serializable] + public class SongBlockData : StoryBlockData + { + [FoldoutGroup("$blockName")] + public string songName; + + + public SongBlockData() + { + this.blockSize = new Vector2(400, 200); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData/StoryData.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData/StoryData.cs.meta new file mode 100644 index 00000000..b63830da --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryData/StoryData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ab917c50249812429ebd44d6574497c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI.meta new file mode 100644 index 00000000..43d82222 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cf843fc3f4fd10e499a6cc1b60bc1861 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks.meta new file mode 100644 index 00000000..d9234b71 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b2c23056fb0ab4b45a8db11866018794 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BeatmapStatusMark.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BeatmapStatusMark.cs new file mode 100644 index 00000000..349b514e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BeatmapStatusMark.cs @@ -0,0 +1,18 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class BeatmapStatusMark : MonoBehaviour +{ + // Start is called before the first frame update + void Start() + { + + } + + // Update is called once per frame + void Update() + { + + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BeatmapStatusMark.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BeatmapStatusMark.cs.meta new file mode 100644 index 00000000..5a3c0900 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BeatmapStatusMark.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b547d2cc398393a46a2a4c503f128cac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs new file mode 100644 index 00000000..4c6a80ec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs @@ -0,0 +1,39 @@ +using System.Collections; +using System.Collections.Generic; +using Ichni.Story.UI; +using SLSUtilities.General; +using UnityEngine; +using UnityEngine.UI.Extensions; + +namespace Ichni.Story +{ + public class BlockConnectorUI : MonoBehaviour + { + public UILineRenderer curve; + public StoryBlockUIBase startBlock; + public StoryBlockUIBase endBlock; + + public void SetCurve(StoryBlockUIBase startBlock = null, StoryBlockUIBase endBlock = null) + { + this.startBlock ??= startBlock; + this.endBlock ??= endBlock; + + if(this.startBlock == null || this.endBlock == null) + { + Debug.LogWarning("Start or end block is not set for the curve."); + return; + } + + Vector2 startPosition = SpaceConverter.GetLocalUIPosition(this.startBlock.outPort, GetComponent()); + Vector2 endPosition = SpaceConverter.GetLocalUIPosition(this.endBlock.inPort, GetComponent()); + + Vector2 mid1 = (startPosition + endPosition) / 2; + Vector2 mid2 = (startPosition + endPosition) / 2; + + mid1.y = startPosition.y; + mid2.y = endPosition.y; + + curve.Points = new Vector2[] { startPosition, /*mid1*/ mid2, endPosition}; + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs.meta new file mode 100644 index 00000000..b5c5d15d --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98bbf463dca50cc43961c0bb2b8d4f22 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs new file mode 100644 index 00000000..fb7f8089 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.Story.UI +{ + public class DialogBlockUI : StoryBlockUIBase + { + public string blockTitle; + public TMP_Text titleText; + + public Button button; + public List choiceGroups; + + public void Initialize(string blockName, Vector2 position, Vector2 positionOffset, + Vector2 size, StoryBlockState state, string blockTitle) + { + base.Initialize(blockName, position, positionOffset, size, state); + + this.blockTitle = blockTitle; + titleText.text = blockTitle; + + button.onClick.AddListener(() => + { + state = this.state; + + if(state == StoryBlockState.Locked) return; + + StoryManager.instance.storyline.currentBlock = this; + + if (state == StoryBlockState.Current) + { + DialogManager.instance.SetDialog(blockName); + DialogManager.instance.PlayNextDialogParagraph(DialogManager.instance.currentDialog); + } + else if (state == StoryBlockState.Completed) + { + DialogManager.instance.SetDialog(blockName); + DialogManager.instance.PlayNextDialogParagraph(DialogManager.instance.currentDialog, false); + DialogManager.instance.RevealDialog(); + } + }); + } + + public override StoryBlockSave GetBlockSave() + { + return new DialogBlockSave(blockName, blockPosition, state); + } + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs.meta new file mode 100644 index 00000000..9168727b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b976128eb3ec59e4e866dbb610441706 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs new file mode 100644 index 00000000..d56a4002 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs @@ -0,0 +1,76 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Ichni.Menu; +using Ichni.RhythmGame; +using TMPro; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEngine.UI; + +namespace Ichni.Story.UI +{ + public class SongBlockUI : StoryBlockUIBase + { + public string songName; + public Button button; + public TMP_Text songNameText; + public RectTransform beatmapStatusMarkContainer; + + public GameObject beatmapStatusMarkPrefab; + + public void Initialize(string blockName, Vector2 position, Vector2 positionOffset, + Vector2 size, StoryBlockState state, string songName) + { + base.Initialize(blockName, position, positionOffset, size, state); + + this.songName = songName; + songNameText.text = songName; + + button.onClick.AddListener(() => + { + //MenuManager.instance.prepareUIPage.SetUpPrepareUIPage(songName); + //MenuManager.instance.prepareUIPage.FadeIn(); + }); + + SetUpBeatmapStatusMarks(); + } + + public override StoryBlockSave GetBlockSave() + { + return new SongBlockSave(blockName, blockPosition, state); + } + + public void SetUpBeatmapStatusMarks() + { + SongStatusSave songStatusSave = GameSaveManager.instance.SongSaveModule.songStatusSaves[songName]; + + string chapter = ChapterSelectionManager.instance.currentChapter.chapterIndex; + ChapterSelectionUnit cpt = ChapterSelectionManager.instance.chapters.First(c => c.chapterIndex == chapter); + SongItemData song = cpt.songs.First(s => s.songName == this.songName); + for (var index = 0; index < song.difficultyDataList.Count; index++) + { + var difficulty = song.difficultyDataList[index]; + var beatmapSave = songStatusSave.beatmapSaves[index]; + + if (beatmapSave.isAllPerfect) + { + GameObject mark = Instantiate(beatmapStatusMarkPrefab, beatmapStatusMarkContainer); + mark.GetComponent().color = difficulty.color; + mark.transform.GetChild(0).GetComponent().color = difficulty.color; + mark.transform.GetChild(0).GetComponent().text = "AP"; + break; + } + + if (beatmapSave.isFullCombo) + { + GameObject mark = Instantiate(beatmapStatusMarkPrefab, beatmapStatusMarkContainer); + mark.GetComponent().color = difficulty.color; + mark.transform.GetChild(0).GetComponent().color = difficulty.color; + mark.transform.GetChild(0).GetComponent().text = "FC"; + break; + } + } + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs.meta new file mode 100644 index 00000000..273499ec --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5efe0a39fe908354e9ab6d1edfdb8843 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs new file mode 100644 index 00000000..36d36506 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; + +namespace Ichni.Story.UI +{ + public abstract class StoryBlockUIBase : MonoBehaviour + { + public string blockName; + public Vector2 blockPosition; + public StoryBlockState state; + + public RectTransform blockRect; + public RectTransform inPort; + public RectTransform outPort; + + protected void Initialize(string blockName, Vector2 position, Vector2 positionOffset, Vector2 size, StoryBlockState state) + { + this.blockName = blockName; + this.blockPosition = position; + this.state = state; + + blockRect.anchoredPosition = position + positionOffset; + blockRect.sizeDelta = size; + } + + public abstract StoryBlockSave GetBlockSave(); + } +} diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs.meta new file mode 100644 index 00000000..2c2f1f2c --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d489f986a1eea1e4c938658f0fd468ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs new file mode 100644 index 00000000..71a7e445 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using DG.Tweening; +using Ichni.Menu; +using SLSUtilities.WwiseAssistance; +using TMPro; +using UniRx; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.Story.UI +{ + public class TutorialBlockUI : StoryBlockUIBase + { + public Button button; + public string tutorialName; + public TMP_Text tutorialNameText; + + public void Initialize(string blockName, Vector2 position, Vector2 positionOffset, Vector2 size, StoryBlockState state, string tutorialName) + { + base.Initialize(blockName, position, positionOffset, size, state); + + this.tutorialName = tutorialName; + tutorialNameText.text = tutorialName; + + button.onClick.AddListener(EnterTutorial); + } + + public override StoryBlockSave GetBlockSave() + { + return new TutorialBlockSave(blockName, blockPosition, state); + } + + private void EnterTutorial() + { + ChapterSelectionUnit chapter = ChapterSelectionManager.instance.currentChapter; + + SongItemData song = ChapterSelectionManager.instance.tutorialCollection.songs[chapter.chapterIndex]; + DifficultyData difficulty = song.difficultyDataList[0]; + InformationTransistor.instance.SetInformation(chapter, song, difficulty); + InformationTransistor.instance.isReturnedFromTutorial = true; + InformationTransistor.instance.isReturnedFromGame = false; + + AudioManager.Post(AK.EVENTS.ENTERTOGAME); + SongSelectionManager.instance.StopPreviewSong(); + + DOTween.KillAll(); + Observable.Timer(TimeSpan.FromSeconds(0.6f)).Subscribe(_ => { MenuManager.instance.EnterGameScene(); }); + } + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs.meta new file mode 100644 index 00000000..0d4a41d2 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 821e50e1519236a46b03eb1f005acebe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI.meta new file mode 100644 index 00000000..8ad1d3f4 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8c234abdb2207c8459280e59152ba1c9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs new file mode 100644 index 00000000..aa54080e --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs @@ -0,0 +1,11 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace Ichni.Story.UI +{ + public class ChoiceButtonUI : MonoBehaviour + { + + } +} \ No newline at end of file diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs.meta b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs.meta new file mode 100644 index 00000000..1fcc6e12 --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90f34f59ff260c44796d71c51b7c0ee6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceGroupUI.cs b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceGroupUI.cs new file mode 100644 index 00000000..8ee0d46b --- /dev/null +++ b/.agent/skills-ichni-creator-studio/unity-technician/syncrefs/Scripts/Story/StoryUI/DialogUI/ChoiceGroupUI.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Collections.Generic; +using I2.Loc; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.Story.UI +{ + public class ChoiceGroupUI : MonoBehaviour + { + public GameObject choiceButtonPrefab; + public RectTransform container; + + public List