feat: 优化关卡以及选关 UI

This commit is contained in:
richarjiang
2026-05-30 22:37:07 +08:00
parent 332ec0081d
commit 7355cb9c2e
14 changed files with 1944 additions and 330 deletions

View File

@@ -75,21 +75,21 @@
],
"_active": true,
"_components": [
{
"__id__": 516
},
{
"__id__": 518
},
{
"__id__": 520
},
{
"__id__": 522
},
{
"__id__": 524
},
{
"__id__": 526
},
{
"__id__": 528
}
],
"_prefab": {
"__id__": 524
"__id__": 530
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -9476,19 +9476,22 @@
},
{
"__id__": 491
},
{
"__id__": 511
}
],
"_active": true,
"_components": [
{
"__id__": 511
"__id__": 517
},
{
"__id__": 513
"__id__": 519
}
],
"_prefab": {
"__id__": 515
"__id__": 521
},
"_lpos": {
"__type__": "cc.Vec3",
@@ -9565,7 +9568,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 0,
"y": 97.33,
"y": 203.25,
"z": 0
},
"_lrot": {
@@ -9577,9 +9580,9 @@
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 0.738,
"y": 0.738,
"z": 0.738
"x": 1.099,
"y": 1.099,
"z": 1.099
},
"_mobility": 0,
"_layer": 1073741824,
@@ -9604,7 +9607,7 @@
"__id__": 395
}
],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 401
@@ -9876,7 +9879,7 @@
"__id__": 393
},
"_children": [],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 407
@@ -10012,7 +10015,7 @@
"__id__": 393
},
"_children": [],
"_active": true,
"_active": false,
"_components": [
{
"__id__": 413
@@ -11411,7 +11414,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": -244.611,
"y": -168.276,
"y": -130.956,
"z": 0
},
"_lrot": {
@@ -11911,7 +11914,7 @@
"_lpos": {
"__type__": "cc.Vec3",
"x": 256.705,
"y": -171.25,
"y": -133.93,
"z": 0
},
"_lrot": {
@@ -12377,6 +12380,142 @@
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.Node",
"_name": "pose",
"_objFlags": 0,
"__editorExtras__": {},
"_parent": {
"__id__": 392
},
"_children": [],
"_active": true,
"_components": [
{
"__id__": 512
},
{
"__id__": 514
}
],
"_prefab": {
"__id__": 516
},
"_lpos": {
"__type__": "cc.Vec3",
"x": -13.206,
"y": 1647.284,
"z": 0
},
"_lrot": {
"__type__": "cc.Quat",
"x": 0,
"y": 0,
"z": 0,
"w": 1
},
"_lscale": {
"__type__": "cc.Vec3",
"x": 1.088,
"y": 1.088,
"z": 1.088
},
"_mobility": 0,
"_layer": 1073741824,
"_euler": {
"__type__": "cc.Vec3",
"x": 0,
"y": 0,
"z": 0
},
"_id": ""
},
{
"__type__": "cc.UITransform",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 511
},
"_enabled": true,
"__prefab": {
"__id__": 513
},
"_contentSize": {
"__type__": "cc.Size",
"width": 308.05999755859375,
"height": 170.60000610351562
},
"_anchorPoint": {
"__type__": "cc.Vec2",
"x": 0.49344282189031463,
"y": 0.5101406545465734
},
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "4c/jNwWQBLtp+QzZzYvv05"
},
{
"__type__": "sp.Skeleton",
"_name": "",
"_objFlags": 0,
"__editorExtras__": {},
"node": {
"__id__": 511
},
"_enabled": true,
"__prefab": {
"__id__": 515
},
"_customMaterial": null,
"_srcBlendFactor": 2,
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_skeletonData": {
"__uuid__": "443e4aca-50d0-4d49-9c21-c1daeb44116d",
"__expectedType__": "sp.SkeletonData"
},
"defaultSkin": "default",
"defaultAnimation": "1",
"_premultipliedAlpha": true,
"_timeScale": 1,
"_preCacheMode": 0,
"_cacheMode": 0,
"_sockets": [],
"_useTint": false,
"_debugMesh": false,
"_debugBones": false,
"_debugSlots": false,
"_enableBatch": false,
"loop": false,
"_id": ""
},
{
"__type__": "cc.CompPrefabInfo",
"fileId": "fehSRPzc5Axb5i4/efoZga"
},
{
"__type__": "cc.PrefabInfo",
"root": {
"__id__": 1
},
"asset": {
"__id__": 0
},
"fileId": "66Wms7/8FMI67++4Kw81Ab",
"instance": null,
"targetOverrides": null,
"nestedPrefabInstanceRoots": null
},
{
"__type__": "cc.UITransform",
"_name": "",
@@ -12387,7 +12526,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 512
"__id__": 518
},
"_contentSize": {
"__type__": "cc.Size",
@@ -12415,7 +12554,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 514
"__id__": 520
},
"_alignFlags": 20,
"_target": null,
@@ -12464,7 +12603,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 517
"__id__": 523
},
"_contentSize": {
"__type__": "cc.Size",
@@ -12492,7 +12631,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 519
"__id__": 525
},
"_alignFlags": 0,
"_target": null,
@@ -12528,7 +12667,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 521
"__id__": 527
},
"_clip": null,
"_loop": false,
@@ -12550,7 +12689,7 @@
},
"_enabled": true,
"__prefab": {
"__id__": 523
"__id__": 529
},
"inputLayout": {
"__id__": 307
@@ -12670,6 +12809,12 @@
"caidaiSkeleton": {
"__id__": 389
},
"poseNode": {
"__id__": 511
},
"poseSkeleton": {
"__id__": 514
},
"clickAudio": {
"__uuid__": "a68a6314-fb7c-48a9-bd6c-0a65ef665d50",
"__expectedType__": "cc.AudioClip"

View File

@@ -41,7 +41,7 @@ export class PageLevel extends BaseView {
private static readonly DEFAULT_STAMINA_MAX = 50;
/** 答案正确后到弹出通关弹窗之间的停留时间(不论是否有谐音梗都保持一致) */
private static readonly PASS_MODAL_DELAY_MS = 2000;
private static readonly PASS_MODAL_DELAY_MS = 1000;
/** 图片2描述默认文案 */
private static readonly DEFAULT_IMAGE2_DESCRIPTION = '这是什么?';
@@ -91,6 +91,17 @@ export class PageLevel extends BaseView {
/** 彩带 spine 动画名(一次播放) */
private static readonly CAIDAI_ANIMATION_NAME = 'open';
/**
* 通关赞美 spinepose 节点)档位:[最小通关数, 动画名],倒序匹配。
* 1-5 关 → "1"6-10 关 → "2"11+ 关 → "3"。
* count <= 0 不会匹配任何档位,自然不播放。
*/
private static readonly POSE_TIERS: ReadonlyArray<readonly [number, string]> = [
[11, '3'],
[6, '2'],
[1, '1'],
];
// ========== 节点引用 ==========
@property(Node)
inputLayout: Node | null = null;
@@ -236,6 +247,14 @@ export class PageLevel extends BaseView {
@property(sp.Skeleton)
caidaiSkeleton: sp.Skeleton | null = null;
/** 通关赞美 spine 节点PassNode 子节点,根据本场通关数选择 1/2/3 动画) */
@property(Node)
poseNode: Node | null = null;
/** 通关赞美 sp.Skeleton 组件,动画名 "1" / "2" / "3"loop=false */
@property(sp.Skeleton)
poseSkeleton: sp.Skeleton | null = null;
// ========== 配置属性 ==========
@property(AudioClip)
clickAudio: AudioClip | null = null;
@@ -330,6 +349,15 @@ export class PageLevel extends BaseView {
/** 通关页所用「已通关数量」(业务数据,给成就体系展示用) */
private _passCompletedLevelCount: number | null = null;
/**
* 本次进入 PageLevel 后累计通关数onViewLoad / _reinitLevelSession 时归零)。
* 普通模式 / 分享模式都计入;用于驱动 pose 赞美动画档位。
*/
private _sessionPassCount: number = 0;
/** pose Spine 隐藏延时定时器setTimeout 句柄);切关 / 关页时需清理避免穿屏触发 */
private _poseHideTimer: ReturnType<typeof setTimeout> | null = null;
/** 通关页动画起点(通关前)的已通关数量;为 null 表示不播跨称号过渡 */
private _passPreviousCompletedLevelCount: number | null = null;
@@ -388,6 +416,9 @@ export class PageLevel extends BaseView {
onViewLoad(): void {
console.log('[PageLevel] onViewLoad');
// 本次进入答题页的会话通关数归零;普通 / 分享模式都重新开始计数
this._sessionPassCount = 0;
// 必须在任何可能改动 InputLayout/punchLayout 位置的逻辑之前记录原始位置
this._captureActionOriginalPositions();
@@ -489,6 +520,9 @@ export class PageLevel extends BaseView {
private _reinitLevelSession(shareMode: boolean): void {
this._isShareMode = shareMode;
// 跨模式切换视为新的"本次进入"会话,赞美动画从最低档位重新开始
this._sessionPassCount = 0;
// 上一场可能遗留的弹窗 / 倒计时一并清掉,避免主线模式还看到分享态弹窗
this._resetPassNode();
this._closeWrongModal();
@@ -1726,13 +1760,6 @@ export class PageLevel extends BaseView {
this.playSound(this.successAudio);
}
/**
* 播放通关成功音效
*/
private playSuccessSound(): void {
this.playSound(this.successAudio);
}
// ========== 倒计时相关方法 ==========
/**
@@ -2099,12 +2126,18 @@ export class PageLevel extends BaseView {
this._isPassNodeShown = true;
// 记录本次进入答题页的累计通关数(普通 / 分享模式都计入)。
// 放在 _showPassNode 而不是 reportLevelCompleted分享模式不会经过 reportLevelCompleted
// 但两种模式都经过 _showPassNode且 _isPassNodeShown 防重入保证只 +1 一次。
this._sessionPassCount++;
// 配置成就体系数据 / 按钮文案 / 事件
this._setupPassNodeContent();
this._bindPassNodeEvents();
// 启动彩带 + 滑入动画 + 淡出底部UI + 通关音效
// 启动彩带 + 滑入动画 + 淡出底部UI + 通关音效 + 赞美 spine
this._playCaidai();
this._playPosePraise(this._sessionPassCount);
this._playPassNodeShowAnimation();
this.playSuccessSound();
@@ -2185,6 +2218,14 @@ export class PageLevel extends BaseView {
if (this.caidaiNode) {
this.caidaiNode.active = false;
}
// pose 节点 prefab 默认 active=true 且 defaultAnimation="1",运行时必须先关掉,
// 否则进入 PageLevel 时 pose 会立即显示并自动跑一次动画。
if (this.poseNode) {
this.poseNode.active = false;
}
this._clearPoseHideTimer();
this._isPassNodeShown = false;
this._isPassNodeAnimating = false;
}
@@ -2274,6 +2315,12 @@ export class PageLevel extends BaseView {
this.caidaiNode.active = false;
}
// 关掉赞美 spinePassNode 一关 pose 自然不可见,仍显式置 false 防御下次显示残留帧)
if (this.poseNode) {
this.poseNode.active = false;
}
this._clearPoseHideTimer();
this._unbindPassNodeEvents();
this._titleAnimator?.stop();
this._isPassNodeShown = false;
@@ -2298,6 +2345,12 @@ export class PageLevel extends BaseView {
this.caidaiNode.active = false;
}
// 同步清掉赞美 spine防止跨模式 / 切关 / 销毁时残留
if (this.poseNode) {
this.poseNode.active = false;
}
this._clearPoseHideTimer();
this._restoreBottomLayersImmediate();
this._unbindPassNodeEvents();
this._titleAnimator?.stop();
@@ -2365,6 +2418,35 @@ export class PageLevel extends BaseView {
this.caidaiSkeleton.setAnimation(0, PageLevel.CAIDAI_ANIMATION_NAME, false);
}
/**
* 根据本次会话累计通关数选择并播放赞美 spine 动画pose 节点)。
* 档位匹配规则见 PageLevel.POSE_TIERS1-5 → "1"6-10 → "2"11+ → "3"。
* 单次 spine 动画太短,这里用 loop=true 持续播放,配合 POSE_DISPLAY_DURATION
* 定时强制隐藏 poseNode。切关 / 销毁会通过 _clearPoseHideTimer 取消定时器。
*/
private _playPosePraise(count: number): void {
if (!this.poseNode || !this.poseSkeleton) return;
const tier = PageLevel.POSE_TIERS.find(([min]) => count >= min);
if (!tier) return;
const [, animName] = tier;
// 重置上一次可能仍在排队的隐藏定时器(极端情况下连击通关)
this._clearPoseHideTimer();
this.poseNode.active = true;
this.poseSkeleton.setAnimation(0, animName, false);
}
/** 清理 pose 隐藏定时器(用于切关 / 销毁 / 跨模式切换前的清场)。 */
private _clearPoseHideTimer(): void {
if (this._poseHideTimer !== null) {
clearTimeout(this._poseHideTimer);
this._poseHideTimer = null;
}
}
/** 底部两层淡出(透明度 → 0完成后 active = false */
private _fadeOutBottomLayers(): void {
for (const layer of [this.bottomLayoutNode, this.tipsLayout]) {

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ const { ccclass, property } = _decorator;
/**
* 布局配置
* view (ScrollView 的可视窗口) 宽 900高 1000
* view (ScrollView 的可视窗口) 宽 900高 1100
* 关卡 item 固定两列,纵向滚动
*
* item 的实际显示尺寸从 ListTpl 的 UITransform * scale 派生,
@@ -29,7 +29,7 @@ const LAYOUT_CONFIG = {
EDGE_PADDING_Y: 32,
CENTER_ROWS: 2,
VIEW_WIDTH: 900,
VIEW_HEIGHT: 1000,
VIEW_HEIGHT: 1300,
};
/** 必须选择的关卡数量 */