【Cocos Creator 2.x】篇——第五章 游戏常用关键技术

本文摘要介绍了Cocos Creator 2.x游戏开发中的多项关键技术:

1.异步编程:通过Promise和async/await处理异步操作;

2.高级UI组件:包括进度条、滚动视图和切换按钮的实现;

3.物理系统:碰撞检测、射线检测和物理关节的应用;

4.音频系统:AudioSource组件的使用方法;

5.设计模式:状态管理器和对象池模式的实现;

6.数据持久化:利用localStorage存储游戏数据;

7.网络请求:封装HTTP请求的实现;

8.性能优化:渲染和资源优化技巧;

9.调试工具:性能监控和调试绘制的实现。这些技术点为Cocos Creator游戏开发提供了全面的解决方案。

1 Promise与异步编程

1.1 Promise基础

复制代码
// 创建Promise
const promise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('操作成功');
        } else {
            reject('操作失败');
        }
    }, 1000);
});
​
// 使用Promise
promise.then(result => {
    console.log(result); // 操作成功
}).catch(error => {
    console.error(error); // 操作失败
});

1.2 Promise链式调用

复制代码
// 加载资源的链式调用
cc.loader.loadRes('texture1', cc.SpriteFrame)
    .then(spriteFrame1 => {
        console.log('第一张图加载完成');
        return cc.loader.loadRes('texture2', cc.SpriteFrame);
    })
    .then(spriteFrame2 => {
        console.log('第二张图加载完成');
        return cc.loader.loadRes('texture3', cc.SpriteFrame);
    })
    .then(spriteFrame3 => {
        console.log('所有资源加载完成');
    })
    .catch(error => {
        console.error('加载失败:', error);
    });

1.3 async/await语法

复制代码
// 异步函数
async function loadResources() {
    try {
        const spriteFrame1 = await cc.loader.loadRes('texture1', cc.SpriteFrame);
        const spriteFrame2 = await cc.loader.loadRes('texture2', cc.SpriteFrame);
        const spriteFrame3 = await cc.loader.loadRes('texture3', cc.SpriteFrame);
        console.log('所有资源加载完成');
    } catch (error) {
        console.error('加载失败:', error);
    }
}
​
// 调用异步函数
loadResources();

2 高级UI组件

2.1 ProgressBar进度条

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        progressBar: cc.ProgressBar,
        targetProgress: 100
    },
    
    start() {
        // 设置进度条属性
        this.progressBar.totalLength = 200;
        this.progressBar.progress = 0;
        
        // 开始加载动画
        this.startLoading();
    },
    
    startLoading() {
        let currentProgress = 0;
        const interval = setInterval(() => {
            currentProgress += 5;
            this.progressBar.progress = currentProgress / this.targetProgress;
            
            if (currentProgress >= this.targetProgress) {
                clearInterval(interval);
                console.log('加载完成');
            }
        }, 100);
    }
});

2.2 ScrollView滚动视图

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        scrollView: cc.ScrollView,
        content: cc.Node,
        itemPrefab: cc.Prefab
    },
    
    start() {
        // 初始化滚动视图
        this.scrollView.horizontal = false;
        this.scrollView.vertical = true;
        this.scrollView.bounceEnabled = true;
        this.scrollView.inertia = true;
        
        // 创建列表项
        this.createListItems();
    },
    
    createListItems() {
        const itemHeight = 50;
        const itemCount = 20;
        
        for (let i = 0; i < itemCount; i++) {
            const item = cc.instantiate(this.itemPrefab);
            item.parent = this.content;
            item.y = -i * itemHeight;
            
            // 设置列表项内容
            const label = item.getComponent(cc.Label);
            if (label) {
                label.string = `列表项 ${i + 1}`;
            }
        }
        
        // 更新content大小
        this.content.height = itemCount * itemHeight;
    }
});

2.3 Toggle切换按钮

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        toggle: cc.Toggle,
        toggleGroup: cc.ToggleGroup
    },
    
    start() {
        // 设置ToggleGroup
        this.toggle.group = this.toggleGroup;
        
        // 添加事件监听
        this.toggle.node.on('toggle', this.onToggle, this);
    },
    
    onToggle(event) {
        const toggle = event.target.getComponent(cc.Toggle);
        if (toggle.isChecked) {
            console.log('Toggle已选中');
        } else {
            console.log('Toggle已取消选中');
        }
    }
});

