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

相关推荐
编程猪猪侠18 分钟前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞22 分钟前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路44 分钟前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到111 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
风清云淡_A1 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js
@大迁世界1 小时前
第7章 React性能优化核心
前端·javascript·react.js·性能优化·前端框架