从零构建引擎:1小时实现基础2D游戏框架

一、核心架构设计:模块化与轻量化

在1小时内开发2D游戏引擎,需优先设计轻量化、模块化的架构。核心模块应包括:

  1. 渲染系统:负责将游戏对象绘制到屏幕
  2. 输入系统:处理键盘、鼠标等输入事件
  3. 物理系统:实现基础碰撞检测和运动逻辑
  4. 游戏循环:驱动主逻辑更新和渲染

架构示意图

  1. ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
  2. Input Physics Renderer
  3. └─────────────┘ └─────────────┘ └─────────────┘
  4. └───────────┬───────┘
  5. ┌───────────────────────────────────┘
  6. ┌─────────────┐
  7. GameLoop
  8. └─────────────┘

二、渲染系统实现:Canvas API快速上手

使用浏览器原生Canvas API可快速实现2D渲染,避免引入复杂图形库。关键步骤如下:

1. 初始化Canvas

  1. <canvas id="gameCanvas" width="800" height="600"></canvas>
  1. const canvas = document.getElementById('gameCanvas');
  2. const ctx = canvas.getContext('2d');

2. 基础绘制函数

  1. function drawRect(x, y, width, height, color) {
  2. ctx.fillStyle = color;
  3. ctx.fillRect(x, y, width, height);
  4. }
  5. function drawText(text, x, y, color = 'black') {
  6. ctx.fillStyle = color;
  7. ctx.font = '16px Arial';
  8. ctx.fillText(text, x, y);
  9. }

3. 精灵类设计

  1. class Sprite {
  2. constructor(x, y, width, height, color) {
  3. this.x = x;
  4. this.y = y;
  5. this.width = width;
  6. this.height = height;
  7. this.color = color;
  8. }
  9. draw() {
  10. drawRect(this.x, this.y, this.width, this.height, this.color);
  11. }
  12. }

三、输入系统:事件监听与状态管理

实现键盘输入响应,需建立状态映射表事件监听

1. 键盘状态管理

  1. const keys = {
  2. ArrowUp: false,
  3. ArrowDown: false,
  4. ArrowLeft: false,
  5. ArrowRight: false,
  6. Space: false
  7. };
  8. // 监听按键按下
  9. window.addEventListener('keydown', (e) => {
  10. if (keys.hasOwnProperty(e.key)) {
  11. keys[e.key] = true;
  12. e.preventDefault(); // 防止页面滚动
  13. }
  14. });
  15. // 监听按键释放
  16. window.addEventListener('keyup', (e) => {
  17. if (keys.hasOwnProperty(e.key)) {
  18. keys[e.key] = false;
  19. }
  20. });

2. 输入处理函数

  1. function handleInput(player) {
  2. const speed = 5;
  3. if (keys.ArrowUp) player.y -= speed;
  4. if (keys.ArrowDown) player.y += speed;
  5. if (keys.ArrowLeft) player.x -= speed;
  6. if (keys.ArrowRight) player.x += speed;
  7. }

四、物理系统:碰撞检测与运动

实现AABB(轴对齐边界框)碰撞检测和简单重力模拟:

1. 碰撞检测

  1. function checkCollision(a, b) {
  2. return a.x < b.x + b.width &&
  3. a.x + a.width > b.x &&
  4. a.y < b.y + b.height &&
  5. a.y + a.height > b.y;
  6. }

2. 重力模拟

  1. class PhysicsObject {
  2. constructor(x, y, width, height) {
  3. this.x = x;
  4. this.y = y;
  5. this.width = width;
  6. this.height = height;
  7. this.velocityY = 0;
  8. this.gravity = 0.5;
  9. }
  10. applyGravity() {
  11. this.velocityY += this.gravity;
  12. this.y += this.velocityY;
  13. }
  14. }

五、游戏循环:时间控制与状态更新

使用requestAnimationFrame实现平滑动画,并分离更新渲染阶段:

