鸿蒙贪吃蛇游戏项目设计文档

一.项目概述

1. 项目简介

本项目是基于鸿蒙操作系统(HarmonyOS)开发的经典贪吃蛇游戏,采用 ArkTS 编程语言实现。游戏不仅包含贪吃蛇移动、吃食物增长、碰撞检测等核心功能,还新增了随机生成的特殊食物系统,同时支持多种游戏模式和皮肤自定义,适合鸿蒙初学者学习组件开发、状态管理、定时器使用等基础技能。

2. 技术栈

开发语言:ArkTS(TypeScript的超集)

开发框架:HarmonyOS 3.0+

UI框架:ArkUI声明式开发范式

开发工具:DevEco Studio

3. 核心功能

基础功能:蛇的移动控制、食物生成、得分计算、碰撞检测(撞墙、撞自身、撞障碍物)。

扩展功能:4种游戏模式(极简/普通(默认)/困难/炼狱)、多种皮肤选择、特殊食物系统(4种特殊效果)、最高分持久化存储。

交互功能:开始/暂停/重新开始/返回主页、方向键控制、弹窗选择模式和皮肤、Toast 提示反馈。

二.需求设计

1. 功能需求

功能模块

具体需求

游戏初始化

蛇初始位置居中、网格数据初始化、得分重置

蛇的控制

上下左右方向移动、防止反向移动、防快速变向

食物系统

普通食物随机生成(不重叠蛇身/障碍物)、特殊食物随机生成(高难度模式生效)

特殊食物

4种类型(金色/粉色/青色/紫色),对应不同得分或蛇身变化效果

游戏模式

不同模式对应不同速度、网格大小、障碍物数量

皮肤系统

自定义蛇头/蛇身/食物颜色及食物图标

碰撞检测

撞墙、撞自身、撞障碍物判定,触发游戏结束

数据存储

最高分持久化(使用@StorageLink)

交互反馈

模式/皮肤选择弹窗、操作Toast提示、倒计时显示

  1. 用户交互流程

(1)启动游戏→进入开始界面(显示最高分、当前模式/皮肤)。

(2)选择游戏模式/皮肤→点击“开始游戏”进入游戏界面。

(3)游戏中:通过方向键控制蛇移动,吃食物得分,触发特殊食物效果。

(4)游戏控制:支持暂停/继续、重新开始、返回主页。

(5)游戏结束:Toast提示结束原因和得分,自动返回开始界面。

三.项目架构设计

1. 核心数据结构设计

1.1 基础数据结构
interface Position {   // 坐标位置接口

  x: number;  // 行坐标

  y: number;  // 列坐标

}

type Direction = 'up' | 'down' | 'left' | 'right';   // 移动方向类型
1.2 游戏模式配置
interface GameMode {

  name: string;        // 模式名称

  speed: number;       // 移动速度

  gridSize: number;    // 网格大小

  obstacleCount: number; // 障碍物数量

}
1.3 皮肤系统设计
interface Skin {

  name: string;        // 皮肤名称

  snakeHead: string;   // 蛇头颜色

  snakeBody: string;   // 蛇身颜色

  food: string;        // 食物颜色

  foodIcon: string;    // 食物图标

}
1.4 特殊食物设计
enum SpecialFoodType {

  NONE = 'none',      // 无特殊食物

  GOLDEN = 'golden',  // 金色小球 +25分

  PINK = 'pink',      // 粉色小球 分数翻倍

  CYAN = 'cyan',      // 青色小球 +20分

  PURPLE = 'purple'   // 紫色小球 减一节

}

interface SpecialFood {

  x: number;           // 横坐标

  y: number;           // 纵坐标

  type: SpecialFoodType; // 食物类型

  spawnTime: number;   // 生成时间戳

  duration: number;    // 持续时间

  active: boolean;     // 是否激活

}

四.核心功能实现详解

1. 游戏初始化流程

1.1 游戏初始化(initSnake 方法)

蛇的初始长度为2,位置在网格中心,符合用户视觉习惯。初始化时会清空之前的定时器和特殊食物,避免残留状态影响新游戏。updateGrid()方法用于刷新网格中蛇、食物、障碍物的显示。

