|
@@ -1,9 +1,881 @@
|
|
<template>
|
|
<template>
|
|
|
|
+ <div class="game-container">
|
|
|
|
+ <div id="gameCanvas" class="game-canvas"></div>
|
|
|
|
|
|
|
|
+ <!-- 游戏启动界面 -->
|
|
|
|
+ <div v-if="currentScene === 'start'" class="gamestart">
|
|
|
|
+ <img src="/static/images/football/game_start.jpg" class="start_bg" />
|
|
|
|
+ <div class="btn rule" @click="showRules = true">查看规则</div>
|
|
|
|
+ <div class="btn start" @click="startGame">开始游戏</div>
|
|
|
|
+
|
|
|
|
+ <!-- 规则弹窗 -->
|
|
|
|
+ <div v-if="showRules" class="ruleshadow">
|
|
|
|
+ <div class="rulebox">
|
|
|
|
+ <span class="x_rulebox" @click="showRules = false"></span>
|
|
|
|
+ <h3>操作方法:</h3>
|
|
|
|
+ <p>手指拖动控制人物带球奔跑的方向和速度。</p>
|
|
|
|
+ <h3>道具说明:</h3>
|
|
|
|
+ <div class="daoju">
|
|
|
|
+ <div class="daoju_item">
|
|
|
|
+ <span class="daoju_1"></span>
|
|
|
|
+ <p>每收集一个,生命值+1;触碰障碍物,生命可再复活。</p>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="daoju_item">
|
|
|
|
+ <span class="daoju_2"></span>
|
|
|
|
+ <p>每收集一个,能力值+1;扫除面前一切障碍,加速前进。</p>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="daoju_item">
|
|
|
|
+ <span class="daoju_3"></span>
|
|
|
|
+ <p>触碰锥桶,生命值-1;成功绕过一次分数+1。</p>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 规则说明 -->
|
|
|
|
+ <div v-if="currentScene === 'rule'" class="zhiyin">
|
|
|
|
+ <div class="btn start" @click="continueGame">继续游戏</div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 倒计时 -->
|
|
|
|
+ <div v-if="currentScene === 'countdown'" class="daoju">
|
|
|
|
+ <div class="daojishibox">
|
|
|
|
+ <div class="daojishinum">{{ countdownNum }}</div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!-- 游戏结束 -->
|
|
|
|
+ <div v-if="currentScene === 'gameover'" class="gameend">
|
|
|
|
+ <div class="game_fenshu">
|
|
|
|
+ <span>{{ score }}</span>分
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="chenghao">恭喜你获得称号<p>{{ getTitle(score) }}</p>
|
|
|
|
+ </div>
|
|
|
|
+ <div class="game_ogain" @click="restartGame">再次游戏</div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
-<script setup name="Football" lang="ts">
|
|
|
|
|
|
+<script setup>
|
|
|
|
+import { ref, onMounted, watch } from 'vue';
|
|
|
|
+import Phaser from 'phaser';
|
|
|
|
+
|
|
|
|
+// 游戏状态管理
|
|
|
|
+const currentScene = ref('start');
|
|
|
|
+const showRules = ref(false);
|
|
|
|
+const countdownNum = ref(3);
|
|
|
|
+const score = ref(0);
|
|
|
|
+const game = ref(null);
|
|
|
|
+const gameConfig = ref(null);
|
|
|
|
+
|
|
|
|
+// 游戏常量
|
|
|
|
+const GAME_WIDTH = 320;
|
|
|
|
+const GAME_HEIGHT = 480;
|
|
|
|
+
|
|
|
|
+// 游戏资源
|
|
|
|
+const gameAssets = {
|
|
|
|
+ images: [
|
|
|
|
+ { key: 'gameStart', url: '/static/images/football/game_start.jpg' },
|
|
|
|
+ { key: 'grass', url: '/static/images/football/Caopi.png' },
|
|
|
|
+ { key: 'player', url: '/static/images/football/Qiuyuan.png' },
|
|
|
|
+ { key: 'playerSuper', url: '/static/images/football/QiuyuanSuper.png' },
|
|
|
|
+ { key: 'playerShoot', url: '/static/images/football/QiuyuanShooting.png' },
|
|
|
|
+ { key: 'playerCollision', url: '/static/images/football/QiuyuanCollision.png' },
|
|
|
|
+ { key: 'pile', url: '/static/images/football/Pile.png' },
|
|
|
|
+ { key: 'jersey', url: '/static/images/football/Jersey.png' },
|
|
|
|
+ { key: 'broom', url: '/static/images/football/Broom.png' },
|
|
|
|
+ { key: 'goalkeeper', url: '/static/images/football/Goalkeeper.png' },
|
|
|
|
+ { key: 'goal', url: '/static/images/football/Goal.png' },
|
|
|
|
+ { key: 'ball', url: '/static/images/football/Ball.png' },
|
|
|
|
+ { key: 'line', url: '/static/images/football/Line.png' },
|
|
|
|
+ { key: 'line2', url: '/static/images/football/Line2.png' },
|
|
|
|
+ { key: 'goalBackground', url: '/static/images/football/GoalBackGround.png' },
|
|
|
|
+ { key: 'gameOver', url: '/static/images/football/Gameover.png' }
|
|
|
|
+ ],
|
|
|
|
+ spritesheets: [
|
|
|
|
+ {
|
|
|
|
+ key: 'playerAnim',
|
|
|
|
+ url: '/static/images/football/Qiuyuan.png',
|
|
|
|
+ frameWidth: 92.5,
|
|
|
|
+ frameHeight: 92.5
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ key: 'ballAnim',
|
|
|
|
+ url: '/static/images/football/Ball.png',
|
|
|
|
+ frameWidth: 31,
|
|
|
|
+ frameHeight: 31
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ key: 'goalkeeperAnim',
|
|
|
|
+ url: '/static/images/football/Goalkeeper.png',
|
|
|
|
+ frameWidth: 55,
|
|
|
|
+ frameHeight: 56
|
|
|
|
+ },
|
|
|
|
+ ]
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 初始化Phaser游戏
|
|
|
|
+const initGame = () => {
|
|
|
|
+ // 游戏配置
|
|
|
|
+ gameConfig.value = {
|
|
|
|
+ type: Phaser.AUTO,
|
|
|
|
+ width: GAME_WIDTH,
|
|
|
|
+ height: GAME_HEIGHT,
|
|
|
|
+ parent: 'gameCanvas',
|
|
|
|
+ physics: {
|
|
|
|
+ default: 'arcade',
|
|
|
|
+ arcade: {
|
|
|
|
+ gravity: { y: 0 },
|
|
|
|
+ debug: false
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ scene: [
|
|
|
|
+ PreloaderScene,
|
|
|
|
+ GameScene
|
|
|
|
+ ]
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ game.value = new Phaser.Game(gameConfig.value);
|
|
|
|
+
|
|
|
|
+ // 监听游戏事件
|
|
|
|
+ game.value.events.on('scoreChanged', (newScore) => {
|
|
|
|
+ score.value = newScore;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ game.value.events.on('gameOver', () => {
|
|
|
|
+ currentScene.value = 'gameover';
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 预加载场景
|
|
|
|
+class PreloaderScene extends Phaser.Scene {
|
|
|
|
+ constructor() {
|
|
|
|
+ super('PreloaderScene');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ preload() {
|
|
|
|
+ // 加载图片资源
|
|
|
|
+ gameAssets.images.forEach(asset => {
|
|
|
|
+ this.load.image(asset.key, asset.url);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 加载精灵表
|
|
|
|
+ gameAssets.spritesheets.forEach(asset => {
|
|
|
|
+ this.load.spritesheet(asset.key, asset.url, {
|
|
|
|
+ frameWidth: asset.frameWidth,
|
|
|
|
+ frameHeight: asset.frameHeight
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 显示加载进度
|
|
|
|
+ const progressBar = this.add.graphics();
|
|
|
|
+ const progressBox = this.add.graphics();
|
|
|
|
+ progressBox.fillStyle(0x222222, 0.8);
|
|
|
|
+ progressBox.fillRect(GAME_WIDTH / 2 - 160, GAME_HEIGHT / 2 - 25, 320, 50);
|
|
|
|
+
|
|
|
|
+ const loadingText = this.make.text({
|
|
|
|
+ x: GAME_WIDTH / 2,
|
|
|
|
+ y: GAME_HEIGHT / 2 - 50,
|
|
|
|
+ text: 'Loading...',
|
|
|
|
+ style: {
|
|
|
|
+ font: '20px monospace',
|
|
|
|
+ fill: '#ffffff'
|
|
|
|
+ }
|
|
|
|
+ }).setOrigin(0.5, 0.5);
|
|
|
|
+
|
|
|
|
+ const percentText = this.make.text({
|
|
|
|
+ x: GAME_WIDTH / 2,
|
|
|
|
+ y: GAME_HEIGHT / 2,
|
|
|
|
+ text: '0%',
|
|
|
|
+ style: {
|
|
|
|
+ font: '18px monospace',
|
|
|
|
+ fill: '#ffffff'
|
|
|
|
+ }
|
|
|
|
+ }).setOrigin(0.5, 0.5);
|
|
|
|
+
|
|
|
|
+ this.load.on('progress', (value) => {
|
|
|
|
+ percentText.setText(`${Math.round(value * 100)}%`);
|
|
|
|
+ progressBar.clear();
|
|
|
|
+ progressBar.fillStyle(0xffffff, 1);
|
|
|
|
+ progressBar.fillRect(GAME_WIDTH / 2 - 150, GAME_HEIGHT / 2 - 15, 300 * value, 30);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ this.load.on('complete', () => {
|
|
|
|
+ progressBar.destroy();
|
|
|
|
+ progressBox.destroy();
|
|
|
|
+ loadingText.destroy();
|
|
|
|
+ percentText.destroy();
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ create() {
|
|
|
|
+ // 初始化动画
|
|
|
|
+ this.initAnimations();
|
|
|
|
+ // 切换到游戏场景
|
|
|
|
+ this.scene.start('GameScene');
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ initAnimations() {
|
|
|
|
+ // 球员动画
|
|
|
|
+ this.anims.create({
|
|
|
|
+ key: 'playerLeft',
|
|
|
|
+ frames: this.anims.generateFrameNumbers('playerAnim', { start: 0, end: 3 }),
|
|
|
|
+ frameRate: 10,
|
|
|
|
+ repeat: -1
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ this.anims.create({
|
|
|
|
+ key: 'playerRight',
|
|
|
|
+ frames: this.anims.generateFrameNumbers('playerAnim', { start: 4, end: 7 }),
|
|
|
|
+ frameRate: 10,
|
|
|
|
+ repeat: -1
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 足球动画
|
|
|
|
+ this.anims.create({
|
|
|
|
+ key: 'ballAnim',
|
|
|
|
+ frames: this.anims.generateFrameNumbers('ballAnim', { start: 0, end: 1 }),
|
|
|
|
+ frameRate: 5,
|
|
|
|
+ repeat: -1
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 守门员动画
|
|
|
|
+ this.anims.create({
|
|
|
|
+ key: 'goalkeeperAnim',
|
|
|
|
+ frames: this.anims.generateFrameNumbers('goalkeeperAnim', { start: 0, end: 1 }),
|
|
|
|
+ frameRate: 3,
|
|
|
|
+ repeat: -1
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 游戏主场景
|
|
|
|
+class GameScene extends Phaser.Scene {
|
|
|
|
+ constructor() {
|
|
|
|
+ super('GameScene');
|
|
|
|
+ this.player = null;
|
|
|
|
+ this.score = 0;
|
|
|
|
+ this.lives = 3;
|
|
|
|
+ this.maxLives = 5;
|
|
|
|
+ this.speed = 6;
|
|
|
|
+ this.acceleration = 1.9;
|
|
|
|
+ this.level = 1;
|
|
|
|
+ this.obstacles = [];
|
|
|
|
+ this.powerUps = [];
|
|
|
|
+ this.isSuper = false;
|
|
|
|
+ this.gameActive = false;
|
|
|
|
+ this.timer = null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ create() {
|
|
|
|
+ // 创建背景
|
|
|
|
+ this.createBackground();
|
|
|
|
+
|
|
|
|
+ // 创建玩家
|
|
|
|
+ this.createPlayer();
|
|
|
|
+
|
|
|
|
+ // 创建计分板
|
|
|
|
+ this.createHUD();
|
|
|
|
+
|
|
|
|
+ // 输入控制
|
|
|
|
+ this.initControls();
|
|
|
|
+
|
|
|
|
+ // 游戏事件
|
|
|
|
+ this.events.on('resume', () => {
|
|
|
|
+ this.gameActive = true;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 等待开始游戏信号
|
|
|
|
+ this.gameActive = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ createBackground() {
|
|
|
|
+ // 创建滚动背景
|
|
|
|
+ this.background = this.add.tileSprite(0, 0, GAME_WIDTH, GAME_HEIGHT, 'grass');
|
|
|
|
+ this.background.setOrigin(0, 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ createPlayer() {
|
|
|
|
+ // 创建玩家
|
|
|
|
+ this.player = this.physics.add.sprite(GAME_WIDTH / 2, GAME_HEIGHT - 100, 'playerAnim');
|
|
|
|
+ this.player.setCollideWorldBounds(true);
|
|
|
|
+ this.player.setScale(0.8);
|
|
|
|
+ this.player.lives = this.lives;
|
|
|
|
+ this.player.setDepth(9);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ createHUD() {
|
|
|
|
+ // 分数显示
|
|
|
|
+ this.scoreText = this.add.text(10, 10, `分数: ${this.score}`, {
|
|
|
|
+ fontSize: '16px',
|
|
|
|
+ fill: '#ffffff',
|
|
|
|
+ backgroundColor: 'rgba(0,0,0,0.5)',
|
|
|
|
+ padding: { x: 5, y: 2 }
|
|
|
|
+ });
|
|
|
|
+ this.scoreText.setDepth(10);
|
|
|
|
+
|
|
|
|
+ // 生命值显示
|
|
|
|
+ this.livesText = this.add.text(GAME_WIDTH - 80, 10, `生命: ${this.lives}`, {
|
|
|
|
+ fontSize: '16px',
|
|
|
|
+ fill: '#ffffff',
|
|
|
|
+ backgroundColor: 'rgba(0,0,0,0.5)',
|
|
|
|
+ padding: { x: 5, y: 2 }
|
|
|
|
+ });
|
|
|
|
+ this.livesText.setDepth(10);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ initControls() {
|
|
|
|
+ // 键盘控制
|
|
|
|
+ this.cursors = this.input.keyboard.createCursorKeys();
|
|
|
|
+
|
|
|
|
+ // 触摸控制
|
|
|
|
+ this.input.on('pointermove', (pointer) => {
|
|
|
|
+ if (this.gameActive && pointer.isDown) {
|
|
|
|
+ this.player.x = Phaser.Math.Clamp(pointer.x, this.player.width / 2, GAME_WIDTH - this.player.width / 2);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ startGame() {
|
|
|
|
+ this.gameActive = true;
|
|
|
|
+ this.score = 0;
|
|
|
|
+ this.updateScore();
|
|
|
|
+
|
|
|
|
+ // 开始生成障碍物和道具
|
|
|
|
+ this.startSpawning();
|
|
|
|
+
|
|
|
|
+ // 开始计时计分
|
|
|
|
+ this.timer = this.time.addEvent({
|
|
|
|
+ delay: 200,
|
|
|
|
+ callback: () => {
|
|
|
|
+ this.score++;
|
|
|
|
+ this.updateScore();
|
|
|
|
+
|
|
|
|
+ // 每200分升级一次
|
|
|
|
+ if (this.score % 200 === 0) {
|
|
|
|
+ this.levelUp();
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ loop: true
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ startSpawning() {
|
|
|
|
+ // 生成障碍物
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 1000 / this.level,
|
|
|
|
+ callback: () => {
|
|
|
|
+ if (this.gameActive) {
|
|
|
|
+ this.spawnObstacle();
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ loop: true
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 生成生命值道具
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 5000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ if (this.gameActive) {
|
|
|
|
+ this.spawnPowerUp('jersey');
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ loop: true
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 生成能力道具
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 8000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ if (this.gameActive) {
|
|
|
|
+ this.spawnPowerUp('broom');
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ loop: true
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ spawnObstacle() {
|
|
|
|
+ const x = Phaser.Math.Between(30, GAME_WIDTH - 30);
|
|
|
|
+ const obstacle = this.physics.add.sprite(x, -50, 'pile');
|
|
|
|
+ obstacle.setScale(0.7);
|
|
|
|
+ obstacle.setVelocityY(this.speed * this.acceleration * 10);
|
|
|
|
+ obstacle.setDepth(8);
|
|
|
|
+ obstacle.type = 'pile';
|
|
|
|
+
|
|
|
|
+ // 碰撞检测
|
|
|
|
+ this.physics.add.overlap(this.player, obstacle, this.handleObstacleCollision, null, this);
|
|
|
|
+
|
|
|
|
+ this.obstacles.push(obstacle);
|
|
|
|
+
|
|
|
|
+ // 超出屏幕后销毁
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 5000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ if (obstacle.active) {
|
|
|
|
+ obstacle.destroy();
|
|
|
|
+ this.obstacles = this.obstacles.filter(o => o !== obstacle);
|
|
|
|
+ // 成功绕过障碍物加分
|
|
|
|
+ this.score++;
|
|
|
|
+ this.updateScore();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ spawnPowerUp(type) {
|
|
|
|
+ const x = Phaser.Math.Between(30, GAME_WIDTH - 30);
|
|
|
|
+ let powerUp;
|
|
|
|
+
|
|
|
|
+ if (type === 'jersey') {
|
|
|
|
+ powerUp = this.physics.add.sprite(x, -50, 'jersey');
|
|
|
|
+ powerUp.type = 'jersey';
|
|
|
|
+ } else if (type === 'broom') {
|
|
|
|
+ powerUp = this.physics.add.sprite(x, -50, 'broom');
|
|
|
|
+ powerUp.type = 'broom';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ powerUp.setScale(0.7);
|
|
|
|
+ powerUp.setVelocityY(this.speed * this.acceleration * 8);
|
|
|
|
+ powerUp.setDepth(8);
|
|
|
|
+
|
|
|
|
+ // 碰撞检测
|
|
|
|
+ this.physics.add.overlap(this.player, powerUp, this.handlePowerUpCollision, null, this);
|
|
|
|
+
|
|
|
|
+ this.powerUps.push(powerUp);
|
|
|
|
+
|
|
|
|
+ // 超出屏幕后销毁
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 5000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ if (powerUp.active) {
|
|
|
|
+ powerUp.destroy();
|
|
|
|
+ this.powerUps = this.powerUps.filter(p => p !== powerUp);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ handleObstacleCollision(player, obstacle) {
|
|
|
|
+ if (this.isSuper) {
|
|
|
|
+ // 超级状态下直接摧毁障碍物
|
|
|
|
+ obstacle.destroy();
|
|
|
|
+ this.obstacles = this.obstacles.filter(o => o !== obstacle);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 扣除生命值
|
|
|
|
+ player.lives--;
|
|
|
|
+ this.lives = player.lives;
|
|
|
|
+ this.livesText.setText(`生命: ${this.lives}`);
|
|
|
|
+
|
|
|
|
+ // 显示受伤效果
|
|
|
|
+ player.setTexture('playerCollision', 0);
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 618,
|
|
|
|
+ callback: () => {
|
|
|
|
+ player.setTexture('playerAnim');
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 销毁障碍物
|
|
|
|
+ obstacle.destroy();
|
|
|
|
+ this.obstacles = this.obstacles.filter(o => o !== obstacle);
|
|
|
|
+
|
|
|
|
+ // 检查游戏是否结束
|
|
|
|
+ if (player.lives <= 0) {
|
|
|
|
+ this.gameOver();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ handlePowerUpCollision(player, powerUp) {
|
|
|
|
+ if (powerUp.type === 'jersey') {
|
|
|
|
+ // 增加生命值
|
|
|
|
+ if (player.lives < this.maxLives) {
|
|
|
|
+ player.lives++;
|
|
|
|
+ this.lives = player.lives;
|
|
|
|
+ this.livesText.setText(`生命: ${this.lives}`);
|
|
|
|
+ }
|
|
|
|
+ } else if (powerUp.type === 'broom') {
|
|
|
|
+ // 激活超级状态
|
|
|
|
+ this.activateSuperMode();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 销毁道具
|
|
|
|
+ powerUp.destroy();
|
|
|
|
+ this.powerUps = this.powerUps.filter(p => p !== powerUp);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ activateSuperMode() {
|
|
|
|
+ this.isSuper = true;
|
|
|
|
+ this.player.setTexture('playerSuper');
|
|
|
|
+ this.acceleration = 10;
|
|
|
|
+
|
|
|
|
+ // 3秒后结束超级状态
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 3000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ this.isSuper = false;
|
|
|
|
+ this.player.setTexture('playerAnim');
|
|
|
|
+ this.acceleration = 1.9 + (this.level - 1) * 0.5;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ levelUp() {
|
|
|
|
+ this.level++;
|
|
|
|
+ this.acceleration += 0.5;
|
|
|
|
+
|
|
|
|
+ // 显示升级提示
|
|
|
|
+ const levelUpText = this.add.text(GAME_WIDTH / 2, GAME_HEIGHT / 2, `Level ${this.level}!`, {
|
|
|
|
+ fontSize: '32px',
|
|
|
|
+ fill: '#ffff00',
|
|
|
|
+ stroke: '#000000',
|
|
|
|
+ strokeThickness: 2
|
|
|
|
+ }).setOrigin(0.5);
|
|
|
|
+ levelUpText.setDepth(10);
|
|
|
|
+
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 1000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ levelUpText.destroy();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ updateScore() {
|
|
|
|
+ this.scoreText.setText(`分数: ${this.score}`);
|
|
|
|
+ // 触发全局分数更新事件
|
|
|
|
+ this.game.events.emit('scoreChanged', this.score);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gameOver() {
|
|
|
|
+ this.gameActive = false;
|
|
|
|
+
|
|
|
|
+ // 停止计时器
|
|
|
|
+ if (this.timer) {
|
|
|
|
+ this.timer.remove();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 显示游戏结束画面
|
|
|
|
+ this.add.image(GAME_WIDTH / 2, GAME_HEIGHT / 2, 'gameOver').setOrigin(0.5);
|
|
|
|
+
|
|
|
|
+ // 触发游戏结束事件
|
|
|
|
+ this.time.addEvent({
|
|
|
|
+ delay: 2000,
|
|
|
|
+ callback: () => {
|
|
|
|
+ this.game.events.emit('gameOver');
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ update() {
|
|
|
|
+ if (!this.gameActive) return;
|
|
|
|
+
|
|
|
|
+ // 滚动背景
|
|
|
|
+ this.background.tilePositionY -= this.speed * this.acceleration;
|
|
|
|
+
|
|
|
|
+ // 键盘控制
|
|
|
|
+ if (this.cursors.left.isDown) {
|
|
|
|
+ this.player.setVelocityX(-200);
|
|
|
|
+ this.player.anims.play('playerLeft', true);
|
|
|
|
+ } else if (this.cursors.right.isDown) {
|
|
|
|
+ this.player.setVelocityX(200);
|
|
|
|
+ this.player.anims.play('playerRight', true);
|
|
|
|
+ } else {
|
|
|
|
+ this.player.setVelocityX(0);
|
|
|
|
+ this.player.anims.stop();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 游戏控制函数
|
|
|
|
+const startGame = () => {
|
|
|
|
+ currentScene.value = 'countdown';
|
|
|
|
+
|
|
|
|
+ // 倒计时
|
|
|
|
+ let count = 3;
|
|
|
|
+ const countdownInterval = setInterval(() => {
|
|
|
|
+ count--;
|
|
|
|
+ countdownNum.value = count;
|
|
|
|
+
|
|
|
|
+ if (count <= 0) {
|
|
|
|
+ clearInterval(countdownInterval);
|
|
|
|
+ currentScene.value = 'game';
|
|
|
|
+ // 通知游戏开始
|
|
|
|
+ if (game.value) {
|
|
|
|
+ const gameScene = game.value.scene.getScene('GameScene');
|
|
|
|
+ if (gameScene) {
|
|
|
|
+ gameScene.startGame();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }, 1000);
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const continueGame = () => {
|
|
|
|
+ startGame();
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const restartGame = () => {
|
|
|
|
+ // 重新加载游戏
|
|
|
|
+ if (game.value) {
|
|
|
|
+ game.value.destroy();
|
|
|
|
+ }
|
|
|
|
+ score.value = 0;
|
|
|
|
+ currentScene.value = 'start';
|
|
|
|
+ initGame();
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+const getTitle = (score) => {
|
|
|
|
+ if (score < 50) return '足球新手';
|
|
|
|
+ if (score < 100) return '业余球员';
|
|
|
|
+ if (score < 200) return '专业选手';
|
|
|
|
+ if (score < 300) return '足球明星';
|
|
|
|
+ return '足球传奇';
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 组件挂载时初始化游戏
|
|
|
|
+onMounted(() => {
|
|
|
|
+ initGame();
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+// 监听场景变化
|
|
|
|
+watch(currentScene, (newVal) => {
|
|
|
|
+ if (newVal === 'game' && game.value) {
|
|
|
|
+ const gameScene = game.value.scene.getScene('GameScene');
|
|
|
|
+ if (gameScene) {
|
|
|
|
+ gameScene.scene.resume();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+});
|
|
</script>
|
|
</script>
|
|
|
|
|
|
-<style scoped></style>
|
|
|
|
|
|
+<style scoped>
|
|
|
|
+.game-container {
|
|
|
|
+ position: relative;
|
|
|
|
+ width: 320px;
|
|
|
|
+ height: 480px;
|
|
|
|
+ margin: 0 auto;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.game-canvas {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: #000;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.gamestart {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ z-index: 100;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.start_bg {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ object-fit: cover;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.btn {
|
|
|
|
+ position: absolute;
|
|
|
|
+ width: 120px;
|
|
|
|
+ height: 40px;
|
|
|
|
+ line-height: 40px;
|
|
|
|
+ text-align: center;
|
|
|
|
+ color: white;
|
|
|
|
+ border-radius: 20px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ z-index: 101;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.rule {
|
|
|
|
+ bottom: 100px;
|
|
|
|
+ left: 50%;
|
|
|
|
+ transform: translateX(-50%);
|
|
|
|
+ background-color: #666;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.start {
|
|
|
|
+ bottom: 50px;
|
|
|
|
+ left: 50%;
|
|
|
|
+ transform: translateX(-50%);
|
|
|
|
+ background-color: #f00;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.ruleshadow {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.7);
|
|
|
|
+ z-index: 102;
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.rulebox {
|
|
|
|
+ width: 280px;
|
|
|
|
+ background-color: white;
|
|
|
|
+ border-radius: 10px;
|
|
|
|
+ padding: 20px;
|
|
|
|
+ position: relative;
|
|
|
|
+ opacity: 0;
|
|
|
|
+ animation: fadeIn 0.5s forwards;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@keyframes fadeIn {
|
|
|
|
+ to {
|
|
|
|
+ opacity: 1;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.x_rulebox {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 10px;
|
|
|
|
+ right: 10px;
|
|
|
|
+ width: 20px;
|
|
|
|
+ height: 20px;
|
|
|
|
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23000'%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/%3E%3C/svg%3E");
|
|
|
|
+ cursor: pointer;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daoju {
|
|
|
|
+ margin-top: 15px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daoju_item {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daoju_1,
|
|
|
|
+.daoju_2,
|
|
|
|
+.daoju_3 {
|
|
|
|
+ display: inline-block;
|
|
|
|
+ width: 30px;
|
|
|
|
+ height: 30px;
|
|
|
|
+ margin-right: 10px;
|
|
|
|
+ background-size: contain;
|
|
|
|
+ background-repeat: no-repeat;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daoju_1 {
|
|
|
|
+ background-image: url("/static/images/football/Jersey.png");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daoju_2 {
|
|
|
|
+ background-image: url("/static/images/football/Broom.png");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daoju_3 {
|
|
|
|
+ background-image: url("/static/images/football/Pile.png");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.zhiyin {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.7);
|
|
|
|
+ z-index: 100;
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daojishi {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
+ z-index: 100;
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.daojishinum {
|
|
|
|
+ font-size: 60px;
|
|
|
|
+ font-weight: bold;
|
|
|
|
+ color: white;
|
|
|
|
+ animation: countDown 1s ease-in-out;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@keyframes countDown {
|
|
|
|
+ 0% {
|
|
|
|
+ transform: scale(3);
|
|
|
|
+ opacity: 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 50% {
|
|
|
|
+ transform: scale(1.2);
|
|
|
|
+ opacity: 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 100% {
|
|
|
|
+ transform: scale(1);
|
|
|
|
+ opacity: 1;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.gameend {
|
|
|
|
+ position: absolute;
|
|
|
|
+ top: 0;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ background-color: rgba(0, 0, 0, 0.7);
|
|
|
|
+ z-index: 100;
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-direction: column;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ align-items: center;
|
|
|
|
+ color: white;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.game_fenshu {
|
|
|
|
+ font-size: 36px;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.chenghao {
|
|
|
|
+ font-size: 24px;
|
|
|
|
+ margin-bottom: 30px;
|
|
|
|
+ text-align: center;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.game_ogain,
|
|
|
|
+.game_chakan,
|
|
|
|
+.game_share {
|
|
|
|
+ width: 150px;
|
|
|
|
+ height: 40px;
|
|
|
|
+ line-height: 40px;
|
|
|
|
+ text-align: center;
|
|
|
|
+ background-color: #f00;
|
|
|
|
+ border-radius: 20px;
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.game_chakan {
|
|
|
|
+ background-color: #666;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.game_share {
|
|
|
|
+ background-color: #008000;
|
|
|
|
+}
|
|
|
|
+</style>
|