项目概述

本项目是一个基于Electron开发的跨平台鸿蒙PC经典吃豆人(Pac-Man)游戏实现。游戏保持了经典吃豆人的核心玩法,同时利用Electron框架提供了跨平台的桌面应用体验。
请添加图片描述

技术栈

  • 主框架: Electron.js
  • 前端技术: HTML5 Canvas, JavaScript, CSS3
  • 渲染引擎: Canvas 2D API
  • 存储: localStorage (本地最高分存储)
  • 构建工具: npm/yarn

核心算法实现

1. 游戏核心逻辑

// 游戏主循环
gameLoop(timestamp) {
  // 计算时间差
  const deltaTime = timestamp - lastTime;
  lastTime = timestamp;
  
  // 更新游戏状态
  updateGame(deltaTime);
  
  // 绘制游戏画面
  drawGame();
  
  // 继续游戏循环
  if (gameState === 'playing') {
    gameLoopId = requestAnimationFrame(gameLoop);
  }
}

游戏采用经典的游戏循环模式,通过requestAnimationFrame实现高效渲染,确保平滑的游戏体验。每帧执行两个核心步骤:更新游戏状态和渲染游戏画面。

2. 吃豆人移动与碰撞检测

// 更新吃豆人位置
function updatePacman(deltaTime) {
  // 尝试转向
  if (canMoveTo(pacman.x + pacman.nextDirection.x * pacman.speed,
                pacman.y + pacman.nextDirection.y * pacman.speed)) {
    pacman.direction = pacman.nextDirection;
  }
  
  // 移动吃豆人
  if (canMoveTo(pacman.x + pacman.direction.x * pacman.speed,
                pacman.y + pacman.direction.y * pacman.speed)) {
    pacman.x += pacman.direction.x * pacman.speed;
    pacman.y += pacman.direction.y * pacman.speed;
  }
  
  // 检查是否吃到食物
  checkFood();
}

// 碰撞检测函数
function canMoveTo(x, y) {
  // 转换为网格坐标
  const gridX = Math.floor(x / TILE_SIZE);
  const gridY = Math.floor(y / TILE_SIZE);
  
  // 检查边界
  if (gridX < 0 || gridX >= GRID_WIDTH || gridY < 0 || gridY >= GRID_HEIGHT) {
    return false;
  }
  
  // 检查是否为墙
  return map[gridY][gridX] !== 1;
}

吃豆人的移动系统采用了网格碰撞检测算法,结合方向预输入机制(nextDirection),实现了流畅的转向体验。碰撞检测通过将像素坐标转换为网格坐标,与地图数据进行比对来判断是否可以移动。

3. 幽灵AI系统

// 幽灵移动更新
function updateGhosts(deltaTime) {
  ghosts.forEach(ghost => {
    // 根据当前模式选择目标
    if (ghost.mode === 'frightened') {
      // 恐惧模式下随机移动
      // ...
    } else if (ghost.mode === 'scatter') {
      // 分散模式,前往预设角落
      // ...
    } else if (ghost.mode === 'chase') {
      // 追击模式,根据不同幽灵设定不同目标
      setChaseTarget(ghost);
    }
    
    // 改变方向
    changeGhostDirection(ghost);
    
    // 移动
    if (canMoveTo(ghost.x + ghost.direction.x * ghost.speed,
                  ghost.y + ghost.direction.y * ghost.speed)) {
      ghost.x += ghost.direction.x * ghost.speed;
      ghost.y += ghost.direction.y * ghost.speed;
    }
  });
  
  // 检查与吃豆人的碰撞
  checkCollisions();
}

幽灵AI系统是游戏的核心特色之一,实现了经典吃豆人中四种不同幽灵的行为模式:

  1. Blinky(红色):直接追击吃豆人当前位置
  2. Pinky(粉色):追击吃豆人前方4格的位置
  3. Inky(青色):基于Blinky和吃豆人的相对位置计算目标
  4. Clyde(橙色):接近时会转向分散模式

