当前位置: 首页 > article >正文

angular贪吃蛇

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

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

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

<!-- 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

/* 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


http://www.kler.cn/a/562138.html

相关文章:

  • mysql 迁移到人大金仓数据库
  • MySQL 面试
  • Plumelog与ELK集成Springboot
  • 探索Grover算法的原理及其实际应用
  • C# Json序列化的常用几种方式
  • ros进阶——强化学习倒立摆的PG算法实现
  • 【Java项目】基于Spring Boot的教师人事档案管理系统
  • Dubbo RPC 原理
  • DeepSeek 助力 Vue 开发:打造丝滑的 复选框(Checkbox)
  • 什么是谷歌富媒体搜索
  • springboot博客系统详解与实现(后端实现)
  • DeepSeek点燃AI大模型战火:编程语言争霸,谁将问鼎“终极武器”王座?
  • axios几种请求类型的格式
  • 企业微信第三方应用开发025_企微通讯录组件使用04_vue中使用ww-open-data通讯录展示组件---企业微信开发027
  • 基于C++“简单且有效”的“数据库连接池”
  • 45.matlab产生正弦叠加信号
  • Spring Boot + Redis 实现分布式锁
  • 安宝特科技 | Vuzix Z100智能眼镜+AugmentOS:重新定义AI可穿戴设备的未来——从操作系统到硬件生态,如何掀起无感智能革命?
  • 飞书webhook监控业务系统端口
  • 基于全志T527+FPGA全国产异步LED显示屏控制卡/屏幕拼接解决方案