|
|
@@ -5,6 +5,7 @@
|
|
|
|
|
|
import { sp, Node, director, Vec3, find, UITransform, tween, Tween, _decorator, Component } from "cc";
|
|
|
import EventBus from "../../../scripts/Core/EventBus";
|
|
|
+import BlinkScaleAnimator from "../../../scripts/Animations/BlinkScaleAnimator";
|
|
|
const { ccclass, property } = _decorator;
|
|
|
|
|
|
@ccclass('GuideUIController')
|
|
|
@@ -30,6 +31,45 @@ export class GuideUIController extends Component {
|
|
|
|
|
|
@property({ type: [Node], tooltip: '每步遮罩区域' })
|
|
|
public guideStepsMaskAreas: Node[] = [];
|
|
|
+ // 新增:每步Frame装饰器(仅用于显隐控制)
|
|
|
+ @property({ type: [Node], tooltip: '每步Frame装饰器(仅控制显隐)' })
|
|
|
+ public guideStepsFrameDecorators: Node[] = [];
|
|
|
+
|
|
|
+ // 显隐控制:遮罩与Frame
|
|
|
+ private setMaskAndFrameVisibility(stepIndex: number, visible: boolean): void {
|
|
|
+ console.log(`[GuideUIController] 设置第${stepIndex}步的遮罩与Frame可见性为${visible}`);
|
|
|
+ const maskNode = this.guideStepsMaskAreas && this.guideStepsMaskAreas[stepIndex];
|
|
|
+ if (maskNode && maskNode.isValid) {
|
|
|
+ maskNode.active = visible;
|
|
|
+ }
|
|
|
+ const frameNode = this.guideStepsFrameDecorators && this.guideStepsFrameDecorators[stepIndex];
|
|
|
+ if (frameNode && frameNode.isValid) {
|
|
|
+ frameNode.active = visible;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private showMaskAndFrameForStep(stepIndex: number): void {
|
|
|
+ // 先隐藏全部
|
|
|
+ console.log('[GuideUIController] 隐藏所有遮罩与Frame');
|
|
|
+ for (const n of this.guideStepsMaskAreas || []) {
|
|
|
+ if (n && n.isValid) n.active = false;
|
|
|
+ }
|
|
|
+ for (const n of this.guideStepsFrameDecorators || []) {
|
|
|
+ if (n && n.isValid) n.active = false;
|
|
|
+ }
|
|
|
+ // 显示当前步骤
|
|
|
+ this.setMaskAndFrameVisibility(stepIndex, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private hideAllMasksAndFrames(): void {
|
|
|
+ console.log('[GuideUIController] 隐藏所有遮罩与Frame');
|
|
|
+ for (const n of this.guideStepsMaskAreas || []) {
|
|
|
+ if (n && n.isValid) n.active = false;
|
|
|
+ }
|
|
|
+ for (const n of this.guideStepsFrameDecorators || []) {
|
|
|
+ if (n && n.isValid) n.active = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
@property({ type: [Node], tooltip: '手指动画起始位置节点(按步骤顺序配置)' })
|
|
|
public fingerStartNodes: Node[] = [];
|
|
|
@@ -37,6 +77,9 @@ export class GuideUIController extends Component {
|
|
|
@property({ type: [Node], tooltip: '手指动画终止位置节点(按步骤顺序配置)' })
|
|
|
public fingerTargetNodes: Node[] = [];
|
|
|
|
|
|
+ @property({ type: Vec3, tooltip: '手指尖相对按钮右下角偏移' })
|
|
|
+ public fingerTipOffset: Vec3 = new Vec3(-20, 12, 0);
|
|
|
+
|
|
|
@property({ type: Number, tooltip: 'auto 步骤延时秒数' })
|
|
|
public autoStepDelay: number = 1.0;
|
|
|
|
|
|
@@ -47,10 +90,8 @@ export class GuideUIController extends Component {
|
|
|
private _autoScheduleCallback: Function | null = null;
|
|
|
private _listeningTarget: Node | null = null;
|
|
|
private _listeningEventName: string | null = null;
|
|
|
+ private _activeBlinkComp: BlinkScaleAnimator | null = null;
|
|
|
|
|
|
- // Tutorial-specific properties
|
|
|
- private _tutorialTargetGrids: string[] = ['Grid_3_1', 'Grid_4_1']; // Target grids for tutorial
|
|
|
- private _tutorialBlockContainers: string[] = ['Block1', 'Block2']; // Block containers for tutorial
|
|
|
|
|
|
|
|
|
/**
|
|
|
@@ -104,10 +145,14 @@ export class GuideUIController extends Component {
|
|
|
this.guideStepsActions.push('wait_event');
|
|
|
this.guideStepsEvents.push('TUTORIAL_BLOCK_2_PLACED');
|
|
|
|
|
|
- // Step 2: Tutorial completion
|
|
|
- this.guideStepsTargets.push(grid4_1);
|
|
|
- this.guideStepsActions.push('auto');
|
|
|
- this.guideStepsEvents.push('');
|
|
|
+ // Step 2: Confirm selection step, wait for confirm click
|
|
|
+ const confirmButton = find('Canvas/GameLevelUI/BlockSelectionUI/diban/confirm');
|
|
|
+ if (!confirmButton) {
|
|
|
+ console.warn('[GuideUIController] Confirm button node not found');
|
|
|
+ }
|
|
|
+ this.guideStepsTargets.push(confirmButton ?? grid4_1);
|
|
|
+ this.guideStepsActions.push('wait_event');
|
|
|
+ this.guideStepsEvents.push('BLOCK_SELECTION_CONFIRMED');
|
|
|
|
|
|
console.log('[GuideUIController] Tutorial steps configured');
|
|
|
}
|
|
|
@@ -357,13 +402,7 @@ export class GuideUIController extends Component {
|
|
|
|
|
|
// 添加缩放动画效果(替换旧 Action)
|
|
|
Tween.stopAllByTarget(node);
|
|
|
- tween(node)
|
|
|
- .repeatForever(
|
|
|
- tween()
|
|
|
- .to(0.5, { scale: 1.2 })
|
|
|
- .to(0.5, { scale: 1.0 })
|
|
|
- )
|
|
|
- .start();
|
|
|
+
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -529,20 +568,6 @@ export class GuideUIController extends Component {
|
|
|
console.log(`创建拖拽动画: ${nodeId} 从 (${finalStartPos.x}, ${finalStartPos.y}) 到 (${finalTargetPos.x}, ${finalTargetPos.y})`);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 为目标节点创建高亮效果(已废弃,改为外部遮罩节点管理)
|
|
|
- */
|
|
|
- public createHighlightEffect(targetNode: Node): void {
|
|
|
- // 高亮与遮罩由外部场景节点/guideStepsMaskAreas管理,此处不再创建
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 移除高亮效果(已废弃,改为外部遮罩节点管理)
|
|
|
- */
|
|
|
- public removeHighlightEffect(): void {
|
|
|
- // 无需清理,本控制器不再管理高亮遮罩
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 按数组顺序开始新手引导
|
|
|
*/
|
|
|
@@ -569,6 +594,9 @@ export class GuideUIController extends Component {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 显示当前步骤的遮罩与Frame
|
|
|
+ this.showMaskAndFrameForStep(idx);
|
|
|
+
|
|
|
const target = this.guideStepsTargets[idx];
|
|
|
const action = (this.guideStepsActions[idx] || 'tap').toLowerCase();
|
|
|
|
|
|
@@ -585,6 +613,8 @@ export class GuideUIController extends Component {
|
|
|
const hasFingerConfig = this.fingerStartNodes && this.fingerStartNodes[idx] &&
|
|
|
this.fingerTargetNodes && this.fingerTargetNodes[idx];
|
|
|
|
|
|
+ console.log(`[GuideUIController] 运行步骤 idx=${idx}, action=${action}, hasFingerConfig=${!!hasFingerConfig}, target=${target?.name || 'null'}`);
|
|
|
+
|
|
|
// 特殊处理第一步:显示从Block1到Grid_3_1的拖拽动画
|
|
|
if (idx === 0 && action === 'wait_event') {
|
|
|
if (hasFingerConfig) {
|
|
|
@@ -609,6 +639,50 @@ export class GuideUIController extends Component {
|
|
|
console.warn('[GuideUIController] 未找到Grid_3_1,使用普通指向动画');
|
|
|
}
|
|
|
}
|
|
|
+ } else if (idx === 2 && action === 'wait_event') {
|
|
|
+ // 第三步:将手指(guild_1)放到确认按钮右下角,并让手指原地缩放
|
|
|
+ if (this._activeBlinkComp) {
|
|
|
+ this._activeBlinkComp.stop();
|
|
|
+ this._activeBlinkComp = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const finger = this.getGuideNode('guild_1');
|
|
|
+ if (finger) {
|
|
|
+ // 先停止手指的Spine动画,避免与缩放冲突
|
|
|
+ this.stopAnimation('guild_1');
|
|
|
+
|
|
|
+ // 计算目标按钮右下角的世界坐标
|
|
|
+ const cornerWorldPos = ui
|
|
|
+ ? ui.convertToWorldSpaceAR(new Vec3(ui.width * 0.5, -ui.height * 0.5, 0))
|
|
|
+ : worldPos;
|
|
|
+
|
|
|
+ // 转换为手指父节点的本地坐标并叠加指尖偏移
|
|
|
+ const localCorner = this.convertWorldToLocalPosition(cornerWorldPos, finger.parent || finger);
|
|
|
+ const finalPos = new Vec3(
|
|
|
+ localCorner.x + this.fingerTipOffset.x,
|
|
|
+ localCorner.y + this.fingerTipOffset.y,
|
|
|
+ localCorner.z + this.fingerTipOffset.z
|
|
|
+ );
|
|
|
+ finger.setPosition(finalPos);
|
|
|
+
|
|
|
+ // 显示手指UI(不启动移动动画)
|
|
|
+ this.showGuideUI('guild_1');
|
|
|
+ Tween.stopAllByTarget(finger);
|
|
|
+
|
|
|
+ // 在手指上播放闪烁缩放动画
|
|
|
+ this._activeBlinkComp = BlinkScaleAnimator.ensure(finger, {
|
|
|
+ scaleFactor: 1.3,
|
|
|
+ upDuration: 0.15,
|
|
|
+ downDuration: 0.15,
|
|
|
+ easingUp: 'sineOut',
|
|
|
+ easingDown: 'sineIn'
|
|
|
+ });
|
|
|
+ console.log('[GuideUIController] 第三步:手指位于按钮右下角并进行缩放闪烁');
|
|
|
+ } else {
|
|
|
+ console.warn('[GuideUIController] 未找到 guild_1 手指节点');
|
|
|
+ // 兜底使用普通指向动画
|
|
|
+ this.createPointingAnimation('guild_1', worldPos);
|
|
|
+ }
|
|
|
} else if (hasFingerConfig && (action === 'wait_event' || action === 'tap')) {
|
|
|
// 其他步骤如果配置了手指动画节点,也使用拖拽动画
|
|
|
this.createDragAnimation('guild_1', undefined, undefined, idx);
|
|
|
@@ -659,6 +733,17 @@ export class GuideUIController extends Component {
|
|
|
Tween.stopAllByTarget(finger);
|
|
|
}
|
|
|
|
|
|
+ // 隐藏当前步骤的遮罩与Frame(例如:第一步完成后隐藏)
|
|
|
+ if (this._currentStepIndex >= 0) {
|
|
|
+ this.setMaskAndFrameVisibility(this._currentStepIndex, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 停止第三步的闪烁缩放效果(如果有)
|
|
|
+ if (this._activeBlinkComp) {
|
|
|
+ this._activeBlinkComp.stop();
|
|
|
+ this._activeBlinkComp = null;
|
|
|
+ }
|
|
|
+
|
|
|
// 清理事件监听/计时器
|
|
|
this.clearCurrentStepListeners();
|
|
|
|
|
|
@@ -699,6 +784,11 @@ export class GuideUIController extends Component {
|
|
|
public stopGuideSequence(): void {
|
|
|
this.clearCurrentStepListeners();
|
|
|
this.hideAllGuideUI();
|
|
|
+ this.hideAllMasksAndFrames();
|
|
|
+ if (this._activeBlinkComp) {
|
|
|
+ this._activeBlinkComp.stop();
|
|
|
+ this._activeBlinkComp = null;
|
|
|
+ }
|
|
|
this._currentStepIndex = -1;
|
|
|
}
|
|
|
|
|
|
@@ -718,6 +808,6 @@ export class GuideUIController extends Component {
|
|
|
|
|
|
onDestroy() {
|
|
|
this.stopGuideSequence();
|
|
|
- // 高亮效果清理由外部负责
|
|
|
+ this.hideAllMasksAndFrames();
|
|
|
}
|
|
|
}
|