锦瑟弦音 2025-12-18 15:06 采纳率: 82.6%
浏览 4
已结题

cocos2d游戏发布到微信小程序问题

问题遇到的现象和发生背景

在cocos 3.8.7中可以使用,但是构建发布到微信小程序就报错,具体要怎么修改呢

img

代码
import {
    _decorator,          // 装饰器
    Component,           // 组件基类
    Prefab,              // 预制体
    instantiate,         // 实例化
    Node,                // 节点
    Sprite,              // Sprite 组件
    SpriteFrame,         // SpriteFrame
    Label,               // Label
    Layout,              // Layout 布局
    resources            // resources 资源加载
} from 'cc';

import { LoadTable } from '../LoadTable';          // 配置表加载
import { EventManager } from '../EventManager';    // 事件管理
import { snail } from '../table/schema';           // 表结构

const { ccclass, property } = _decorator;

/**
 * 根据配置生成皮肤预制体列表
 */
@ccclass('SkinList')
export class SkinList extends Component {

    // 👉 这里拖拽的就是原来的 List1.prefab(带背景图的那个)
    @property(Prefab)
    skinPrefab: Prefab = null;

    // 皮肤配置数据
    private skinDataList: snail.Skin[] = [];

    // SpriteFrame 缓存
    private skinSpriteFrames: Map<string, SpriteFrame> = new Map();

    /**
     * 组件启动
     */
    start() {
        // 监听配置加载完成
        EventManager.getInstance().on(
            'configLoaded',
            this.onConfigLoaded,
            this
        );

        // 已加载直接执行
        if (LoadTable.tab && LoadTable.tab.TvSkin) {
            this.onConfigLoaded();
        }
    }

    /**
     * 组件销毁
     */
    onDestroy() {
        EventManager.getInstance().off(
            'configLoaded',
            this.onConfigLoaded,
            this
        );
    }

    /**
     * 配置表加载完成
     */
    private onConfigLoaded() {
        if (!LoadTable.tab || !LoadTable.tab.TvSkin) {
            console.error("配置表数据未加载完成");
            return;
        }

        // 获取皮肤数据
        this.skinDataList = LoadTable.tab.TvSkin.getDataList();

        if (!this.skinDataList || this.skinDataList.length === 0) {
            console.error("皮肤配置数据为空");
            return;
        }

        // 👉 Layout 只加在 List 节点上
        this.setupLayout(this.node);

        // 加载皮肤图片
        this.loadSkinSprites();
    }

    /**
     * 加载皮肤 SpriteFrame
     */
    private loadSkinSprites() {
        resources.loadDir(
            'game/skin',
            SpriteFrame,
            (err, assets: SpriteFrame[]) => {

                if (err) {
                    console.error('加载皮肤图片失败', err);
                    return;
                }

                // 缓存 SpriteFrame
                assets.forEach(sf => {
                    this.skinSpriteFrames.set(sf.name, sf);
                });

                // 创建皮肤 Item
                this.createSkinItems();
            }
        );
    }

    /**
     * 设置 Layout
     * 作用对象:List 节点
     */
    private setupLayout(listNode: Node) {

        let layout = listNode.getComponent(Layout);
        if (!layout) {
            layout = listNode.addComponent(Layout);
        }

        // 网格布局
        layout.type = Layout.Type.GRID;

        // 固定列数
        layout.constraint = Layout.Constraint.FIXED_COL;

        // 每行 4 个
        layout.constraintNum = 4;

        // 间距
        layout.spacingX = 70;
        layout.spacingY = 70;

        // 内边距
        layout.paddingTop = -200;
        layout.paddingBottom = 100;
        layout.paddingLeft = -180;
        layout.paddingRight = 0;

        // 不改变自身尺寸
        layout.resizeMode = Layout.ResizeMode.CHILDREN;
    }

    /**
     * 创建皮肤 Item(每一个都是 List1 预制体)
     */
    private createSkinItems() {

        if (!this.skinPrefab) {
            console.error('skinPrefab 未设置(请拖拽 List1.prefab)');
            return;
        }

        this.skinDataList.forEach(skinData => {

            // 实例化 List1 预制体
            const skinItem = instantiate(this.skinPrefab);

            // 命名
            skinItem.name = `skin_${skinData.id}`;

            // 直接添加到 List 节点
            this.node.addChild(skinItem);

            // 设置名称
            const nameLabel = skinItem
                .getChildByName('NameLabel')
                ?.getComponent(Label);

            if (nameLabel) {
                nameLabel.string = skinData.name;
            }

            // 设置价格
            const priceLabel = skinItem
                .getChildByName('PriceLabel')
                ?.getComponent(Label);

            if (priceLabel) {
                priceLabel.string = `价格: ${skinData.gold} 金币`;
            }

            // 设置 Icon 图片
            const spriteName = `${skinData.icon}_0`;
            const spriteFrame = this.skinSpriteFrames.get(spriteName);

            const iconSprite = skinItem
                .getChildByName('Icon')
                ?.getComponent(Sprite);

            if (iconSprite && spriteFrame) {
                iconSprite.spriteFrame = spriteFrame;
            }
        });
    }
}