3 物理系统详解

3.1 碰撞检测

复制代码
cc.Class({
    extends: cc.Component,
    
    onLoad() {
        const collider = this.node.getComponent(cc.Collider2D);
        if (collider) {
            // 监听碰撞事件
            collider.on('begin-contact', this.onBeginContact, this);
            collider.on('end-contact', this.onEndContact, this);
            collider.on('pre-solve', this.onPreSolve, this);
            collider.on('post-solve', this.onPostSolve, this);
        }
    },
    
    onBeginContact(contact, selfCollider, otherCollider) {
        console.log('开始碰撞:', otherCollider.node.name);
        
        // 获取碰撞点
        const worldManifold = contact.getWorldManifold();
        const points = worldManifold.points;
        console.log('碰撞点:', points);
    },
    
    onEndContact(contact, selfCollider, otherCollider) {
        console.log('结束碰撞:', otherCollider.node.name);
    },
    
    onPreSolve(contact, selfCollider, otherCollider) {
        // 可以在这里修改碰撞结果
        // contact.setEnabled(false); // 禁用碰撞
    },
    
    onPostSolve(contact, selfCollider, otherCollider) {
        // 碰撞处理后的回调
    }
});

3.2 射线检测

复制代码
cc.Class({
    extends: cc.Component,
    
    start() {
        // 创建射线
        const start = cc.v2(0, 0);
        const end = cc.v2(200, 200);
        
        // 执行射线检测
        const results = cc.director.getPhysicsManager().rayCast(start, end);
        
        if (results.length > 0) {
            console.log('检测到碰撞:', results.length);
            results.forEach(result => {
                console.log('碰撞对象:', result.collider.node.name);
                console.log('碰撞点:', result.point);
            });
        }
    }
});

3.3 物理关节

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        connectedBody: cc.RigidBody2D
    },
    
    start() {
        const rigidBody = this.node.getComponent(cc.RigidBody2D);
        
        // 创建距离关节
        const joint = this.node.addComponent(cc.DistanceJoint2D);
        joint.connectedBody = this.connectedBody;
        joint.distance = 100;
        joint.dampingRatio = 0.5;
        joint.frequency = 2;
    }
});

4 音频系统

4.1 AudioSource组件

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        bgmClip: cc.AudioClip,
        sfxClip: cc.AudioClip
    },
    
    onLoad() {
        // 添加AudioSource组件
        this.audioSource = this.node.addComponent(cc.AudioSource);
    },
    
    start() {
        // 播放背景音乐
        this.playBGM();
    },
    
    playBGM() {
        this.audioSource.clip = this.bgmClip;
        this.audioSource.loop = true;
        this.audioSource.volume = 0.5;
        this.audioSource.play();
    },
    
    playSFX() {
        // 播放音效(使用临时音频源)
        cc.audioEngine.play(this.sfxClip, false, 0.8);
    },
    
    stopAudio() {
        this.audioSource.stop();
    },
    
    pauseAudio() {
        this.audioSource.pause();
    },
    
    resumeAudio() {
        this.audioSource.resume();
    }
});

5 状态管理器模式

5.1 游戏状态管理

