引导系统基本完成,开始制作 引导步骤

This commit is contained in:
2025-08-24 23:40:37 +08:00
parent d693499397
commit 6a29821a7b
46 changed files with 11593 additions and 8594 deletions

View File

@@ -24,10 +24,6 @@ export class Main extends Root {
// EPhysics2DDrawFlags.Joint |
// EPhysics2DDrawFlags.Shape;
}
private isWxClient(): boolean {
// 检查是否存在微信API
return typeof wx !== 'undefined' && typeof (wx as any).getSystemInfoSync === 'function';
}
protected async run() {
smc.initialize = ecs.getEntity<Initialize>(Initialize);
smc.vmAdd()

View File

@@ -31,6 +31,8 @@ export class SingletonModuleComp extends ecs.Comp {
in_select:false,
in_fight:false,
};
guides:any=[0,0,0,0,0]
current_guide:number=0
data:any={
score:0,
mission:1,
@@ -94,11 +96,17 @@ export class SingletonModuleComp extends ecs.Comp {
*/
private isWxClient(): boolean {
// 检查是否存在微信API
return typeof wx !== 'undefined' && typeof (wx as any).getSystemInfoSync === 'function';
}
finishGuide(index:number){
smc.guides[index]=1
this.syncGuide()
}
syncGuide(){
//存储到远程服务器 后续再添加
}
//调试用
syncDataFromLocal(){
if(this.isWxClient()) return

View File

@@ -14,6 +14,9 @@ export enum GameEvent {
LoginSuccess = "LoginSuccess",
MAP_MOVE_END_LEFT = "MAP_MOVE_END_LEFT",
MAP_MOVE_END_RIGHT = "MAP_MOVE_END_RIGHT",
GuideStart = "GuideStart",
GuideEnd = "GuideEnd",
GuideComplete = "GuideComplete",
UpdateHero = "UpdateHero",
UpdateFightHero = "UpdateFightHero",
CastSkill = "CastSkill",
@@ -52,8 +55,11 @@ export enum GameEvent {
AD_BACK_FALSE = "AD_BACK_FALSE",
DO_AD_BACK = "DO_AD_BACK",
ShopOpen = "ShopOpen",
HerosOpen = "HerosOpen",
RestOpen = "RestOpen",
HeroInfoOpen = "HeroInfoOpen",
HeroLvUp = "HeroLvUp",
HeroUnlock = "HeroUnlock",
MonDead = "MonDead",
HeroDead = "HeroDead",
GOLD_UPDATE = "GOLD_UPDATE",

View File

@@ -20,6 +20,7 @@ export enum UIID {
Victory,
HeroSelect,
ItemInfo,
Guide=1001,
// Shop_Page,
// Hero_Page,
}
@@ -34,6 +35,7 @@ export var UIConfigData: { [key: number]: UIConfig } = {
[UIID.Victory]: { layer: LayerType.UI, prefab: "gui/element/victory" },
[UIID.HeroSelect]: { layer: LayerType.UI, prefab: "gui/hero_select" },
[UIID.ItemInfo]: { layer: LayerType.UI, prefab: "gui/element/item_info" },
[UIID.Guide]: { layer: LayerType.UI, prefab: "gui/element/guide_step" },
// [UIID.Shop_Page]: { layer: LayerType.UI, prefab: "gui/shop_page" },
// [UIID.Hero_Page]: { layer: LayerType.UI, prefab: "gui/heros_page" },
// [UIID.Toast]: { layer: LayerType.PopUp, prefab: "common/prefab/toast" },

View File

@@ -0,0 +1,247 @@
import { oops } from "db://oops-framework/core/Oops";
import { smc } from "../SingletonModuleComp";
import { GameEvent } from "./GameEvent";
import { UIID } from "./GameUIConfig";
/** 引导步骤类型枚举 */
export enum GuideStepType {
/** 点击操作 */
CLICK = "click",
/** 拖拽操作 */
DRAG = "drag",
/** 显示提示 */
TIP = "tip",
/** 等待操作 */
WAIT = "wait",
}
export const finishCurrGuide=(key:number)=>{
if(smc.guides[key]==0){
oops.message.dispatchEvent(GameEvent.GuideEnd,key)
}
}
export const startGuide=(key:number)=>{
if(smc.guides[key-1]==1&&smc.guides[key]==0){
oops.message.dispatchEvent(GameEvent.GuideStart,key)
}
}
/** 引导步骤配置接口 */
export interface IGuideStep {
/** 步骤ID */
id: string;
/** 步骤类型 */
type: GuideStepType;
/** 目标节点路径(用于查找目标节点) */
key: number;
targetPath?: string;
/** 提示文本 */
tipText?: string;
/** 提示文本偏移 */
tipOffset?: { x: number, y: number };
/** 手指动画位置偏移 */
handOffset?: { x: number, y: number };
/** 下一步骤ID */
nextStep?: string;
/** 是否可跳过 */
skippable?: boolean;
/** 触发条件 */
condition?: () => boolean;
/** 对应的UI界面ID */
uiId?: UIID;
/** 等待时间 */
waitTime?: number;
/** 结束事件 */
end_event?: string;
/** 是否禁用输入 */
noInput?: boolean;
}
/** 引导配置数据 */
export const GuideConfig: { [key: number]: IGuideStep } = {
0: {
id: "welcome", type: GuideStepType.WAIT,key:0,
targetPath: "root/gui/LayerUI/role_controller/mission_home/start/name",
tipText: "欢迎来到游戏",
handOffset: { x: 42, y: -45 }, tipOffset: { x: 0, y: 200 },
nextStep: "start_battle", end_event: "click", noInput: true,
},
1: {
id: "start_battle", type: GuideStepType.CLICK,key:1,
targetPath: "root/gui/LayerUI/role_controller/mission_home/start/name",
tipText: "让我们抵御兽人的入侵吧!",
handOffset: { x: 42, y: -45 }, tipOffset: { x: 0, y: 200 },
// 42 45是手指的偏移量 0 0是提示的偏移量
},
2: {
id: "hero_page", type: GuideStepType.CLICK,key:2,
// 使用相对路径,从场景根节点开始查找
targetPath: "root/gui/LayerUI/role_controller/mission_home/hero_page",
tipText: "去招募更多英雄增强队伍吧",
handOffset: { x: 42, y: -45 }, // 向上偏移50像素避免遮挡
},
3: {
id: "hero_info", type: GuideStepType.CLICK,key:3,
// 使用相对路径,从场景根节点开始查找
targetPath: "root/gui/LayerUI/role_controller/mission_home/hero_page",
tipText: "选择可以解锁的英雄吧",
handOffset: { x: 42, y: -45 }, // 向上偏移50像素避免遮挡
},
4: {
id: "re_battle", type: GuideStepType.CLICK,key:4,
// 使用相对路径,从场景根节点开始查找
targetPath: "root/gui/LayerUI/hero_select/main/view/heros/hero2",
tipText: "点击这里选择你的第一个英雄!",
handOffset: { x: 42, y: -45 }, // 向上偏移50像素避免遮挡
},
5: {
id: "up_lv_hero", type: GuideStepType.CLICK,key:5,
// 使用相对路径,从场景根节点开始查找
targetPath: "root/gui/LayerUI/hero_select/main/view/heros/hero2",
tipText: "点击这里选择你的第一个英雄!",
handOffset: { x: 42, y: -45 }, // 向上偏移50像素避免遮挡
},
};
/** 引导配置数组(用于兼容性) */
export const GuideConfigArray: IGuideStep[] = Object.values(GuideConfig);
/** 结束事件到引导索引的映射 */
export const EndEventToKey: { [key: string]: number[] } = {
[GameEvent.UpdateFightHero]: [4],
[GameEvent.MonDead]: [1],
[GameEvent.HeroLvUp]: [5],
// 示例:一个事件对应多个引导步骤
// [GameEvent.BattleStart]: [2, 3, 4], // 战斗开始时触发多个引导
// [GameEvent.LevelUp]: [5, 6, 7], // 升级时触发多个引导
// [GameEvent.HeroUnlock]: [8, 9], // 解锁英雄时触发多个引导
// [GameEvent.MissionComplete]: [10, 11, 12], // 任务完成时触发多个引导
}
/** 通过 end_event 查找引导索引数组 */
export function findGuidesByEndEvent(event: string): number[] {
return EndEventToKey[event] || [];
}
/** 通过 end_event 查找引导索引(保持向后兼容) */
export function findGuideByEndEvent(event: string): number | undefined {
const guides = EndEventToKey[event];
return guides && guides.length > 0 ? guides[0] : undefined;
}
/** 通过引导ID查找引导索引 */
export function findGuideIndexById(guideId: string): number {
for (const [key, guide] of Object.entries(GuideConfig)) {
if (guide.id === guideId) {
return parseInt(key);
}
}
return -1;
}
/** 通过引导ID查找引导配置 */
export function findGuideById(guideId: string): IGuideStep | undefined {
for (const guide of Object.values(GuideConfig)) {
if (guide.id === guideId) {
return guide;
}
}
return undefined;
}
/** 通过数字ID查找引导配置 */
export function findGuideByNumberId(numberId: number): IGuideStep | undefined {
return GuideConfig[numberId];
}
/** 获取所有引导ID列表 */
export function getAllGuideIds(): number[] {
return Object.keys(GuideConfig).map(key => parseInt(key));
}
/** 获取引导配置总数 */
export function getGuideConfigCount(): number {
return Object.keys(GuideConfig).length;
}
/**
* 使用示例如何使用新的数字ID配置格式
*
* 1. 配置格式:
* export const GuideConfig: { [key: number]: IGuideStep } = {
* 0: { id: "welcome", ... },
* 1: { id: "start_battle", ... },
* 2: { id: "hero_page", ... },
* }
*
* 2. 通过数字ID查找引导
* const guide = findGuideByNumberId(0); // 直接通过数字ID查找
*
* 3. 通过字符串ID查找引导
* const guide = findGuideById("welcome"); // 通过字符串ID查找
*
* 4. 获取引导的数字索引:
* const index = findGuideIndexById("welcome"); // 返回 0
*
* 5. 事件映射配置:
* export const EndEventToKey: { [key: string]: number[] } = {
* [GameEvent.BattleStart]: [2, 3, 4], // 一个事件对应多个引导
* }
*
* 6. 启动引导:
* oops.message.dispatchEvent(GameEvent.GuideStart, 0); // 启动ID为0的引导
*
* 7. 优势:
* - 数字ID与 smc.guides 数组索引完全对应
* - 支持一个事件触发多个引导
* - 向后兼容,现有代码无需大幅修改
* - 便于扩展新的引导步骤
* - 数组索引从0开始符合编程习惯
*/
/** 引导管理器配置 */
export interface IGuideManagerConfig {
/** 是否启用引导 */
enabled: boolean;
/** 引导配置文件路径 */
configPath: string;
/** 引导进度保存键 */
progressKey: string;
/** 默认引导ID */
defaultGuideId: string;
/** 引导层优先级 */
layerPriority: number;
}
/** 默认引导管理器配置 */
export const DefaultGuideConfig: IGuideManagerConfig = {
enabled: true,
configPath: "config/Guide",
progressKey: "guide_progress",
defaultGuideId: "welcome",
layerPriority: 1000
};
/** 引导事件类型 */
export enum GuideEventType {
/** 引导开始 */
GUIDE_START = "guide_start",
/** 引导步骤完成 */
STEP_COMPLETE = "step_complete",
/** 引导跳过 */
GUIDE_SKIP = "guide_skip",
/** 引导完成 */
GUIDE_COMPLETE = "guide_complete",
/** 引导错误 */
GUIDE_ERROR = "guide_error"
}
/** 引导步骤状态 */
export enum GuideStepStatus {
/** 未开始 */
NOT_STARTED = "not_started",
/** 进行中 */
IN_PROGRESS = "in_progress",
/** 已完成 */
COMPLETED = "completed",
/** 已跳过 */
SKIPPED = "skipped"
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "2a50d441-0bd3-4762-bd48-0f3007ad829f",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,16 @@
export const Tasks = [
{
id: 0,
name: "新手指引",
description: "新手指引描述",
reward: 100,
type: 0,
},
{
id: 1,
name: "每日任务",
description: "每日任务描述",
reward: 100,
},
]

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "8668f7df-174a-4b7a-9c82-370e572d0d35",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -13,8 +13,9 @@ import { EquipSpecialAttr } from "../common/config/Equips";
import { FightSet, getExpDrops, getStoneDrops, TooltipTypes } from "../common/config/Mission";
import { RandomManager } from "db://oops-framework/core/common/random/RandomManager";
import { EnhancementType } from "../common/config/LevelUp";
import { MonsterDropManager } from "../common/config/Items";
import { Items, MonsterDropManager } from "../common/config/Items";
import { HeroInfo } from "../common/config/heroSet";
import { finishCurrGuide, GuideConfig, startGuide } from "../common/config/Guide";
const { ccclass, property } = _decorator;
@@ -124,7 +125,6 @@ export class HeroViewComp extends CCComp {
onLoad() {
this.as = this.getComponent(HeroSpine);
//console.log("[HeroViewComp]:hero view comp ",this.FIGHTCON)
this.on(GameEvent.HeroLvUp,this.to_update_lv,this)
this.on(GameEvent.FightEnd,this.do_fight_end,this)
const collider = this.node.getComponent(BoxCollider2D);
this.scheduleOnce(()=>{
@@ -273,13 +273,18 @@ export class HeroViewComp extends CCComp {
}
do_drop(){
let drop_item=MonsterDropManager.calculateMonsterDrops(this.hero_uuid,smc.data.mission,1)
startGuide(2)
this.scheduleOnce(()=>{
if(smc.guides[2]==0&&smc.current_guide==2&&smc.guides[1]==1) {
drop_item.push({item_uuid:Items[1008].uuid,count:20})
}
},0.1)
let {exp,gold,diamond}=MonsterDropManager.calculateBaseResourceDrops(HeroInfo[this.hero_uuid].quality,smc.data.mission,this.BUFFS.length)
oops.message.dispatchEvent(GameEvent.MonDead,{mon_uuid:this.hero_uuid,drops:drop_item,game_data:{exp:exp, gold:gold, diamond:diamond}})
if(drop_item.length>0){
for(let i=0;i<drop_item.length;i++){
let d_item=drop_item[i]
smc.addItem(d_item.item_uuid,d_item.count)
}
}
if(exp>0){
@@ -583,16 +588,6 @@ export class HeroViewComp extends CCComp {
}
to_update_lv(event:string,data:any){
if(this.fac==FacSet.MON) return
// console.log("[HeroViewComp]:升级",this.BUFFCOMP)
if(this.hero_uuid!=data.uuid) return
this.apply_buff(BuffAttr.HP_MAX,data.hp)
this.apply_buff(BuffAttr.AP,data.ap)
this.BUFFCOMP.lv_up()
// this.BUFFCOMP.tooltip(TooltipTypes.lvup)
}
/** 显示伤害数字 */
showDamage(damage: number, isCrit: boolean,anm:string="atked") {

View File

@@ -16,16 +16,12 @@ export class CardControllerComp extends CCComp {
bbg_y:number=40
bbg_x:any=[-300,-150,0,150,300]
protected onLoad(): void {
// this.on(GameEvent.MissionEnd,this.mission_home_to_mission,this)
}
start() {
console.log("CardControllerComp start",this.node)
this.page_init()
}
show_info(uuid:number,type:number){
// console.log("show_info",uuid)
}
protected update(dt: number): void {
if(smc.vmdata.game_over||smc.vmdata.game_pause){
return
@@ -35,28 +31,12 @@ export class CardControllerComp extends CCComp {
page_init(){
this.node.getChildByName("mission_home").active=true;
this.node.getChildByName("mission").active=false;
}
mission_home_to_mission(){
let mission=this.node.getChildByName("mission").getComponent(MissionComp)
mission.node.active = true;
mission.mission_start()
smc.mission.play = true;
}
mission_to_mission_home(){
let mission_home=this.node.getChildByName("mission_home").getComponent(MissionHomeComp)
this.node.getChildByName("mission_home").active = true
let mission=this.node.getChildByName("mission")
mission.active = false
mission_home.load_ui_heros()
}
show_hero_home(){
let hero_home=this.node.getChildByName("hero_home")
hero_home.active = true
}
/** 视图对象通过 ecs.Entity.remove(ControllerComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "c7b87b89-72d6-4bf8-9ed2-ead66b613984",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,243 @@
import { _decorator, director, Node } from "cc";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { GuideConfig, GuideConfigArray, GuideStepType, IGuideStep, findGuideByEndEvent, findGuideById, findGuideIndexById, findGuideByNumberId, finishCurrGuide, startGuide } from "../common/config/Guide";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { UIID } from "../common/config/GameUIConfig";
import { GameEvent } from "../common/config/GameEvent";
import { smc } from "../common/SingletonModuleComp";
const { ccclass } = _decorator;
/** 新手引导组件 */
@ccclass('GuideConComp')
export class GuideConComp extends CCComp {
/** Cocos Creator 组件生命周期方法 */
onLoad() {
console.log("[GuideConComp] onLoad 被调用");
this.on(GameEvent.GuideStart, this.GuideStart, this);
this.on(GameEvent.GuideEnd, this.GuideEnd, this);
}
start() {
this.init();
}
/** 初始化引导管理器 */
init() {
console.log("[GuideConComp] init");
this.initializeGuideProgress();
// 移除自动启动第一个引导,所有引导都变成触发式
// this.checkFirstGuide();
}
/** 初始化引导进度数组 */
private initializeGuideProgress() {
// 确保 smc.guides 存在且长度匹配
if (!smc.guides || smc.guides.length !== GuideConfigArray.length) {
smc.guides = new Array(GuideConfigArray.length).fill(0);
smc.syncGuide()
console.log("[GuideCon] 初始化 smc.guides 为全0");
}
}
GuideStart(e: any, data: any) {
console.log("[GuideCon] 监听到开始引导 key: ", data);
smc.current_guide=data
if(this.isAllGuidesCompleted()) return
const guide = findGuideByNumberId(data);
if (guide) this.displayStep(guide);
}
GuideEnd(e: any, data: any) {
console.log("[GuideCon] 监听到结束引导 key: ", data);
this.completeGuide(data)
}
/** 显示引导步骤 */
private displayStep(step: IGuideStep) {
console.log("[Tutorial] 根据step类型显示引导", step);
switch (step.type) {
case GuideStepType.TIP:
this.showTip(step);
break;
case GuideStepType.CLICK:
this.showClickGuide(step);
break;
case GuideStepType.DRAG:
this.showDragGuide(step);
break;
case GuideStepType.WAIT:
this.showWaitGuide(step);
break;
}
}
/** 信息引导 */
private showTip(step: IGuideStep) {
this.showGuideStepUI(step);
this.scheduleOnce(() => {
this.onStepCompleted(step);
}, step.waitTime ?? 2000);
}
/** 点击引导 */
private showClickGuide(step: IGuideStep) {
console.log("[Tutorial] 显示点击引导", step);
if (!step.targetPath) {
console.error("[Tutorial] 点击引导缺少目标路径");
return;
}
const targetNode = this.findTargetNode(step.targetPath);
if (!targetNode) {
console.error(`[Tutorial] 找不到目标节点: ${step.targetPath}`);
return;
}
console.log("[Tutorial] 开始点击引导UI", step.targetPath);
this.showGuideStepUI(step, targetNode);
}
/** 显示拖拽引导 */
private showDragGuide(step: IGuideStep) {
console.log("[Tutorial] 显示拖拽引导:", step.id);
this.showGuideStepUI(step);
}
/** 显示等待引导 */
private showWaitGuide(step: IGuideStep) {
console.log("[Tutorial] 显示等待引导:", step.id);
this.showGuideStepUI(step);
// 触摸监听器现在由 GuideSetpComp 管理
}
/** 步骤完成回调 */
private onStepCompleted(step: IGuideStep) {
console.log(`[Tutorial] 步骤完成: ${step.id}`);
finishCurrGuide(step.key)
// 检查是否有下一个引导
if (step.nextStep && step.nextStep.trim() !== "") {
const nextGuide = findGuideById(step.nextStep);
if (nextGuide && !this.isGuideCompleted(nextGuide.id)) {
console.log(`[GuideCon] 自动开始下一个引导: ${nextGuide.id}`);
startGuide(nextGuide.key)
return;
}
}
// 引导完成关闭UI
this.closeGuideStepUI();
}
private completeAllGuide(){
smc.guides = new Array(GuideConfigArray.length).fill(1);
smc.syncGuide()
}
/** 完成指定引导 */
private completeGuide(key:any) {
smc.finishGuide(key);
console.log(`[GuideCon] 引导 ${key} 已完成,进度数组: ${JSON.stringify(smc.guides)}`);
}
/** 判断指定引导是否已完成 */
private isGuideCompleted(guideId: string): boolean {
const guideIndex = findGuideIndexById(guideId);
if (guideIndex !== -1 && guideIndex < GuideConfigArray.length) {
return smc.guides[guideIndex] === 1;
}
return false;
}
/** 判断所有引导是否已完成 */
private isAllGuidesCompleted(): boolean {
return smc.guides.every(stepStatus => stepStatus === 1);
}
/** 打开引导UI */
private showGuideStepUI(step: IGuideStep, targetNode?: Node) {
console.log(`[GuideCon] 开始打开UI: ${step.id}, uiId: ${UIID.Guide}`);
// 关闭之前的引导UI
if (oops.gui.has(UIID.Guide)) {
oops.gui.remove(UIID.Guide);
console.log("[Tutorial] 关闭之前的引导UI", UIID.Guide);
}
this.doOpenGuideStepUI(step, targetNode);
}
doOpenGuideStepUI(step: IGuideStep, targetNode?: Node) {
try {
console.log("[Tutorial] 打开新的引导UI", UIID.Guide);
oops.gui.open(UIID.Guide, {
step: step,
stepIndex: 0,
totalSteps: 1,
targetNode: targetNode,
callbacks: {
onStepComplete: this.onStepCompleted.bind(this),
onComplete: this.completeAllGuide.bind(this)
}
});
console.log(`[GuideCon] 成功打开引导步骤UI: ${UIID.Guide}`);
} catch (error) {
console.error(`[GuideCon] 打开引导步骤UI失败: ${UIID.Guide}`, error);
oops.gui.toast(step.tipText );
}
}
/** 关闭引导步骤UI */
private closeGuideStepUI() {
console.log("[GuideCon] 关闭引导步骤UI", UIID.Guide);
oops.gui.remove(UIID.Guide);
}
/** 查找目标节点 */
private findTargetNode(path: string): Node | null {
console.log(`[GuideCon] 开始查找目标节点: ${path}`);
const pathParts = path.split('/');
let currentNode: any = director.getScene();
for (const part of pathParts) {
if (!currentNode || !currentNode.getChildByName) {
console.error(`[GuideCon] 节点 ${part} 不存在或没有getChildByName方法`);
break;
}
const childNode = currentNode.getChildByName(part);
if (!childNode) {
console.error(`[GuideCon] 找不到子节点: ${part}`);
console.log(`[GuideCon] 当前节点 ${currentNode.name} 的子节点:`, currentNode.children.map(c => c.name));
break;
}
currentNode = childNode;
}
if (currentNode) {
console.log(`[GuideCon] 目标节点查找成功:`, currentNode.position, currentNode.getWorldPosition());
return currentNode as Node;
} else {
console.error(`[GuideCon] 目标节点查找失败: ${path}`);
return null;
}
}
/** 重置引导 */
resetGuide() {
// 重置进度数组为全0
smc.guides = new Array(GuideConfigArray.length).fill(0);
smc.syncGuide()
console.log("[GuideCon] 重置引导,进度数组重置为:", JSON.stringify(smc.guides));
}
/** 组件销毁时清理 */
reset() {
// this.guideProgress = []; // This line is removed
oops.gui.remove(UIID.Guide);
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "1e280c31-a416-42c8-8239-f7f6782b904b",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,224 @@
import { _decorator, BlockInputEvents, Button, Label, Node, UITransform, Vec3 } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { IGuideStep, GuideStepType } from "../common/config/Guide";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
const { ccclass, property } = _decorator;
/** 引导步骤UI组件 - 完整的引导UI容器 */
@ccclass('GuideSetpComp')
@ecs.register('GuideSetp', false)
export class GuideSetpComp extends CCComp {
@property(Label)
private tipLabel: Label = null!;
@property(Node)
private tipNode: Node = null!;
@property(Node)
private handNode: Node = null!;
@property(Button)
private skipButton: Button = null!;
/** 当前引导步骤数据 */
private currentStep: IGuideStep | null = null;
/** 当前步骤索引 */
private currentStepIndex: number = 0;
/** 总步骤数 */
private totalSteps: number = 0;
/** 目标节点 */
private _targetNode: Node | null = null;
private _showTip: boolean = false;
private _showHand: boolean = false;
private _callback: any = null;
private _noInput: any = null;
/** 触摸监听器标志位 */
private _hasTouchListener: boolean = false;
/** 添加触摸监听器 */
private addTouchListener() {
// 如果已经有监听器,不要重复添加
if (this._hasTouchListener) {
console.log("[GuideSetpComp] 触摸监听器已存在,跳过添加");
return;
}
// 直接监听当前节点的触摸事件
this.node.on(Node.EventType.TOUCH_START, this.onTouchStart, this);
this._hasTouchListener = true;
console.log("[GuideSetpComp] 已添加触摸监听器到当前节点,等待用户点击");
}
/** 触摸开始事件处理 */
private onTouchStart(event: any) {
// 检查是否当前有等待引导在运行
if (!this.currentStep || this.currentStep.type !== GuideStepType.WAIT) {
return; // 如果没有等待引导,不处理触摸事件
}
console.log("[GuideSetpComp] 检测到触摸事件,完成等待引导");
// 移除触摸监听器
this.removeTouchListener();
// 完成当前引导
if (this._callback && this._callback.onStepComplete) {
this._callback.onStepComplete(this.currentStep);
}
}
/** 移除触摸监听器 */
private removeTouchListener() {
// 如果没有监听器,直接返回
if (!this._hasTouchListener) {
return;
}
// 直接从当前节点移除触摸事件监听
this.node.off(Node.EventType.TOUCH_START, this.onTouchStart, this);
this._hasTouchListener = false;
console.log("[GuideSetpComp] 已移除触摸监听器");
}
/** 组件初始化 */
start() {
console.log("[GuideSetpComp] start", this.node);
}
onAdded(args: any) {
console.log("[GuideSetpComp] onAdded", this.node);
this.initUI();
this._noInput=this.node.getComponent(BlockInputEvents);
this._noInput.enabled=false;
// 如有传入的参数,直接处理
if (args && args.step) {
this.handleStepInfo(args);
}
}
protected onEnable(): void {
console.log("[GuideSetpComp] onEnable", this.node);
}
/** 处理步骤信息 */
private handleStepInfo(data: any) {
const { step, stepIndex, totalSteps, targetNode,callbacks } = data;
this._noInput.enabled=step.noInput??false;
this._callback=callbacks;
if(targetNode){
this._targetNode=targetNode;
this._showTip=true;
this._showHand=true;
}else{
this._targetNode=this.node;
this._showTip=true;
this._showHand=false;
}
console.log("[GuideSetpComp] 处理步骤信息:", step);
// 显示步骤
this.showStep(step, stepIndex, totalSteps,targetNode);
// 如果有手指位置,直接设置
// this.setHandPosition(this._targetNode);
}
/** 初始化UI */
private initUI() {
// 设置初始状态
this.node.active = false;
this.node.setSiblingIndex(1000);
this.tipNode.active = false;
this.handNode.active = false;
}
/** 显示引导步骤 */
showStep(step: IGuideStep, stepIndex: number, totalSteps: number,targetNode: Node) {
this.currentStep = step;
this.currentStepIndex = stepIndex;
this.totalSteps = totalSteps;
// 将handNode和tipNode从当前父节点移除
this.handNode.parent=this._targetNode;
this.tipNode.parent=this._targetNode;
this.handNode.setPosition(this.currentStep?.handOffset?.x || 0, this.currentStep?.handOffset?.y || 0);
this.tipNode.setPosition(this.currentStep?.tipOffset?.x || 0, this.currentStep?.tipOffset?.y || 0);
// 设置setSiblingIndex最大
this.handNode.setSiblingIndex(this._targetNode.children.length - 1);
this.tipNode.setSiblingIndex(this._targetNode.children.length - 1);
// 更新UI内容
this.updateStepContent()
// 显示组件
this.show();
// 如果是等待引导,添加触摸监听器
if (step.type === GuideStepType.WAIT) {
this.addTouchListener();
}
}
/** 更新步骤内容 */
private updateStepContent() {
if (!this.currentStep || !this._targetNode) return;
// 根据目标节点调整tipNode的位置
// 假设tipNode相对于目标节点是固定的偏移
// 更新提示文本
if (this.tipLabel) {
this.tipLabel.string = this.currentStep.tipText ||"";
}
// 控制跳过按钮显示
}
/** 显示组件 */
private show() {
this.node.active = true;
if (this.tipNode) {
this.tipNode.active = true;
}
if (this.handNode) {
this.handNode.active = true;
}
}
/** 跳过按钮点击事件 */
private onSkipButtonClick() {
console.log("[GuideSetpComp] 跳过按钮点击事件");
this.tipNode.destroy();
this.handNode.destroy();
// oops.gui.removeByNode(this.node);
this._callback.onComplete();
}
/** 清理资源 */
private cleanup() {
// 移除触摸监听器
this.removeTouchListener();
this.currentStep = null;
this.currentStepIndex = 0;
this.totalSteps = 0;
}
/** 组件销毁时清理 */
reset() {
this.cleanup();
this.node.active = false;
}
/** 组件销毁时清理 */
onDestroy() {
// 清理资源
this.removeTouchListener();
this.tipNode.destroy();
this.handNode.destroy();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "afbf476b-b77b-4ca9-979f-ada073411c72",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -21,6 +21,8 @@ export class HCardUICom extends CCComp {
}
protected onLoad(): void {
this.on(GameEvent.UpdateHero,this.to_update_hero,this)
this.on(GameEvent.HeroUnlock,this.to_update_hero,this)
this.on(GameEvent.HeroLvUp,this.to_update_hero,this)
}
update(deltaTime: number) {

View File

@@ -6,24 +6,40 @@ import { smc } from '../common/SingletonModuleComp';
import { GameEvent } from '../common/config/GameEvent';
import { NumberFormatter } from '../common/config/BoxSet';
import { Items } from '../common/config/Items';
import { finishCurrGuide, GuideConfig } from '../common/config/Guide';
const { ccclass, property } = _decorator;
@ccclass('HInfoComp')
export class HInfoComp extends Component {
h_uuid:number=0
private guides:any=[0,1]
private guideends:any=[3,5]
start() {
}
onAdded(args: any) {
// console.log("[HInfoComp]:onAdded",args)
this.endGuide()
this.startNextGuide()
this.update_data(args)
}
startNextGuide(){
this.guides.forEach(guide_key=>{
if(smc.guides[guide_key]==0){
console.log("[HInfoComp]:推送启动引导:key",guide_key)
oops.message.dispatchEvent(GameEvent.GuideStart, guide_key)
return
}
})
}
endGuide(){
finishCurrGuide(3)
}
update(deltaTime: number) {
}
update_data(uuid:number){
this.h_uuid=uuid
let hero_data = HeroInfo[uuid]
console.log("[HInfoComp]:update_data",uuid,hero_data,this.node)
@@ -90,7 +106,8 @@ export class HInfoComp extends Component {
}
oops.gui.toast("英雄< "+HeroInfo[this.h_uuid].name+" >解锁成功")
this.update_data(this.h_uuid)
oops.message.dispatchEvent(GameEvent.UpdateHero, {uuid:this.h_uuid})
oops.message.dispatchEvent(GameEvent.GuideEnd,GuideConfig[3].key)
oops.message.dispatchEvent(GameEvent.HeroUnlock, {uuid:this.h_uuid})
}
uplevel(){
let lv=smc.heros[this.h_uuid].lv
@@ -111,8 +128,7 @@ export class HInfoComp extends Component {
}
this.update_data(this.h_uuid)
oops.gui.toast(`英雄< ${HeroInfo[this.h_uuid].name} >升级成功`)
oops.message.dispatchEvent(GameEvent.UpdateHero, {uuid:this.h_uuid})
oops.message.dispatchEvent(GameEvent.HeroLvUp, {uuid:this.h_uuid})
}
next_hero(){
let heros=getHeroList()

View File

@@ -9,7 +9,7 @@ const { ccclass, property } = _decorator;
export class HeroSelectCom extends Component {
slot:number=0
start() {
console.log("[HeroSelectCom]:start",this.node)
}
onAdded(args: any) {
// console.log("[HeroSelectCom]:onAdded",args)
@@ -29,7 +29,7 @@ export class HeroSelectCom extends Component {
let hero=heros[i]
// console.log("[HeroPageComp]:hero",hero)
if(hero){
this.load_hero(hero)
this.load_hero(hero,i)
}
}
}
@@ -42,7 +42,7 @@ export class HeroSelectCom extends Component {
children[i].destroy()
}
}
load_hero(uuid:number){
load_hero(uuid:number,index:number){
// console.log("[HeroPageComp]:load_hero",uuid)
let parent=this.node.getChildByName("main").getChildByName("view").getChildByName("heros")
let path = "game/gui/hcard"
@@ -52,6 +52,7 @@ export class HeroSelectCom extends Component {
return;
}
const node = instantiate(prefab) as unknown as Node;
node.name="hero"+index.toString()
node.parent = parent;
let hcard = node.getComponent(HCardUICom)!;
hcard.update_data(uuid,{type:HeroConSet.SELECT,slot:this.slot})

View File

@@ -23,7 +23,6 @@ export class MissionHeroCompComp extends CCComp {
current_hero_num:number=-1
heros:any=[]
onLoad(){
this.on(GameEvent.UseHeroCard,this.call_hero,this)
this.on(GameEvent.FightReady,this.fight_ready,this)
this.on(GameEvent.Zhaohuan,this.zhao_huan,this)
this.on(GameEvent.FightEnd,this.clear_heros,this)
@@ -69,53 +68,7 @@ export class MissionHeroCompComp extends CCComp {
this.addHero(args.uuid,false)
}
call_hero(event: string, args: any){
// console.log("[MissionHeroComp]:call_hero",this.heros,this.current_hero_num,args)
// 尝试升级现有英雄
if (this.tryUpgradeExistingHero(args.uuid)) {
return
}
// 添加新英雄
this.addNewHero(args.uuid)
}
/**
* 尝试升级现有英雄
* @param uuid 英雄UUID
* @returns 是否成功升级
*/
private tryUpgradeExistingHero(uuid: number): boolean {
for (let i = 0; i < this.heros.length; i++) {
// console.log("[MissionHeroComp]:tryUpgradeExistingHero",this.heros,i,uuid)
if (this.heros[i].uuid === uuid) {
this.heros[i].count++
smc.vmdata[`hero${i+1}`].count=this.heros[i].count
oops.message.dispatchEvent(GameEvent.HeroLvUp, { uuid: uuid })
return true
}
}
return false
}
/**
* 添加新英雄到当前槽位
* @param uuid 英雄UUID
*/
private addNewHero(uuid: number) {
this.current_hero_num++
if(this.current_hero_num >= FightSet.HERO_MAX_NUM) return
this.current_hero_uuid = uuid
this.heros[this.current_hero_num].uuid = uuid
this.heros[this.current_hero_num].count = 1
this.heros[this.current_hero_num].quality = QualitySet.GREEN
this.addHero(uuid, false)
}
/** 添加英雄 */
private addHero(uuid:number=1001,is_zhaohuan:boolean=false) {

View File

@@ -5,6 +5,7 @@ import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/O
import { smc } from "../common/SingletonModuleComp";
import { GameEvent } from "../common/config/GameEvent";
import { HeroPageComp } from "./HeroPageComp";
import { finishCurrGuide, GuideConfig, startGuide } from "../common/config/Guide";
const { ccclass, property } = _decorator;
@@ -13,7 +14,6 @@ const { ccclass, property } = _decorator;
@ecs.register('MissionHome', false)
export class MissionHomeComp extends CCComp {
protected onLoad(): void {
this.on(GameEvent.MissionEnd,this.mission_end,this)
}
@@ -21,8 +21,26 @@ export class MissionHomeComp extends CCComp {
start() {
this.home_active()
}
onEnable(){
this.endGuide()
this.startNextGuide(); // 启动第一个引导
startGuide(1)
startGuide(2)
}
/** 启动下一个引导 */
private startNextGuide() {
// 检查是否还有未完成的引导
if(smc.guides[GuideConfig[0].key]==0){
oops.message.dispatchEvent(GameEvent.GuideStart,0)
}
}
private endGuide(){
// finishCurrGuide(0)
}
start_mission() {
finishCurrGuide(1)
oops.message.dispatchEvent(GameEvent.MissionStart, {})
this.node.active=false;
}
@@ -69,6 +87,7 @@ export class MissionHomeComp extends CCComp {
break
case "heros":
page_heros.active=true
finishCurrGuide(2)
let page_heros_com=page_heros.getComponent(HeroPageComp)!
page_heros_com.update_heros()
btn_heros.getChildByName("act").active=true