import { JsonAsset, resources } from "cc";
import { Tables } from "./table/schema";

export class LoadTable {

    // 存储加载的配置表数据
    static tab: Tables;

    /**
     * 初始化 LoadTable。
     * 加载配置表数据,并创建 Tables 实例。
     */
    static init(){
        /**
         * 表名数组
         * 
         * 声明了一个名为 tableNames 的变量,
         * 并将其赋值为一个
         * 包含单个字符串元素 "tvskin" 的数组。
         */
        let tableNames = ["tvskin"];

        // 为每个表名添加 "config/" 前缀
        for (let i in tableNames) {
            tableNames[i] = "config/" + tableNames[i]
        }

        // 使用 resources.load 方法加载表数据
        resources.load(tableNames,JsonAsset,
            (err: Error,data:JsonAsset[]) => {
                // 加载完成后创建 Tables 实例,并将数据存储在 tab 属性中
                LoadTable.tab = new Tables(LoadTable.loadJson);
            }
        )
    }


    /**
     * 加载指定名称的 JSON 数据。
     * @param jsonName JSON 文件的名称。
     * @return 返回加载的 JSON 数据。
     */
    static loadJson(jsonName: string) {
        // 使用 resources.get 方法获取指定名称的 JSON 资源
        let asset = resources.get("config/" + jsonName, JsonAsset);
        // 如果获取成功并且该资源具有 json 属性,则返回该属性的值
        if (asset && asset.json) {
            return asset.json;
        }
        // 否则,返回一个空数组
        return [];
    }
}


import { _decorator, Component, director, assetManager, ProgressBar, log, SpriteFrame, JsonAsset, ImageAsset } from 'cc';
import { LoadTable } from '../main/scripts/LoadTable';
import { ProgressBar001 } from './ProgressBar001';

const { ccclass, property } = _decorator;

// 使用装饰器定义组件类
@ccclass('StartCom')
export class StartCom extends Component {
    // 进度条属性,在编辑器中可配置
    @property(ProgressBar001)
    bar: ProgressBar001 = null;

    // 总资源数量
    private totalResources = 0;
    // 已加载资源数量
    private loadedResources = 0;
    // 已成功加载的资源路径数组
    private loadedPaths: string[] = [];
    // 需要加载的所有资源路径数组
    private resourcePaths: string[] = [];


    // 组件开始时的生命周期方法
    start() {
        // 输出日志标识开始加载资源
        log('=== 开始加载资源 ===');
        // 初始化进度条为0
        this.bar.progress = 0;

        // 确保进度条组件设置正确
        this.setupProgressBar();

        // 直接测试加载配置表文件(先确保配置表能加载)
        this.testLoadConfigDirectly();
    }

    /**
     * 设置进度条属性
     */
    private setupProgressBar() {
        // 设置移动方向为从左到右
        this.bar.moveLeftToRight = true;

        // 设置可见宽度(根据你的图片尺寸调整)
        // 414是图片显示的宽度,可以根据需要调整
        this.bar.visibleWidth = 350; // 使用你测试的350或500

        // 输出设置信息用于调试
        log(`进度条设置: 方向=${this.bar.moveLeftToRight ? '左→右' : '右→左'}, 宽度=${this.bar.visibleWidth}`);
    }

