目录
[1 3D基础](#1 3D基础)
[1.1 3D场景设置](#1.1 3D场景设置)
[1.2 3D模型和材质](#1.2 3D模型和材质)
[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 渲染优化)
[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 本地存储)
[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);
}
}
总结
通过本章的学习,你应该掌握了:
高级技术部分
-
3D基础:摄像机、光照系统、PBR材质、自定义着色器
-
粒子系统:粒子效果创建、纹理动画
-
骨骼动画:Spine动画、DragonBones动画
-
性能优化:渲染优化、LOD系统、资源缓存、对象池
-
打包发布:构建配置、热更新、多平台适配
-
调试工具:性能分析、调试绘制
实战应用部分
-
数据持久化:localStorage、文件存储、SQLite数据库
-
AI系统:状态机、行为树
-
多人游戏基础:房间系统、玩家同步、状态同步
-
编辑器扩展:自定义面板、自定义属性编辑器
这些高级技术和实战应用将帮助你开发更加复杂和专业的游戏项目!