浏览代码

日常开发

林旭祥 4 天之前
父节点
当前提交
2012a408c1
共有 1 个文件被更改,包括 874 次插入2 次删除
  1. 874 2
      src/views/game/football.vue

+ 874 - 2
src/views/game/football.vue

@@ -1,9 +1,881 @@
 <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>
 
-<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>
 
-<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>