品牌型网站制作天津seo推广
说明:
vue和angular实现飞机大战
1.用表情符合代替图片资源(火箭是自己战机,黄色是子弹,其他是敌人战机)
2.用鼠标移动飞机左右方向
3.空格键发射子弹
效果图:
step1:C:\Users\wangrusheng\PycharmProjects\untitled3\src\views\Flay.vue
<template><div class="game-container"><canvas ref="canvas" :width="game.SWIDTH" :height="game.SHEIGHT" @mousemove="onMouseMove"></canvas><div v-if="game.gameState === 'menu'" class="menu overlay"><h2>飞机大战</h2><button @click="game.startGame()">开始游戏</button><button @click="exitGame">退出游戏</button></div><div v-if="game.gameState === 'gameover'" class="gameover overlay"><h2>游戏结束! 分数: {{ game.score }}</h2><button @click="game.startGame()">重新开始</button></div></div>
</template><script setup>
import { reactive, ref, onMounted, onUnmounted } from 'vue';// 转换GameService为响应式对象
const game = reactive({ENEMY_EMOJIS: ["🦄","🐺","🐗","🐴","🦋","🐌","🐞","🐝","🦀","🐡","🐬","🦑","🐊","🦓","🐇"],SWIDTH: 600,SHEIGHT: 900,HERO_SIZE: 40,ENEMY_SIZE: 30,BULLET_SPEED: 8,heroX: 300,heroY: 800,bullets: [],enemies: [],score: 0,gameState: 'menu',lastTime: 0,startGame() {this.gameState = 'playing';this.heroX = this.SWIDTH / 2;this.heroY = this.SHEIGHT - 100;this.bullets = [];this.enemies = [];this.score = 0;this.lastTime = 0;this.gameLoop(0);},gameLoop(timestamp) {if (this.gameState !== 'playing') return;const deltaTime = timestamp - this.lastTime;this.lastTime = timestamp;if (Math.random() < 0.05 && this.enemies.length < 10) {this.enemies.push(new Enemy(this.ENEMY_EMOJIS[Math.floor(Math.random() * this.ENEMY_EMOJIS.length)],Math.random() * (this.SWIDTH - this.ENEMY_SIZE),-this.ENEMY_SIZE));}this.bullets = this.bullets.filter(bullet => {bullet.y -= this.BULLET_SPEED;return bullet.y > -this.BULLET_SPEED;});this.enemies = this.enemies.filter(enemy => {enemy.y += 4;const hit = this.bullets.some(bullet =>bullet.x < enemy.x + this.ENEMY_SIZE &&bullet.x + 10 > enemy.x &&bullet.y < enemy.y + this.ENEMY_SIZE &&bullet.y + 20 > enemy.y);if (hit) this.score++;const crash =this.heroX < enemy.x + this.ENEMY_SIZE &&this.heroX + this.HERO_SIZE > enemy.x &&this.heroY < enemy.y + this.ENEMY_SIZE &&this.heroY + this.HERO_SIZE > enemy.y;if (crash) this.gameState = 'gameover';return !hit && enemy.y < this.SHEIGHT && !crash;});requestAnimationFrame((t) => this.gameLoop(t));}
});class Enemy {constructor(emoji, x, y) {this.emoji = emoji;this.x = x;this.y = y;}
}const canvas = ref(null);
let animationFrameId;const onMouseMove = (e) => {if (game.gameState === 'playing') {const rect = canvas.value.getBoundingClientRect();game.heroX = e.clientX - rect.left - game.HERO_SIZE / 2;game.heroY = e.clientY - rect.top - game.HERO_SIZE / 2;keepInBounds();}
};const keepInBounds = () => {game.heroX = Math.max(0, Math.min(game.SWIDTH - game.HERO_SIZE, game.heroX));game.heroY = Math.max(0, Math.min(game.SHEIGHT - game.HERO_SIZE, game.heroY));
};const handleKeyDown = (e) => {if (e.code === 'Space' && game.gameState === 'playing') {game.bullets.push({x: game.heroX + game.HERO_SIZE / 2 - 5,y: game.heroY - 10});}
};const draw = () => {const ctx = canvas.value.getContext('2d');ctx.clearRect(0, 0, game.SWIDTH, game.SHEIGHT);ctx.font = '40px Segoe UI Emoji';ctx.fillText('🚀', game.heroX, game.heroY);ctx.fillStyle = 'gold';game.bullets.forEach(b => ctx.fillRect(b.x, b.y, 10, 20));ctx.fillStyle = 'red';game.enemies.forEach(enemy => {ctx.font = '30px Segoe UI Emoji';ctx.fillText(enemy.emoji, enemy.x, enemy.y);});ctx.fillStyle = 'black';ctx.font = '20px Arial';ctx.fillText(`Score: ${game.score}`, 10, 30);animationFrameId = requestAnimationFrame(draw);
};const exitGame = () => {// 退出逻辑
};onMounted(() => {document.addEventListener('keydown', handleKeyDown);draw();
});onUnmounted(() => {document.removeEventListener('keydown', handleKeyDown);cancelAnimationFrame(animationFrameId);
});
</script><style scoped>
.game-container {position: relative;background: linear-gradient(135deg, #1a1a1a 0%, #4a4a4a 100%);border-radius: 15px;box-shadow: 0 0 20px rgba(0,0,0,0.5);overflow: hidden;margin: 20px;
}canvas {display: block;border-radius: 10px;box-shadow: inset 0 0 10px rgba(255,255,255,0.1);
}.overlay {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);background: rgba(0, 0, 0, 0.8);padding: 2rem;border-radius: 15px;text-align: center;box-shadow: 0 0 20px rgba(0,0,0,0.5);color: white;
}button {background: #4CAF50;border: none;padding: 10px 20px;margin: 10px;border-radius: 5px;color: white;cursor: pointer;transition: background 0.3s;
}button:hover {background: #45a049;
}h2 {margin: 0 0 1rem 0;color: #fff;
}
</style>
手动分割线
以下是angular代码
step101:C:\Users\wangrusheng\PycharmProjects\untitled15\src\app\flay\flay.component.ts
import { Component, HostListener, ViewChild, ElementRef } from '@angular/core';
import { GameService } from './game.service';
import {NgIf} from '@angular/common';
@Component({selector: 'app-flay',imports: [NgIf],template: `<canvas #canvas [width]="game.SWIDTH" [height]="game.SHEIGHT"(mousemove)="onMouseMove($event)"></canvas><div *ngIf="game.gameState === 'menu'" class="menu"><h2>飞机大战</h2><button (click)="game.startGame()">开始游戏</button><button (click)="exitGame()">退出游戏</button></div><div *ngIf="game.gameState === 'gameover'" class="gameover"><h2>游戏结束! 分数: {{game.score}}</h2><button (click)="game.startGame()">重新开始</button></div>`,styles: [`canvas { border: 1px solid #ccc; }.menu, .gameover {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);text-align: center;}`]
})export class FlayComponent {@ViewChild('canvas') canvas!: ElementRef<HTMLCanvasElement>;constructor(public game: GameService) {}ngAfterViewInit() {this.draw();}onMouseMove(e: MouseEvent) {if (this.game.gameState === 'playing') {const rect = this.canvas.nativeElement.getBoundingClientRect();this.game.heroX = e.clientX - rect.left - this.game.HERO_SIZE/2;this.game.heroY = e.clientY - rect.top - this.game.HERO_SIZE/2;this.keepInBounds();}}@HostListener('document:keydown', ['$event'])onKeyDown(e: KeyboardEvent) {if (e.code === 'Space' && this.game.gameState === 'playing') {this.game.bullets.push({x: this.game.heroX + this.game.HERO_SIZE/2 - 5,y: this.game.heroY - 10});}}private keepInBounds() {this.game.heroX = Math.max(0, Math.min(this.game.SWIDTH - this.game.HERO_SIZE,this.game.heroX));this.game.heroY = Math.max(0, Math.min(this.game.SHEIGHT - this.game.HERO_SIZE,this.game.heroY));}private draw() {requestAnimationFrame(() => this.draw());const ctx = this.canvas.nativeElement.getContext('2d')!;ctx.clearRect(0, 0, this.game.SWIDTH, this.game.SHEIGHT);// 绘制玩家ctx.font = '40px Segoe UI Emoji';ctx.fillText('🚀', this.game.heroX, this.game.heroY);// 绘制子弹ctx.fillStyle = 'gold';this.game.bullets.forEach(b => ctx.fillRect(b.x, b.y, 10, 20));// 绘制敌人ctx.fillStyle = 'red';this.game.enemies.forEach(enemy => {ctx.font = '30px Segoe UI Emoji';ctx.fillText(enemy.emoji, enemy.x, enemy.y);});// 绘制分数ctx.fillStyle = 'black';ctx.font = '20px Arial';ctx.fillText(`Score: ${this.game.score}`, 10, 30);}exitGame() {// 退出逻辑(根据实际需求实现)}
}
step102:C:\Users\wangrusheng\PycharmProjects\untitled15\src\app\flay\game.service.ts
// game.service.ts
import { Injectable } from '@angular/core';@Injectable({ providedIn: 'root' })
export class GameService {readonly ENEMY_EMOJIS = ["🦄","🐺","🐗","🐴","🦋","🐌","🐞","🐝","🦀","🐡","🐬","🦑","🐊","🦓","🐇"];readonly SWIDTH = 600;readonly SHEIGHT = 900;readonly HERO_SIZE = 40;readonly ENEMY_SIZE = 30;readonly BULLET_SPEED = 8;heroX = this.SWIDTH / 2;heroY = this.SHEIGHT - 100;bullets: { x: number, y: number }[] = [];enemies: Enemy[] = [];score = 0;gameState: 'menu' | 'playing' | 'gameover' = 'menu';private lastTime = 0;startGame() {this.gameState = 'playing';this.heroX = this.SWIDTH / 2;this.heroY = this.SHEIGHT - 100;this.bullets = [];this.enemies = [];this.score = 0;this.gameLoop(0);}private gameLoop(timestamp: number) {if (this.gameState !== 'playing') return;const deltaTime = timestamp - this.lastTime;this.lastTime = timestamp;// 生成敌人if (Math.random() < 0.05 && this.enemies.length < 10) {this.enemies.push(new Enemy(this.ENEMY_EMOJIS[Math.floor(Math.random() * this.ENEMY_EMOJIS.length)],Math.random() * (this.SWIDTH - this.ENEMY_SIZE),-this.ENEMY_SIZE));}// 更新子弹this.bullets = this.bullets.filter(bullet => {bullet.y -= this.BULLET_SPEED;return bullet.y > -this.BULLET_SPEED;});// 更新敌人并检测碰撞this.enemies = this.enemies.filter(enemy => {enemy.y += 4;// 子弹碰撞const hit = this.bullets.some(bullet =>bullet.x < enemy.x + this.ENEMY_SIZE &&bullet.x + 10 > enemy.x &&bullet.y < enemy.y + this.ENEMY_SIZE &&bullet.y + 20 > enemy.y);if (hit) this.score++;// 玩家碰撞const crash =this.heroX < enemy.x + this.ENEMY_SIZE &&this.heroX + this.HERO_SIZE > enemy.x &&this.heroY < enemy.y + this.ENEMY_SIZE &&this.heroY + this.HERO_SIZE > enemy.y;if (crash) this.gameState = 'gameover';return !hit && enemy.y < this.SHEIGHT && !crash;});requestAnimationFrame((t) => this.gameLoop(t));}
}class Enemy {constructor(public emoji: string,public x: number,public y: number) {}
}
end