Przeglądaj źródła

Merge branch 'master' of http://59.110.91.129:3000/zhangmengying/AIClass into wanzi

丸子 5 miesięcy temu
rodzic
commit
3f19678b08
1 zmienionych plików z 205 dodań i 8 usunięć
  1. 205 8
      src/components/blockly/MapGame.vue

+ 205 - 8
src/components/blockly/MapGame.vue

@@ -41,6 +41,18 @@
               :class="{ 'collision': isColliding, 'success': hasReachedEnd }"
           >
           </div>
+
+          <!-- 怀表倒计时容器 -->
+          <div v-if="showCountdown" class="watch-container" :style="countdownStyle">
+            <div class="watch-face">
+              <div class="watch-center"></div>
+              <div class="watch-hands">
+                <div class="watch-hand hour-hand"></div>
+                <div class="watch-hand minute-hand"></div>
+              </div>
+              <div class="watch-countdown-number">{{ countdownValue }}</div>
+            </div>
+          </div>
           <!-- 携带物品容器 -->
           <div class="carried-items-container" v-show="gameState.player.carriedItems.length > 0">
             <div
@@ -72,6 +84,7 @@
             <block type="turn_around"></block>
             <block type="pickup_item"></block>
             <block type="use_item"></block>
+            <block type="pause"></block>
           </category>
 
           <!-- 逻辑控制积木 -->
@@ -118,6 +131,9 @@ import 'blockly/msg/zh-hans';
 import { javascriptGenerator } from "blockly/javascript";
 import playerImage from '@/assets/images/blockly/user.png';
 
+// 游戏接口数据
+import { getMapGameById } from '@/api/blockly/game.js';
+
 // 定义组件属性
 const props = defineProps({
   // 游戏ID
@@ -182,9 +198,6 @@ const props = defineProps({
   }
 });
 
-// 游戏接口数据
-import { getMapGameById } from '@/api/blockly/game.js';
-
 // 配置常量
 const CONFIG = {
   // 动画时长配置(毫秒)
@@ -247,6 +260,10 @@ let executionAbortController = null;
 // 添加响应式的容器尺寸
 const mapContainerDimensions = ref({ width: 0, height: 0 });
 
+// 暂停模块-倒计时相关状态
+const showCountdown = ref(false);
+const countdownValue = ref(0);
+
 // Blockly相关状态
 let workspace = null;
 
@@ -388,10 +405,10 @@ const fetchGameData = async () => {
         userImage: props.userImage,
         info: props.info
       };
-      
+
       // 直接更新游戏状态
       await updateGameStateFromData(currentGameData.value);
-      
+
       // 数据更新后强制刷新容器尺寸(等待DOM更新)
       await nextTick();
       updateMapContainerDimensions();
@@ -537,6 +554,21 @@ function getCarriedItemStyle(index, item) {
   };
 }
 
+// 倒计时样式计算
+const countdownStyle = computed(() => {
+  return {
+    position: 'absolute',
+    left: playerPosition.value.x * tileSize.value - tileSize.value - (tileSize.value * 0.2) + 'px',
+    top: playerPosition.value.y * tileSize.value - tileSize.value - (tileSize.value * 0.2) + 'px',
+    width: tileSize.value * 0.5 + 'px',
+    height: tileSize.value * 0.5 + 'px',
+    display: 'flex',
+    justifyContent: 'center',
+    alignItems: 'center',
+    zIndex: 100
+  };
+});
+
 // 注册自定义积木
 function registerCustomBlocks() {
   // 向前移动积木
@@ -628,6 +660,31 @@ function registerCustomBlocks() {
       });
     }
   };
+
+  // 暂停积木
+  Blockly.Blocks['pause'] = {
+    init: function() {
+      this.jsonInit({
+        "type": "pause",
+        "message0": "暂停 %1 秒",
+        "args0": [
+          {
+            "type": "field_number",
+            "name": "SECONDS",
+            "value": 1,
+            "min": 1,
+            "max": 10,
+            "precision": 1
+          }
+        ],
+        "previousStatement": null,
+        "nextStatement": null,
+        "colour": 0,
+        "tooltip": "让角色在原地停留指定的秒数",
+        "helpUrl": ""
+      });
+    }
+  };
 }
 
 // 注册JavaScript生成器
@@ -662,6 +719,12 @@ function registerJavaScriptGenerators() {
     return 'await useItem();\n';
   };
 