initSnake(): void {

  const mid = Math.floor(this.selectedMode.gridSize / 2);  // 计算网格中心位置

  this.snake = [   // 初始化蛇身(初始两节:蛇头和蛇身)

    { x: mid, y: mid + 1 }, // 蛇头

    { x: mid, y: mid }      // 蛇身

  ];

this.currentDirection = 'right'; // 初始方向向右

  this.score = 0; // 得分重置

  this.obstacles = []; // 清空障碍物

  this.updateGrid(); // 更新网格数据

   if (this.specialFoodTimer !== -1) { // 清空特殊食物相关资源

    clearInterval(this.specialFoodTimer);

    this.specialFoodTimer = -1;

  }

  this.specialFood = null;

}
1.2 网格数据更新(updateGrid 方法)

网格是二维数组gridData,每个元素代表一个单元格的类型(0 = 空白,1 = 蛇身,2 = 普通食物,3 = 蛇头,4 = 障碍物,5 = 特殊食物)。通过遍历蛇身、食物、障碍物的坐标,更新二维数组的值,后续 UI 根据这些值渲染不同的样式。

updateGrid(): void {  // 创建空白网格

  this.gridData = Array(this.selectedMode.gridSize)

    .fill(0)

    .map(() => Array(this.selectedMode.gridSize).fill(0));

  this.obstacles.forEach((obs: Position) => {

    this.gridData[obs.x][obs.y] = 4;  // 标记障碍物位置(值:4)

  });

  this.snake.forEach((seg: Position, index: number) => {

    this.gridData[seg.x][seg.y] = index === 0 ? 3 : 1;  // 标记蛇身位置(蛇头:3,蛇身:1)

  });

  if (this.foodPos.x !== -1) {

    this.gridData[this.foodPos.x][this.foodPos.y] = 2;  // 标记食物位置(值:2)

  }

  if (this.specialFood && this.specialFood.active) {  // 标记特殊食物位置(值:5)

    this.gridData[this.specialFood.x][this.specialFood.y] = 5;

  }

}

2. 食物生成系统

2.1 普通食物生成(generateFood 方法)

使用Math.random()随机生成 x/y 坐标,范围是 0 到网格大小 - 1。

do-while循环确保食物不重叠:通过some方法判断坐标是否已被蛇身或障碍物占用。

加入maxAttempts防止极端情况下(格子快满)无限循环。

generateFood(): void {

  const maxCells = this.selectedMode.gridSize * this.selectedMode.gridSize;

  if (this.snake.length >= maxCells - 1) return;  // 检查网格是否已满

  let newFood: Position;

  let attempts = 0;

  const maxAttempts = 100;

  do {  // 随机生成食物位置,确保不在蛇身或障碍物上

    newFood = {

      x: Math.floor(Math.random() * this.selectedMode.gridSize),

      y: Math.floor(Math.random() * this.selectedMode.gridSize)

    };

    attempts++;

  } while (this.isPositionOccupied(newFood) && attempts < maxAttempts);

  this.foodPos = newFood;

  this.maybeGenerateSpecialFood(); // 尝试生成特殊食物

}
2.2 特殊食物生成法(maybeGenerateSpecialFood 方法)

普通模式特殊食物规则

食物类型

生成概率

效果

持续时间

金色(GOLDEN)

5%

+25分

20秒

粉色(PINK)

1%

分数翻倍(最高+100)

10秒

青色(CYAN)

5%

+20分

20秒

紫色(PURPLE)

2%

蛇身减1(长度<3时+30分)

15秒

