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

22 KiB
Raw Permalink Blame History

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组件
  • 🎥 摄像机系统:支持跟随、缩放、旋转和震动效果
  • 粒子系统:创建各种视觉效果
  • ⚙️ 组件系统:基于组件的游戏对象系统
  • 📈 游戏循环:固定时间步长的游戏循环

安装

# 克隆项目
git clone <repository-url>

# 安装依赖
npm install

快速开始

开发模式

# 启动开发服务器
npm run dev

构建项目

# 构建生产版本
npm run build

# 预览构建结果
npm run preview

简化版API用法 (推荐)

// 创建引擎实例
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('动画完成!');
      }
    });
  });

基本用法

// 创建引擎实例
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

// 创建移动动画
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();

基本用法

// 创建移动动画
game.tween(player, 1000, 'easeInOutQuad')
  .to({ x: 300, y: 200 })
  .onUpdate(progress => {
    console.log(`动画进度: ${Math.round(progress * 100)}%`);
  })
  .onComplete(() => {
    console.log('动画完成!');
  })
  .start();

使用场景管理

简化版API

// 创建场景
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
  }
});

基本用法

// 创建场景
const menuScene = game.createScene('menu');
const gameScene = game.createScene('game');

// 切换场景
game.switchScene('menu');

API 参考

引擎初始化

简化版API

const engine = PE.createEngine(elementSelector, options);

基本用法

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 游戏循环帧率

插件系统

// 安装插件
engine.use(YourPlugin, options);

资源加载

简化版API

// 加载单个资源
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('所有资源加载完成!');
});

基本用法

engine.load(imageUrl)
  .then(img => { /* 使用图像 */ })
  .catch(error => { /* 处理错误 */ });

音频管理

// 加载音效
engine.loadSound('shoot', 'sounds/shoot.wav');

// 播放音效
engine.playSound('shoot');

// 加载音乐
engine.loadMusic('background', 'sounds/bg.mp3');

// 播放音乐
engine.playMusic('background', { loop: true });

动画系统

简化版API

// 创建补间动画
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();

基本用法

// 创建补间动画
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

// 创建场景
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);

基本用法

// 创建场景
const scene = engine.createScene(name);

// 切换场景
engine.switchScene(name);

// 场景过渡效果
engine.transitionFade('game', 1000);
engine.transitionSlide('game', 'left', 500);

摄像机系统

简化版API

// 设置摄像机位置 (带动画)
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);

基本用法

// 设置摄像机位置
engine.setCameraPosition(400, 300);

// 跟随目标
engine.follow(player, { x: 0, y: 100 });

// 摄像机缩放
engine.setCameraZoom(1.5);

// 屏幕震动
engine.shakeCamera(10, 500);

UI系统

简化版API

// 创建按钮 (链式调用)
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);

基本用法

// 创建按钮
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

// 创建精灵 (链式调用)
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)
const sprite = engine.createSprite(imgElement, x, y, width, height);
  • 支持位置和尺寸控制
  • 支持事件绑定
容器 (createBox)
const container = engine.createBox(x, y, width, height);
  • 用于元素分组管理
  • 支持嵌套结构
文本 (createText)
const text = engine.createText('Hello', 50, 50);
text.setColor('#ff0000');
text.setFont('bold 24px Arial');
HTML 元素 (createHtml)
const html = engine.createHtml('<button>Click</button>', 300, 200);
SVG 元素 (createSvg)
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

// 创建游戏对象 (链式调用)
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);

基本用法

// 创建游戏对象
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);

碰撞检测

// 检查两个对象是否碰撞
if (engine.checkCollision(player, enemy)) {
  console.log('发生碰撞!');
}

音频系统

简化版API

// 加载音频
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');

基本用法

// 加载音效
engine.loadSound('shoot', 'sounds/shoot.wav');

// 播放音效
engine.playSound('shoot');

// 加载音乐
engine.loadMusic('background', 'sounds/bg.mp3');

// 播放音乐
engine.playMusic('background', { loop: true });

销毁引擎

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 作为构建工具:

# 启动开发服务器(热重载)
npm run dev

# 构建生产版本
npm run build

# 预览构建结果
npm run preview

场景化开发

使用场景化组织结构开发应用:

src/scenes/
├── index/           # 主场景
│   ├── index.js     # 场景结构和逻辑
│   └── index.less   # 场景样式
└── other-scene/     # 其他场景
    ├── index.js
    └── index.less

简化版API开发指南

简化版API提供了更简洁的开发方式推荐新项目使用

// 使用简化版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!');
  }
});

扩展功能

可以通过插件系统扩展引擎功能:

// 定义插件
const MyPlugin = {
  name: 'MyPlugin',
  install(engine, options) {
    // 扩展引擎功能
    engine.myMethod = () => {
      console.log('This is my custom method');
    };
  }
};

// 使用插件
engine.use(MyPlugin);

自定义元素

可以通过继承 BaseElement 创建自定义元素:

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系统创建复杂的游戏对象

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