复制代码
// 状态枚举
const GameState = {
    MENU: 'menu',
    PLAYING: 'playing',
    PAUSED: 'paused',
    GAMEOVER: 'gameover'
};
​
cc.Class({
    extends: cc.Component,
    
    properties: {
        currentState: {
            default: GameState.MENU,
            visible: false
        }
    },
    
    onLoad() {
        // 注册状态监听
        this.stateListeners = {};
    },
    
    // 添加状态监听
    addStateListener(state, callback) {
        if (!this.stateListeners[state]) {
            this.stateListeners[state] = [];
        }
        this.stateListeners[state].push(callback);
    },
    
    // 移除状态监听
    removeStateListener(state, callback) {
        if (this.stateListeners[state]) {
            const index = this.stateListeners[state].indexOf(callback);
            if (index > -1) {
                this.stateListeners[state].splice(index, 1);
            }
        }
    },
    
    // 切换状态
    changeState(newState) {
        if (this.currentState === newState) return;
        
        const previousState = this.currentState;
        this.currentState = newState;
        
        console.log(`状态切换: ${previousState} -> ${newState}`);
        
        // 通知所有监听者
        if (this.stateListeners[newState]) {
            this.stateListeners[newState].forEach(callback => {
                callback(newState);
            });
        }
        
        // 执行状态切换逻辑
        this.onStateChanged(previousState, newState);
    },
    
    // 状态切换处理
    onStateChanged(previousState, newState) {
        switch (newState) {
            case GameState.MENU:
                this.enterMenu();
                break;
            case GameState.PLAYING:
                this.enterPlaying();
                break;
            case GameState.PAUSED:
                this.enterPaused();
                break;
            case GameState.GAMEOVER:
                this.enterGameOver();
                break;
        }
    },
    
    enterMenu() {
        cc.log('进入菜单状态');
    },
    
    enterPlaying() {
        cc.log('进入游戏状态');
    },
    
    enterPaused() {
        cc.log('进入暂停状态');
    },
    
    enterGameOver() {
        cc.log('进入游戏结束状态');
    }
});

6 对象池模式

6.1 实现对象池

复制代码
// 对象池管理类
const ObjectPool = (function() {
    const pools = {};
    
    return {
        // 获取对象池
        getPool(poolName) {
            if (!pools[poolName]) {
                pools[poolName] = {
                    objects: [],
                    prefab: null
                };
            }
            return pools[poolName];
        },
        
        // 初始化对象池
        initPool(poolName, prefab, count) {
            const pool = this.getPool(poolName);
            pool.prefab = prefab;
            
            // 预创建对象
            for (let i = 0; i < count; i++) {
                const obj = cc.instantiate(prefab);
                obj.active = false;
                pool.objects.push(obj);
            }
        },
        
        // 获取对象
        getObject(poolName) {
            const pool = this.getPool(poolName);
            
            // 如果有空闲对象,直接返回
            if (pool.objects.length > 0) {
                const obj = pool.objects.pop();
                obj.active = true;
                return obj;
            }
            
            // 如果没有,创建新对象
            const obj = cc.instantiate(pool.prefab);
            obj.active = true;
            return obj;
        },
        
        // 回收对象
        recycleObject(poolName, obj) {
            const pool = this.getPool(poolName);
            obj.active = false;
            obj.setPosition(0, 0);
            pool.objects.push(obj);
        },
        
        // 清理对象池
        clearPool(poolName) {
            const pool = this.getPool(poolName);
            pool.objects.forEach(obj => {
                obj.destroy();
            });
            pool.objects = [];
        }
    };
})();
​
// 使用对象池
cc.Class({
    extends: cc.Component,
    
    properties: {
        bulletPrefab: cc.Prefab,
        container: cc.Node
    },
    
    start() {
        // 初始化对象池
        ObjectPool.initPool('bullets', this.bulletPrefab, 10);
    },
    
    fireBullet() {
        // 获取对象
        const bullet = ObjectPool.getObject('bullets');
        bullet.parent = this.container;
        bullet.setPosition(0, 0);
        
        // 2秒后回收
        setTimeout(() => {
            ObjectPool.recycleObject('bullets', bullet);
        }, 2000);
    }
});

7 数据持久化

7.1 localStorage存储

复制代码
cc.Class({
    extends: cc.Component,
    
    // 保存数据
    saveData(key, data) {
        try {
            const jsonStr = JSON.stringify(data);
            localStorage.setItem(key, jsonStr);
            cc.log('数据保存成功');
        } catch (error) {
            cc.error('数据保存失败:', error);
        }
    },
    
    // 读取数据
    loadData(key) {
        try {
            const jsonStr = localStorage.getItem(key);
            if (jsonStr) {
                return JSON.parse(jsonStr);
            }
            return null;
        } catch (error) {
            cc.error('数据读取失败:', error);
            return null;
        }
    },
    
    // 删除数据
    deleteData(key) {
        localStorage.removeItem(key);
    },
    
    // 示例:保存游戏进度
    saveGameProgress(progress) {
        this.saveData('gameProgress', progress);
    },
    
    // 示例:加载游戏进度
    loadGameProgress() {
        return this.loadData('gameProgress');
    }
});