每个幽灵都有三种主要行为模式:

  • Scatter Mode:前往地图角落
  • Chase Mode:追击吃豆人
  • Frightened Mode:被能量球击中后随机移动,可被吃掉

4. 地图系统

// 地图数据表示
const map = [
  [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
  [1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
  // ... 更多地图数据
];

// 地图渲染函数
function drawMap() {
  for (let y = 0; y < GRID_HEIGHT; y++) {
    for (let x = 0; x < GRID_WIDTH; x++) {
      const tile = map[y][x];
      const pixelX = x * TILE_SIZE;
      const pixelY = y * TILE_SIZE;
      
      // 绘制墙
      if (tile === 1) {
        ctx.fillStyle = '#0000FF';
        ctx.fillRect(pixelX, pixelY, TILE_SIZE, TILE_SIZE);
      }
      // 绘制食物
      else if (tile === 2) {
        ctx.fillStyle = '#FFFF00';
        ctx.beginPath();
        ctx.arc(pixelX + TILE_SIZE/2, pixelY + TILE_SIZE/2, 2, 0, Math.PI * 2);
        ctx.fill();
      }
      // 绘制能量球
      else if (tile === 3) {
        ctx.fillStyle = '#FFFF00';
        ctx.beginPath();
        ctx.arc(pixelX + TILE_SIZE/2, pixelY + TILE_SIZE/2, 6, 0, Math.PI * 2);
        ctx.fill();
      }
    }
  }
}

地图系统使用二维数组表示,支持多种元素类型:

  • 0: 空白
  • 1: 墙
  • 2: 普通食物
  • 3: 能量球

渲染时通过Canvas API将二维数组转换为视觉元素,实现了高效的地图绘制。

5. 动画系统

// 更新吃豆人嘴部动画
function updatePacmanMouth(deltaTime) {
  pacman.mouthTimer += deltaTime;
  if (pacman.mouthTimer > 200) { // 200ms切换一次
    pacman.mouthOpen = !pacman.mouthOpen;
    pacman.mouthTimer = 0;
  }
}

// 绘制吃豆人
function drawPacman() {
  ctx.fillStyle = '#FFFF00';
  ctx.beginPath();
  
  // 根据嘴部状态和方向绘制扇形
  if (pacman.mouthOpen) {
    // 绘制张嘴状态
    // ...
  } else {
    // 绘制闭嘴状态
    // ...
  }
  
  ctx.fill();
}

游戏实现了流畅的动画系统,包括:

  • 吃豆人的嘴部开合动画
  • 幽灵的闪烁效果(恐惧模式)
  • 过渡动画(如幽灵被吃掉后的眼睛返回效果)

游戏状态管理

// 游戏状态枚举
const GAME_STATUS = {
  READY: 'ready',
  PLAYING: 'playing',
  PAUSED: 'paused',
  GAME_OVER: 'game_over',
  LEVEL_COMPLETE: 'level_complete'
};

// 游戏状态管理变量
let gameState = 'idle'; // idle, playing, paused, gameOver, levelComplete
let score = 0;
let lives = 3;
let level = 1;
let highScore = localStorage.getItem('pacman-high-score') || 0;

游戏采用状态机模式管理整体流程,包括以下核心状态:

  • idle: 游戏初始状态
  • playing: 游戏进行中
  • paused: 游戏暂停
  • gameOver: 游戏结束
  • levelComplete: 关卡完成

状态转换由各种游戏事件触发,如按键操作、碰撞检测结果等。

分数与关卡系统

// 分数计算函数
function checkFood() {
  const gridX = Math.floor(pacman.x / TILE_SIZE);
  const gridY = Math.floor(pacman.y / TILE_SIZE);
  
  // 吃到普通食物
  if (map[gridY][gridX] === 2) {
    score += 10;
    map[gridY][gridX] = 0;
    dotsRemaining--;
  }
  // 吃到能量球
  else if (map[gridY][gridX] === 3) {
    score += 50;
    map[gridY][gridX] = 0;
    powerPelletsRemaining--;
    
    // 激活恐惧模式
    activateFrightenedMode();
  }
  
  // 更新显示
  updateDisplay();
  
  // 检查关卡完成
  checkLevelComplete();
}

// 关卡完成检查
function checkLevelComplete() {
  if (dotsRemaining === 0) {
    levelComplete();
  }
}

// 进入下一关
function nextLevel() {
  level++;
  resetMap();
  resetGhosts();
  
  // 提高难度(增加幽灵速度)
  ghosts.forEach(ghost => {
    ghost.speed = Math.min(ghost.speed + 0.2, 2.5);
  });
  
  // 重置游戏状态
  gameState = 'playing';
  levelCompleteModal.style.display = 'none';
  startGameLoop();
}

分数系统根据吃到的不同食物类型增加不同分数:

  • 普通食物: 10分
  • 能量球: 50分
  • 恐惧模式下吃幽灵: 200, 400, 800, 1600分(累进)

关卡系统支持多关卡游戏,每完成一个关卡会重置地图并增加游戏难度(提高幽灵速度)。

Electron应用架构

1. 主进程实现

// main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    title: '吃豆人游戏',
    webPreferences: {
      preload: path.join(__dirname, 'src', 'preload.js'),
      nodeIntegration: true,
      contextIsolation: false
    }
  });

  mainWindow.loadFile(path.join(__dirname, 'src', 'index.html'));
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

