Archive
This commit is contained in:
parent
853886797c
commit
01750e1b83
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Unity generated folders
|
||||||
|
Library/
|
||||||
|
Temp/
|
||||||
|
Obj/
|
||||||
|
Logs/
|
||||||
|
Build/
|
||||||
|
Builds/
|
||||||
|
UserSettings/
|
||||||
|
MemoryCaptures/
|
||||||
|
Recordings/
|
||||||
|
|
||||||
|
# IDE / tool caches
|
||||||
|
.vs/
|
||||||
|
.idea/
|
||||||
|
.gradle/
|
||||||
|
.cache/
|
||||||
|
|
||||||
|
# OS / misc
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Node / frontend caches if present
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Test / coverage outputs
|
||||||
|
coverage/
|
||||||
|
TestResults/
|
||||||
|
|
||||||
|
# Large binary assets that usually don't help code editing
|
||||||
|
*.apk
|
||||||
|
*.aab
|
||||||
|
*.unitypackage
|
||||||
|
*.zip
|
||||||
|
*.7z
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# Optional: generated csproj/sln if you mainly use Unity-generated ones
|
||||||
|
# *.csproj
|
||||||
|
# *.sln
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
# CodeX TODO
|
||||||
|
|
||||||
|
最后更新:2026-03-07
|
||||||
|
|
||||||
|
## 当前目标
|
||||||
|
|
||||||
|
按 `docs/CombatNodeArchitecture.md` 收敛 `CombatNode` 域职责,重点是:
|
||||||
|
- `CombatScheduler` 收敛为“状态机管理器”,不再继续堆业务细节。
|
||||||
|
- 局内 `Coin / Gold / BaseHp / Loot Backpack / BuildTowerSnapshots` 由 `CombatInRunResourceManager` 作为唯一真值来源。
|
||||||
|
- `EnemyManager` 只上报敌人事件,不直接决定资源入账或状态切换。
|
||||||
|
- `PhaseEndType` 退出条件从 `PhaseLoopRuntime` 中抽离到 `IPhaseEndCondition` 实现类。
|
||||||
|
- 结束链、加载链、奖励选择链逐步从 `CombatScheduler.cs` 本体挪到状态类或专用服务。
|
||||||
|
|
||||||
|
## 已完成
|
||||||
|
|
||||||
|
### 1. 状态类拆分完成
|
||||||
|
- `CombatScheduler.cs` 内部的嵌套状态类已经迁移到 `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/`。
|
||||||
|
- 保留了 `partial class CombatScheduler + 嵌套类` 的结构。
|
||||||
|
- 旧 `CombatState` 已统一替换为 `CombatStateBase`。
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs`
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/CombatStateBase.cs`
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/*.cs`
|
||||||
|
|
||||||
|
### 2. 第一轮目标命名和骨架已建立
|
||||||
|
- `CombatResourceManager` 已重命名为 `CombatInRunResourceManager`。
|
||||||
|
- 已新增掉落解析骨架:
|
||||||
|
- `EnemyDropResolveContext`
|
||||||
|
- `EnemyDropResolveResult`
|
||||||
|
- `EnemyDropResolver`
|
||||||
|
- 已新增 phase end 骨架:
|
||||||
|
- `IPhaseEndCondition`
|
||||||
|
- `PhaseEndConditionContext`
|
||||||
|
- `PhaseEndConditionFactory`
|
||||||
|
- `NonePhaseEndCondition`
|
||||||
|
- `TimeElapsedPhaseEndCondition`
|
||||||
|
- `EnemiesClearedPhaseEndCondition`
|
||||||
|
- `BossDeadPhaseEndCondition`
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs`
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDropResolver.cs`
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseEndConditions/`
|
||||||
|
|
||||||
|
### 3. 局内资源真值已迁到 CombatInRunResourceManager
|
||||||
|
目前 `CombatInRunResourceManager` 已经接管:
|
||||||
|
- `CurrentCoin`
|
||||||
|
- `CurrentBaseHp`
|
||||||
|
- `MaxBaseHp`
|
||||||
|
- `GainedCoin`
|
||||||
|
- `GainedGold`
|
||||||
|
- 本局 `BuildTowerStats` 快照
|
||||||
|
- 奖励背包快照
|
||||||
|
|
||||||
|
已实现的资源接口:
|
||||||
|
- `InitializeForCombat(DRLevel level)`
|
||||||
|
- `MarkCombatEnded()`
|
||||||
|
- `Reset()`
|
||||||
|
- `TryConsumeCoin(int coin)`
|
||||||
|
- `AddCoin(int coin)`
|
||||||
|
- `ApplyBaseDamage(int damage)`
|
||||||
|
- `TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)`
|
||||||
|
- `AddEnemyDefeatedReward(int gainedCoin, int gainedGold)`
|
||||||
|
- `AddSettlementGold(int gainedGold)`
|
||||||
|
- `GetRewardInventorySnapshot()`
|
||||||
|
|
||||||
|
附带完成:
|
||||||
|
- `CombatCoinChangedEventArgs` 增加了 `DeltaCoin`
|
||||||
|
- `CombatBaseHpChangedEventArgs` 增加了 `DeltaBaseHp`
|
||||||
|
- coin/baseHp 变化事件现在由 `CombatInRunResourceManager` 发布,而不是 `CombatNodeComponent`
|
||||||
|
- `Reset()` 现在会清理 `ParticipantTowerInstanceIds`
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs`
|
||||||
|
- `Assets/GameMain/Scripts/Event/Combat/CombatCoinChangedEventArgs.cs`
|
||||||
|
- `Assets/GameMain/Scripts/Event/Combat/CombatBaseHpChangedEventArgs.cs`
|
||||||
|
|
||||||
|
### 4. CombatScheduler / CombatNodeComponent 已做一轮收口
|
||||||
|
#### CombatScheduler
|
||||||
|
- 启动时会先初始化 `CombatInRunResourceManager`
|
||||||
|
- 提供资源转发属性:
|
||||||
|
- `CurrentCoin`
|
||||||
|
- `CurrentGold`
|
||||||
|
- `CurrentBaseHp`
|
||||||
|
- `CurrentBuildTowerCount`
|
||||||
|
- 提供资源转发方法:
|
||||||
|
- `TryConsumeCoin(...)`
|
||||||
|
- `AddCoin(...)`
|
||||||
|
- `TryGetBuildTowerStats(...)`
|
||||||
|
- 不再通过 `CombatNodeComponent` 读写 baseHp/coin 真值
|
||||||
|
|
||||||
|
#### CombatNodeComponent
|
||||||
|
- 不再持有这些字段:
|
||||||
|
- `_currentCoin`
|
||||||
|
- `_currentGold`
|
||||||
|
- `_currentBaseHp`
|
||||||
|
- `_currentBuildTowerStats`
|
||||||
|
- 当前只保留:
|
||||||
|
- 关卡数据缓存
|
||||||
|
- `CurrentLevel / CurrentThemeType`
|
||||||
|
- 战斗启动/结束协调
|
||||||
|
- 最终结算摘要字段(`LastDefeatedEnemyCount / LastGainedCoin / LastGainedGold`)
|
||||||
|
- 对 `CombatScheduler` 的只读/操作转发
|
||||||
|
|
||||||
|
关键文件:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs`
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs`
|
||||||
|
|
||||||
|
## 还没完成
|
||||||
|
|
||||||
|
### 1. EnemyManager 事件边界还没改干净
|
||||||
|
当前仍然存在的问题:
|
||||||
|
- `EnemyManager` 还在自己计算 `droppedCoin / droppedGold / baseDamage`
|
||||||
|
- 然后把这些原始数值直接传给 `CombatScheduler`
|
||||||
|
|
||||||
|
目标状态:
|
||||||
|
- `EnemyManager` 只上报:
|
||||||
|
- `OnEnemyDefeated(DREnemy enemy)`
|
||||||
|
- `OnEnemyReachedBase(DREnemy enemy)`
|
||||||
|
- `CombatScheduler` 统一调用:
|
||||||
|
- `EnemyDropResolver`
|
||||||
|
- `CombatInRunResourceManager`
|
||||||
|
|
||||||
|
下一步建议:
|
||||||
|
- 先改 `EnemyManager` 上报签名
|
||||||
|
- 再把 coin/gold/baseDamage 的公共副作用挪到 `CombatScheduler`
|
||||||
|
|
||||||
|
### 2. PhaseEndCondition 骨架已建,但还没接入
|
||||||
|
当前仍然是旧逻辑:
|
||||||
|
- `PhaseLoopRuntime.ShouldEndCurrentPhase(...)` 还在使用
|
||||||
|
- `CombatWaitingForPhaseEndState` 还没有改成通过 `IPhaseEndCondition` 判定
|
||||||
|
|
||||||
|
目标状态:
|
||||||
|
- `PhaseLoopRuntime` 只保留 phase runtime 数据
|
||||||
|
- `CombatWaitingForPhaseEndState` 通过 `PhaseEndConditionFactory.Create(...)` 获取当前判定器
|
||||||
|
- `PhaseEndType` 的规则不再写在 `PhaseLoopRuntime` 里
|
||||||
|
|
||||||
|
### 3. CombatScheduler 本体仍然过重
|
||||||
|
当前仍然还在 `CombatScheduler.cs` 里的业务:
|
||||||
|
- 结算上下文构建
|
||||||
|
- 基地血量结算修正
|
||||||
|
- 奖励选择 UI 准备
|
||||||
|
- FinishForm 打开逻辑
|
||||||
|
- 一部分加载和清理编排
|
||||||
|
|
||||||
|
目标状态:
|
||||||
|
- `Loading` 负责加载和 `MapData` 组装
|
||||||
|
- `Settlement` 负责结算上下文和奖励准入判断
|
||||||
|
- `RewardSelection` 只处理选择流程
|
||||||
|
- `FinishForm` 只处理展示
|
||||||
|
- `WaitingForReturn` 只处理回退和最终清理
|
||||||
|
|
||||||
|
### 4. MapData + Event 解耦还没开始
|
||||||
|
当前:
|
||||||
|
- `MapData` 只有 `LevelId`
|
||||||
|
- `MapEntity` 仍通过 `GameEntry.CombatNode` 反查 coin / build stats
|
||||||
|
|
||||||
|
目标状态:
|
||||||
|
- `CombatLoadingState` 从 `CombatInRunResourceManager` 读取快照
|
||||||
|
- `CombatLoadingState` 组装更完整的 `MapData`
|
||||||
|
- `MapEntity` 后续通过 `MapData + Event` 获取上下文,而不是反查 `CombatNode`
|
||||||
|
|
||||||
|
## 推荐的后续执行顺序
|
||||||
|
|
||||||
|
1. 改 `EnemyManager` 上报边界,接入 `EnemyDropResolver`
|
||||||
|
2. 接入 `IPhaseEndCondition`,移除 `PhaseLoopRuntime.ShouldEndCurrentPhase(...)`
|
||||||
|
3. 把结算链逻辑从 `CombatScheduler.cs` 继续剥离到状态类
|
||||||
|
4. 开始做 `MapData + Event` 解耦
|
||||||
|
|
||||||
|
## 当前做变更时要记住的约束
|
||||||
|
|
||||||
|
- 状态切换只能通过 `CombatScheduler.ChangeState(...)`
|
||||||
|
- 不要把新业务继续堆回 `CombatScheduler.cs`
|
||||||
|
- `CombatNodeComponent` 现在应该保持轻量 facade,不要再把 coin/baseHp/build snapshot 回流到它
|
||||||
|
- coin/baseHp 变化事件应继续由 `CombatInRunResourceManager` 发布
|
||||||
|
- `Failed` 只处理异常失败,不处理“基地血量归零”的正常失败路径
|
||||||
|
|
||||||
|
## 当前关键入口文件速查
|
||||||
|
|
||||||
|
- 状态机宿主:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs`
|
||||||
|
- 局内资源真值:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs`
|
||||||
|
- 状态类:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatStates/`
|
||||||
|
- 加载服务:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs`
|
||||||
|
- phase runtime:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseLoopRuntime.cs`
|
||||||
|
- phase end 条件骨架:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseEndConditions/`
|
||||||
|
- 敌人域 facade:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyManager.cs`
|
||||||
|
- CombatNode 入口 facade:
|
||||||
|
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatNodeComponent.cs`
|
||||||
|
|
||||||
|
## 备注
|
||||||
|
|
||||||
|
- 当前环境没有可用的本地 C# 编译器(`dotnet / mcs / csc / msbuild / xbuild` 都不可用),所以本轮改动主要依赖文本级检查。
|
||||||
|
- 仓库目前有一些与本任务无关的脏文件,继续改动时只聚焦 `CombatNode` 相关文件即可,不要顺手回滚无关改动。
|
||||||
|
|
@ -1,225 +0,0 @@
|
||||||
使用的 5 个属性:
|
|
||||||
- AttackDamage(MuzzleComp)
|
|
||||||
- DamageRandomRate(MuzzleComp)
|
|
||||||
- RotateSpeed(BearingComp)
|
|
||||||
- AttackRange(BearingComp)
|
|
||||||
- AttackSpeed(BaseComp)
|
|
||||||
|
|
||||||
下面我给你一套专门为这 5 个属性设计的稳定映射方案,目标是:
|
|
||||||
|
|
||||||
* 不出现“未使用 RGB 位导致底色聚集”
|
|
||||||
* 不出现颜色难以区分
|
|
||||||
* 不随数值微调产生大跳变
|
|
||||||
* 能一眼看出“偏伤害 / 偏射速 / 偏范围”等方向
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 一、先定颜色语义(非常关键)
|
|
||||||
|
|
||||||
我们先给每个属性一个**语义色相(Hue)**,并且颜色间隔拉开,避免混淆。
|
|
||||||
|
|
||||||
| 属性 | 含义倾向 | Hue建议 |
|
|
||||||
|------------------|--------|----------|
|
|
||||||
| AttackDamage | 爆发/力量 | 0°(红) |
|
|
||||||
| DamageRandomRate | 波动/暴击感 | 30°(橙) |
|
|
||||||
| RotateSpeed | 灵活/机动 | 200°(青蓝) |
|
|
||||||
| AttackRange | 空间/覆盖 | 120°(绿) |
|
|
||||||
| AttackSpeed | 频率/连发 | 270°(紫) |
|
|
||||||
|
|
||||||
解释:
|
|
||||||
|
|
||||||
* 伤害 → 红(直觉最强)
|
|
||||||
* 范围 → 绿(覆盖感)
|
|
||||||
* 攻速 → 紫(科技感/高频)
|
|
||||||
* 旋转 → 青蓝(灵活)
|
|
||||||
* 随机伤害 → 橙(不稳定爆发)
|
|
||||||
|
|
||||||
Hue 间隔 > 60°,视觉上足够区分。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 二、不要直接映射到 RGB,而是走 HSV
|
|
||||||
|
|
||||||
### Step 1:归一化
|
|
||||||
|
|
||||||
对每个属性:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
x_i = clamp01((value_i - min_i) / (max_i - min_i))
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ 不要用当前组件的 min/max,要用全局平衡区间。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 三、核心算法:主导属性 + 混合属性
|
|
||||||
|
|
||||||
## 1️⃣ 找出前两名属性
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
i1 = argmax(x_i)
|
|
||||||
i2 = secondMax(x_i)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2️⃣ Hue 插值
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
w = x_i2 / (x_i1 + x_i2 + 0.0001f)
|
|
||||||
hue = lerp(Hue[i1], Hue[i2], w)
|
|
||||||
```
|
|
||||||
|
|
||||||
这能避免“单一属性颜色单调”的问题。
|
|
||||||
|
|
||||||
例如:
|
|
||||||
|
|
||||||
* 高伤害 + 高攻速 → 红紫之间
|
|
||||||
* 高范围 + 高旋转 → 绿青之间
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 四、饱和度(Saturation)
|
|
||||||
|
|
||||||
饱和度表达“专精度”。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
purity = x_i1 - x_i2
|
|
||||||
sat = lerp(0.45f, 0.9f, clamp01(purity * 1.5f))
|
|
||||||
```
|
|
||||||
|
|
||||||
* 越单一专精 → 越鲜艳
|
|
||||||
* 属性均衡 → 稍微偏灰
|
|
||||||
|
|
||||||
这样“杂牌组件”会看起来更灰一点,专精流派更亮。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 五、明度(Value)
|
|
||||||
|
|
||||||
明度表达“整体强度”。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
intensity = (x1 + x2 + x3 + x4 + x5) / 5f
|
|
||||||
val = lerp(0.55f, 0.95f, intensity)
|
|
||||||
```
|
|
||||||
|
|
||||||
强度越高越亮。
|
|
||||||
|
|
||||||
⚠️ 不要让 val 太低,否则会看起来像“禁用状态”。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 六、品质不要改 Hue
|
|
||||||
|
|
||||||
品质建议只影响:
|
|
||||||
|
|
||||||
* 外描边颜色
|
|
||||||
* 发光
|
|
||||||
* 明度偏移
|
|
||||||
* 图标底板
|
|
||||||
|
|
||||||
例如:
|
|
||||||
|
|
||||||
| 品质 | 处理方式 |
|
|
||||||
|----|-----------------|
|
|
||||||
| 白 | val × 0.9 |
|
|
||||||
| 绿 | 无变化 |
|
|
||||||
| 蓝 | 外发光 |
|
|
||||||
| 紫 | val ×1.05 + 外发光 |
|
|
||||||
| 红 | 双层发光 + 轻微高对比 |
|
|
||||||
|
|
||||||
不要把红品质直接改成红色,那会和 AttackDamage 冲突。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 七、避免“颜色基地”的关键
|
|
||||||
|
|
||||||
你之前的问题是:
|
|
||||||
|
|
||||||
> 某些属性没有值 → RGB 位没用 → 颜色集中
|
|
||||||
|
|
||||||
用现在方案:
|
|
||||||
|
|
||||||
* 即便某些属性接近 0,Hue 仍由主导属性决定
|
|
||||||
* 不会出现“默认黑色/灰色聚集”
|
|
||||||
|
|
||||||
另外建议:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
sat = max(sat, 0.4f);
|
|
||||||
val = max(val, 0.6f);
|
|
||||||
```
|
|
||||||
|
|
||||||
避免太灰太暗。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 八、额外增强:组件类型偏移
|
|
||||||
|
|
||||||
你有三类组件(枪口/轴承/底座),可以轻微加一个 hue 偏移:
|
|
||||||
|
|
||||||
* 枪口:+0°
|
|
||||||
* 轴承:+15°
|
|
||||||
* 底座:-15°
|
|
||||||
|
|
||||||
这样即便数值类似,也能区分结构类型。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 九、完整伪代码(你可以直接用)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
Color GenerateColor(float[] normalizedStats, ComponentType type)
|
|
||||||
{
|
|
||||||
float[] hueMap = { 0f, 30f, 200f, 120f, 270f };
|
|
||||||
|
|
||||||
int i1 = MaxIndex(normalizedStats);
|
|
||||||
int i2 = SecondMaxIndex(normalizedStats);
|
|
||||||
|
|
||||||
float w = normalizedStats[i2] /
|
|
||||||
(normalizedStats[i1] + normalizedStats[i2] + 0.0001f);
|
|
||||||
|
|
||||||
float hue = Mathf.Lerp(hueMap[i1], hueMap[i2], w);
|
|
||||||
|
|
||||||
float purity = normalizedStats[i1] - normalizedStats[i2];
|
|
||||||
float sat = Mathf.Lerp(0.45f, 0.9f, Mathf.Clamp01(purity * 1.5f));
|
|
||||||
|
|
||||||
float intensity = normalizedStats.Average();
|
|
||||||
float val = Mathf.Lerp(0.55f, 0.95f, intensity);
|
|
||||||
|
|
||||||
if (type == ComponentType.Axis) hue += 15f;
|
|
||||||
if (type == ComponentType.Base) hue -= 15f;
|
|
||||||
|
|
||||||
return Color.HSVToRGB(hue / 360f, sat, val);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 十、视觉稳定性测试建议
|
|
||||||
|
|
||||||
做一个小测试场景:
|
|
||||||
|
|
||||||
* 随机生成 200 个组件
|
|
||||||
* 打印颜色到网格
|
|
||||||
* 看是否:
|
|
||||||
|
|
||||||
* 有大量颜色过于相似
|
|
||||||
* 有大面积灰色
|
|
||||||
* 是否能一眼区分“偏伤害”“偏范围”等
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 结论
|
|
||||||
|
|
||||||
你不应该:
|
|
||||||
|
|
||||||
❌ 把 5 个属性硬塞进 RGB 位
|
|
||||||
|
|
||||||
你应该:
|
|
||||||
|
|
||||||
✅ 用 Hue 表达方向
|
|
||||||
✅ 用 Saturation 表达专精
|
|
||||||
✅ 用 Value 表达强度
|
|
||||||
✅ 用外观表达品质
|
|
||||||
Loading…
Reference in New Issue