8 网络请求

8.1 HTTP请求封装

复制代码
cc.Class({
    extends: cc.Component,
    
    // GET请求
    get(url, params = {}) {
        return new Promise((resolve, reject) => {
            // 拼接参数
            const queryString = Object.keys(params)
                .map(key => `${key}=${encodeURIComponent(params[key])}`)
                .join('&');
            
            const fullUrl = queryString ? `${url}?${queryString}` : url;
            
            const xhr = new XMLHttpRequest();
            xhr.open('GET', fullUrl, true);
            xhr.onload = () => {
                if (xhr.status >= 200 && xhr.status < 300) {
                    try {
                        const response = JSON.parse(xhr.responseText);
                        resolve(response);
                    } catch (error) {
                        reject(error);
                    }
                } else {
                    reject(new Error(xhr.statusText));
                }
            };
            xhr.onerror = () => {
                reject(new Error('网络请求失败'));
            };
            xhr.send();
        });
    },
    
    // POST请求
    post(url, data = {}) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', url, true);
            xhr.setRequestHeader('Content-Type', 'application/json');
            
            xhr.onload = () => {
                if (xhr.status >= 200 && xhr.status < 300) {
                    try {
                        const response = JSON.parse(xhr.responseText);
                        resolve(response);
                    } catch (error) {
                        reject(error);
                    }
                } else {
                    reject(new Error(xhr.statusText));
                }
            };
            xhr.onerror = () => {
                reject(new Error('网络请求失败'));
            };
            xhr.send(JSON.stringify(data));
        });
    },
    
    // 示例:获取排行榜
    async getLeaderboard() {
        try {
            const response = await this.get('https://api.example.com/leaderboard', {
                limit: 10
            });
            console.log('排行榜数据:', response);
            return response;
        } catch (error) {
            console.error('获取排行榜失败:', error);
            throw error;
        }
    },
    
    // 示例:提交分数
    async submitScore(score) {
        try {
            const response = await this.post('https://api.example.com/submit', {
                score: score,
                playerId: 'player123'
            });
            console.log('提交结果:', response);
            return response;
        } catch (error) {
            console.error('提交分数失败:', error);
            throw error;
        }
    }
});

9 性能优化技巧

9.1 渲染优化

复制代码
cc.Class({
    extends: cc.Component,
    
    start() {
        // 合并静态对象
        this.mergeStaticObjects();
        
        // 设置LOD
        this.setupLOD();
    },
    
    // 合并静态对象
    mergeStaticObjects() {
        const renderers = this.node.getComponentsInChildren(cc.Sprite);
        const materialGroups = {};
        
        // 按材质分组
        renderers.forEach(renderer => {
            const material = renderer.getMaterial(0);
            const key = material.name;
            if (!materialGroups[key]) {
                materialGroups[key] = [];
            }
            materialGroups[key].push(renderer);
        });
        
        console.log(`材质组数: ${Object.keys(materialGroups).length}`);
        console.log(`总渲染器数: ${renderers.length}`);
    },
    
    // 设置LOD
    setupLOD() {
        const lodGroup = this.node.addComponent(cc.LODGroup);
        lodGroup.levels = [
            {
                screenRelativeTransitionHeight: 0.5,
                renderers: [this.getComponent(cc.Sprite)]
            },
            {
                screenRelativeTransitionHeight: 0.2,
                renderers: [] // 使用简化版本
            }
        ];
    }
});