+  // 暂停生成器
+  javascriptGenerator.forBlock['pause'] = function(block) {
+    const seconds = block.getFieldValue('SECONDS');
+    return `await pause(${seconds});\n`;
+  };
+
   // 为重复循环块注册自定义生成器,确保支持异步操作
   javascriptGenerator.forBlock['controls_repeat_ext'] = function(block) {
     const repeats = javascriptGenerator.valueToCode(block, 'TIMES', javascriptGenerator.ORDER_ATOMIC) || '0';
@@ -981,7 +1044,7 @@ async function animateItemPickup(item, itemX, itemY) {
   const itemTop = itemY * tileSize.value - tileSize.value + marginSize;
 
   // 平行移动到容器位置,同时调整大小
-  const finalSize = tileSize.value * CONFIG.STYLES.PLAYER_SIZE_RATIO * 0.3;
+  const finalSize = tileSize.value * CONFIG.STYLES.PLAYER_SIZE_RATIO * 0.5;
 
   // 获取携带物品容器的位置(左上角)
   // 注意:这里不需要考虑当前已携带物品数量,因为物品还没被添加
@@ -1048,7 +1111,7 @@ async function animateItemPickup(item, itemX, itemY) {
 async function animateItemUse(item, itemIndex) {
   // 创建临时动画元素
   const tempItem = document.createElement('div');
-  const finalSize = tileSize.value * CONFIG.STYLES.PLAYER_SIZE_RATIO * 0.3;
+  const finalSize = tileSize.value * CONFIG.STYLES.PLAYER_SIZE_RATIO * 0.5;
   const iconSize = tileSize.value * CONFIG.STYLES.PLAYER_SIZE_RATIO;
   const marginSize = tileSize.value * CONFIG.STYLES.PLAYER_SIZE_MARGIN;
 
@@ -1268,6 +1331,28 @@ window.useItem = async function() {
   await new Promise(resolve => setTimeout(resolve, CONFIG.DELAY.ACTION_DELAY));
 }
 
+// 暂停函数
+window.pause = async function(seconds) {
+  if (shouldStopExecution || isSliding.value) {
+    return;
+  }
+
+  // 显示倒计时
+  showCountdown.value = true;
+  countdownValue.value = seconds;
+
+  // 倒计时循环
+  while (countdownValue.value > 0 && !shouldStopExecution) {
+    await new Promise(resolve => setTimeout(resolve, 1000));
+    countdownValue.value--;
+  }
+
+  // 隐藏倒计时
+  showCountdown.value = false;
+
+  await new Promise(resolve => setTimeout(resolve, CONFIG.DELAY.ACTION_DELAY));
+}
+
 // 向前移动
 window.moveForward = async function() {
   if (shouldStopExecution || isColliding.value || isSliding.value) {
@@ -1920,7 +2005,119 @@ onUnmounted(() => {
 /* 携带物品样式 */
 .carried-item {
   animation: bounceIn 0.3s ease-out forwards;
-  opacity: 0;
+}
+
+/* 【暂停】怀表容器样式 */
+.watch-container {
+  animation: fadeInCountdown 0.3s ease-out;
+  position: relative;
+}
+
+/* 【暂停】怀表表盘样式 */
+.watch-face {
+  width: 100%;
+  height: 100%;
+  border-radius: 50%;
+  background: linear-gradient(145deg, rgba(240, 240, 240, 0.8), rgba(200, 200, 200, 0.6));
+  border: 4px solid rgba(100, 100, 100, 0.8);
+  box-shadow:
+    0 0 20px rgba(0, 0, 0, 0.3),
+    inset 0 0 20px rgba(0, 0, 0, 0.1);
+  position: relative;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  backdrop-filter: blur(5px);
+}
+
+/* 【暂停】怀表中心圆点 */
+.watch-center {
+  width: 10%;
+  height: 10%;
+  background-color: rgba(100, 50, 20, 0.8);
+  border-radius: 50%;
+  position: absolute;
+  z-index: 10;
+}
+
+/* 【暂停】表针容器 */
+.watch-hands {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+/* 【暂停】表针基础样式 */
+.watch-hand {
+  position: absolute;
+  background-color: rgba(100, 50, 20, 0.8);
+  transform-origin: bottom center;
+  border-radius: 2px;
+}
+
+/* 【暂停】时针样式 */
+.hour-hand {
+  width: 4%;
+  height: 30%;
+  top: 20%;
+  animation: rotateHourHand 12s linear infinite;
+}
+
+/* 【暂停】分针样式 */
+.minute-hand {
+  width: 3%;
+  height: 40%;
+  top: 10%;
+  animation: rotateMinuteHand 1s linear infinite;
+}
+
+/* 【暂停】怀表中心倒计时数字 */
+.watch-countdown-number {
+  font-size: calc(var(--tile-size, 143px) * 0.3);
+  font-weight: bold;
+  color: rgba(100, 50, 20, 0.9);
+  text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);
+  z-index: 5;
+  animation: pulseCountdown 1s infinite;
+}
+
+/* 【暂停】怀表淡入动画 */
+@keyframes fadeInCountdown {
+  0% {
+    opacity: 0;
+    transform: scale(0.8) rotate(-30deg);
+  }
+  100% {
+    opacity: 1;
+    transform: scale(1) rotate(0deg);
+  }
+}
+
+/* 【暂停】倒计时数字脉冲动画 */
+@keyframes pulseCountdown {
+  0%, 100% {
+    opacity: 0.9;
+    transform: scale(1);
+  }
+  50% {
+    opacity: 0.7;
+    transform: scale(1.05);
+  }
+}
+
+/* 【暂停】时针旋转动画 */
+@keyframes rotateHourHand {
+  from { transform: rotate(0deg); }
+  to { transform: rotate(360deg); }
+}
+
+/* 【暂停】分针旋转动画 */
+@keyframes rotateMinuteHand {
+  from { transform: rotate(0deg); }
+  to { transform: rotate(360deg); }
 }
 
 /* 弹入动画 */