angular贪吃蛇

news/2025/2/26 5:55:58

说明:我计划用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

<!-- 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.niftyadmin.cn/n/5868117.html

相关文章

前缀和代码解析

前缀和是指数组一定范围的数的总和,常见的有两种,一维和二维,我会用两道题来分别解析 一维 DP34 【模板】前缀和 题目: 题目解析: 暴力解法 直接遍历数组,遍历到下标为 l 时,开始进行相加,直到遍历到下标为 r ,最后返回总和.这样做的时间复杂度为: O(n) public class Main …

[字节青训_AI对话框]SSE交互规范、自定义事件、前后端数据传递、状态监听、连接和断开详解

1.SSE基础 以下是关于 Server-Sent Events (SSE) 的前后端交互规范、常见方法及自定义扩展的完整指南: 一、SSE 交互规范 1. 基础协议 HTTP 协议:基于 HTTP/1.1 长连接,响应头需包含:Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive2. 数…

deepseek部署:ELK + Filebeat + Zookeeper + Kafka

## 1. 概述 本文档旨在指导如何在7台机器上部署ELK&#xff08;Elasticsearch, Logstash, Kibana&#xff09;堆栈、Filebeat、Zookeeper和Kafka。该部署方案适用于日志收集、处理和可视化场景。 ## 2. 环境准备 ### 2.1 机器分配 | 机器编号 | 主机名 | IP地址 | 部署组件 |-…

WIFI的SSID超长,隐藏,重复 (2.4G和5G差异)

目录 1、2.4G和5G的频率范围‌ 2、2.4G和5G的差异‌&#xff1a; 3、隐藏ssid显示为\x00 4、 重复的ssid名称 扩展 前言 最近处理wifi设备时发现&#xff0c;小小一个ssid就有超多的问题。 不是中文转义就是超长&#xff0c;现在还发现空字符的&#xff0c;原来时对方路由隐藏了…

【实战 ES】实战 Elasticsearch:快速上手与深度实践-1.1.1对比传统数据库与搜索引擎(MySQL vs ES)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 为什么选择Elasticsearch&#xff1f;——从MySQL到Elasticsearch的深度对比引言一、核心概念对比1. 数据模型差异2. 查询语言对比 二、适用场景对比1. MySQL的典型场景2. E…

CPU多级缓存机制

目录 一、前置知识 ---- CPU的核心 1.1. 单核与多核CPU 二、CPU多级缓存机制 三. 缓存的基本结构/缓存的存储结构 四、CPU缓存的运作流程/工作原理 五、CPU多级缓存机制的工作原理【简化版】 5.1. 缓存访问的过程 (5.1.1) L1缓存&#xff08;一级缓存&#xff09;访问 …

Nginx的安装和部署以及Nginx的反向代理与负载均衡

Nginx的安装和部署以及Nginx的反向代理与负载均衡 1. 本文内容 Nginx的安装Nginx的静态网站部署Nginx的反向代理与负载均衡&#xff0c;配置反向代理与负载均衡 2. Nginx的安装与启动 2.1 什么是Nginx Nginx是一款高性能的http服务器/反向代理服务器及电子邮件&#xff08…

Room记录搜索记录逻辑思路

记录数据使用ROOM&#xff0c;传递使用ViewModel LiveDataBus,这篇文章主要记录 搜索记录 本地 线上&#xff0c;上传失败&#xff0c;记录本地&#xff0c;网络回复统一上传等逻辑的操作。 目录 首先是设计数据表&#xff1a; 定义DAO操作接口 定义数据库类 Mvvm 模式中封…