angular贪吃蛇

说明:我计划用angular实现一个贪吃蛇的程序,并且有方向键去控制蛇的上下左右的移动,并且有得分系统,当蛇撞到墙壁或者自身,游戏结束

效果图:

step1: C:\Users\Administrator\WebstormProjects\untitled4\src\app\snake\snake.component.ts

javascript 复制代码
import { Component , OnInit, OnDestroy, HostListener } from '@angular/core';


interface Position {
  x: number;
  y: number;
}

@Component({
  selector: 'app-snake',
  imports: [],
  templateUrl: './snake.component.html',
  styleUrl: './snake.component.css'
})
export class SnakeComponent implements OnInit, OnDestroy{
  private ctx!: CanvasRenderingContext2D;
  private gameLoop!: any;
  private gridSize = 20;

  snake: Position[] = [{ x: 10, y: 10 }];
  food: Position = this.generateFood();
  direction: 'UP' | 'DOWN' | 'LEFT' | 'RIGHT' = 'RIGHT';
  score = 0;
  gameSpeed = 150;
  isPaused = false;

  @HostListener('window:keydown', ['$event'])
  handleKeydown(event: KeyboardEvent) {
    this.handleDirection(event.key.replace('Arrow', '').toUpperCase());
  }

  ngOnInit() {
    const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
    this.ctx = canvas.getContext('2d')!;
    this.startGame();
  }

  ngOnDestroy() {
    clearInterval(this.gameLoop);
  }

  handleDirection(newDirection: string) {
    const validMoves: Record<string, boolean> = {
      'UP': this.direction !== 'DOWN',
      'DOWN': this.direction !== 'UP',
      'LEFT': this.direction !== 'RIGHT',
      'RIGHT': this.direction !== 'LEFT'
    };

    if (validMoves[newDirection]) {
      this.direction = newDirection as any;
    }
  }

  private startGame() {
    this.gameLoop = setInterval(() => this.update(), this.gameSpeed);
  }

  private update() {
    if (this.isPaused) return;

    const head = { ...this.snake[0] };
    switch (this.direction) {
      case 'UP': head.y--; break;
      case 'DOWN': head.y++; break;
      case 'LEFT': head.x--; break;
      case 'RIGHT': head.x++; break;
    }

    if (this.checkCollision(head)) {
      clearInterval(this.gameLoop);
      alert(`游戏结束! 得分: ${this.score}`);
      return;
    }

    this.snake.unshift(head);

    if (head.x === this.food.x && head.y === this.food.y) {
      this.score++;
      this.food = this.generateFood();
      this.gameSpeed = Math.max(50, this.gameSpeed - 2);
    } else {
      this.snake.pop();
    }

    this.draw();
  }

  private draw() {
    // 游戏区域背景
    this.ctx.fillStyle = '#1a1b26';
    this.ctx.fillRect(0, 0, 400, 400);

    // 食物绘制
    this.ctx.fillStyle = '#f7768e';
    this.ctx.beginPath();
    this.ctx.roundRect(
      this.food.x * this.gridSize + 2,
      this.food.y * this.gridSize + 2,
      this.gridSize - 4,
      this.gridSize - 4,
      4
    );
    this.ctx.fill();

    // 蛇身绘制
    this.snake.forEach((segment, index) => {
      this.ctx.fillStyle = index === 0 ? '#9ece6a' : '#73daca';
      this.ctx.beginPath();
      this.ctx.roundRect(
        segment.x * this.gridSize + 2,
        segment.y * this.gridSize + 2,
        this.gridSize - 4,
        this.gridSize - 4,
        index === 0 ? 6 : 4
      );
      this.ctx.fill();
    });
  }

  private generateFood(): Position {
    return {
      x: Math.floor(Math.random() * 20),
      y: Math.floor(Math.random() * 20)
    };
  }

  private checkCollision(pos: Position): boolean {
    return pos.x < 0 || pos.x >= 20 || pos.y < 0 || pos.y >= 20 ||
      this.snake.some(segment => segment.x === pos.x && segment.y === pos.y);
  }

  togglePause() {
    this.isPaused = !this.isPaused;
  }

  resetGame() {
    this.snake = [{ x: 10, y: 10 }];
    this.direction = 'RIGHT';
    this.score = 0;
    this.gameSpeed = 150;
    this.food = this.generateFood();
    this.isPaused = false;
    clearInterval(this.gameLoop);
    this.startGame();
  }
}

step2: C:\Users\Administrator\WebstormProjects\untitled4\src\app\snake\snake.component.html

xml 复制代码
<!-- snake.component.html -->
<div class="game-container">
  <div class="game-header">
    <div class="score-box">
      <span>得分: {{ score }}</span>
    </div>
    <div class="controls">
      <button class="control-btn pause-btn" (click)="togglePause()">
        {{ isPaused ? '继续' : '暂停' }}
      </button>
      <button class="control-btn reset-btn" (click)="resetGame()">重置</button>
    </div>
  </div>

  <canvas id="gameCanvas" width="400" height="400"></canvas>

  <div class="direction-controls">
    <div class="control-row">
      <button class="direction-btn up" (click)="handleDirection('UP')">↑</button>
    </div>
    <div class="control-row">
      <button class="direction-btn left" (click)="handleDirection('LEFT')">←</button>
      <button class="direction-btn down" (click)="handleDirection('DOWN')">↓</button>
      <button class="direction-btn right" (click)="handleDirection('RIGHT')">→</button>
    </div>
  </div>
</div>

step3: C:\Users\Administrator\WebstormProjects\untitled4\src\app\snake\snake.component.css

css 复制代码
/* snake.component.css */
.game-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.5rem;
  padding: 2rem;
  background: #24283b;
  border-radius: 1.5rem;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}

.game-header {
  display: flex;
  justify-content: space-between;
  width: 100%;
  max-width: 400px;
  color: #c0caf5;
}

.score-box {
  background: #414868;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  font-size: 1.1rem;
}

.controls {
  display: flex;
  gap: 0.5rem;
}

.control-btn {
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 0.5rem;
  cursor: pointer;
  transition: transform 0.2s, opacity 0.2s;
}

.pause-btn {
  background: linear-gradient(135deg, #7aa2f7, #2ac3de);
  color: white;
}

.reset-btn {
  background: linear-gradient(135deg, #f7768e, #ff9e64);
  color: white;
}

.control-btn:hover {
  opacity: 0.9;
  transform: translateY(-1px);
}

.direction-controls {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.control-row {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
}

.direction-btn {
  width: 3.5rem;
  height: 3.5rem;
  border: none;
  border-radius: 1rem;
  background: linear-gradient(145deg, #414868, #565f89);
  color: #c0caf5;
  font-size: 1.5rem;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.direction-btn:hover {
  background: linear-gradient(145deg, #565f89, #414868);
  transform: scale(1.05);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.direction-btn:active {
  transform: scale(0.95);
}

/* 移动端优化 */
@media (max-width: 480px) {
  .game-container {
    padding: 1rem;
    width: 95vw;
  }

  canvas {
    width: 95vw;
    height: 95vw;
  }

  .direction-btn {
    width: 3rem;
    height: 3rem;
    font-size: 1.2rem;
  }
}

end

相关推荐
子兮曰14 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰14 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万14 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝14 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋15 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁16 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄16 小时前
JavaScript 隐式全局变量解析
javascript
汉堡大王952717 小时前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大17 小时前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师18 小时前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端