# Pandona Engine (PE) 一个功能完整的 JavaScript 2D 游戏引擎,基于 DOM 实现,用于创建各种类型的 2D 游戏和动画。 ## 编译时架构 Pandona Engine 现在支持现代编译时架构,使用 Vite 作为构建工具,支持: - ES6 模块系统 - Less CSS 预处理器 - 代码压缩和优化 - 开发服务器热重载 - 场景化组织结构 ## 简化版API (推荐) 为了让开发者更容易上手,Pandona Engine提供了简化版API,参考了PixiJS、Egret和Vue 3的设计理念,让代码更加简洁易读。 ## 特性 - 🎮 **完整的游戏开发功能**:支持创建各种类型的2D游戏 - 🌟 **模块化架构**:核心功能拆分为独立模块,易于扩展和维护 - 🌟 **插件系统**:支持自定义插件扩展引擎功能 - 🎨 **多种元素类型**:支持精灵、容器、文本、HTML 和 SVG 元素 - 🌈 **高级动画系统**:内置补间动画、帧动画、动画序列和时间轴 - 🎵 **音频管理**:支持音效和背景音乐播放 - 🌍 **场景管理**:支持多场景切换和过渡效果 - 📡 **事件系统**:增强的事件总线,支持全局和局部事件 - 🧹 **内存管理**:完善的资源清理机制 - 🎯 **碰撞检测**:矩形碰撞检测系统 - 🎮 **输入处理**:键盘和鼠标输入处理 - 📱 **UI系统**:按钮、标签、图像、滑块等UI组件 - 🎥 **摄像机系统**:支持跟随、缩放、旋转和震动效果 - ✨ **粒子系统**:创建各种视觉效果 - ⚙️ **组件系统**:基于组件的游戏对象系统 - 📈 **游戏循环**:固定时间步长的游戏循环 ## 安装 ```bash # 克隆项目 git clone # 安装依赖 npm install ``` ## 快速开始 ### 开发模式 ```bash # 启动开发服务器 npm run dev ``` ### 构建项目 ```bash # 构建生产版本 npm run build # 预览构建结果 npm run preview ``` ### 简化版API用法 (推荐) ```javascript // 创建引擎实例 const game = PE.createEngine('#game-container', { width: 800, height: 600, background: '#f0f0f0' }); // 加载资源 game.assets.load('character.png') .then(img => { // 创建精灵 (链式调用) const player = game.create('sprite', { image: img, x: 100, y: 100, width: 50, height: 50 }).position(100, 100) .size(50, 50) .on('click', () => { player.position(200, 150); }); // 添加到舞台 game.stage.add(player); // 创建移动动画 game.animate(player, { x: 300, y: 200 }, 1000, { easing: 'easeInOutQuad', onComplete: () => { console.log('动画完成!'); } }); }); ``` ### 基本用法 ```javascript // 创建引擎实例 const game = new Engine('#game-container', { width: 800, height: 600, background: '#f0f0f0' }); // 加载资源 game.load('character.png') .then(img => { // 创建精灵 const player = game.createSprite(img, 100, 100, 50, 50); // 添加到舞台 game.stage.add(player); // 绑定点击事件 player.on('click', () => { player.setPosition(200, 150); }); }); ``` ### 使用动画系统 #### 简化版API ```javascript // 创建移动动画 game.animate(player, { x: 300, y: 200 }, 1000, { easing: 'easeInOutQuad', onUpdate: (progress) => { console.log(`动画进度: ${Math.round(progress * 100)}%`); }, onComplete: () => { console.log('动画完成!'); } }); // 创建帧动画 const walkAnimation = game.animate.frame(player, [ 'walk1.png', 'walk2.png', 'walk3.png' ], 10, { loop: true }); // 播放帧动画 walkAnimation.play(); ``` #### 基本用法 ```javascript // 创建移动动画 game.tween(player, 1000, 'easeInOutQuad') .to({ x: 300, y: 200 }) .onUpdate(progress => { console.log(`动画进度: ${Math.round(progress * 100)}%`); }) .onComplete(() => { console.log('动画完成!'); }) .start(); ``` ### 使用场景管理 #### 简化版API ```javascript // 创建场景 const menuScene = game.scene.create('menu'); const gameScene = game.scene.create('game'); // 切换场景 game.scene.switch('menu'); // 带过渡效果的场景切换 game.scene.switch('game', { transition: { type: 'fade', duration: 1000 } }); ``` #### 基本用法 ```javascript // 创建场景 const menuScene = game.createScene('menu'); const gameScene = game.createScene('game'); // 切换场景 game.switchScene('menu'); ``` ## API 参考 ### 引擎初始化 #### 简化版API ```javascript const engine = PE.createEngine(elementSelector, options); ``` #### 基本用法 ```javascript const engine = new Engine(elementSelector, options); ``` | 参数 | 类型 | 默认值 | 说明 | |------|------|---------|------| | `elementSelector` | string | `'body'` | 挂载舞台的 DOM 选择器 | | `options.width` | number | 800 | 舞台宽度(px) | | `options.height` | number | 600 | 舞台高度(px) | | `options.background` | string | `'transparent'` | 舞台背景色 | | `options.fps` | number | 60 | 游戏循环帧率 | ### 插件系统 ```javascript // 安装插件 engine.use(YourPlugin, options); ``` ### 资源加载 #### 简化版API ```javascript // 加载单个资源 game.assets.load(imageUrl) .then(img => { /* 使用图像 */ }) .catch(error => { /* 处理错误 */ }); // 批量加载资源 game.assets.load([imageUrl1, imageUrl2, imageUrl3]) .then(resources => { /* 使用资源 */ }) .catch(error => { /* 处理错误 */ }); // 预加载资源并显示进度 game.assets.preload([ 'image1.png', 'image2.png', 'sound1.wav' ], (progress, loaded, total) => { console.log(`加载进度: ${Math.round(progress * 100)}% (${loaded}/${total})`); }).then(() => { console.log('所有资源加载完成!'); }); ``` #### 基本用法 ```javascript engine.load(imageUrl) .then(img => { /* 使用图像 */ }) .catch(error => { /* 处理错误 */ }); ``` ### 音频管理 ```javascript // 加载音效 engine.loadSound('shoot', 'sounds/shoot.wav'); // 播放音效 engine.playSound('shoot'); // 加载音乐 engine.loadMusic('background', 'sounds/bg.mp3'); // 播放音乐 engine.playMusic('background', { loop: true }); ``` ### 动画系统 #### 简化版API ```javascript // 创建补间动画 game.animate(target, { x: 100, y: 200 }, 1000, { easing: 'easeInOutQuad', delay: 500, repeat: 2, yoyo: true, onUpdate: (progress) => { /* 更新回调 */ }, onComplete: () => { /* 完成回调 */ } }); // 创建并行动画 const parallelAnimation = game.animate.parallel([ game.animate(target1, { x: 100 }, 1000), game.animate(target2, { y: 200 }, 1000) ]); parallelAnimation.play(); // 创建链式动画 const chainAnimation = game.animate.chain([ game.animate(target, { x: 100 }, 1000), game.animate(target, { y: 200 }, 1000) ]); chainAnimation.play(); ``` #### 基本用法 ```javascript // 创建补间动画 engine.tween(target, duration, easing) .to({ x: 100, y: 200 }) .onUpdate(callback) .onComplete(callback) .start(); // 创建帧动画 engine.createFrameAnimation('walk', [ 'walk1.png', 'walk2.png', 'walk3.png' ], 10); // 播放帧动画 engine.playFrameAnimation(sprite, 'walk', true); ``` 支持的缓动函数: - `linear` - `easeInQuad` - `easeOutQuad` - `easeInOutQuad` - `easeInCubic` - `easeOutCubic` - `easeInOutCubic` - `easeInQuart` - `easeOutQuart` - `easeInOutQuart` - `easeInQuint` - `easeOutQuint` - `easeInOutQuint` - `easeInSine` - `easeOutSine` - `easeInOutSine` - `easeInExpo` - `easeOutExpo` - `easeInOutExpo` - `easeInCirc` - `easeOutCirc` - `easeInOutCirc` - `easeInBack` - `easeOutBack` - `easeInOutBack` - `easeInElastic` - `easeOutElastic` - `easeInOutElastic` - `easeInBounce` - `easeOutBounce` - `easeInOutBounce` ### 场景管理 #### 简化版API ```javascript // 创建场景 const scene = engine.scene.create(name); // 切换场景 engine.scene.switch(name); // 场景过渡效果 engine.scene.transition.fade('game', 1000); engine.scene.transition.slide('game', 'left', 500); engine.scene.transition.zoom('game', 1000); engine.scene.transition.flip('game', 1000); ``` #### 基本用法 ```javascript // 创建场景 const scene = engine.createScene(name); // 切换场景 engine.switchScene(name); // 场景过渡效果 engine.transitionFade('game', 1000); engine.transitionSlide('game', 'left', 500); ``` ### 摄像机系统 #### 简化版API ```javascript // 设置摄像机位置 (带动画) engine.camera.move(400, 300, 1000).then(() => { console.log('摄像机移动完成'); }); // 跟随目标 engine.camera.follow(player, { x: 0, y: 100 }); // 摄像机缩放 (带动画) engine.camera.zoom(1.5, 1000).then(() => { console.log('摄像机缩放完成'); }); // 摄像机旋转 (带动画) engine.camera.rotate(PE.PI, 1000).then(() => { console.log('摄像机旋转完成'); }); // 屏幕震动 engine.camera.shake(10, 500); ``` #### 基本用法 ```javascript // 设置摄像机位置 engine.setCameraPosition(400, 300); // 跟随目标 engine.follow(player, { x: 0, y: 100 }); // 摄像机缩放 engine.setCameraZoom(1.5); // 屏幕震动 engine.shakeCamera(10, 500); ``` ### UI系统 #### 简化版API ```javascript // 创建按钮 (链式调用) const button = engine.ui.create('button', { x: 100, y: 100, width: 200, height: 50, text: '点击我' }).position(100, 100) .size(200, 50) .background('#007acc') .hover('#005a99') .pressed('#003d66') .color('#ffffff') .on('click', () => { console.log('按钮被点击了!'); }); engine.ui.add(button); // 创建标签 (链式调用) const label = engine.ui.create('label', { x: 100, y: 200, text: 'Hello World' }).position(100, 200) .text('Hello World') .font('20px Arial') .color('#ffffff'); engine.ui.add(label); ``` #### 基本用法 ```javascript // 创建按钮 const button = new UIButton(100, 100, 200, 50, '点击我'); button.on('click', () => { console.log('按钮被点击了!'); }); engine.addUIElement(button); // 创建标签 const label = new UILabel(100, 200, 'Hello World'); engine.addUIElement(label); ``` ### 创建元素 #### 简化版API ```javascript // 创建精灵 (链式调用) const sprite = engine.create('sprite', { image: imgElement, x: 100, y: 100, width: 50, height: 50 }).position(100, 100) .size(50, 50) .alpha(0.8) .scale(1.2) .rotate(PE.PI_2) .on('click', () => { console.log('精灵被点击了!'); }); // 创建容器 (链式调用) const container = game.create('box', { x: 50, y: 50, width: 200, height: 200 }).position(50, 50) .size(200, 200) .addChild(sprite); // 创建文本 (链式调用) const text = game.create('text', { text: 'Hello World', x: 100, y: 100 }).position(100, 100) .text('Hello World') .font('bold 24px Arial') .color('#ff0000') .align('center'); // 创建HTML元素 (链式调用) const html = game.create('html', { html: '', x: 300, y: 200 }).position(300, 200) .html(''); // 创建SVG元素 (链式调用) const svg = game.create('svg', { x: 400, y: 300, width: 100, height: 100, content: '' }).position(400, 300) .size(100, 100) .content(''); ``` #### 基本用法 ##### 精灵 (createSprite) ```javascript const sprite = engine.createSprite(imgElement, x, y, width, height); ``` - 支持位置和尺寸控制 - 支持事件绑定 ##### 容器 (createBox) ```javascript const container = engine.createBox(x, y, width, height); ``` - 用于元素分组管理 - 支持嵌套结构 ##### 文本 (createText) ```javascript const text = engine.createText('Hello', 50, 50); text.setColor('#ff0000'); text.setFont('bold 24px Arial'); ``` ##### HTML 元素 (createHtml) ```javascript const html = engine.createHtml('', 300, 200); ``` ##### SVG 元素 (createSvg) ```javascript const svg = engine.createSvg(400, 300, 100, 100, ''); ``` ### 元素通用方法 #### 简化版API 所有创建的元素支持以下链式调用方法: | 方法 | 说明 | 示例 | |------|------|------| | `position(x, y)` | 设置元素位置 | `sprite.position(150, 200)` | | `size(width, height)` | 设置元素尺寸 | `box.size(100, 80)` | | `alpha(value)` | 设置透明度 | `sprite.alpha(0.8)` | | `scale(x, y)` | 设置缩放 | `sprite.scale(1.2, 1.2)` | | `rotate(angle)` | 设置旋转 | `sprite.rotate(PE.PI_2)` | | `add(child)` | 添加子元素 | `container.add(sprite)` | | `remove(child)` | 移除子元素 | `container.remove(sprite)` | | `on(event, callback)` | 绑定事件 | `sprite.on('click', handleClick)` | | `visible(visible)` | 显示/隐藏元素 | `sprite.visible(false)` | #### 基本用法 所有创建的元素支持以下方法: | 方法 | 说明 | 示例 | |------|------|------| | `setPosition(x, y)` | 设置元素位置 | `sprite.setPosition(150, 200)` | | `setSize(width, height)` | 设置元素尺寸 | `box.setSize(100, 80)` | | `setStyle(cssText)` | 添加自定义样式 | `text.setStyle('opacity: 0.8;')` | | `add(child)` | 添加子元素 | `container.add(sprite)` | | `remove(child)` | 移除子元素 | `container.remove(sprite)` | | `on(event, callback)` | 绑定事件 | `sprite.on('click', handleClick)` | | `off(event, callback)` | 解绑事件 | `sprite.off('click', handleClick)` | | `hide()` | 隐藏元素 | `sprite.hide()` | | `show()` | 显示元素 | `sprite.show()` | ### 游戏对象系统 #### 简化版API ```javascript // 创建游戏对象 (链式调用) const player = engine.gameobject.create({ x: 100, y: 100, width: 50, height: 50, name: 'player', components: { physics: PE.components.physics({ velocityX: 100, velocityY: 0 }), collider: PE.components.collider({ width: 50, height: 50 }) }, events: { update: (deltaTime) => { // 更新逻辑 } } }).position(100, 100) .size(50, 50) .addComponent('animation', PE.components.animation()) .on('update', (deltaTime) => { // 更新逻辑 }); // 添加到引擎 engine.addGameObject(player); ``` #### 基本用法 ```javascript // 创建游戏对象 const player = new GameObject(100, 100, 50, 50); // 添加组件 player.addComponent('physics', new PhysicsComponent({ velocityX: 100, velocityY: 0 })); player.addComponent('collider', new ColliderComponent({ width: 50, height: 50 })); // 添加到引擎 engine.addGameObject(player); ``` ### 碰撞检测 ```javascript // 检查两个对象是否碰撞 if (engine.checkCollision(player, enemy)) { console.log('发生碰撞!'); } ``` ### 音频系统 #### 简化版API ```javascript // 加载音频 engine.sound.load('shoot', 'sounds/shoot.wav'); engine.sound.load('background', 'sounds/bg.mp3', { type: 'music' }); // 播放音频 engine.sound.play('shoot'); engine.sound.play('background', { loop: true, type: 'music' }); // 控制音量 engine.sound.volume(0.5, 'master'); // 主音量 engine.sound.volume(0.8, 'sound'); // 音效音量 engine.sound.volume(0.6, 'music'); // 音乐音量 // 停止音频 engine.sound.stop('background', 'music'); // 暂停/恢复音频 engine.sound.pause('background', 'music'); engine.sound.resume('background', 'music'); ``` #### 基本用法 ```javascript // 加载音效 engine.loadSound('shoot', 'sounds/shoot.wav'); // 播放音效 engine.playSound('shoot'); // 加载音乐 engine.loadMusic('background', 'sounds/bg.mp3'); // 播放音乐 engine.playMusic('background', { loop: true }); ``` ### 销毁引擎 ```javascript engine.destroy(); ``` ## 项目结构 ``` cssEngine/ ├── index.html # 主页面 ├── main.js # 应用入口文件 ├── vite.config.js # Vite配置文件 ├── package.json # 项目配置 ├── README.md # 项目说明文档 ├── public/ # 静态资源 │ └── sprite.css # 精灵样式文件 ├── style/ # 全局样式 │ └── base.less # 基础样式 ├── examples/ # 示例项目 │ ├── spaceShooter.js # 太空射击游戏示例 │ └── spaceShooter.html # 示例游戏HTML文件 ├── src/ │ ├── core/ # 核心模块 │ │ ├── Engine.js # 引擎主类 │ │ └── SimplifiedEngine.js # 简化版引擎API │ ├── elements/ # 元素类 │ │ ├── BaseElement.js │ │ ├── Sprite.js │ │ ├── Box.js │ │ ├── TextElement.js │ │ ├── HtmlElement.js │ │ ├── SvgElement.js │ │ ├── GameObject.js │ │ ├── Component.js │ │ ├── SimplifiedElements.js # 简化版元素API │ │ ├── SimplifiedGameObject.js # 简化版游戏对象API │ │ └── SimplifiedComponent.js # 简化版组件API │ ├── managers/ # 管理器 │ │ ├── ResourceManager.js │ │ ├── SceneManager.js │ │ ├── AudioManager.js │ │ ├── TransitionManager.js │ │ ├── Camera.js │ │ ├── SimplifiedResourceManager.js # 简化版资源管理API │ │ ├── SimplifiedSceneManager.js # 简化版场景管理API │ │ ├── SimplifiedAudioManager.js # 简化版音频管理API │ │ └── SimplifiedCamera.js # 简化版摄像机API │ ├── animation/ # 动画系统 │ │ ├── Tween.js │ │ ├── AnimationSystem.js │ │ ├── AnimationController.js │ │ └── SimplifiedAnimation.js # 简化版动画API │ ├── effects/ # 特效系统 │ │ ├── ParticleSystem.js │ │ └── SimplifiedParticleSystem.js # 简化版粒子系统API │ ├── ui/ # UI系统 │ │ ├── UI.js │ │ └── SimplifiedUI.js # 简化版UI系统API │ ├── utils/ # 工具类 │ │ ├── EventBus.js │ │ └── SimplifiedEventBus.js # 简化版事件系统API │ └── scenes/ # 场景目录 │ └── index/ # 主场景 │ ├── index.js # 场景结构和逻辑 │ └── index.less # 场景样式 └── test/ # 测试文件 └── ... # 各模块测试 ``` ## 开发指南 ### 编译时架构开发指南 Pandona Engine 现在支持现代编译时架构,使用 Vite 作为构建工具: ```bash # 启动开发服务器(热重载) npm run dev # 构建生产版本 npm run build # 预览构建结果 npm run preview ``` ### 场景化开发 使用场景化组织结构开发应用: ``` src/scenes/ ├── index/ # 主场景 │ ├── index.js # 场景结构和逻辑 │ └── index.less # 场景样式 └── other-scene/ # 其他场景 ├── index.js └── index.less ``` ### 简化版API开发指南 简化版API提供了更简洁的开发方式,推荐新项目使用: ```javascript // 使用简化版API创建游戏 const game = PE.createEngine('#game-container', { width: 800, height: 600, background: '#000022' }); // 使用链式调用创建和配置元素 const player = game.create('sprite', { image: playerImage, x: 100, y: 100, width: 50, height: 50 }).position(100, 100) .size(50, 50) .on('click', () => { console.log('Player clicked!'); }); // 添加到舞台 game.stage.add(player); // 创建动画 game.animate(player, { x: 300, y: 200 }, 1000, { easing: 'easeInOutQuad', onComplete: () => { console.log('Animation completed!'); } }); ``` ### 扩展功能 可以通过插件系统扩展引擎功能: ```javascript // 定义插件 const MyPlugin = { name: 'MyPlugin', install(engine, options) { // 扩展引擎功能 engine.myMethod = () => { console.log('This is my custom method'); }; } }; // 使用插件 engine.use(MyPlugin); ``` ### 自定义元素 可以通过继承 `BaseElement` 创建自定义元素: ```javascript import BaseElement from './src/elements/BaseElement.js'; class CustomElement extends BaseElement { constructor(x, y, width, height, engineId) { super('div', engineId); this.setPosition(x, y); this.setSize(width, height); // 添加自定义逻辑 } } ``` ### 游戏对象和组件 可以通过GameObject和Component系统创建复杂的游戏对象: ```javascript import { GameObject } from './src/elements/GameObject.js'; import { PhysicsComponent, ColliderComponent } from './src/elements/Component.js'; class Player extends GameObject { constructor(x, y) { super(x, y, 50, 50); // 添加物理组件 this.addComponent('physics', new PhysicsComponent({ velocityX: 0, velocityY: 0, friction: 0.9 })); // 添加碰撞组件 this.addComponent('collider', new ColliderComponent({ width: 50, height: 50 })); } update(deltaTime) { super.update(deltaTime); // 自定义更新逻辑 } } ``` ## 示例游戏 项目包含一个完整的太空射击游戏示例,展示了引擎的各种功能: 1. 游戏对象系统 2. 组件系统 3. 碰撞检测 4. 动画系统 5. 音频管理 6. 场景管理 7. UI系统 8. 摄像机系统 9. 粒子效果 10. 场景过渡效果 要运行示例游戏,请在浏览器中打开 `examples/spaceShooter.html`。 ## 注意事项 1. 所有元素位置基于舞台左上角(0,0)计算 2. 使用 `destroy()` 方法会完全移除舞台并清理内存 3. 图像元素自动添加 `user-drag: none` 防止拖动 4. 舞台默认启用硬件加速 (`transform: translateZ(0)`) 5. 每个引擎实例有唯一ID标识 (`data-engine-id`) 6. 游戏循环基于requestAnimationFrame实现 7. 坐标系统基于世界坐标和屏幕坐标的转换 Pandona Engine是一个功能完整的2D游戏引擎,适合创建各种类型的2D游戏和动画,使用原生DOM操作,无需额外依赖。