You've already forked Pandona-Engine
942 lines
22 KiB
Markdown
942 lines
22 KiB
Markdown
# 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 <repository-url>
|
||
|
||
# 安装依赖
|
||
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: '<button>Click</button>',
|
||
x: 300,
|
||
y: 200
|
||
}).position(300, 200)
|
||
.html('<button>Click</button>');
|
||
|
||
// 创建SVG元素 (链式调用)
|
||
const svg = game.create('svg', {
|
||
x: 400,
|
||
y: 300,
|
||
width: 100,
|
||
height: 100,
|
||
content: '<circle cx="50" cy="50" r="40"/>'
|
||
}).position(400, 300)
|
||
.size(100, 100)
|
||
.content('<circle cx="50" cy="50" r="40"/>');
|
||
```
|
||
|
||
#### 基本用法
|
||
|
||
##### 精灵 (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('<button>Click</button>', 300, 200);
|
||
```
|
||
|
||
##### SVG 元素 (createSvg)
|
||
```javascript
|
||
const svg = engine.createSvg(400, 300, 100, 100, '<circle cx="50" cy="50" r="40"/>');
|
||
```
|
||
|
||
### 元素通用方法
|
||
|
||
#### 简化版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操作,无需额外依赖。 |