【江鸟中原】贪吃蛇小游戏(鸿蒙)

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

一. 项目概述

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 基础数据结构
TypeScript 复制代码
interface Position {   // 坐标位置接口

  x: number;  // 行坐标

  y: number;  // 列坐标

}

type Direction = 'up' | 'down' | 'left' | 'right';   // 移动方向类型
1 .2 游戏模式配置
TypeScript 复制代码
interface GameMode {

  name: string;        // 模式名称

  speed: number;       // 移动速度

  gridSize: number;    // 网格大小

  obstacleCount: number; // 障碍物数量

}
1 .3 皮肤系统设计
TypeScript 复制代码
interface Skin {

  name: string;        // 皮肤名称

  snakeHead: string;   // 蛇头颜色

  snakeBody: string;   // 蛇身颜色

  food: string;        // 食物颜色

  foodIcon: string;    // 食物图标

}
1 .4 特殊食物 设计
TypeScript 复制代码
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()方法用于刷新网格中蛇、食物、障碍物的显示。

TypeScript 复制代码
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 根据这些值渲染不同的样式。

TypeScript 复制代码
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防止极端情况下(格子快满)无限循环。

TypeScript 复制代码
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秒 |

TypeScript 复制代码
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显示操作反馈,提升用户体验。

TypeScript 复制代码
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 方法)

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

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

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

TypeScript 复制代码
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'),确保传入的方向合法。

TypeScript 复制代码
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 秒后自动返回开始界面,流程更友好。

TypeScript 复制代码
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. 开始界面设计

TypeScript 复制代码
// 开始界面(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 游戏状态显示区
TypeScript 复制代码
// 顶部信息栏(模式、得分、特殊食物倒计时)

  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 游戏网格渲染
TypeScript 复制代码
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 网格单元格渲染逻辑
TypeScript 复制代码
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 控制区域设计
TypeScript 复制代码
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(障碍物数量)实现难度梯度。

TypeScript 复制代码
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 模式选择实现
TypeScript 复制代码
selectMode(mode: GameMode): void {

  this.selectedMode = mode;

  this.showModeDialog = false;

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

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

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

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

];
2 .2 皮肤应用 实现
TypeScript 复制代码
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. 响应式设计

TypeScript 复制代码
// 自适应图标大小

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

相关推荐
秋刀鱼 ..19 天前
第五届机电一体化、自动化与智能控制国际学术会议(MAIC 2025)
运维·人工智能·python·机器人·自动化·制造·新人首发
weixin_5484442620 天前
JavaScript性能优化终极指南
新人首发
我的世界伊若21 天前
打造极致真实的游戏世界
新人首发
秋刀鱼 ..21 天前
2025机器人与智能制造技术国际会议 (ISRIMT 2025)
人工智能·python·机器人·制造·新人首发
秋刀鱼 ..22 天前
【IEEE出版】第五届高性能计算、大数据与通信工程国际学术会议(ICHBC 2025)
大数据·人工智能·python·机器人·制造·新人首发
秋刀鱼 ..22 天前
第五届计算机、物联网与控制工程国际学术会议(CITCE 2025)
人工智能·python·物联网·机器人·制造·新人首发
我的世界伊若23 天前
30分钟掌握Docker实战技巧
新人首发
我的世界伊若24 天前
VSCode打造高效AI开发全攻略
新人首发