9.2 资源优化

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        cachedResources: {
            default: {},
            visible: false
        }
    },
    
    // 缓存资源
    async loadAndCache(path, type) {
        const key = `${path}_${type.name}`;
        
        // 如果已缓存,直接返回
        if (this.cachedResources[key]) {
            return this.cachedResources[key];
        }
        
        // 加载资源
        return new Promise((resolve, reject) => {
            cc.loader.loadRes(path, type, (err, asset) => {
                if (err) {
                    reject(err);
                    return;
                }
                
                // 缓存资源
                this.cachedResources[key] = asset;
                resolve(asset);
            });
        });
    },
    
    // 释放缓存
    releaseCache(path, type) {
        const key = `${path}_${type.name}`;
        if (this.cachedResources[key]) {
            cc.loader.releaseRes(path, type);
            delete this.cachedResources[key];
        }
    },
    
    // 清空所有缓存
    clearAllCache() {
        Object.keys(this.cachedResources).forEach(key => {
            cc.loader.releaseAsset(this.cachedResources[key]);
        });
        this.cachedResources = {};
    }
});

10 调试工具

10.1 性能监控

复制代码
cc.Class({
    extends: cc.Component,
    
    properties: {
        fpsLabel: cc.Label,
        drawCallsLabel: cc.Label
    },
    
    start() {
        // 开启性能监控
        this.schedule(this.updatePerformance, 1);
    },
    
    updatePerformance() {
        // 获取帧率
        const fps = cc.game.getFrameRate();
        this.fpsLabel.string = `FPS: ${fps}`;
        
        // 获取Draw Call数量
        const drawCalls = cc.renderer.drawCalls;
        this.drawCallsLabel.string = `Draw Calls: ${drawCalls}`;
    },
    
    onDestroy() {
        this.unschedule(this.updatePerformance);
    }
});

10.2 调试绘制

复制代码
cc.Class({
    extends: cc.Component,
    
    start() {
        // 注册渲染回调
        cc.director.on('render', this.onRender, this);
    },
    
    onRender() {
        // 绘制调试信息
        const ctx = cc.renderer.getContext();
        
        // 绘制线段
        ctx.strokeStyle = '#ff0000';
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.moveTo(100, 100);
        ctx.lineTo(200, 200);
        ctx.stroke();
        
        // 绘制矩形
        ctx.strokeStyle = '#00ff00';
        ctx.strokeRect(300, 100, 100, 100);
        
        // 绘制文字
        ctx.fillStyle = '#0000ff';
        ctx.font = '20px Arial';
        ctx.fillText('调试信息', 100, 300);
    },
    
    onDestroy() {
        cc.director.off('render', this.onRender, this);
    }
});

总结

通过本章的补充扩展,你应该掌握了:

  1. 异步编程:Promise、async/await的使用

  2. 高级UI组件:ProgressBar、ScrollView、Toggle的使用

  3. 物理系统:碰撞检测、射线检测、物理关节

  4. 音频系统:AudioSource组件的使用

  5. 设计模式:状态管理器、对象池模式

  6. 数据持久化:localStorage的使用

  7. 网络请求:HTTP请求封装

  8. 性能优化:渲染优化、资源优化

  9. 调试工具:性能监控、调试绘制

这些内容可以帮助你更好地理解和使用Cocos Creator 2.x进行游戏开发!

相关推荐
英俊潇洒美少年1 小时前
前端全量资源预加载优化指南(React内置API + Vue实现 + prerender/prefetch深度对比)
前端·react.js·前端框架
道友可好1 小时前
3 个人,100 万行代码,一行都没人写:OpenAI 的 Harness Engineering 实验
前端·人工智能·后端
caimouse1 小时前
2D 与 3D 跨平台游戏引擎
游戏引擎
W_LuYi1852 小时前
Tauri + Rust + Vue 3 打造极速轻量桌面应用
java·开发语言·vue.js·rust
winfredzhang2 小时前
用 Node.js + SQLite + 原生前端写一个本地情绪急救 Web App:情绪降落伞 Mood Parachute
前端·sqlite·node.js·express·情绪管理
qq4356947012 小时前
Vue03
javascript·vue.js
樱花的浪漫2 小时前
Typescript、Zod基础
前端·javascript·人工智能·语言模型·自然语言处理·typescript
Bigger2 小时前
记一次坑爹的 Cloudflare Pages 部署:Failed to load module script 是怎么把我的 SPA 搞挂的
前端·ci/cd·浏览器
用户549591657502 小时前
TinyVue Tree树形控件完全指南
vue.js