2. 预加载脚本

// preload.js
const { contextBridge, ipcRenderer } = require('electron');

// 游戏常量定义
const PACMAN_CONSTANTS = {
  DIRECTIONS: { /* ... */ },
  GAME_STATUS: { /* ... */ },
  ELEMENT_TYPES: { /* ... */ },
  GHOST_STATES: { /* ... */ },
  GHOST_COLORS: { /* ... */ },
  GAME_CONFIG: { /* ... */ }
};

// 暴露API给渲染进程
contextBridge.exposeInMainWorld('pacmanGame', {
  game: { /* ... */ },
  window: { /* ... */ },
  dialog: { /* ... */ },
  constants: PACMAN_CONSTANTS
});

应用遵循Electron的多进程架构:

  • 主进程(main.js):负责创建和管理浏览器窗口,处理应用生命周期
  • 预加载脚本(preload.js):在渲染器进程加载前执行,提供安全的IPC通信机制
  • 渲染进程(renderer.js):运行在浏览器窗口中,负责游戏逻辑和UI渲染

性能优化策略

1. 高效渲染优化

  • 局部渲染:通过只重绘变化的区域减少Canvas绘制操作
  • requestAnimationFrame:利用浏览器渲染时机优化动画性能
  • 对象池模式:复用游戏对象减少内存分配

2. 算法优化

  • 网格碰撞检测:使用整数坐标系统加速碰撞计算
  • 空间分区:通过网格划分减少碰撞检测次数
  • 方向预计算:提前计算幽灵可能的移动方向

3. 资源管理

  • 本地存储:使用localStorage保存高分记录
  • 懒加载:按需加载游戏资源
  • 内存优化:及时释放不再使用的对象和引用

错误处理机制

// 错误处理函数
function handleError(error, context) {
  console.error(`[${context}] Error:`, error);
  
  // 记录错误信息
  logError(error, context);
  
  // 根据错误类型进行恢复
  switch(error.type) {
    case 'game_state':
      // 重置游戏状态
      resetGame();
      break;
    case 'rendering':
      // 尝试重新渲染
      drawGame();
      break;
    default:
      // 显示用户友好的错误消息
      showErrorMessage('游戏发生错误,正在恢复...');
  }
}

// 监听未捕获异常
window.addEventListener('error', (e) => {
  handleError(e.error, 'global');
  e.preventDefault();
});

