Angular 17贪吃蛇游戏问题:蛇与水果无法在棋盘显示
Angular 17 贪吃蛇游戏:蛇与水果无法显示问题解决
问题描述
我正在开发贪吃蛇游戏的初始阶段,需完成以下步骤:
- 创建游戏棋盘
- 创建蛇和水果
- 在棋盘上显示蛇和水果
当前问题:蛇和水果生成后,棋盘数据已更新,但它们并未显示在游戏棋盘上。
说明:棋盘单元格中0代表空,1代表蛇身,2代表水果。
相关代码
HTML(gameboard组件)
<div class="gameB"> <div *ngFor="let i of board" class="row"> <div *ngFor="let j of i" class="cell" [ngClass]="{'snake' : board[board.indexOf(i)][j] === 1, 'fruit': board[board.indexOf(i)][j] === 2}"> {{ board[board.indexOf(i)][j] }} </div> </div> </div>
TypeScript(gameboard组件)
import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { Snake } from '../snake-iterface'; @Component({ selector: 'app-game-board', standalone: true, imports: [CommonModule], templateUrl: './game-board.component.html', styleUrl: './game-board.component.css' }) export class GameBoardComponent implements OnInit{ boardWidth:number = 20; boardHeight:number = 20; board:number[][] = []; snake:Snake = {} as Snake; fruit:number[] = [1,1]; createBoard(){ let temp:number[]; for(let i = 0; i < this.boardHeight;i++){ temp =[] for(let j = 0; j < this.boardWidth;j++){ temp.push(0) } this.board.push(temp) } } spawnSnake():void{ this.board[this.snake.headPositionY][this.snake.headPositionX] = 1; } updateBoard():void{ for(let i = 0; i < this.boardHeight; i++){ for(let j = 0; j < this.boardWidth;j++){ if(this.snake.bodyPositions.includes([j, i]) ){ this.board[i][j] = 1; }else if((j === this.fruit[0] && i == this.fruit[1])){ this.board[i][j] = 2; } } } } ngOnInit(): void { this.createBoard() console.log("Before Snake creation", this.board) this.snake = { headPositionX: Math.floor(this.boardWidth / 2), headPositionY: Math.floor(this.boardHeight / 2), bodyPositions: [[Math.floor(this.boardWidth / 2), Math.floor(this.boardHeight / 2)]], length: 1, direction: 'right' }; console.log("Before Spawn", this.board) this.spawnSnake() this.updateBoard() console.log("After Update 1", this.board) setTimeout(this.updateBoard, 1000) console.log("After Update 2", this.board) } }
CSS(gameboard组件)
.gameB { margin: auto; padding:10px; display: flex; flex-direction: column; gap: 3px; background-color: black; width:fit-content; } .row { display: flex; flex-direction: row; gap: 3px; } .cell { width:20px; height:20px; background-color: grey; } .snake { background-color:red; } .fruit { background-color:purple; }
其他文件
HTML(app组件)
<app-game-board></app-game-board>
HTML(index.html)
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>SnakeGame</title> <base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="favicon.ico"> </head> <body> <app-root></app-root> </body> </html>
问题分析
- HTML模板逻辑错误:
*ngFor遍历中,j是数组元素而非索引,board[board.indexOf(i)][j]的取值逻辑完全错误,无法正确获取单元格的数值。 - 数组引用判断失效:
this.snake.bodyPositions.includes([j, i])中,数组是引用类型,两个内容相同的数组引用不同,includes会返回false,导致蛇身无法被正确标记。 setTimeout的this指向问题:直接传递this.updateBoard给setTimeout,会导致方法内的this指向全局对象而非组件实例,无法访问snake、board等属性。- 控制台日志的误导:数组是引用类型,
console.log存储的是数组引用,会显示后续修改后的状态,而非打印时的真实状态,所以看起来蛇和水果提前出现在棋盘中。
解决方案
1. 修正HTML模板
使用*ngFor的索引变量来正确访问棋盘数据:
<div class="gameB"> <div *ngFor="let row of board; let i = index" class="row"> <div *ngFor="let cell of row; let j = index" class="cell" [ngClass]="{'snake' : board[i][j] === 1, 'fruit': board[i][j] === 2}"> {{ board[i][j] }} </div> </div> </div>
2. 修复updateBoard方法
重写方法逻辑,先清空棋盘再重新设置蛇和水果,避免引用判断错误:
updateBoard():void{ // 先清空棋盘,清除旧数据 for(let i = 0; i < this.boardHeight; i++){ for(let j = 0; j < this.boardWidth;j++){ this.board[i][j] = 0; } } // 设置蛇身 this.snake.bodyPositions.forEach(pos => { const [x, y] = pos; this.board[y][x] = 1; }); // 设置水果 const [fruitX, fruitY] = this.fruit; this.board[fruitY][fruitX] = 2; }
3. 修正setTimeout的this指向
使用箭头函数绑定组件实例的this:
// 替换原来的setTimeout setTimeout(() => this.updateBoard(), 1000)
4. 优化控制台日志
使用深拷贝打印数组的实时状态,避免引用误导:
ngOnInit(): void { this.createBoard() // 用深拷贝打印当前状态 console.log("Before Snake creation", JSON.parse(JSON.stringify(this.board))) this.snake = { headPositionX: Math.floor(this.boardWidth / 2), headPositionY: Math.floor(this.boardHeight / 2), bodyPositions: [[Math.floor(this.boardWidth / 2), Math.floor(this.boardHeight / 2)]], length: 1, direction: 'right' }; console.log("Before Update", JSON.parse(JSON.stringify(this.board))) // 移除spawnSnake,因为updateBoard已经会设置蛇身 this.updateBoard() console.log("After Update 1", JSON.parse(JSON.stringify(this.board))) setTimeout(() => this.updateBoard(), 1000) }
可选:移除冗余的spawnSnake方法
由于updateBoard已经负责设置蛇身,spawnSnake方法重复操作,可以直接删除。
补充说明
- 控制台日志的“提前显示蛇和水果”是因为数组引用特性,
console.log会动态更新显示数组的最新状态,用JSON.parse(JSON.stringify())深拷贝后可以打印出日志执行时的真实状态。 - 所有修改完成后,蛇和水果会正确渲染到棋盘上,后续可以继续开发移动、碰撞检测等功能。
内容的提问来源于stack exchange,提问作者UnPatched Main
相关产品推荐
相关产品推荐