    /** 
     * 直接测试加载配置表文件
     * 这个方法绕过LoadTable,直接测试配置表文件是否能正常加载
     */
    private testLoadConfigDirectly() {
        // 输出日志
        log('直接测试加载配置表...');

        // 加载resources资源包
        assetManager.loadBundle('resources', (err, bundle) => {
            // 如果加载失败
            if (err) {
                log(` 加载resources失败: ${err.message}`);
                // 直接完成加载流程
                this.finishLoading();
                return;
            }

            // 配置表文件路径
            const configPath = 'config/snail_tvskin';
            log(`尝试加载配置表: ${configPath}`);

            // 直接加载配置表文件(使用JsonAsset类型)
            bundle.load(configPath, JsonAsset, (err2, jsonAsset) => {
                // 如果加载失败
                if (err2) {
                    log(` 直接加载配置表失败: ${err2.message}`);
                } else {
                    // 加载成功
                    log(` 直接加载配置表成功`);

                    // 检查配置表数据
                    if (jsonAsset.json && Array.isArray(jsonAsset.json)) {
                        // 输出配置表数据数量
                        log(`配置表有 ${jsonAsset.json.length} 条数据`);
                        // 显示前3条数据作为示例
                        jsonAsset.json.slice(0, 3).forEach((item: any, index: number) => {
                            log(`数据 ${index + 1}: id=${item.id}, name=${item.name}, icon=${item.icon}`);
                        });
                    } else {
                        // 数据格式错误
                        log(` 配置表数据格式错误:`, jsonAsset.json);
                    }
                }

                // 继续使用LoadTable进行正式的配置表加载
                this.loadConfigTables();
            });
        });
    }

    /** 
     * 加载配置表
     * 使用LoadTable加载所有配置表数据
     */
    private loadConfigTables() {
        log('开始通过LoadTable加载配置表...');

        // 检查LoadTable是否存在
        if (!LoadTable) {
            log(' LoadTable 未定义');
            this.finishLoading();
            return;
        }

        try {
            // 重新初始化配置表(调用LoadTable的初始化方法)
            LoadTable.init();
            log(' LoadTable.init() 调用完成');

            // 等待更长时间确保配置表加载完成(异步加载需要时间)
            setTimeout(() => {
                this.debugLoadTable();
            }, 800);
        } catch (error) {
            // 初始化失败
            log(` LoadTable初始化失败: ${error}`);
            this.finishLoading();
        }
    }

    /** 
     * 调试LoadTable状态
     * 检查LoadTable是否正确加载了配置表数据
     */
    private debugLoadTable() {
        log('=== 调试LoadTable状态 ===');

        // 检查LoadTable的所有属性
        log(`LoadTable 对象:`, LoadTable);
        log(`LoadTable.tab:`, LoadTable.tab);

        // 如果LoadTable.tab存在
        if (LoadTable.tab) {
            // 检查所有表(特别是TvSkin表)
            log(`LoadTable.tab.TvSkin:`, LoadTable.tab.TvSkin);

            // 如果TvSkin表存在
            if (LoadTable.tab.TvSkin) {
                // 检查TvSkin的方法是否存在
                log(`TvSkin.getDataMap:`, LoadTable.tab.TvSkin.getDataMap);
                log(`TvSkin.getDataList:`, LoadTable.tab.TvSkin.getDataList);

                // 尝试调用方法获取数据
                try {
                    // 获取数据映射
                    const dataMap = LoadTable.tab.TvSkin.getDataMap();
                    // 获取数据列表
                    const dataList = LoadTable.tab.TvSkin.getDataList();

                    // 输出数据大小信息
                    log(`getDataMap() 返回: Map(${dataMap?.size})`);
                    log(`getDataList() 返回: Array(${dataList?.length})`);

                    // 检查数据列表
                    if (dataList && Array.isArray(dataList)) {
                        log(` 从getDataList()获取到 ${dataList.length} 个皮肤数据`);
                        // 显示前5条数据作为示例
                        dataList.slice(0, 5).forEach((skin: any, index: number) => {
                            log(`皮肤 ${index + 1}: id=${skin.id}, name=${skin.name}, icon=${skin.icon}`);
                        });
                        // 显示剩余数据数量
                        log(`... 还有 ${dataList.length - 5} 个皮肤`);
                    } else {
                        // 数据格式错误
                        log(` getDataList() 返回的不是数组:`, dataList);
                    }
                } catch (error) {
                    // 调用方法失败
                    log(` 调用getDataList()失败: ${error}`);
                }
            }
        }

        // 继续加载资源包
        this.loadResourcesBundle();
    }

    /** 
     * 加载整个resources资源包
     * 这是正式的加载流程
     */
    private loadResourcesBundle() {
        log('正在加载 resources 资源包...');

        // 重新加载bundle,确保是最新的(避免缓存问题)
        assetManager.loadBundle('resources', (err, bundle) => {
            // 如果加载失败
            if (err) {
                log(` 加载resources资源包失败: ${err.message}`);
                this.finishLoading();
                return;
            }

            // 加载成功
            log(' resources资源包加载成功');

            // 获取需要加载的资源列表
            this.getResourceList(bundle);
        });
    }