游戏实现了完善的错误处理机制:

  • 全局错误监听:捕获未处理的JavaScript异常
  • 游戏逻辑错误恢复:检测并纠正游戏状态异常
  • 资源加载失败处理:优雅降级处理资源加载问题
  • 用户友好的错误提示:避免技术错误直接暴露给用户

运行与开发

安装依赖

npm install

开发模式运行

npm start

构建应用

# 构建Windows版本
npm run build:win

# 构建macOS版本
npm run build:mac

# 构建Linux版本
npm run build:linux

鸿蒙适配后结构(需整合到 Electron 鸿蒙项目模板中):


ohos_hap/
├── electron/
│   ├── libs/
│   │   └── arm64-v8a/  # 鸿蒙核心库文件
│   │       ├── libelectron.so
│   │       ├── libadapter.so
│   │       ├── libffmpeg.so
│   │       └── libc++_shared.so
├── web_engine/
│   └── src/
│       └── main/
│           └── resources/
│               └── resfile/
│                   └── resources/
│                       └── app/  # 放置electron应用代码
│                           ├── main.js
│                           ├── package.json
│                           └── src/
└── module.json5        # 鸿蒙应用配置文件

鸿蒙PC适配改造指南

1. 环境准备

  • 系统要求:Windows 10/11、8GB RAM以上、20GB可用空间

  • 工具安装
    DevEco Studio 5.0+(安装鸿蒙SDK API 20+)

  • Node.js 18.x+

2. 获取Electron鸿蒙编译产物

  1. 登录Electron 鸿蒙官方仓库

  2. 下载Electron 34+版本的Release包(.zip格式)

  3. 解压到项目目录,确认electron/libs/arm64-v8a/下包含核心.so库

3. 部署应用代码

将Electron应用代码按以下目录结构放置:
在这里插入图片描述


web_engine/src/main/resources/resfile/resources/app/
├── main.js
├── package.json
└── src/
    ├── index.html
    ├── preload.js
    ├── renderer.js
    └── style.css

4. 配置与运行

  1. 打开项目:在DevEco Studio中打开ohos_hap目录

  2. 配置签名
    进入File → Project Structure → Signing Configs

  3. 自动生成调试签名或导入已有签名

  4. 连接设备
    启用鸿蒙设备开发者模式和USB调试

  5. 通过USB Type-C连接电脑

  6. 编译运行:点击Run按钮或按Shift+F10

5. 验证检查项

  • ✅ 应用窗口正常显示

  • ✅ 窗口大小可调整,响应式布局生效

  • ✅ 控制台无"SysCap不匹配"或"找不到.so文件"错误

  • ✅ 动画效果正常播放

跨平台兼容性

平台 适配策略 特殊处理
Windows 标准Electron运行 无特殊配置
macOS 标准Electron运行 保留dock图标激活逻辑
Linux 标准Electron运行 确保系统依赖库完整
鸿蒙PC 通过Electron鸿蒙适配层 禁用硬件加速,使用特定目录结构

鸿蒙开发调试技巧

1. 日志查看

在DevEco Studio的Log面板中过滤"Electron"关键词,查看应用运行日志和错误信息。

2. 常见问题解决

  • "SysCap不匹配"错误:检查module.json5中的reqSysCapabilities,只保留必要系统能力

  • "找不到.so文件"错误:确认arm64-v8a目录下四个核心库文件完整

  • 窗口不显示:在main.js中添加app.disableHardwareAcceleration()

  • 动画卡顿:简化CSS动画效果,减少重绘频率

结语

本项目实现了一个功能完整的经典吃豆人游戏,结合了Electron框架的跨平台能力和现代Web技术的交互体验。游戏核心算法包括吃豆人移动系统、幽灵AI、碰撞检测等,通过精心设计的游戏状态管理和性能优化策略,提供了流畅的游戏体验。

该实现不仅保留了经典吃豆人的游戏乐趣,还通过模块化设计和清晰的代码结构,为进一步扩展和定制提供了良好的基础。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