maybeGenerateSpecialFood(): void {

  const allowedModes = ['普通模式', '困难模式', '炼狱模式'];  // 检查是否支持特殊食物的模式

  if (!allowedModes.includes(this.selectedMode.name)) return;

  if (this.specialFood && this.specialFood.active) return; //检查是否已有特殊食物

  const rand = Math.random() * 100;

  let newSpecialFood: SpecialFood | null = null;

  // 根据不同模式设置不同的生成概率

  if (this.selectedMode.name === '普通模式') {

    if (rand < 5) { // 5% 概率生成金色食物

      newSpecialFood=this.createSpecialFood(SpecialFoodType.GOLDEN, 20000);

    }  // 其他类型概率...

  }

  if (newSpecialFood) {  // 其他模式概率设置...

    this.specialFood = newSpecialFood;

    this.startSpecialFoodCountdown(); // 启动倒计时

  }
2.3 特殊食物类型效果

通过switch判断食物类型,执行对应的得分或蛇身修改逻辑,并用promptAction.showToast显示操作反馈,提升用户体验。

handleSpecialFood(type: SpecialFoodType): void {

  let message = '';

  switch (type) {

    case SpecialFoodType.GOLDEN:

      this.score += 25;

      message = '获得奖牌!+25分';

      break;

    case SpecialFoodType.PINK:

      if (this.score > 100) {

        this.score += 100;

        message = '获得爱心!分数已达上限,+100分';

      } else {

        this.score *= 2;

        message = '获得爱心!分数翻倍!';

      }

      break;

    case SpecialFoodType.CYAN:

      this.score += 20;

      message = '获得钻石!+20分';

      break;

    case SpecialFoodType.PURPLE:

      if (this.snake.length > 3) {

        this.snake.pop();

        message = '获得紫水晶球!蛇身缩短一节!';

      } else {

        this.score += 30;

        message = '蛇身太短,获得30分!';

      }

      break;

  }

  // 显示效果提示

  promptAction.showToast({ message, duration: 2000 });

}

3. 蛇的移动与方向控制

3.1 蛇的移动(moveSnake 方法)

移动原理:复制蛇头→更新蛇头位置→添加新蛇头→移除尾部(没吃到食物时)。

碰撞检测:先判断蛇头是否超出网格(撞墙)、是否与蛇身重叠(撞自己)、是否与障碍物重叠(撞障碍),触发任意一种则游戏结束。

吃食物逻辑:普通食物增长蛇身,特殊食物不增长但有额外效果。

moveSnake(): void {

  // 复制当前蛇头作为新头部

  const head: Position = { x: this.snake[0].x, y: this.snake[0].y };

  switch (this.currentDirection) {  // 根据方向移动头部

    case 'up': head.x--; break;

    case 'down': head.x++; break;

    case 'left': head.y--; break;

    case 'right': head.y++; break;

  }

 // 碰撞检测(撞墙、撞自身、撞障碍物)

  if (head.x < 0 || head.x >= this.selectedMode.gridSize ||

      head.y < 0 || head.y >= this.selectedMode.gridSize) {

    this.gameOverReason = '撞墙了!';

    this.gameOver(true);

    return;

  }

  if (this.snake.some(seg => seg.x === head.x && seg.y === head.y)) {

    this.gameOverReason = '撞到自己了!';

    this.gameOver(true);

    return;

  }

  if (this.obstacles.some(obs => obs.x === head.x && obs.y === head.y)) {

    this.gameOverReason = '撞到障碍物了!';

    this.gameOver(true);

    return;

  }

// 吃普通食物判断:吃到后得分+5,生成新食物,蛇身增长(不移除尾部)

  let ateFood = false;

  if (head.x === this.foodPos.x && head.y === this.foodPos.y) {

    this.score += 5;

    this.generateFood();

    ateFood = true;

    // 更新最高分

    if (this.score > this.highScore) {

      this.highScore = this.score;

    }

  }

  // 吃特殊食物判断:触发对应效果,不移除蛇尾(蛇身长度不变)

  if (this.specialFood && this.specialFood.active &&

      head.x === this.specialFood.x && head.y === this.specialFood.y) {

    this.handleSpecialFood(this.specialFood.type); // 处理特殊效果

    this.removeSpecialFood('eaten'); // 移除已吃的特殊食物

    ateFood = false;

  }

  this.snake.unshift(head);  // 新增蛇头到数组头部

  if (!ateFood) {  // 没吃到食物则移除尾部(蛇身长度不变)

    this.snake.pop();

  }

  this.updateGrid(); // 刷新网格显示}

3.2 方向控制(changeDirection 方法)

加入了“防快速变向”和“防反向”逻辑,避免游戏操作失误,提升体验。

Direction是之前定义的类型('up'|'down'|'left'|'right'),确保传入的方向合法。

changeDirection(newDir: Direction): void {

  if (!this.isPlaying) return; // 游戏暂停时不允许改方向

  const now = Date.now();

  // 100ms内不允许再次改方向,防止快速操作导致蛇反向

  if (now - this.lastDirectionTime < this.directionChangeDelay) {

    return;

  }

  // 定义反向映射:不允许直接掉头(如向上时不能直接向下)

  const oppositeDirs: Record<Direction, Direction> = {

    'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left'

  };

  if (oppositeDirs[this.currentDirection] !== newDir) {

    this.currentDirection = newDir;

    this.lastDirectionTime = now;

  }

}

4.游戏结束(gameOver 方法)

游戏结束时必须清除所有定时器,避免内存泄漏;通过Toast提示结束原因和得分,1 秒后自动返回开始界面,流程更友好。

gameOver(isWallCollision: boolean): void {

  this.isPlaying = false;

  // 清除所有定时器(移动、特殊食物倒计时、特殊食物消失)

  if (this.gameTimer !== -1) {

    clearInterval(this.gameTimer);

    this.gameTimer = -1;

  }

  this.stopSpecialFoodCountdown();

  if (this.specialFoodTimer !== -1) {

    clearInterval(this.specialFoodTimer);

    this.specialFoodTimer = -1;

  }

  this.specialFood = null;  // 清空特殊食物

  this.showSpecialFoodTimer = false;

  promptAction.showToast({  // 显示游戏结束提示

    message: `${this.gameOverReason} 游戏结束!得分: ${this.score}`,

    duration: 3000

  });

  setTimeout(() => {  // 1秒后返回开始界面

    if (isWallCollision) {

      this.returnToStartScreen();

    }

  }, 1000);

}

五.用户界面设计

1. 开始界面设计

  

// 开始界面(gameStarted为false时显示)

      if (!this.gameStarted) {

        Column() {

          Text('贪吃蛇游戏').fontSize(30).margin(30)

          Text(`历史最高分: ${this.highScore}`).fontSize(20).margin(20)

          Button('选择模式').onClick(() => this.showModeDialog = true)

          Button('选择皮肤').onClick(() => this.showSkinDialog = true)

          Button('开始游戏').onClick(() => this.startGame())

        }

        .justifyContent(FlexAlign.Center)

      }
  1. 游戏界面设计

网格渲染:使用Grid+ForEach循环渲染二维数组gridData,每个单元格根据cell值渲染对应元素(蛇头、蛇身、食物等)。

条件渲染:特殊食物倒计时、弹窗等通过if-else控制是否显示。

样式自定义:蛇的颜色、食物图标等通过selectedSkin状态获取,实现皮肤切换功能。

2.1 游戏状态显示区
// 顶部信息栏(模式、得分、特殊食物倒计时)

  Row() {

    Text(`当前模式: ${this.selectedMode.name}`)

    Text(`当前得分: ${this.score}`)

    // 特殊食物倒计时(条件渲染)

   if (this.showSpecialFoodTimer && this.specialFood) {

      Text(`${this.getSpecialFoodName(this.specialFood.type)}: ${this.specialFoodRemainingSeconds}秒`)

      .fontColor(this.getSpecialFoodColor(this.specialFood.type))

    }

  }
2.2 游戏网格渲染
Grid() {

  ForEach(this.gridData, (row: number[], rowIndex: number) => {

    ForEach(row, (cell: number, colIndex: number) => {

      GridItem() {

        this.renderGridCell(rowIndex, colIndex);

      }

    })

  })

}

.columnsTemplate(`1fr `.repeat(this.selectedMode.gridSize))

.rowsTemplate(`1fr `.repeat(this.selectedMode.gridSize))
2.3 网格单元格渲染逻辑
private renderGridCell(rowIndex: number, colIndex: number): void {

  const cellValue = this.gridData[rowIndex][colIndex];

  switch (cellValue) {

    case 5: // 特殊食物

      Text(this.getSpecialFoodIcon(this.specialFood!.type))

        .fontSize(this.getFoodIconSize())

        .fontColor(this.getSpecialFoodColor(this.specialFood!.type))

      break;

    case 2: // 普通食物

      Text(this.selectedSkin.foodIcon)

        .fontSize(this.getFoodIconSize())

      break;

    case 3: // 蛇头

      Column() { Blank() }

        .backgroundColor(this.selectedSkin.snakeHead)

        .borderRadius(10)

      break;

    case 1: // 蛇身

      Column() { Blank() }

        .backgroundColor(this.selectedSkin.snakeBody)

        .borderRadius(8)

      break;

    case 4: // 障碍物

      Text('✵')

        .fontSize(this.getObstacleIconSize())

      break;

    default: // 空白格子

      Column() { Blank() }

        .backgroundColor('#F8F8F8')

  }

}
2.4 控制区域设计
Column() {

  Row() {  // 功能按钮行

    Button('重新开始').onClick(() => this.startGame())

      Button(this.isPlaying ? '暂 ⏸ 停' : '继 ⏯️ 续').onClick(() => {

        if (this.isPlaying) {

         clearInterval(this.gameTimer); // 暂停:清除移动定时器

        } else {

        this.startGameTimer(); // 继续:重启移动定时器

       }

       this.isPlaying = !this.isPlaying;

      })

    Button('返回主页').onClick(() => this.returnToStartScreen())

  }

  Column() {  // 方向控制按钮

    Button('↑').onClick(() => this.changeDirection('up'))

    Row() {

      Button('←').onClick(() => this.changeDirection('left'))

      Button('→').onClick(() => this.changeDirection('right'))

    }

    Button('↓').onClick(() => this.changeDirection('down'))

  }

}
  • 游戏配置系统
  1. 游戏模式选择
1.1 模式配置详情

模式数组中每个元素对应一种游戏难度,通过修改speed(移动速度)、gridSize(网格大小)、obstacleCount(障碍物数量)实现难度梯度。

private gameModes: GameMode[] = [

  { name: '极简模式', speed: 2, gridSize: 10, obstacleCount: 0 }, // 速度慢、网格小、无障碍物

  { name: '普通模式', speed: 3, gridSize: 15, obstacleCount: 0 }, // 默认模式

  { name: '困难模式', speed: 4, gridSize: 15, obstacleCount: 3 }, // 速度快、有3个障碍物

  { name: '炼狱模式', speed: 5, gridSize: 20, obstacleCount: 6 }  // 速度最快、网格大、6个障碍物

];
1.2 模式选择实现
selectMode(mode: GameMode): void {

  this.selectedMode = mode;

  this.showModeDialog = false;

  promptAction.showToast({ message: `已选择${mode.name}`, duration: 1500 });

}
  1. 皮肤选择系统
2.1. 皮肤配置选项
private skinOptions: Skin[] = [

  { name: '经典绿', snakeHead: '#2E8B57', snakeBody: '#3CB371', food: '#32CD32', foodIcon: '��' },

  { name: '沙漠黄', snakeHead: '#8B4513', snakeBody: '#F4A460', food: '#32CD32', foodIcon: '��' },  // 其他皮肤(代码略)...

];
2.2 皮肤应用实现
selectSkin(skin: Skin): void {

  this.selectedSkin = skin;

  this.showSkinDialog = false;

  this.updateGrid();  // 立即更新界面显示

  promptAction.showToast({

    message: `已选择${skin.name}皮肤`,

    duration: 1500

  });

}

七.性能优化策略

1. 渲染优化

条件渲染:使用if语句控制UI元素的显示

局部更新:只更新变化的网格单元格

轻量组件:优化组件层级结构

2. 内存管理

定时器清理:及时清除不再需要的定时器

对象复用:避免频繁创建和销毁对象

状态管理:合理使用@State装饰器

3. 响应式设计

// 自适应图标大小

getFoodIconSize(): number {

  const baseSize = 25;

  const gridSize = this.selectedMode.gridSize;

  // 根据网格大小调整图标尺寸

  if (gridSize <= 10) return 22;

  else if (gridSize <= 15) return 18;

  else return 14;

}

. 项目总结与学习要点

1. 技术要点总结

1. ArkTS基础语法:接口、枚举、类的使用

2. HarmonyOS状态管理:@State、@StorageLink装饰器

3. ArkUI声明式开发:组件化、响应式UI构建

4. 游戏逻辑设计:碰撞检测、食物生成、移动算法

5. 性能优化:定时器管理、内存优化、渲染优化

2. 扩展学习方向

1. 多人在线对战:利用分布式能力实现联机对战

2. AI对手:实现智能AI算法

3. 关卡系统:设计不同的游戏关卡

4. 成就系统:添加游戏成就和奖励机制

5. 新增音效功能:吃食物、游戏结束时播放音效(使用鸿蒙audio组件)。

代码链接:https://gitee.com/its-way/harmony-project---snake-game

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