|
|
@@ -117,23 +117,45 @@ const route = useRoute();
|
|
|
const gameTitle = ref('地图游戏编程'); // 默认标题
|
|
|
const currentGameData = ref(null);
|
|
|
|
|
|
+onMounted(() => {
|
|
|
+ //数据
|
|
|
+ fetchGameData();
|
|
|
+
|
|
|
+ // 初始化可行走点集合
|
|
|
+ initWalkablePointsSet();
|
|
|
+ // 注册自定义积木
|
|
|
+ registerCustomBlocks();
|
|
|
+ // 注册JavaScript生成器
|
|
|
+ registerJavaScriptGenerators();
|
|
|
+ // 初始化Blockly工作区
|
|
|
+ initBlockly();
|
|
|
+ // 重置玩家位置
|
|
|
+ resetPlayer();
|
|
|
+
|
|
|
+ // 获取路由参数中的gameName
|
|
|
+ const nameFromRoute = route.query.gameName;
|
|
|
+ if (nameFromRoute) {
|
|
|
+ gameTitle.value = nameFromRoute;
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
// 获取游戏数据
|
|
|
const fetchGameData = async () => {
|
|
|
try {
|
|
|
const gameId = route.query.gameId;
|
|
|
const nameFromRoute = route.query.gameName;
|
|
|
-
|
|
|
+
|
|
|
if (nameFromRoute) {
|
|
|
gameTitle.value = nameFromRoute;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const res = await GameList();
|
|
|
if (res && res.data && res.data.list) {
|
|
|
// 根据gameId找到对应的游戏数据
|
|
|
- const gameItem = gameId
|
|
|
+ const gameItem = gameId
|
|
|
? res.data.list.find(item => item.id === parseInt(gameId))
|
|
|
: res.data.list[0]; // 如果没有gameId,使用第一个游戏
|
|
|
-
|
|
|
+
|
|
|
if (gameItem) {
|
|
|
currentGameData.value = gameItem;
|
|
|
updateGameStateFromData(gameItem);
|
|
|
@@ -159,7 +181,7 @@ const updateGameStateFromData = (gameData) => {
|
|
|
// 地图起点
|
|
|
if (gameData.mapStartPoint) {
|
|
|
const startPoint = JSON.parse(gameData.mapStartPoint);
|
|
|
- gameState.mapData.startPoint = { x: startPoint.x, y: startPoint.y };
|
|
|
+ gameState.mapData.startPoint = { x: startPoint.x, y: startPoint.y };
|
|
|
}
|
|
|
// 地图终点
|
|
|
if (gameData.mapEndPoint) {
|
|
|
@@ -176,9 +198,6 @@ const updateGameStateFromData = (gameData) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-onMounted(() => {
|
|
|
- fetchGameData();
|
|
|
-})
|
|
|
|
|
|
|
|
|
// 创建游戏状态的响应式对象
|
|
|
@@ -194,13 +213,15 @@ const gameState = reactive({
|
|
|
// 玩家相关状态
|
|
|
player: {
|
|
|
// 玩家当前位置坐标
|
|
|
- position: { x: 2, y: 2 },
|
|
|
+ position: { x: 1, y: 1 },
|
|
|
// 玩家当前朝向:0=上, 1=右, 2=下, 3=左
|
|
|
- direction: 0,
|
|
|
+ direction: 1,
|
|
|
// 是否正在发生碰撞
|
|
|
isColliding: false,
|
|
|
// 是否已到达终点
|
|
|
hasReachedEnd: false,
|
|
|
+ // 是否正在冰块上滑行
|
|
|
+ isSliding: false,
|
|
|
},
|
|
|
|
|
|
// 游戏状态信息
|
|
|
@@ -214,14 +235,15 @@ const gameState = reactive({
|
|
|
// 地图数据信息
|
|
|
mapData: {
|
|
|
// 游戏起点位置
|
|
|
- startPoint: { x: 2, y: 2 },
|
|
|
+ startPoint: { x: 1, y: 1 },
|
|
|
// 游戏终点位置
|
|
|
- endPoint: { x: 3, y: 2 },
|
|
|
- // 地图上所有可行走的点坐标集合
|
|
|
+ endPoint: { x: 4, y: 3 },
|
|
|
+ // 地图上所有可行走的点坐标集合,添加type属性区分普通点和冰块
|
|
|
walkablePoints: [
|
|
|
- { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 },
|
|
|
- { x: 2, y: 2 }, { x: 3, y: 2 },
|
|
|
- { x: 1, y: 3 }, { x: 3, y: 3 },
|
|
|
+ { x: 1, y: 1, type: 'ice' }, { x: 2, y: 1, type: 'ice' }, { x: 3, y: 1 }, { x: 4, y: 1 },
|
|
|
+ { x: 1, y: 2 }, { x: 2, y: 2 }, { x: 3, y: 2, type: 'ice' }, { x: 4, y: 2 },
|
|
|
+ { x: 1, y: 3 }, { x: 2, y: 3 }, { x: 3, y: 3 }, { x: 4, y: 3 },
|
|
|
+ { x: 4, y: 3 },
|
|
|
]
|
|
|
}
|
|
|
});
|
|
|
@@ -246,33 +268,45 @@ const playerImageSrc = computed(() => {
|
|
|
}
|
|
|
return playerImage;
|
|
|
});
|
|
|
+const isSliding = computed(() => gameState.player.isSliding);
|
|
|
+
|
|
|
|
|
|
// Blockly相关状态
|
|
|
let workspace = null;
|
|
|
|
|
|
-// 使用 Set 存储可行走点,提高查询效率
|
|
|
-let walkablePointsSet = new Set();
|
|
|
+// 使用 Map 存储可行走点及其类型,提高查询效率
|
|
|
+let walkablePointsMap = new Map();
|
|
|
|
|
|
-// 初始化可行走点集合
|
|
|
+// 初始化可行走点映射
|
|
|
function initWalkablePointsSet() {
|
|
|
- walkablePointsSet.clear();
|
|
|
+ walkablePointsMap.clear();
|
|
|
gameState.mapData.walkablePoints.forEach(point => {
|
|
|
- walkablePointsSet.add(`${point.x},${point.y}`);
|
|
|
+ walkablePointsMap.set(`${point.x},${point.y}`, point.type);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-// 优化后的可行走检查函数
|
|
|
+// 可行走检查函数
|
|
|
function isWalkable(x, y) {
|
|
|
- return walkablePointsSet.has(`${x},${y}`);
|
|
|
+ return walkablePointsMap.has(`${x},${y}`);
|
|
|
+}
|
|
|
+
|
|
|
+// 检查是否是冰块点 - 基于walkablePoints中的type属性
|
|
|
+function isIce(x, y) {
|
|
|
+ return walkablePointsMap.get(`${x},${y}`) === 'ice';
|
|
|
}
|
|
|
|
|
|
// 计算点的样式
|
|
|
function getPointStyle(point) {
|
|
|
+ // 检查是否是冰块点,添加不同的样式
|
|
|
+ const isIcePoint = isIce(point.x, point.y);
|
|
|
return {
|
|
|
left: point.x * tileSize.value - tileSize.value + 'px',
|
|
|
top: point.y * tileSize.value - tileSize.value + 'px',
|
|
|
width: tileSize.value + 'px',
|
|
|
- height: tileSize.value + 'px'
|
|
|
+ height: tileSize.value + 'px',
|
|
|
+ backgroundColor: isIcePoint ? 'rgba(144, 202, 249, 0.5)' : 'rgba(52, 152, 219, 0.2)',
|
|
|
+ border: isIcePoint ? '1px solid rgba(33, 150, 243, 0.8)' : '1px solid rgba(52, 152, 219, 0.5)',
|
|
|
+ boxShadow: isIcePoint ? '0 0 10px rgba(33, 150, 243, 0.5)' : 'none',
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -285,7 +319,7 @@ const playerStyle = computed(() => ({
|
|
|
'--player-image': `url(${playerImageSrc.value})`,
|
|
|
width: (tileSize.value * 0.8) + 'px',
|
|
|
height: (tileSize.value * 0.8) + 'px',
|
|
|
- margin: (tileSize.value * 0.1) + 'px'
|
|
|
+ margin: (tileSize.value * 0.1) + 'px',
|
|
|
}));
|
|
|
|
|
|
// 显示游戏消息
|
|
|
@@ -411,7 +445,6 @@ function registerJavaScriptGenerators() {
|
|
|
|
|
|
// 为重复循环块注册自定义生成器,确保支持异步操作
|
|
|
javascriptGenerator.forBlock['controls_repeat_ext'] = function(block) {
|
|
|
- // 获取循环次数 - 修改为使用 valueToCode 方法
|
|
|
const repeats = javascriptGenerator.valueToCode(block, 'TIMES', javascriptGenerator.ORDER_ATOMIC) || '0';
|
|
|
|
|
|
// 确保获取到的是数字类型,如果是字符串需要转换
|
|
|
@@ -535,7 +568,7 @@ async function smoothMoveTo(targetX, targetY) {
|
|
|
|
|
|
// 创建通用的移动函数
|
|
|
async function move(direction) {
|
|
|
- if (isColliding.value) {
|
|
|
+ if (isColliding.value || isSliding.value) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -565,6 +598,11 @@ async function move(direction) {
|
|
|
if (isWalkable(newX, newY)) {
|
|
|
// 使用平滑移动动画
|
|
|
await smoothMoveTo(newX, newY);
|
|
|
+
|
|
|
+ // 检查新位置是否是冰块,如果是则触发滑行
|
|
|
+ if (isIce(newX, newY)) {
|
|
|
+ await handleIceSliding();
|
|
|
+ }
|
|
|
} else {
|
|
|
// 发生碰撞
|
|
|
gameState.player.isColliding = true;
|
|
|
@@ -575,7 +613,6 @@ async function move(direction) {
|
|
|
executionAbortController.abort();
|
|
|
}
|
|
|
|
|
|
- // 保留碰撞抖动效果,但确保不会改变当前朝向
|
|
|
// 1秒后取消碰撞状态
|
|
|
setTimeout(() => {
|
|
|
gameState.player.isColliding = false;
|
|
|
@@ -586,6 +623,67 @@ async function move(direction) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 处理冰块滑行逻辑
|
|
|
+async function handleIceSliding() {
|
|
|
+ gameState.player.isSliding = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 循环检查是否可以继续滑行
|
|
|
+ while (true) {
|
|
|
+ // 计算下一个位置
|
|
|
+ let nextX = playerPosition.value.x;
|
|
|
+ let nextY = playerPosition.value.y;
|
|
|
+
|
|
|
+ // 根据当前方向计算下一个位置
|
|
|
+ switch(playerDirection.value) {
|
|
|
+ case 0: nextY--; break;
|
|
|
+ case 1: nextX++; break;
|
|
|
+ case 2: nextY++; break;
|
|
|
+ case 3: nextX--; break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查下一个位置是否可行走
|
|
|
+ if (isWalkable(nextX, nextY)) {
|
|
|
+ // 执行平滑移动到下一个位置
|
|
|
+ await smoothMoveTo(nextX, nextY);
|
|
|
+
|
|
|
+ // 检查下一个位置是否还是冰块
|
|
|
+ if (!isIce(nextX, nextY)) {
|
|
|
+ // 如果不是冰块,结束滑行
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加滑行间隔时间
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
+ } else {
|
|
|
+ // 下一个位置不可行走,检查是否是墙体
|
|
|
+ gameState.player.isColliding = true;
|
|
|
+ showGameMessage('哎呀,撞到墙了!', 'error');
|
|
|
+
|
|
|
+ // 立即中止整个代码执行
|
|
|
+ if (executionAbortController) {
|
|
|
+ executionAbortController.abort();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1秒后取消碰撞状态
|
|
|
+ setTimeout(() => {
|
|
|
+ gameState.player.isColliding = false;
|
|
|
+ }, 1000);
|
|
|
+
|
|
|
+ // 添加碰撞延迟
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('滑行过程中发生错误:', error);
|
|
|
+ } finally {
|
|
|
+ // 无论如何都要确保滑行状态被重置
|
|
|
+ gameState.player.isSliding = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 保留原有的API接口
|
|
|
window.moveForward = async function() {
|
|
|
await move(1);
|
|
|
@@ -643,9 +741,10 @@ window.isFinish = async function() {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ console.log(gameState.player.hasReachedEnd);
|
|
|
if (gameState.player.position.x === endPoint.value.x && gameState.player.position.y === endPoint.value.y) {
|
|
|
gameState.player.hasReachedEnd = true;
|
|
|
- showGameMessage('恭喜你到达终点!', 'success');
|
|
|
+ showGameMessage('恭喜你到达终点!', 'success' );
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -669,6 +768,7 @@ const runCode = async () => {
|
|
|
|
|
|
// 生成JavaScript代码
|
|
|
const code = javascriptGenerator.workspaceToCode(workspace) + "await isFinish();";
|
|
|
+ console.log(code);
|
|
|
|
|
|
try {
|
|
|
// 增强的安全检查
|
|
|
@@ -743,21 +843,12 @@ const resetPlayer = () => {
|
|
|
}
|
|
|
|
|
|
gameState.player.position = { ...startPoint.value };
|
|
|
- gameState.player.direction = 0; // 重置为向上方向
|
|
|
+ gameState.player.direction = 1; // 重置为向上方向
|
|
|
gameState.player.isColliding = false; //碰撞标志
|
|
|
gameState.player.hasReachedEnd = false;
|
|
|
+ gameState.player.isSliding = false; // 重置滑行状态
|
|
|
};
|
|
|
|
|
|
-// 组件挂载时初始化
|
|
|
-onMounted(() => {
|
|
|
- // 初始化可行走点集合
|
|
|
- initWalkablePointsSet();
|
|
|
-
|
|
|
- registerCustomBlocks();
|
|
|
- registerJavaScriptGenerators();
|
|
|
- initBlockly();
|
|
|
-});
|
|
|
-
|
|
|
// 组件卸载时清理
|
|
|
onUnmounted(() => {
|
|
|
if (workspace) {
|
|
|
@@ -926,6 +1017,14 @@ onUnmounted(() => {
|
|
|
100% { transform: rotate(var(--player-rotation)) translateX(0) translateY(0) scale(1); }
|
|
|
}
|
|
|
|
|
|
+/* 滑行动画 */
|
|
|
+@keyframes sliding {
|
|
|
+ 0% { transform: rotate(var(--player-rotation)) translateX(0) translateY(0); }
|
|
|
+ 25% { transform: rotate(var(--player-rotation)) translateX(2px) translateY(0); }
|
|
|
+ 75% { transform: rotate(var(--player-rotation)) translateX(-2px) translateY(0); }
|
|
|
+ 100% { transform: rotate(var(--player-rotation)) translateX(0) translateY(0); }
|
|
|
+}
|
|
|
+
|
|
|
/* 成功到达终点动画 */
|
|
|
.player.success {
|
|
|
animation: success 1s ease-in-out;
|