    /** 
     * 获取需要加载的资源列表
     * 根据配置表数据构建需要加载的所有资源路径
     */
    private getResourceList(bundle: any) {
        log('构建资源加载列表...');

        // 基础资源列表(必须加载的资源)
        const resourceList = [
            'game/listBg'  // 游戏列表背景图
        ];

        // 如果配置表有数据,添加所有皮肤资源
        try {
            // 检查LoadTable和TvSkin是否存在
            if (LoadTable.tab && LoadTable.tab.TvSkin) {
                // 获取皮肤数据列表
                const dataList = LoadTable.tab.TvSkin.getDataList();
                // 检查数据列表是否有效
                if (dataList && Array.isArray(dataList) && dataList.length > 0) {
                    log(`从配置表获取 ${dataList.length} 个皮肤,开始添加所有皮肤...`);

                    // 添加所有皮肤资源路径
                    dataList.forEach((skin: any, index: number) => {
                        // 构建皮肤资源路径(格式:game/skin/characterX_0)
                        const skinPath = `game/skin/${skin.icon}_0`;
                        // 添加到资源列表
                        resourceList.push(skinPath);

                        // 每10个皮肤输出一次日志,避免日志太多
                        if (index < 5 || index % 10 === 0) {
                            log(`添加皮肤 ${index + 1}: ${skin.name} -> ${skinPath}`);
                        }
                    });

                    log(` 已添加所有 ${dataList.length} 个皮肤资源`);
                } else {
                    // 数据为空或不是数组
                    log(` 配置表数据为空或不是数组`);
                }
            } else {
                // LoadTable或TvSkin不存在
                log(` LoadTable.tab 或 LoadTable.tab.TvSkin 不存在`);
            }
        } catch (error) {
            // 获取配置表数据失败
            log(` 获取配置表数据失败: ${error}`);
        }

        // 去重(确保没有重复的资源路径)
        this.resourcePaths = [...new Set(resourceList)];
        // 更新总资源数量
        this.totalResources = this.resourcePaths.length;

        // 输出加载统计信息
        log(`需要加载 ${this.totalResources} 个资源 (包括 ${this.resourcePaths.length - 1} 个皮肤资源):`);

        // 只显示前几个和最后几个资源路径,避免日志太长
        const maxShow = 10;
        for (let i = 0; i < Math.min(this.resourcePaths.length, maxShow); i++) {
            log(`${i + 1}. ${this.resourcePaths[i]}`);
        }

        // 如果资源数量超过显示上限
        if (this.resourcePaths.length > maxShow) {
            log(`... 还有 ${this.resourcePaths.length - maxShow} 个资源未显示`);
            log(`最后几个资源:`);
            // 显示最后几个资源
            for (let i = Math.max(maxShow, this.resourcePaths.length - 3); i < this.resourcePaths.length; i++) {
                log(`${i + 1}. ${this.resourcePaths[i]}`);
            }
        }

        // 测试加载第一个资源(验证资源是否能正常加载)
        this.testLoadFirstResource(bundle);
    }

    /** 
     * 测试加载第一个资源
     * 验证资源包中的资源是否能正常加载
     */
    private testLoadFirstResource(bundle: any) {
        // 如果没有资源需要加载
        if (this.resourcePaths.length === 0) {
            this.finishLoading();
            return;
        }

        // 获取第一个资源路径
        const testPath = this.resourcePaths[0];
        log(`测试加载第一个资源: ${testPath}`);

        // 尝试加载第一个资源
        bundle.load(testPath, (err: any, asset: any) => {
            // 如果加载失败
            if (err) {
                log(` 测试资源加载失败 [${testPath}]: ${err.message}`);
                log(`尝试其他可能的路径...`);

                // 尝试其他路径格式
                this.tryAlternativePaths(bundle, testPath);
            } else {
                // 加载成功
                log(` 测试资源加载成功 [${testPath}] (类型: ${asset?.constructor?.name})`);
                // 开始加载所有资源
                this.startLoadingAllResources(bundle);
            }
        });
    }

