【Cocos Creator 3.x】篇——第五章 项目实战优化技术

目录

[1 3D基础](#1 3D基础)

[1.1 3D场景设置](#1.1 3D场景设置)

摄像机 (Camera)

光照系统

[1.2 3D模型和材质](#1.2 3D模型和材质)

PBR材质

自定义着色器

[2 粒子系统](#2 粒子系统)

[2.1 创建粒子效果](#2.1 创建粒子效果)

[2.2 粒子纹理动画](#2.2 粒子纹理动画)

[3 骨骼动画](#3 骨骼动画)

[3.1 Spine动画](#3.1 Spine动画)

[3.2 DragonBones动画](#3.2 DragonBones动画)

[4 性能优化](#4 性能优化)

[4.1 渲染优化](#4.1 渲染优化)

合批优化

LOD系统

[4.2 内存管理](#4.2 内存管理)

资源缓存

对象池

[5 打包发布](#5 打包发布)

[5.1 构建配置](#5.1 构建配置)

[5.2 热更新](#5.2 热更新)

[5.3 多平台适配](#5.3 多平台适配)

[6 调试工具](#6 调试工具)

[6.1 性能分析](#6.1 性能分析)

[6.2 调试绘制](#6.2 调试绘制)

[7 数据持久化](#7 数据持久化)

[7.1 本地存储](#7.1 本地存储)

localStorage

文件存储(Native)

[7.2 SQLite数据库](#7.2 SQLite数据库)

[8 AI系统](#8 AI系统)

[8.1 状态机](#8.1 状态机)

[8.2 行为树](#8.2 行为树)

[9 多人游戏基础](#9 多人游戏基础)

[9.1 房间系统](#9.1 房间系统)

[9.2 玩家同步](#9.2 玩家同步)

[9.3 状态同步](#9.3 状态同步)

[10 编辑器扩展](#10 编辑器扩展)

[10.1 创建自定义编辑器面板](#10.1 创建自定义编辑器面板)

[10.2 自定义属性编辑器](#10.2 自定义属性编辑器)

总结

高级技术部分

实战应用部分


本文摘要:文章系统介绍了游戏开发中的高级技术与实战应用。3D基础部分涵盖场景设置、PBR材质和自定义着色器;粒子系统讲解粒子效果创建与纹理动画;骨骼动画介绍Spine和DragonBones实现。性能优化章节包含渲染优化、LOD系统和内存管理策略。打包发布涉及构建配置、热更新和多平台适配。调试工具部分展示性能分析和调试绘制方法。实战应用包括数据持久化方案(本地存储/SQLite)、AI系统(状态机/行为树)、多人游戏开发(房间/同步系统)以及编辑器扩展技术(自定义面板/属性编辑器)。这些内容为开发复杂游戏项目提供了全面技术指导。

1 3D基础

1.1 3D场景设置

摄像机 (Camera)

复制代码
import { _decorator, Component, Node, Camera } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('CameraExample')
export class CameraExample extends Component {
    @property({ type: Node })
    public cameraNode: Node = null!;
​
    start() {
        const camera = this.cameraNode.getComponent(Camera);
        
        if (camera) {
            // 设置视野范围
            camera.near = 0.1;
            camera.far = 1000;
            
            // 设置视场角
            camera.fov = 60;
            
            // 设置投影模式
            camera.projection = Camera.Projection.PERSPECTIVE;
            
            // 设置背景颜色
            camera.clearColor.set(0.1, 0.1, 0.3, 1);
            
            // 设置清除标志
            camera.clearFlags = Camera.ClearFlags.SKYBOX;
        }
    }
}

光照系统

复制代码
import { _decorator, Component, Node, DirectionalLight, AmbientLight } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('LightingExample')
export class LightingExample extends Component {
    @property({ type: Node })
    public dirLightNode: Node = null!;
    
    @property({ type: Node })
    public ambientLightNode: Node = null!;
​
    start() {
        // 方向光
        const dirLight = this.dirLightNode.getComponent(DirectionalLight);
        if (dirLight) {
            // 设置颜色
            dirLight.color.set(1, 1, 1, 1);
            
            // 设置强度
            dirLight.intensity = 1;
            
            // 设置阴影
            dirLight.shadowEnabled = true;
            dirLight.shadowDistance = 50;
        }
        
        // 环境光
        const ambientLight = this.ambientLightNode.getComponent(AmbientLight);
        if (ambientLight) {
            // 设置环境光颜色
            ambientLight.ambientColor.set(0.3, 0.3, 0.3, 1);
            
            // 设置天空盒
            // ambientLight.skybox = skyboxAsset;
        }
    }
}

1.2 3D模型和材质

PBR材质

复制代码
import { _decorator, Component, Node, MeshRenderer, Material } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('PBRMaterial')
export class PBRMaterial extends Component {
    @property({ type: Material })
    public pbrMaterial: Material = null!;
​
    start() {
        const renderer = this.node.getComponent(MeshRenderer);
        
        if (renderer) {
            // 设置材质
            renderer.setMaterial(this.pbrMaterial, 0);
            
            // 获取材质属性
            const albedoColor = this.pbrMaterial.getProperty('albedoColor');
            console.log('材质颜色:', albedoColor);
            
            // 修改材质属性
            this.pbrMaterial.setProperty('albedoColor', new Color(1, 0.5, 0.5, 1));
            this.pbrMaterial.setProperty('metallic', 0.8);
            this.pbrMaterial.setProperty('roughness', 0.2);
        }
    }
}

自定义着色器

复制代码
import { _decorator, Component, Node, Material, Shader } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('CustomShader')
export class CustomShader extends Component {
    start() {
        // 创建自定义材质
        const material = new Material();
        
        // 加载着色器
        Shader.load('custom-shader', (err, shader) => {
            if (err) {
                console.error('加载着色器失败:', err);
                return;
            }
            
            material.shader = shader;
            
            // 设置着色器参数
            material.setProperty('uColor', new Color(1, 0, 0, 1));
            material.setProperty('uIntensity', 2.0);
            
            // 应用到渲染器
            const renderer = this.node.getComponent(MeshRenderer);
            if (renderer) {
                renderer.setMaterial(material, 0);
            }
        });
    }
}

2 粒子系统

2.1 创建粒子效果

复制代码
import { _decorator, Component, Node, ParticleSystem } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('ParticleExample')
export class ParticleExample extends Component {
    @property({ type: Node })
    public particleNode: Node = null!;
​
    start() {
        const particleSystem = this.particleNode.getComponent(ParticleSystem);
        
        if (particleSystem) {
            // 设置粒子数量
            particleSystem.maxParticles = 1000;
            
            // 设置发射速率
            particleSystem.emissionRate = 100;
            
            // 设置粒子生命周期
            particleSystem.lifetime = 2;
            particleSystem.lifetimeVariance = 0.5;
            
            // 设置粒子大小
            particleSystem.startSize = 0.5;
            particleSystem.endSize = 0.1;
            
            // 设置速度
            particleSystem.speed = 5;
            particleSystem.speedVariance = 2;
            
            // 设置颜色
            particleSystem.startColor.set(1, 0.8, 0, 1);
            particleSystem.endColor.set(1, 0, 0, 0);
            
            // 设置发射器形状
            particleSystem.emitterShape = ParticleSystem.EmitterShape.SPHERE;
            particleSystem.radius = 1;
            
            // 播放粒子
            particleSystem.play();
        }
    }
    
    stopParticle() {
        const particleSystem = this.particleNode.getComponent(ParticleSystem);
        if (particleSystem) {
            particleSystem.stop();
        }
    }
}

2.2 粒子纹理动画

复制代码
import { _decorator, Component, Node, ParticleSystem, SpriteFrame } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('ParticleAnimation')
export class ParticleAnimation extends Component {
    @property({ type: [SpriteFrame] })
    public spriteFrames: SpriteFrame[] = [];
​
    start() {
        const particleSystem = this.node.getComponent(ParticleSystem);
        
        if (particleSystem && this.spriteFrames.length > 0) {
            // 设置粒子使用纹理动画
            particleSystem.textureSheetAnimation = true;
            
            // 设置纹理数组
            particleSystem.spriteFrames = this.spriteFrames;
            
            // 设置动画模式
            particleSystem.animationMode = ParticleSystem.AnimationMode.WHOLE_SHEET;
            
            // 设置帧率
            particleSystem.animationSpeed = 10;
        }
    }
}

3 骨骼动画

3.1 Spine动画

复制代码
import { _decorator, Component, Node, sp } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('SpineAnimation')
export class SpineAnimation extends Component {
    @property({ type: sp.SkeletonData })
    public skeletonData: sp.SkeletonData = null!;
    
    private spine: sp.Skeleton | null = null;
​
    start() {
        this.spine = this.node.getComponent(sp.Skeleton);
        
        if (this.spine) {
            // 设置骨骼数据
            this.spine.skeletonData = this.skeletonData;
            
            // 设置动画
            this.spine.setAnimation(0, 'idle', true);
            
            // 添加动画
            this.spine.addAnimation(0, 'walk', true, 0);
            
            // 设置皮肤
            this.spine.setSkin('default');
            
            // 设置混合模式
            this.spine.setMix('idle', 'walk', 0.2);
            
            // 监听动画事件
            this.spine.setCompleteListener((trackIndex: number, loopCount: number) => {
                console.log(`动画完成: ${trackIndex}`);
            });
        }
    }
    
    playAnimation(animName: string, loop: boolean = true) {
        if (this.spine) {
            this.spine.setAnimation(0, animName, loop);
        }
    }
}

3.2 DragonBones动画

复制代码
import { _decorator, Component, Node, dragonBones } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('DragonBonesAnimation')
export class DragonBonesAnimation extends Component {
    @property({ type: dragonBones.ArmatureDisplay })
    public armatureDisplay: dragonBones.ArmatureDisplay = null!;
​
    start() {
        // 获取动画列表
        const animationNames = this.armatureDisplay.animationNames;
        console.log('可用动画:', animationNames);
        
        // 播放动画
        this.armatureDisplay.playAnimation('idle', 0);
        
        // 设置动画速度
        this.armatureDisplay.timeScale = 1;
        
        // 监听动画事件
        this.armatureDisplay.addEventListener(dragonBones.EventObject.COMPLETE, (event: any) => {
            console.log('动画完成:', event.animationName);
        });
    }
    
    play(animName: string) {
        this.armatureDisplay.playAnimation(animName, 0);
    }
}

4 性能优化

4.1 渲染优化

合批优化

复制代码
import { _decorator, Component, Node, MeshRenderer } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('RenderOptimization')
export class RenderOptimization extends Component {
    start() {
        // 合并静态对象
        const renderers = this.node.getComponentsInChildren(MeshRenderer);
        
        // 将使用相同材质的对象合并
        const materialGroups: Map<string, MeshRenderer[]> = new Map();
        
        for (const renderer of renderers) {
            const matKey = renderer.getMaterial(0)?.name || 'default';
            if (!materialGroups.has(matKey)) {
                materialGroups.set(matKey, []);
            }
            materialGroups.get(matKey)?.push(renderer);
        }
        
        console.log(`材质组数: ${materialGroups.size}`);
        console.log(`总渲染器数: ${renderers.length}`);
    }
}

LOD系统

复制代码
import { _decorator, Component, Node, LODGroup } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('LODExample')
export class LODExample extends Component {
    @property({ type: Node })
    public highModel: Node = null!;
    
    @property({ type: Node })
    public mediumModel: Node = null!;
    
    @property({ type: Node })
    public lowModel: Node = null!;
​
    start() {
        const lodGroup = this.node.addComponent(LODGroup);
        
        // 设置LOD级别
        const levels = [
            { screenRelativeTransitionHeight: 0.5, renderers: [this.highModel.getComponent(MeshRenderer)] },
            { screenRelativeTransitionHeight: 0.2, renderers: [this.mediumModel.getComponent(MeshRenderer)] },
            { screenRelativeTransitionHeight: 0.1, renderers: [this.lowModel.getComponent(MeshRenderer)] }
        ];
        
        lodGroup.levels = levels;
        
        // 初始状态只显示高模
        this.highModel.active = true;
        this.mediumModel.active = false;
        this.lowModel.active = false;
    }
}

4.2 内存管理

资源缓存

复制代码
import { _decorator, Component, Node, SpriteFrame, resources } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('ResourceCache')
export class ResourceCache {
    private static cache: Map<string, any> = new Map();
    
    public static async load<T>(path: string, type: { new(): T }): Promise<T> {
        // 检查缓存
        const key = `${path}_${type.name}`;
        if (this.cache.has(key)) {
            return this.cache.get(key);
        }
        
        // 加载资源
        const asset = await resources.loadAsync(path, type);
        
        // 存入缓存
        this.cache.set(key, asset);
        
        return asset;
    }
    
    public static release(path: string, type: { new(): any }) {
        const key = `${path}_${type.name}`;
        if (this.cache.has(key)) {
            this.cache.delete(key);
            resources.release(path, type);
        }
    }
    
    public static clearCache() {
        this.cache.clear();
        resources.releaseAll();
    }
}
​
// 使用缓存
@ccclass('CacheUser')
export class CacheUser extends Component {
    async loadSprite() {
        const spriteFrame = await ResourceCache.load('textures/player', SpriteFrame);
        const sprite = this.node.getComponent(Sprite);
        sprite.spriteFrame = spriteFrame;
    }
}

对象池

复制代码
import { _decorator, Component, Node, Prefab, instantiate } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('ObjectPool')
export class ObjectPool {
    private pool: Node[] = [];
    private prefab: Prefab;
    private parent: Node;
    
    constructor(prefab: Prefab, parent: Node) {
        this.prefab = prefab;
        this.parent = parent;
    }
    
    // 获取对象
    get(): Node {
        if (this.pool.length > 0) {
            const node = this.pool.pop()!;
            node.active = true;
            return node;
        }
        
        // 创建新对象
        const node = instantiate(this.prefab);
        node.parent = this.parent;
        return node;
    }
    
    // 回收对象
    recycle(node: Node) {
        node.active = false;
        node.setPosition(0, 0, 0);
        this.pool.push(node);
    }
    
    // 清理池
    clear() {
        for (const node of this.pool) {
            node.destroy();
        }
        this.pool = [];
    }
}
​
// 使用对象池
@ccclass('PoolUser')
export class PoolUser extends Component {
    @property({ type: Prefab })
    public bulletPrefab: Prefab = null!;
    
    private bulletPool: ObjectPool | null = null;
​
    start() {
        // 初始化对象池
        this.bulletPool = new ObjectPool(this.bulletPrefab, this.node);
        
        // 预创建一些对象
        for (let i = 0; i < 10; i++) {
            const bullet = this.bulletPool.get();
            this.bulletPool.recycle(bullet);
        }
    }
    
    fireBullet() {
        if (this.bulletPool) {
            const bullet = this.bulletPool.get();
            bullet.setPosition(this.node.position);
            
            // 2秒后回收
            setTimeout(() => {
                this.bulletPool?.recycle(bullet);
            }, 2000);
        }
    }
}

5 打包发布

5.1 构建配置

复制代码
import { _decorator, Component, Node, Editor } from 'cc';
const { ccclass, property } = _decorator;
​
// 注意:此代码需在编辑器脚本中运行
@ccclass('BuildConfig')
export class BuildConfig extends Component {
    // 在编辑器中配置构建参数
    configureBuild() {
        // 获取构建配置
        const buildOptions = Editor.Builder.buildOptions;
        
        // 设置构建目标平台
        buildOptions.platform = 'web-mobile';
        
        // 设置构建路径
        buildOptions.buildPath = './build/web-mobile';
        
        // 设置场景列表
        buildOptions.scenes = ['scenes/Start.scene', 'scenes/Game.scene'];
        
        // 设置压缩选项
        buildOptions.compression = {
            jsCompress: true,
            jsCompressOptions: {
                compress: true,
                mangle: true
            }
        };
        
        // 设置MD5缓存
        buildOptions.md5Cache = true;
        
        // 设置资源压缩
        buildOptions.assetBundle = {
            config: {
                'bundle1': ['textures/**', 'prefabs/**'],
                'bundle2': ['audio/**']
            }
        };
    }
}

5.2 热更新

复制代码
import { _decorator, Component, Node, AssetManager, RemoteAssetBundle } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('HotUpdate')
export class HotUpdate extends Component {
    @property({ type: String })
    public remoteUrl: string = 'http://example.com/update/';
    
    private bundle: RemoteAssetBundle | null = null;
​
    async checkUpdate() {
        try {
            // 加载远程资源包
            this.bundle = await AssetManager.loadBundle(this.remoteUrl, { type: 'remote' });
            
            // 获取版本信息
            const version = await this.bundle.getVersion();
            console.log('远程版本:', version);
            
            // 检查是否需要更新
            const localVersion = this.getLocalVersion();
            if (version > localVersion) {
                console.log('需要更新');
                await this.downloadUpdate();
            } else {
                console.log('已是最新版本');
            }
        } catch (error) {
            console.error('检查更新失败:', error);
        }
    }
    
    async downloadUpdate() {
        if (!this.bundle) return;
        
        // 获取所有资源列表
        const manifest = await this.bundle.getManifest();
        
        // 下载资源
        for (const asset of manifest.assets) {
            const progress = await this.bundle.load(asset.path, asset.type);
            console.log(`下载进度: ${asset.path} - ${progress}%`);
        }
        
        // 更新本地版本
        this.saveVersion(manifest.version);
        
        console.log('更新完成');
    }
    
    private getLocalVersion(): number {
        const version = localStorage.getItem('game_version');
        return version ? parseInt(version) : 0;
    }
    
    private saveVersion(version: number) {
        localStorage.setItem('game_version', version.toString());
    }
}

5.3 多平台适配

复制代码
import { _decorator, Component, Node, sys } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('PlatformAdapter')
export class PlatformAdapter extends Component {
    start() {
        // 获取当前平台
        const platform = sys.platform;
        
        console.log('当前平台:', platform);
        
        // 平台判断
        if (sys.isBrowser) {
            console.log('浏览器平台');
        }
        
        if (sys.isMobile) {
            console.log('移动平台');
        }
        
        if (sys.isAndroid) {
            console.log('Android平台');
        }
        
        if (sys.isIOS) {
            console.log('iOS平台');
        }
        
        if (sys.isWindows) {
            console.log('Windows平台');
        }
        
        if (sys.isMacOS) {
            console.log('MacOS平台');
        }
        
        // 获取设备信息
        const deviceInfo = {
            model: sys.deviceModel,
            osVersion: sys.osVersion,
            language: sys.language,
            screenWidth: sys.windowWidth,
            screenHeight: sys.windowHeight
        };
        
        console.log('设备信息:', deviceInfo);
    }
}

6 调试工具

6.1 性能分析

复制代码
import { _decorator, Component, Node, profiler } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('PerformanceMonitor')
export class PerformanceMonitor extends Component {
    start() {
        // 开启性能分析
        profiler.startCPUProfiling();
        
        // 监听帧率
        setInterval(() => {
            const fps = profiler.getFPS();
            const drawCalls = profiler.getDrawCalls();
            const triangles = profiler.getTriangles();
            
            console.log(`FPS: ${fps}, Draw Calls: ${drawCalls}, Triangles: ${triangles}`);
        }, 1000);
    }
    
    onDestroy() {
        // 停止性能分析
        profiler.stopCPUProfiling();
    }
}

6.2 调试绘制

复制代码
import { _decorator, Component, Node, gfx, renderer } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('DebugDraw')
export class DebugDraw extends Component {
    start() {
        // 注册调试绘制回调
        renderer.on('render', this.onRender, this);
    }
    
    onRender() {
        // 获取调试绘制器
        const debugDraw = renderer.debugDraw;
        
        // 绘制线段
        debugDraw.drawLine(
            new Vec3(0, 0, 0),
            new Vec3(100, 100, 0),
            new Color(255, 0, 0, 255)
        );
        
        // 绘制球体
        debugDraw.drawSphere(
            new Vec3(0, 0, 0),
            50,
            new Color(0, 255, 0, 255)
        );
        
        // 绘制包围盒
        const aabb = new AABB();
        aabb.set(new Vec3(0, 0, 0), new Vec3(50, 50, 50));
        debugDraw.drawAABB(aabb, new Color(0, 0, 255, 255));
    }
    
    onDestroy() {
        renderer.off('render', this.onRender, this);
    }
}

7 数据持久化

7.1 本地存储

localStorage

复制代码
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('LocalStorageExample')
export class LocalStorageExample extends Component {
    // 保存数据
    saveData() {
        const playerData = {
            name: 'Hero',
            level: 10,
            gold: 1000,
            items: ['sword', 'shield', 'potion']
        };
        
        // 将对象转为JSON字符串存储
        localStorage.setItem('playerData', JSON.stringify(playerData));
        console.log('数据保存成功');
    }
    
    // 读取数据
    loadData() {
        const dataStr = localStorage.getItem('playerData');
        
        if (dataStr) {
            const playerData = JSON.parse(dataStr);
            console.log('读取的数据:', playerData);
            return playerData;
        }
        
        return null;
    }
    
    // 删除数据
    clearData() {
        localStorage.removeItem('playerData');
        // 或者清空所有数据
        // localStorage.clear();
        console.log('数据已删除');
    }
    
    // 检查数据是否存在
    hasData(): boolean {
        return localStorage.getItem('playerData') !== null;
    }
}

文件存储(Native)

复制代码
import { _decorator, Component, Node, native } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('FileStorage')
export class FileStorage extends Component {
    // 写入文件
    async writeFile(filename: string, content: string) {
        try {
            // 获取可写路径
            const writablePath = native.fileUtils.getWritablePath();
            const filePath = `${writablePath}${filename}`;
            
            // 写入文件
            native.fileUtils.writeStringToFile(content, filePath);
            console.log(`文件写入成功: ${filePath}`);
        } catch (error) {
            console.error('写入文件失败:', error);
        }
    }
    
    // 读取文件
    readFile(filename: string): string | null {
        try {
            const writablePath = native.fileUtils.getWritablePath();
            const filePath = `${writablePath}${filename}`;
            
            if (native.fileUtils.isFileExist(filePath)) {
                const content = native.fileUtils.getStringFromFile(filePath);
                return content;
            }
            
            return null;
        } catch (error) {
            console.error('读取文件失败:', error);
            return null;
        }
    }
    
    // 保存游戏进度
    saveGameProgress(progress: any) {
        const content = JSON.stringify(progress, null, 2);
        this.writeFile('game_progress.json', content);
    }
    
    // 加载游戏进度
    loadGameProgress(): any | null {
        const content = this.readFile('game_progress.json');
        if (content) {
            return JSON.parse(content);
        }
        return null;
    }
}

7.2 SQLite数据库

复制代码
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('DatabaseExample')
export class DatabaseExample extends Component {
    private db: any = null;
​
    // 初始化数据库
    initDatabase() {
        // 注意:需要引入SQLite插件
        // this.db = new SQLite();
        // this.db.open('game.db');
        
        // 创建表
        // this.db.executeSql(`
        //     CREATE TABLE IF NOT EXISTS players (
        //         id INTEGER PRIMARY KEY,
        //         name TEXT,
        //         level INTEGER,
        //         gold INTEGER
        //     )
        // `);
    }
    
    // 插入数据
    insertPlayer(player: any) {
        // this.db.executeSql(
        //     'INSERT INTO players (name, level, gold) VALUES (?, ?, ?)',
        //     [player.name, player.level, player.gold]
        // );
    }
    
    // 查询数据
    getPlayer(id: number): any | null {
        // const result = this.db.executeSql(
        //     'SELECT * FROM players WHERE id = ?',
        //     [id]
        // );
        // return result.rows.length > 0 ? result.rows[0] : null;
        return null;
    }
}

8 AI系统

8.1 状态机

复制代码
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
​
// 状态枚举
enum AIState {
    IDLE,
    PATROL,
    CHASE,
    ATTACK,
    FLEE
}
​
@ccclass('AIStateMachine')
export class AIStateMachine extends Component {
    private currentState: AIState = AIState.IDLE;
    private playerNode: Node | null = null;
    
    start() {
        // 获取玩家节点
        this.playerNode = this.node.getParent()?.getChildByName('Player');
        
        // 开始状态更新
        this.schedule(this.updateState, 0.5);
    }
    
    updateState() {
        // 根据条件切换状态
        const distance = this.getDistanceToPlayer();
        
        switch (this.currentState) {
            case AIState.IDLE:
                if (distance < 100) {
                    this.changeState(AIState.CHASE);
                } else if (Math.random() < 0.1) {
                    this.changeState(AIState.PATROL);
                }
                break;
                
            case AIState.PATROL:
                if (distance < 100) {
                    this.changeState(AIState.CHASE);
                } else if (Math.random() < 0.05) {
                    this.changeState(AIState.IDLE);
                }
                break;
                
            case AIState.CHASE:
                if (distance < 20) {
                    this.changeState(AIState.ATTACK);
                } else if (distance > 150) {
                    this.changeState(AIState.IDLE);
                }
                break;
                
            case AIState.ATTACK:
                if (distance > 20) {
                    this.changeState(AIState.CHASE);
                }
                break;
        }
        
        // 执行当前状态行为
        this.executeState();
    }
    
    changeState(newState: AIState) {
        console.log(`状态切换: ${AIState[this.currentState]} -> ${AIState[newState]}`);
        this.currentState = newState;
    }
    
    executeState() {
        switch (this.currentState) {
            case AIState.IDLE:
                this.idle();
                break;
            case AIState.PATROL:
                this.patrol();
                break;
            case AIState.CHASE:
                this.chase();
                break;
            case AIState.ATTACK:
                this.attack();
                break;
        }
    }
    
    idle() {
        // 待机动画
        console.log('待机中...');
    }
    
    patrol() {
        // 巡逻移动
        console.log('巡逻中...');
        // this.node.setPosition(/* 随机位置 */);
    }
    
    chase() {
        // 追逐玩家
        console.log('追逐玩家...');
        if (this.playerNode) {
            // 朝向玩家移动
            // const direction = this.playerNode.position.subtract(this.node.position).normalize();
            // this.node.translate(direction.multiplyScalar(5));
        }
    }
    
    attack() {
        // 攻击玩家
        console.log('攻击玩家!');
    }
    
    getDistanceToPlayer(): number {
        if (!this.playerNode) return Infinity;
        // return this.node.position.distance(this.playerNode.position);
        return 100; // 模拟距离
    }
}

8.2 行为树

复制代码
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
​
// 行为节点状态
enum BehaviorStatus {
    SUCCESS,
    FAILURE,
    RUNNING
}
​
// 行为节点基类
abstract class BehaviorNode {
    protected parent: BehaviorNode | null = null;
    
    abstract tick(deltaTime: number): BehaviorStatus;
    
    setParent(parent: BehaviorNode) {
        this.parent = parent;
    }
}
​
// 条件节点
class ConditionNode extends BehaviorNode {
    private condition: () => boolean;
    
    constructor(condition: () => boolean) {
        super();
        this.condition = condition;
    }
    
    tick(deltaTime: number): BehaviorStatus {
        return this.condition() ? BehaviorStatus.SUCCESS : BehaviorStatus.FAILURE;
    }
}
​
// 动作节点
class ActionNode extends BehaviorNode {
    private action: (deltaTime: number) => BehaviorStatus;
    
    constructor(action: (deltaTime: number) => BehaviorStatus) {
        super();
        this.action = action;
    }
    
    tick(deltaTime: number): BehaviorStatus {
        return this.action(deltaTime);
    }
}
​
// 选择节点(OR逻辑)
class SelectorNode extends BehaviorNode {
    private children: BehaviorNode[] = [];
    
    addChild(child: BehaviorNode) {
        child.setParent(this);
        this.children.push(child);
    }
    
    tick(deltaTime: number): BehaviorStatus {
        for (const child of this.children) {
            const status = child.tick(deltaTime);
            if (status !== BehaviorStatus.FAILURE) {
                return status;
            }
        }
        return BehaviorStatus.FAILURE;
    }
}
​
// 序列节点(AND逻辑)
class SequenceNode extends BehaviorNode {
    private children: BehaviorNode[] = [];
    
    addChild(child: BehaviorNode) {
        child.setParent(this);
        this.children.push(child);
    }
    
    tick(deltaTime: number): BehaviorStatus {
        for (const child of this.children) {
            const status = child.tick(deltaTime);
            if (status !== BehaviorStatus.SUCCESS) {
                return status;
            }
        }
        return BehaviorStatus.SUCCESS;
    }
}
​
@ccclass('BehaviorTreeAI')
export class BehaviorTreeAI extends Component {
    private behaviorTree: BehaviorNode | null = null;
    
    start() {
        this.buildBehaviorTree();
        this.schedule(this.updateAI, 0.1);
    }
    
    buildBehaviorTree() {
        // 创建根节点(选择器)
        const root = new SelectorNode();
        
        // 创建攻击分支(序列)
        const attackSequence = new SequenceNode();
        attackSequence.addChild(new ConditionNode(() => this.canAttack()));
        attackSequence.addChild(new ActionNode((dt) => this.performAttack(dt)));
        
        // 创建追逐分支(序列)
        const chaseSequence = new SequenceNode();
        chaseSequence.addChild(new ConditionNode(() => this.canSeePlayer()));
        chaseSequence.addChild(new ActionNode((dt) => this.performChase(dt)));
        
        // 创建巡逻分支
        const patrolAction = new ActionNode((dt) => this.performPatrol(dt));
        
        // 添加到根节点
        root.addChild(attackSequence);
        root.addChild(chaseSequence);
        root.addChild(patrolAction);
        
        this.behaviorTree = root;
    }
    
    updateAI(deltaTime: number) {
        if (this.behaviorTree) {
            this.behaviorTree.tick(deltaTime);
        }
    }
    
    canAttack(): boolean {
        // 检查是否可以攻击玩家
        return false;
    }
    
    canSeePlayer(): boolean {
        // 检查是否看到玩家
        return false;
    }
    
    performAttack(deltaTime: number): BehaviorStatus {
        console.log('攻击中...');
        return BehaviorStatus.SUCCESS;
    }
    
    performChase(deltaTime: number): BehaviorStatus {
        console.log('追逐中...');
        return BehaviorStatus.RUNNING;
    }
    
    performPatrol(deltaTime: number): BehaviorStatus {
        console.log('巡逻中...');
        return BehaviorStatus.RUNNING;
    }
}

9 多人游戏基础

9.1 房间系统

复制代码
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('RoomSystem')
export class RoomSystem extends Component {
    private rooms: Map<string, Room> = new Map();
    
    // 创建房间
    createRoom(roomId: string, maxPlayers: number = 4): Room {
        const room = new Room(roomId, maxPlayers);
        this.rooms.set(roomId, room);
        return room;
    }
    
    // 加入房间
    joinRoom(roomId: string, playerId: string): boolean {
        const room = this.rooms.get(roomId);
        
        if (room && !room.isFull()) {
            room.addPlayer(playerId);
            console.log(`玩家 ${playerId} 加入房间 ${roomId}`);
            return true;
        }
        
        return false;
    }
    
    // 离开房间
    leaveRoom(roomId: string, playerId: string) {
        const room = this.rooms.get(roomId);
        
        if (room) {
            room.removePlayer(playerId);
            
            // 如果房间为空,删除房间
            if (room.getPlayerCount() === 0) {
                this.rooms.delete(roomId);
            }
        }
    }
    
    // 获取房间信息
    getRoomInfo(roomId: string): Room | undefined {
        return this.rooms.get(roomId);
    }
}
​
// 房间类
class Room {
    private id: string;
    private maxPlayers: number;
    private players: Set<string> = new Set();
    
    constructor(id: string, maxPlayers: number) {
        this.id = id;
        this.maxPlayers = maxPlayers;
    }
    
    addPlayer(playerId: string) {
        if (!this.isFull()) {
            this.players.add(playerId);
        }
    }
    
    removePlayer(playerId: string) {
        this.players.delete(playerId);
    }
    
    isFull(): boolean {
        return this.players.size >= this.maxPlayers;
    }
    
    getPlayerCount(): number {
        return this.players.size;
    }
    
    getPlayers(): string[] {
        return Array.from(this.players);
    }
}

9.2 玩家同步

复制代码
import { _decorator, Component, Node, Vec3 } from 'cc';
const { ccclass, property } = _decorator;
​
@ccclass('PlayerSync')
export class PlayerSync extends Component {
    private isLocalPlayer: boolean = false;
    private syncInterval: number | null = null;
    
    start() {
        // 判断是否是本地玩家
        this.isLocalPlayer = this.node.name === 'LocalPlayer';
        
        if (this.isLocalPlayer) {
            // 本地玩家:发送位置更新
            this.syncInterval = setInterval(() => {
                this.sendPositionUpdate();
            }, 50);
        }
    }
    
    // 发送位置更新
    sendPositionUpdate() {
        const position = this.node.position;
        const rotation = this.node.rotation;
        
        // 通过网络发送
        // network.send({
        //     type: 'positionUpdate',
        //     playerId: 'local',
        //     position: { x: position.x, y: position.y, z: position.z },
        //     rotation: { x: rotation.x, y: rotation.y, z: rotation.z, w: rotation.w },
        //     timestamp: Date.now()
        // });
    }
    
    // 接收位置更新
    receivePositionUpdate(data: any) {
        if (this.isLocalPlayer) return;
        
        // 使用插值平滑移动
        const targetPos = new Vec3(data.position.x, data.position.y, data.position.z);
        this.smoothMoveTo(targetPos);
    }
    
    // 平滑移动
    smoothMoveTo(targetPos: Vec3) {
        const currentPos = this.node.position;
        const direction = targetPos.subtract(currentPos).normalize();
        const distance = currentPos.distance(targetPos);
        
        if (distance > 0.1) {
            const moveSpeed = 10;
            const newPos = currentPos.add(direction.multiplyScalar(moveSpeed));
            this.node.setPosition(newPos);
        } else {
            this.node.setPosition(targetPos);
        }
    }
    
    onDestroy() {
        if (this.syncInterval) {
            clearInterval(this.syncInterval);
        }
    }
}

9.3 状态同步

复制代码
import { _decorator, Component, Node } from 'cc';
const { ccclass, property } = _decorator;
​
// 游戏状态枚举
enum GameState {
    WAITING,
    PLAYING,
    PAUSED,
    FINISHED
}
​
@ccclass('GameStateManager')
export class GameStateManager extends Component {
    private currentState: GameState = GameState.WAITING;
    private listeners: Set<(state: GameState) => void> = new Set();
    
    // 添加状态监听器
    addListener(callback: (state: GameState) => void) {
        this.listeners.add(callback);
    }
    
    // 移除状态监听器
    removeListener(callback: (state: GameState) => void) {
        this.listeners.delete(callback);
    }
    
    // 切换状态
    setState(newState: GameState) {
        if (this.currentState === newState) return;
        
        const previousState = this.currentState;
        this.currentState = newState;
        
        console.log(`游戏状态切换: ${GameState[previousState]} -> ${GameState[newState]}`);
        
        // 通知所有监听器
        for (const callback of this.listeners) {
            callback(newState);
        }
        
        // 执行状态切换逻辑
        this.onStateChange(previousState, newState);
    }
    
    // 状态切换处理
    onStateChange(previousState: GameState, newState: GameState) {
        switch (newState) {
            case GameState.WAITING:
                this.onEnterWaiting();
                break;
            case GameState.PLAYING:
                this.onEnterPlaying();
                break;
            case GameState.PAUSED:
                this.onEnterPaused();
                break;
            case GameState.FINISHED:
                this.onEnterFinished();
                break;
        }
    }
    
    onEnterWaiting() {
        console.log('进入等待状态');
        // 重置游戏
    }
    
    onEnterPlaying() {
        console.log('进入游戏状态');
        // 开始游戏逻辑
    }
    
    onEnterPaused() {
        console.log('进入暂停状态');
        // 暂停游戏
    }
    
    onEnterFinished() {
        console.log('进入结束状态');
        // 显示结果
    }
    
    // 获取当前状态
    getState(): GameState {
        return this.currentState;
    }
    
    // 快捷方法
    startGame() {
        this.setState(GameState.PLAYING);
    }
    
    pauseGame() {
        this.setState(GameState.PAUSED);
    }
    
    resumeGame() {
        this.setState(GameState.PLAYING);
    }
    
    endGame() {
        this.setState(GameState.FINISHED);
    }
}

10 编辑器扩展

10.1 创建自定义编辑器面板

复制代码
import { _decorator, Component, Node } from 'cc';
import { Editor, Panel } from 'cc/editor';
const { ccclass, property } = _decorator;
​
// 注意:此代码需在编辑器扩展中运行
@ccclass('CustomPanel')
export class CustomPanel extends Panel {
    static panelName: string = 'custom-panel';
    
    onLoad() {
        // 初始化面板内容
        this.createUI();
    }
    
    createUI() {
        // 创建面板UI
        const container = document.createElement('div');
        container.style.padding = '20px';
        
        // 添加标题
        const title = document.createElement('h2');
        title.textContent = '自定义工具面板';
        container.appendChild(title);
        
        // 添加按钮
        const button = document.createElement('button');
        button.textContent = '执行命令';
        button.style.padding = '10px 20px';
        button.addEventListener('click', () => {
            this.executeCommand();
        });
        container.appendChild(button);
        
        // 添加内容到面板
        this.element.appendChild(container);
    }
    
    executeCommand() {
        // 执行自定义命令
        console.log('执行自定义命令');
        
        // 例如:选中所有节点
        // const nodes = Editor.Selection.getSelectedNodes();
        // console.log('选中的节点:', nodes);
    }
}
​
// 注册面板
// Editor.Panel.register({
//     name: CustomPanel.panelName,
//     type: CustomPanel
// });

10.2 自定义属性编辑器

复制代码
import { _decorator, Component, Node } from 'cc';
import { Editor, PropertyEditor } from 'cc/editor';
const { ccclass, property } = _decorator;
​
// 注意:此代码需在编辑器扩展中运行
@ccclass('CustomPropertyEditor')
export class CustomPropertyEditor extends PropertyEditor {
    init(property: any) {
        super.init(property);
        
        // 创建自定义UI
        const container = document.createElement('div');
        
        // 添加输入框
        const input = document.createElement('input');
        input.type = 'text';
        input.value = property.value || '';
        input.addEventListener('change', (e) => {
            this.onChange((e.target as HTMLInputElement).value);
        });
        container.appendChild(input);
        
        this.element.appendChild(container);
    }
    
    onChange(value: any) {
        // 触发属性变更
        this.emit('change', value);
    }
}

总结

通过本章的学习,你应该掌握了:

高级技术部分

  1. 3D基础:摄像机、光照系统、PBR材质、自定义着色器

  2. 粒子系统:粒子效果创建、纹理动画

  3. 骨骼动画:Spine动画、DragonBones动画

  4. 性能优化:渲染优化、LOD系统、资源缓存、对象池

  5. 打包发布:构建配置、热更新、多平台适配

  6. 调试工具:性能分析、调试绘制

实战应用部分

  1. 数据持久化:localStorage、文件存储、SQLite数据库

  2. AI系统:状态机、行为树

  3. 多人游戏基础:房间系统、玩家同步、状态同步

  4. 编辑器扩展:自定义面板、自定义属性编辑器

这些高级技术和实战应用将帮助你开发更加复杂和专业的游戏项目!

相关推荐
AZaLEan__1 小时前
JavaScript 基础语法
开发语言·javascript·ecmascript
有梦想的程序星空1 小时前
【环境配置】使用 Vue CLI 构建 Vue 项目脚手架完整指南
前端·javascript·vue.js
影视飓风TIM1 小时前
C++ 核心语法笔记:拷贝构造、深浅拷贝与运算符重载
java·开发语言·javascript
之歆1 小时前
Ajax 进阶:跨域、CORS、JSONP 与请求封装实战
前端·javascript·ajax
杨先生哦1 小时前
【2026 热端攻防系列 2/12】DOM 型 XSS 深度实战:AI 多态变形免杀 + 全维度防御
前端·人工智能·笔记·安全·web安全·xss
sugar__salt1 小时前
前端Ajax核心原理与实战:从异步机制到接口请求全解析
前端·javascript·ajax
问心无愧05131 小时前
ctf show web入门115
android·前端·笔记
難釋懷1 小时前
Nginx缓冲区
前端·javascript·nginx
程序猿小泓1 小时前
2026 前端面试全攻略:手写题、算法与计网/TS 高频考点
前端·javascript·css