Files
Pandona-Engine/README.md
2025-10-03 16:49:53 +08:00

942 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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操作无需额外依赖。