    /** 
     * 尝试其他可能的路径
     * 当默认路径加载失败时,尝试其他可能的路径格式
     */
    private tryAlternativePaths(bundle: any, originalPath: string) {
        // 生成可能的替代路径数组
        const alternatives = [
            originalPath.replace('game/skin/', 'skin/'),  // 去掉game/前缀
            originalPath.replace('_0', ''),              // 去掉_0后缀
            originalPath.replace('game/skin/', 'game/'), // 去掉skin/子目录
            originalPath.replace('game/', '')            // 去掉game/前缀
        ];

        // 已尝试的路径数量
        let tried = 0;
        // 总尝试路径数量
        const total = alternatives.length;

        // 遍历所有替代路径
        alternatives.forEach((altPath) => {
            // 跳过原始路径(已经尝试过了)
            if (altPath === originalPath) return;

            // 尝试加载替代路径
            bundle.load(altPath, (err: any, asset: any) => {
                // 增加尝试计数
                tried++;

                // 如果加载失败
                if (err) {
                    log(` 替代路径失败 [${altPath}]: ${err.message}`);
                } else {
                    // 加载成功
                    log(` 替代路径成功 [${altPath}] (类型: ${asset?.constructor?.name})`);
                    // 更新资源路径列表中的路径
                    const index = this.resourcePaths.indexOf(originalPath);
                    if (index !== -1) {
                        this.resourcePaths[index] = altPath;
                    }
                }

                // 如果所有路径都尝试过了
                if (tried >= total) {
                    // 开始加载所有资源
                    this.startLoadingAllResources(bundle);
                }
            });
        });
    }

    /** 
     * 开始加载所有资源
     * 并行加载所有资源路径对应的资源
     */
    private startLoadingAllResources(bundle: any) {
        log(`开始加载所有 ${this.totalResources} 个资源...`);

        // 记录开始时间(用于计算加载耗时)
        const startTime = Date.now();
        // 已完成加载的资源数量
        let completedCount = 0;

        // 遍历所有资源路径
        this.resourcePaths.forEach((path, index) => {
            // 使用不指定类型的方式加载(让引擎自动判断资源类型)
            bundle.load(path, (err: any, asset: any) => {
                // 增加完成计数
                completedCount++;

                // 如果加载失败
                if (err) {
                    log(` 资源加载失败 [${path}]: ${err.message}`);
                } else {
                    // 加载成功,添加到已加载路径列表
                    this.loadedPaths.push(path);

                    // 每加载10个资源输出一次进度,或者加载完成时输出
                    if (completedCount % 10 === 0 || completedCount === this.totalResources) {
                        const currentTime = Date.now();
                        const elapsedTime = (currentTime - startTime) / 1000;
                        log(` 已加载 ${completedCount}/${this.totalResources} 个资源 (用时: ${elapsedTime.toFixed(2)}s)`);
                    }
                }

                // 更新进度
                this.updateProgress();
            });
        });
    }

    /** 
     * 更新进度
     * 更新进度条显示和检查是否所有资源都加载完成
     */
    private updateProgress() {
        // 增加已加载资源计数
        this.loadedResources++;

        // 计算当前进度(0-1)
        const progress = this.loadedResources / this.totalResources;

        // 更新进度条显示
        this.bar.progress = progress;

        // 强制调用进度条的更新方法(确保草的位置更新)
        if (this.bar['_updateBarStatus']) {
            this.bar['_updateBarStatus']();
        }

        // 每10%输出一次日志,或者加载完成时输出
        if (this.loadedResources % Math.ceil(this.totalResources / 10) === 0 || this.loadedResources === this.totalResources) {
            log(`进度: ${(progress * 100).toFixed(1)}% (${this.loadedResources}/${this.totalResources})`);
        }

        // 检查是否所有资源都加载完成
        if (this.loadedResources >= this.totalResources) {
            log(`所有资源加载完成 (成功: ${this.loadedPaths.length}, 失败: ${this.totalResources - this.loadedPaths.length})`);
            // 完成加载流程
            this.finishLoading();
        }
    }

    /** 
     * 所有资源加载完成
     * 跳转到游戏场景
     */
    private finishLoading() {
        log('=====================');
        log('所有资源加载流程完成');
        log(`成功加载的资源 (${this.loadedPaths.length} 个):`);

        // 只显示前几个和最后几个成功加载的资源,避免日志太长
        const maxShow = 10;
        for (let i = 0; i < Math.min(this.loadedPaths.length, maxShow); i++) {
            log(`${i + 1}. ${this.loadedPaths[i]}`);
        }

        // 如果成功加载的资源超过显示上限
        if (this.loadedPaths.length > maxShow) {
            log(`... 还有 ${this.loadedPaths.length - maxShow} 个资源未显示`);
        }

        // 确保至少等待1秒,让用户能看到加载完成的进度条
        setTimeout(() => {
            log('准备跳转到 Game 场景...');
            // 加载游戏场景
            director.loadScene('Game', (err: any) => {
                // 如果跳转失败
                if (err) {
                    log(`跳转场景失败: ${err.message}`);
                } else {
                    // 跳转成功
                    log('成功跳转到 Game 场景');
                }
            });
        }, 1000);
    }

}


  • 写回答

0条回答 默认 最新

    报告相同问题?

    问题事件

    • 系统已结题 12月26日
    • 创建了问题 12月18日