refactor: 新增富文本描边工具并优化深色面板文案可读性
1. 新增RichOutline工具类,封装cc.RichText的outline标签包裹逻辑 2. 为所有道具/技能/英雄描述富文本添加黑色2px描边,提升深色背景下的可读性 3. 优化技能描述渲染逻辑: - 调整数值高亮色为更鲜亮的绿色 - 使用罗马数字作为技能等级徽章 - 移除升级预览弹窗的重复描述,改为当前/升级后双版本对比 - 简化升级确认弹窗的格式排版 4. 调整道具/技能预制体的文字颜色为白色,适配深色面板
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -2585,9 +2585,9 @@
|
||||
"_fontSize": 18,
|
||||
"_fontColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 32,
|
||||
"g": 32,
|
||||
"b": 32,
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_maxWidth": 189.9,
|
||||
|
||||
@@ -2322,9 +2322,9 @@
|
||||
"_fontSize": 18,
|
||||
"_fontColor": {
|
||||
"__type__": "cc.Color",
|
||||
"r": 32,
|
||||
"g": 32,
|
||||
"b": 32,
|
||||
"r": 255,
|
||||
"g": 255,
|
||||
"b": 255,
|
||||
"a": 255
|
||||
},
|
||||
"_maxWidth": 190,
|
||||
|
||||
37
assets/script/game/common/RichOutline.ts
Normal file
37
assets/script/game/common/RichOutline.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @file RichOutline.ts
|
||||
* @description RichText 描边(outline)BBCode 包裹工具
|
||||
*
|
||||
* 背景:
|
||||
* Cocos Creator 3.x 的 cc.RichText 原生支持 <outline> 标签:
|
||||
* <outline color=#000000 width=2>文本</outline>
|
||||
* 无需新增组件即可为富文本提供描边能力。
|
||||
*
|
||||
* 使用:
|
||||
* import { outlineRich } from "./RichOutline";
|
||||
* this.info_rich.string = outlineRich(desc); // 默认黑色 2px
|
||||
* this.info_rich.string = outlineRich(desc, "#000", 1); // 自定义
|
||||
*
|
||||
* 注意:
|
||||
* - <outline> 为包裹型标签,需成对出现;内部可嵌套 <color>/<size> 等标签。
|
||||
* - width 为像素宽度(int),color 支持 #RGB/#RRGGBB。
|
||||
* - 空字符串直接返回,不包裹空标签。
|
||||
*/
|
||||
|
||||
/** 默认描边颜色(深黑,深色面板通用) */
|
||||
export const RICH_OUTLINE_COLOR = "#000000";
|
||||
|
||||
/** 默认描边宽度(像素) */
|
||||
export const RICH_OUTLINE_WIDTH = 2;
|
||||
|
||||
/**
|
||||
* 为 RichText 富文本串包裹描边标签
|
||||
* @param text 原始 BBCode 富文本(可为空串)
|
||||
* @param color 描边颜色(默认 #000000)
|
||||
* @param width 描边宽度像素(默认 2)
|
||||
* @returns 包裹 <outline> 后的富文本串;空串原样返回
|
||||
*/
|
||||
export function outlineRich(text: string, color: string = RICH_OUTLINE_COLOR, width: number = RICH_OUTLINE_WIDTH): string {
|
||||
if (!text) return text;
|
||||
return `<outline color=${color} width=${width}>${text}</outline>`;
|
||||
}
|
||||
9
assets/script/game/common/RichOutline.ts.meta
Normal file
9
assets/script/game/common/RichOutline.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "567d290a-d379-4074-ba04-05d463cc8f7b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -88,8 +88,8 @@ const ATTR_NAME: Partial<Record<Attrs, string>> = {
|
||||
[Attrs.hp_max]: "最大生命",
|
||||
};
|
||||
|
||||
/** 富文本数值高亮色(亮绿色,与项目绿色系同源,深色面板上清晰可读) */
|
||||
const RICH_VALUE_COLOR = "#27AE60";
|
||||
/** 富文本数值高亮色(鲜亮绿色,深色面板上高亮醒目) */
|
||||
const RICH_VALUE_COLOR = "#3CE66E";
|
||||
|
||||
/** 富文本技能名高亮色(蓝色,与数值绿色区分,标识技能名「」片段) */
|
||||
const RICH_NAME_COLOR = "#2E86C1";
|
||||
@@ -100,6 +100,9 @@ const RICH_LOCKED_COLOR = "#95A5A6";
|
||||
/** 技能名「」匹配模式(仅本模块行首/行中技能名片段使用) */
|
||||
const SKILL_NAME_PATTERN = /「([^」]*)」/g;
|
||||
|
||||
/** 技能等级罗马数字徽章(索引 = 技能等级 1~5),用于技能名后的品质感等级标识 */
|
||||
const SKILL_LV_BADGE = ["", "Ⅰ", "Ⅱ", "Ⅲ", "Ⅳ", "Ⅴ"];
|
||||
|
||||
/** "持续X秒"匹配模式(卡片描述后处理:把持续时间数值也高亮) */
|
||||
const DURATION_PATTERN = /持续(\d+)秒/g;
|
||||
|
||||
@@ -510,8 +513,8 @@ export function buildEquipRich(fieldUuids: number[], showName: boolean = false):
|
||||
*
|
||||
* 格式:
|
||||
* 「技能名」 Lv.当前技能等级:当前档技能效果
|
||||
* 升级提示:Lv.当前 → Lv.下一级:下一级技能效果(金色,仅有更高档时出现)
|
||||
*
|
||||
* 仅展示当前生效档位,不含升级提示;升级前后对比由升级确认框单独提供。
|
||||
* 数据源为运行时 HeroAttrsComp(lv 取当前英雄等级),逐触发类型逐技能渲染。
|
||||
* 技能等级取 LvTriggerEntry.s_lv,未标注时按档位序号(1/2/3...)兜底。
|
||||
*
|
||||
@@ -555,19 +558,14 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
||||
const curIdx = activeIdx >= 0 ? activeIdx : 0;
|
||||
const cur = sorted[curIdx];
|
||||
const curSLv = cur.s_lv ?? (curIdx + 1);
|
||||
const curTrig = needCount ? `${trigName}×${cur.t_num}` : trigName;
|
||||
const curHead = `「${skillName}」 Lv.${curSLv}: ${curTrig},`;
|
||||
// 触发次数直接写为富文本(数字绿色高亮),如 "受伤3次"
|
||||
const curTrig = needCount
|
||||
? `${trigName}<color=${RICH_VALUE_COLOR}>${cur.t_num}</color>次`
|
||||
: trigName;
|
||||
// 技能名 + 罗马数字等级徽章(如 "不屈壁垒Ⅰ"),等级徽标随技能名一并蓝色高亮
|
||||
const badge = SKILL_LV_BADGE[curSLv] ?? `${curSLv}`;
|
||||
const curHead = `「${skillName}${badge}」: ${curTrig},`;
|
||||
lines.push(effectRich(buildEffectLine(curHead, mergeSkillParams(base, cur.overrides))));
|
||||
|
||||
// 升级提示:存在更高档时,金色预览下一档
|
||||
if (curIdx + 1 < sorted.length) {
|
||||
const next = sorted[curIdx + 1];
|
||||
const nextSLv = next.s_lv ?? (curIdx + 2);
|
||||
const nextTrig = needCount ? `${trigName}×${next.t_num}` : trigName;
|
||||
const nextLine = buildEffectLine(`${nextTrig},`, mergeSkillParams(base, next.overrides));
|
||||
const nextText = `${nextLine.prefix}${nextLine.value}${nextLine.suffix}`;
|
||||
lines.push(`<color=${RICH_UPGRADE_COLOR}>升级 Lv.${curSLv}→Lv.${nextSLv}: ${nextText}</color>`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -600,9 +598,6 @@ function highlightDuration(text: string): string {
|
||||
return text.replace(DURATION_PATTERN, `持续<color=${RICH_VALUE_COLOR}>$1</color>秒`);
|
||||
}
|
||||
|
||||
/** 富文本升级提示色(金色,标识"升级后"的下一档预览,与未激活灰区分) */
|
||||
const RICH_UPGRADE_COLOR = "#E8A33D";
|
||||
|
||||
/**
|
||||
* 结构化行 → 富文本串(技能名蓝色、数值绿色)。
|
||||
* buildEquipRich / buildCardDescRich / buildSkillDescRich 共用,保证渲染规则唯一。
|
||||
|
||||
@@ -23,6 +23,7 @@ import { CardConfig, CardType } from "../common/config/CardSet";
|
||||
import { HeroInfo } from "../common/config/heroSet";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -284,9 +285,10 @@ export class CHeroComp extends CCComp {
|
||||
this.updateBgNode();
|
||||
|
||||
// 描述富文本:直接取 heroSet 的 info 字段(单条手写文案)
|
||||
// outlineRich 为整段富文本包裹描边,提升深色面板可读性
|
||||
if (this.info_rich) {
|
||||
const desc = hero?.info || this.cardData.info || "";
|
||||
this.info_rich.string = desc;
|
||||
this.info_rich.string = outlineRich(desc);
|
||||
this.info_rich.node.active = !!desc;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
|
||||
import { CardConfig } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { buildEquipRich } from "../common/config/HeroSkillDesc";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
@@ -155,9 +156,10 @@ export class EquipListComp extends CCComp {
|
||||
this.updateBgNode();
|
||||
|
||||
// 属性词条富文本(多词条装备由 buildEquipRich 按 \n 多行渲染,数值实时高亮)
|
||||
// outlineRich 为整段富文本包裹描边,提升深色面板可读性
|
||||
if (this.info_rich) {
|
||||
const desc = buildEquipRich(this.cardData.field || []);
|
||||
this.info_rich.string = desc;
|
||||
this.info_rich.string = outlineRich(desc);
|
||||
this.info_rich.node.active = !!desc;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ import { HeroInfo } from "../common/config/heroSet";
|
||||
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
|
||||
import { Hero } from "../hero/Hero";
|
||||
import { FacSet, FightSet, getLvColor, NumberFormatter } from "../common/config/GameSet";
|
||||
import { buildSkillDesc, ISkillDescSource } from "../common/config/HeroSkillDesc";
|
||||
import { buildSkillDescMoreRich, ISkillDescSource } from "../common/config/HeroSkillDesc";
|
||||
import { MissionEconomy } from "./MissionEconomy";
|
||||
import { UIID } from "../common/config/GameUIConfig";
|
||||
import { HeroMoreCom } from "./HeroMoreCom";
|
||||
@@ -180,12 +180,12 @@ export class HeroBoxComp extends CCComp {
|
||||
oops.message.dispatchEvent(GameEvent.ShowSmallTip, { type: "buy_coin", text: "金币不足" });
|
||||
return;
|
||||
}
|
||||
const preview = this.buildUpgradePreview(nextLv);
|
||||
const preview = this.buildUpgradePreview(heroLv, nextLv);
|
||||
|
||||
// oops.gui 公共确认窗口(自定义 CommonPrompt:内容支持富文本高亮)
|
||||
oops.gui.open(UIID.Window, {
|
||||
title: `升级英雄`,
|
||||
content: `<color=#C0392B>${heroName}</color> Lv.${heroLv} → <color=#1E8449>Lv.${nextLv}</color>\n消耗 <color=#E67E22>${cost}</color> 金币\n\n<b>升级后获得:</b>\n${preview}`,
|
||||
content: `<color=#C0392B>${heroName}</color> Lv.${heroLv} → <color=#1E8449>Lv.${nextLv}</color>\n消耗 <color=#E67E22>${cost}</color> 金币\n\n${preview}`,
|
||||
okWord: "升级",
|
||||
cancelWord: "取消",
|
||||
needCancel: true,
|
||||
@@ -205,28 +205,20 @@ export class HeroBoxComp extends CCComp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建升级后新能力预览富文本。
|
||||
* 数据源取 HeroInfo 静态配置(lv 置为目标等级),由描述标准层统一渲染,
|
||||
* 与面板技能描述同一套文案规则,杜绝数值漂移。
|
||||
* 构建升级前后对比富文本(当前能力 vs 升级后能力)。
|
||||
* 数据源取 HeroInfo 静态配置,分别按当前 lv 与 nextLv 渲染同一份技能描述,
|
||||
* 与英雄信息弹窗(buildSkillDescMoreRich)同一套文案规则,杜绝数值漂移。
|
||||
*
|
||||
* @param heroLv 当前英雄等级
|
||||
* @param nextLv 目标等级
|
||||
* @returns 多行富文本:属性成长 + 下一档触发技能/光环/复活/加成
|
||||
* @returns 多行富文本:当前属性→升级后属性 + 当前技能 vs 升级后技能
|
||||
*/
|
||||
private buildUpgradePreview(nextLv: number): string {
|
||||
private buildUpgradePreview(heroLv: number, nextLv: number): string {
|
||||
const hero = HeroInfo[this.model?.hero_uuid ?? 0];
|
||||
if (!hero) return "";
|
||||
|
||||
// ---- 属性成长(按 HERO_LV_MULTIPLIER 幂成长 + 等级额外加成) ----
|
||||
const mult = Math.pow(FightSet.HERO_LV_MULTIPLIER, nextLv - 1);
|
||||
const nextAp = Math.floor(hero.ap * mult + HeroBoxComp.sumBonus(hero.ap_bonus, nextLv));
|
||||
const nextHp = Math.floor(hero.hp * mult + HeroBoxComp.sumBonus(hero.hp_bonus, nextLv));
|
||||
const lines: string[] = [
|
||||
`攻击 <color=#27AE60>${nextAp}</color> 生命 <color=#27AE60>${nextHp}</color>`
|
||||
];
|
||||
|
||||
// ---- 新能力(触发技能 / 驻场光环 / 复活 / 额外加成,取目标等级生效档) ----
|
||||
const source: ISkillDescSource = {
|
||||
lv: nextLv,
|
||||
const buildSource = (lv: number): ISkillDescSource => ({
|
||||
lv: lv,
|
||||
call: hero.call,
|
||||
dead: hero.dead,
|
||||
fstart: hero.fstart,
|
||||
@@ -235,11 +227,29 @@ export class HeroBoxComp extends CCComp {
|
||||
atked: hero.atked,
|
||||
field: hero.field,
|
||||
revive: hero.revive,
|
||||
// 预览仅展示增量,额外加成由上方属性成长体现,避免重复
|
||||
});
|
||||
|
||||
// ---- 属性成长(按 HERO_LV_MULTIPLIER 幂成长 + 等级额外加成) ----
|
||||
const attrLine = (lv: number): string => {
|
||||
const mult = Math.pow(FightSet.HERO_LV_MULTIPLIER, lv - 1);
|
||||
const ap = Math.floor(hero.ap * mult + HeroBoxComp.sumBonus(hero.ap_bonus, lv));
|
||||
const hp = Math.floor(hero.hp * mult + HeroBoxComp.sumBonus(hero.hp_bonus, lv));
|
||||
return `攻击 <color=#27AE60>${ap}</color> 生命 <color=#27AE60>${hp}</color>`;
|
||||
};
|
||||
const skillDesc = buildSkillDesc(source);
|
||||
if (skillDesc) lines.push(skillDesc);
|
||||
return lines.join("\n");
|
||||
|
||||
// ---- 升级前后能力对比(当前 vs 升级后,同一份文案规则按 lv 渲染) ----
|
||||
const curDesc = buildSkillDescMoreRich(buildSource(heroLv));
|
||||
const nextDesc = buildSkillDescMoreRich(buildSource(nextLv));
|
||||
|
||||
return [
|
||||
`<b>当前 Lv.${heroLv}:</b>`,
|
||||
attrLine(heroLv),
|
||||
curDesc,
|
||||
``,
|
||||
`<b>升级后 Lv.${nextLv}:</b>`,
|
||||
attrLine(nextLv),
|
||||
nextDesc,
|
||||
].filter(s => s !== undefined).join("\n");
|
||||
}
|
||||
|
||||
/** 累加等级额外加成(≤目标等级的所有档位) */
|
||||
|
||||
@@ -20,6 +20,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
|
||||
import { CardConfig, CardTriggerType, CKind } from "../common/config/CardSet";
|
||||
import { EquipPoolList } from "../common/config/EquipSet";
|
||||
import { buildCardDescRich, buildEquipRich } from "../common/config/HeroSkillDesc";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
@@ -172,7 +173,7 @@ export class ItemListComp extends CCComp {
|
||||
} else {
|
||||
desc = this.cardData.info || "";
|
||||
}
|
||||
this.info_rich.string = desc;
|
||||
this.info_rich.string = outlineRich(desc);
|
||||
this.info_rich.node.active = !!desc;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { CardConfig } from "../common/config/CardSet";
|
||||
import { SkillCardList } from "../common/config/SCardSet";
|
||||
import { SkillSet } from "../common/config/SkillSet";
|
||||
import { buildCardDescRich } from "../common/config/HeroSkillDesc";
|
||||
import { outlineRich } from "../common/RichOutline";
|
||||
import { oops } from "db://oops-framework/core/Oops";
|
||||
import { getCardIconId, setSpriteFrame } from "../common/CardIconHelper";
|
||||
import { GameEvent } from "../common/config/GameEvent";
|
||||
@@ -196,9 +197,10 @@ export class SCardComp extends CCComp {
|
||||
this.updateBgNode();
|
||||
|
||||
// 描述富文本(数值随 overrides 档位自动同步高亮)
|
||||
// outlineRich 为整段富文本包裹描边,提升深色面板可读性
|
||||
if (this.info_rich) {
|
||||
const desc = buildCardDescRich(this.cardData);
|
||||
this.info_rich.string = desc;
|
||||
this.info_rich.string = outlineRich(desc);
|
||||
this.info_rich.node.active = !!desc;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user