1. 主游戏循环

  1. let lastTime = 0;
  2. const targetFPS = 60;
  3. const frameInterval = 1000 / targetFPS;
  4. function gameLoop(timestamp) {
  5. // 控制帧率
  6. if (timestamp - lastTime < frameInterval) {
  7. requestAnimationFrame(gameLoop);
  8. return;
  9. }
  10. lastTime = timestamp;
  11. // 更新游戏状态
  12. update();
  13. // 渲染画面
  14. render();
  15. requestAnimationFrame(gameLoop);
  16. }

2. 更新与渲染分离

  1. const player = new Sprite(100, 100, 50, 50, 'blue');
  2. const ground = { x: 0, y: 550, width: 800, height: 50 };
  3. function update() {
  4. handleInput(player);
  5. // 简单重力与地面碰撞
  6. player.y += 5; // 模拟重力
  7. if (checkCollision(player, ground)) {
  8. player.y = ground.y - player.height;
  9. }
  10. }
  11. function render() {
  12. // 清空画布
  13. ctx.clearRect(0, 0, canvas.width, canvas.height);
  14. // 绘制游戏对象
  15. player.draw();
  16. drawRect(ground.x, ground.y, ground.width, ground.height, 'green');
  17. // 显示FPS
  18. drawText(`FPS: ${Math.round(1000 / (timestamp - lastTime))}`, 10, 20);
  19. }

六、性能优化与扩展建议

  1. 对象池技术:复用游戏对象减少内存分配
  2. 分层渲染:将静态背景与动态对象分开绘制
  3. 时间缩放:使用deltaTime保证不同设备上的运动一致性
  4. ES模块化:将各系统拆分为独立模块

示例:DeltaTime实现

  1. let lastTimestamp = 0;
  2. function update(timestamp) {
  3. const deltaTime = (timestamp - lastTimestamp) / 16.67; // 转换为60FPS的倍数
  4. lastTimestamp = timestamp;
  5. // 使用deltaTime实现帧率无关的运动
  6. player.x += 100 * deltaTime;
  7. }

七、完整示例:1小时可完成的2D平台游戏

结合上述模块,1小时内可实现以下功能:

  • 玩家角色移动与跳跃
  • 地面碰撞检测
  • 简单敌人AI(巡逻)
  • 分数系统
  • 游戏重启逻辑

关键代码整合

  1. // 初始化
  2. const canvas = document.getElementById('gameCanvas');
  3. const ctx = canvas.getContext('2d');
  4. let score = 0;
  5. // 游戏对象
  6. const player = new PhysicsObject(100, 100, 50, 50);
  7. const enemy = new Sprite(600, 500, 50, 50, 'red');
  8. let enemyDirection = 1;
  9. function update() {
  10. // 玩家输入
  11. if (keys.ArrowUp && checkCollision(player, ground)) {
  12. player.velocityY = -10;
  13. }
  14. handleInput(player);
  15. // 敌人AI
  16. enemy.x += 2 * enemyDirection;
  17. if (enemy.x < 50 || enemy.x > 700) {
  18. enemyDirection *= -1;
  19. }
  20. // 碰撞检测
  21. if (checkCollision(player, enemy)) {
  22. alert('Game Over!');
  23. resetGame();
  24. }
  25. player.applyGravity();
  26. }
  27. function render() {
  28. ctx.clearRect(0, 0, canvas.width, canvas.height);
  29. // 绘制对象
  30. new Sprite(player.x, player.y, player.width, player.height, 'blue').draw();
  31. enemy.draw();
  32. drawRect(ground.x, ground.y, ground.width, ground.height, 'green');
  33. // 显示分数
  34. drawText(`Score: ${score}`, 10, 20);
  35. }

总结与后续方向

1小时内完成的引擎已具备基础游戏功能,后续可扩展:

  • 添加更多游戏对象类型
  • 实现关卡系统
  • 引入状态机管理游戏状态
  • 添加音效系统

这种从零开始的开发方式,能帮助开发者深入理解游戏引擎底层原理,为后续使用成熟引擎(如行业常见技术方案)打下坚实基础。