|
@@ -81,6 +81,7 @@
|
|
|
<div class="workspace-section">
|
|
<div class="workspace-section">
|
|
|
<div class="controls">
|
|
<div class="controls">
|
|
|
<button id="runCode" @click="runCode" :disabled="isRunning">运行代码</button>
|
|
<button id="runCode" @click="runCode" :disabled="isRunning">运行代码</button>
|
|
|
|
|
+<!-- <button @click="generatePythonCode">生成Python代码</button>-->
|
|
|
<button @click="resetPlayer" >重置玩家</button>
|
|
<button @click="resetPlayer" >重置玩家</button>
|
|
|
<button @click="clearWorkspace">清空工作区</button>
|
|
<button @click="clearWorkspace">清空工作区</button>
|
|
|
</div>
|
|
</div>
|
|
@@ -94,6 +95,7 @@
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import {ref, onMounted, onUnmounted, reactive, computed, nextTick, watch, defineEmits} from 'vue';
|
|
import {ref, onMounted, onUnmounted, reactive, computed, nextTick, watch, defineEmits} from 'vue';
|
|
|
import { javascriptGenerator } from "blockly/javascript";
|
|
import { javascriptGenerator } from "blockly/javascript";
|
|
|
|
|
+import { pythonGenerator } from "blockly/python";
|
|
|
import playerImage from '@/assets/images/blockly/user.png';
|
|
import playerImage from '@/assets/images/blockly/user.png';
|
|
|
|
|
|
|
|
// 导入音频文件
|
|
// 导入音频文件
|
|
@@ -102,7 +104,7 @@ import failureMp3 from '@/assets/music/blockly/failure.MP3';
|
|
|
import errorMp3 from '@/assets/music/blockly/error.MP3'
|
|
import errorMp3 from '@/assets/music/blockly/error.MP3'
|
|
|
|
|
|
|
|
// 游戏接口数据
|
|
// 游戏接口数据
|
|
|
-import { registerCustomBlocks, registerJavaScriptGenerators, initBlockly } from '@/api/blockly/blockly.js';
|
|
|
|
|
|
|
+import { registerCustomBlocks, registerJavaScriptGenerators, registerPythonGenerators, initBlockly } from '@/api/blockly/blockly.js';
|
|
|
import {playMp3} from "@/api/blockly/music.js";
|
|
import {playMp3} from "@/api/blockly/music.js";
|
|
|
|
|
|
|
|
// 定义emits
|
|
// 定义emits
|
|
@@ -249,6 +251,9 @@ const mapContainerDimensions = ref({ width: 0, height: 0 });
|
|
|
// 用于控制运行/重置按钮状态
|
|
// 用于控制运行/重置按钮状态
|
|
|
const isRunning = ref(false);
|
|
const isRunning = ref(false);
|
|
|
|
|
|
|
|
|
|
+// 存储生成的Python代码
|
|
|
|
|
+const generatedPythonCode = ref('');
|
|
|
|
|
+
|
|
|
// 暂停模块-倒计时相关状态
|
|
// 暂停模块-倒计时相关状态
|
|
|
const showCountdown = ref(false);
|
|
const showCountdown = ref(false);
|
|
|
const countdownValue = ref(0);
|
|
const countdownValue = ref(0);
|
|
@@ -407,6 +412,9 @@ onMounted(async () => {
|
|
|
// 注册JavaScript生成器
|
|
// 注册JavaScript生成器
|
|
|
registerJavaScriptGenerators(props.blocklySpecialBlocks);
|
|
registerJavaScriptGenerators(props.blocklySpecialBlocks);
|
|
|
|
|
|
|
|
|
|
+ // 注册Python生成器
|
|
|
|
|
+ registerPythonGenerators(props.blocklySpecialBlocks);
|
|
|
|
|
+
|
|
|
// 动态生成工具箱XML
|
|
// 动态生成工具箱XML
|
|
|
const toolboxContainer = document.getElementById('toolbox');
|
|
const toolboxContainer = document.getElementById('toolbox');
|
|
|
toolboxContainer.innerHTML = generateToolboxXml();
|
|
toolboxContainer.innerHTML = generateToolboxXml();
|
|
@@ -432,6 +440,8 @@ function generateToolboxXml() {
|
|
|
<block type="turn_left"></block>
|
|
<block type="turn_left"></block>
|
|
|
<block type="turn_right"></block>
|
|
<block type="turn_right"></block>
|
|
|
<block type="turn_around"></block>
|
|
<block type="turn_around"></block>
|
|
|
|
|
+ <block type="move_forward_param"></block>
|
|
|
|
|
+ <block type="move_backward_param"></block>
|
|
|
</category>
|
|
</category>
|
|
|
<category name="功能" colour="120">
|
|
<category name="功能" colour="120">
|
|
|
<block type="pickup_item"></block>
|
|
<block type="pickup_item"></block>
|
|
@@ -752,6 +762,25 @@ const clearWorkspace = () => {
|
|
|
showGameMessage('工作区已清空', 'info');
|
|
showGameMessage('工作区已清空', 'info');
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+// 生成Python代码
|
|
|
|
|
+const generatePythonCode = () => {
|
|
|
|
|
+ if (!workspace) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 生成Python代码
|
|
|
|
|
+ const pythonCode = pythonGenerator.workspaceToCode(workspace);
|
|
|
|
|
+ // 存储到常量中
|
|
|
|
|
+ generatedPythonCode.value = pythonCode;
|
|
|
|
|
+ console.log('生成的Python代码:', pythonCode);
|
|
|
|
|
+ // 可以添加提示信息
|
|
|
|
|
+ showGameMessage('Python代码生成成功!', 'success');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('生成Python代码时出错:', error);
|
|
|
|
|
+ showGameMessage('生成Python代码失败', 'error');
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 统一处理撞到墙时的停止逻辑
|
|
// 统一处理撞到墙时的停止逻辑
|
|
|
async function handleWallCollision(endMsg = CONFIG.TIPS.NO_ENTRY) {
|
|
async function handleWallCollision(endMsg = CONFIG.TIPS.NO_ENTRY) {
|
|
|
// 设置碰撞状态
|
|
// 设置碰撞状态
|
|
@@ -790,75 +819,47 @@ function showGameMessage(message, type = 'info', duration = CONFIG.DELAY.MESSAGE
|
|
|
//================积木组件方法=====================
|
|
//================积木组件方法=====================
|
|
|
|
|
|
|
|
// 向前移动
|
|
// 向前移动
|
|
|
-window.moveForward = async function() {
|
|
|
|
|
|
|
+window.moveForward = async function(stepCount = 1) {
|
|
|
if (shouldStopExecution || isColliding.value || isSliding.value) {
|
|
if (shouldStopExecution || isColliding.value || isSliding.value) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let newX = playerPosition.value.x;
|
|
|
|
|
- let newY = playerPosition.value.y;
|
|
|
|
|
-
|
|
|
|
|
- // 向前移动
|
|
|
|
|
- switch(playerDirection.value) {
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.UP: newY--; break;
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.RIGHT: newX++; break;
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.DOWN: newY++; break;
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.LEFT: newX--; break;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 检查是否可以移动
|
|
|
|
|
- if (walkablePointsMap.has(`${newX},${newY}`)) {
|
|
|
|
|
-
|
|
|
|
|
- // 处理方块类型逻辑
|
|
|
|
|
- await switchMapType(0);
|
|
|
|
|
-
|
|
|
|
|
- // 使用平滑移动动画
|
|
|
|
|
- await smoothMoveTo(newX, newY);
|
|
|
|
|
-
|
|
|
|
|
- // 处理方块类型逻辑
|
|
|
|
|
- await switchMapType(1, 0);
|
|
|
|
|
|
|
+ for (let i = 1; i <= stepCount; i++) {
|
|
|
|
|
+ let newX = playerPosition.value.x;
|
|
|
|
|
+ let newY = playerPosition.value.y;
|
|
|
|
|
+ // 向前移动
|
|
|
|
|
+ switch(playerDirection.value) {
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.UP: newY--; break;
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.RIGHT: newX++; break;
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.DOWN: newY++; break;
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.LEFT: newX--; break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- await new Promise(resolve => setTimeout(resolve, CONFIG.DELAY.ACTION_DELAY));
|
|
|
|
|
- } else {
|
|
|
|
|
- // 发生碰撞 使用统一的碰撞处理方法
|
|
|
|
|
- await handleWallCollision();
|
|
|
|
|
|
|
+ console.log(`第${i}步`,newX, newY);
|
|
|
|
|
+ await moveStep(newX, newY);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 向后移动
|
|
// 向后移动
|
|
|
-window.moveBackward = async function() {
|
|
|
|
|
|
|
+window.moveBackward = async function(stepCount = 1) {
|
|
|
// 如果已经发生过碰撞,不再执行任何移动
|
|
// 如果已经发生过碰撞,不再执行任何移动
|
|
|
if (shouldStopExecution || isColliding.value || isSliding.value) {
|
|
if (shouldStopExecution || isColliding.value || isSliding.value) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- let newX = playerPosition.value.x;
|
|
|
|
|
- let newY = playerPosition.value.y;
|
|
|
|
|
-
|
|
|
|
|
- // 根据当前方向计算新位置(向后移动)
|
|
|
|
|
- switch(playerDirection.value) {
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.UP: newY++; break;
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.RIGHT: newX--; break;
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.DOWN: newY--; break;
|
|
|
|
|
- case CONFIG.GAME.DIRECTIONS.LEFT: newX++; break;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 检查是否可以移动
|
|
|
|
|
- if (walkablePointsMap.has(`${newX},${newY}`)) {
|
|
|
|
|
-
|
|
|
|
|
- // 处理方块类型逻辑
|
|
|
|
|
- await switchMapType(0);
|
|
|
|
|
|
|
+ for (let i = 1; i <= stepCount; i++) {
|
|
|
|
|
+ let newX = playerPosition.value.x;
|
|
|
|
|
+ let newY = playerPosition.value.y;
|
|
|
|
|
|
|
|
- // 使用平滑移动动画
|
|
|
|
|
- await smoothMoveTo(newX, newY);
|
|
|
|
|
-
|
|
|
|
|
- // 处理方块类型逻辑
|
|
|
|
|
- await switchMapType(1, 1);
|
|
|
|
|
|
|
+ // 根据当前方向计算新位置(向后移动)
|
|
|
|
|
+ switch(playerDirection.value) {
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.UP: newY--; break;
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.RIGHT: newX--; break;
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.DOWN: newY++; break;
|
|
|
|
|
+ case CONFIG.GAME.DIRECTIONS.LEFT: newX++; break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- await new Promise(resolve => setTimeout(resolve, CONFIG.DELAY.ACTION_DELAY));
|
|
|
|
|
- } else {
|
|
|
|
|
- // 发生碰撞 使用统一的碰撞处理方法
|
|
|
|
|
- await handleWallCollision();
|
|
|
|
|
|
|
+ await moveStep(newX, newY, 1);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -1180,8 +1181,6 @@ window.construct = async function() {
|
|
|
processingSpecialTasksDisappearing()
|
|
processingSpecialTasksDisappearing()
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
//校验是否到达终点
|
|
//校验是否到达终点
|
|
|
window.isFinish = async function() {
|
|
window.isFinish = async function() {
|
|
|
// 如果已经发生过碰撞,不再执行任何检查
|
|
// 如果已经发生过碰撞,不再执行任何检查
|
|
@@ -1224,6 +1223,28 @@ window.isFinish = async function() {
|
|
|
|
|
|
|
|
//================特殊组件积木逻辑=====================
|
|
//================特殊组件积木逻辑=====================
|
|
|
|
|
|
|
|
|
|
+//移动逻辑处理(前后通用)
|
|
|
|
|
+async function moveStep(newX, newY, moveDirection = 0){
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否可以移动
|
|
|
|
|
+ if (walkablePointsMap.has(`${newX},${newY}`)) {
|
|
|
|
|
+
|
|
|
|
|
+ // 处理方块类型逻辑
|
|
|
|
|
+ await switchMapType(0);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用平滑移动动画
|
|
|
|
|
+ await smoothMoveTo(newX, newY);
|
|
|
|
|
+
|
|
|
|
|
+ // 处理方块类型逻辑
|
|
|
|
|
+ await switchMapType(1, moveDirection);
|
|
|
|
|
+
|
|
|
|
|
+ await new Promise(resolve => setTimeout(resolve, CONFIG.DELAY.ACTION_DELAY));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 发生碰撞 使用统一的碰撞处理方法
|
|
|
|
|
+ await handleWallCollision();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 处理地图类型逻辑
|
|
// 处理地图类型逻辑
|
|
|
async function switchMapType(type, moveDirection = 0) {
|
|
async function switchMapType(type, moveDirection = 0) {
|
|
|
//取人物当前位置
|
|
//取人物当前位置
|
|
@@ -1266,31 +1287,6 @@ async function switchMapType(type, moveDirection = 0) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 特殊任务处理消失(不需要物品)
|
|
|
|
|
-function processingSpecialTasksDisappearing(errorTip = CONFIG.TIPS.ERROR_ERROR) {
|
|
|
|
|
- let x = playerPosition.value.x;
|
|
|
|
|
- let y = playerPosition.value.y;
|
|
|
|
|
- let tileMap = walkablePointsMap.get(`${x},${y}`);
|
|
|
|
|
-
|
|
|
|
|
- if (tileMap && tileMap.type === BLOCKLY_MAP_TYPE_DICT.TASK) {
|
|
|
|
|
- const pointIndex = gameState.mapData.walkablePoints.findIndex(
|
|
|
|
|
- p => p.x === x && p.y === y
|
|
|
|
|
- );
|
|
|
|
|
- if (pointIndex !== -1) {
|
|
|
|
|
- const updatedPoint = { ...gameState.mapData.walkablePoints[pointIndex] };
|
|
|
|
|
- updatedPoint.img = updatedPoint.endImg;
|
|
|
|
|
- updatedPoint.status = true;
|
|
|
|
|
- gameState.mapData.walkablePoints.splice(pointIndex, 1, updatedPoint);
|
|
|
|
|
- walkablePointsMap.set(`${x},${y}`, updatedPoint);
|
|
|
|
|
- }
|
|
|
|
|
- showGameMessage(tileMap.finishedTip || CONFIG.TIPS.USE_ITEM_SUCCESS, 'success');
|
|
|
|
|
- return true;
|
|
|
|
|
- }else{
|
|
|
|
|
- showGameMessage(errorTip, 'info');
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// 平滑移动函数
|
|
// 平滑移动函数
|
|
|
async function smoothMoveTo(targetX, targetY) {
|
|
async function smoothMoveTo(targetX, targetY) {
|
|
|
const startX = playerPosition.value.x;
|
|
const startX = playerPosition.value.x;
|
|
@@ -1333,6 +1329,31 @@ async function smoothMoveTo(targetX, targetY) {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 特殊任务处理消失(不需要物品)
|
|
|
|
|
+function processingSpecialTasksDisappearing(errorTip = CONFIG.TIPS.ERROR_ERROR) {
|
|
|
|
|
+ let x = playerPosition.value.x;
|
|
|
|
|
+ let y = playerPosition.value.y;
|
|
|
|
|
+ let tileMap = walkablePointsMap.get(`${x},${y}`);
|
|
|
|
|
+
|
|
|
|
|
+ if (tileMap && tileMap.type === BLOCKLY_MAP_TYPE_DICT.TASK) {
|
|
|
|
|
+ const pointIndex = gameState.mapData.walkablePoints.findIndex(
|
|
|
|
|
+ p => p.x === x && p.y === y
|
|
|
|
|
+ );
|
|
|
|
|
+ if (pointIndex !== -1) {
|
|
|
|
|
+ const updatedPoint = { ...gameState.mapData.walkablePoints[pointIndex] };
|
|
|
|
|
+ updatedPoint.img = updatedPoint.endImg;
|
|
|
|
|
+ updatedPoint.status = true;
|
|
|
|
|
+ gameState.mapData.walkablePoints.splice(pointIndex, 1, updatedPoint);
|
|
|
|
|
+ walkablePointsMap.set(`${x},${y}`, updatedPoint);
|
|
|
|
|
+ }
|
|
|
|
|
+ showGameMessage(tileMap.finishedTip || CONFIG.TIPS.USE_ITEM_SUCCESS, 'success');
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }else{
|
|
|
|
|
+ showGameMessage(errorTip, 'info');
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 处理冰块滑行逻辑
|
|
// 处理冰块滑行逻辑
|
|
|
async function slidingLogic(moveDirection = 0) {
|
|
async function slidingLogic(moveDirection = 0) {
|
|
|
if (shouldStopExecution || isColliding.value) {
|
|
if (shouldStopExecution || isColliding.value) {
|