From 27e1c8d440c85e2cbbba2d1b1c242710c2633101 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 25 Jan 2026 15:09:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=8F=AF=E8=A7=86?= =?UTF-8?q?=E5=8C=96=E5=9C=B0=E5=9B=BE=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建地图布局工具(mapLayout.js) - 预定义区域位置配置 - BFS计算可达距离和最短路径 - 地图数据结构构建 - 添加迷你地图组件(MiniMap.vue) - 可视化显示所有区域节点和连接线 - 当前位置脉冲动画高亮 - 相邻区域虚线标记 - 锁定区域显示🔒图标 - 显示到各区域的距离步数 - 点击区域显示前往路径 - 图例说明不同区域类型 Co-Authored-By: Claude Opus 4.5 --- components/common/MiniMap.vue | 454 +++++++++++++++++++++++++++++++++ components/panels/MapPanel.vue | 10 + utils/mapLayout.js | 254 ++++++++++++++++++ 3 files changed, 718 insertions(+) create mode 100644 components/common/MiniMap.vue create mode 100644 utils/mapLayout.js diff --git a/components/common/MiniMap.vue b/components/common/MiniMap.vue new file mode 100644 index 0000000..cf65fe5 --- /dev/null +++ b/components/common/MiniMap.vue @@ -0,0 +1,454 @@ + + + + + diff --git a/components/panels/MapPanel.vue b/components/panels/MapPanel.vue index 7416ebe..fd134ce 100644 --- a/components/panels/MapPanel.vue +++ b/components/panels/MapPanel.vue @@ -14,6 +14,11 @@ {{ currentLocation.description }} + + + + + ⚔️ 战斗中 @@ -123,6 +128,7 @@ import { triggerExploreEvent, tryFlee } from '@/utils/eventSystem.js' import TextButton from '@/components/common/TextButton.vue' import ProgressBar from '@/components/common/ProgressBar.vue' import FilterTabs from '@/components/common/FilterTabs.vue' +import MiniMap from '@/components/common/MiniMap.vue' const game = useGameStore() const player = usePlayerStore() @@ -424,6 +430,10 @@ function toggleAutoCombat() { margin-bottom: 16rpx; } + &__minimap { + margin-bottom: 16rpx; + } + &__locations-header { padding: 12rpx 16rpx 8rpx; } diff --git a/utils/mapLayout.js b/utils/mapLayout.js new file mode 100644 index 0000000..df4ab23 --- /dev/null +++ b/utils/mapLayout.js @@ -0,0 +1,254 @@ +/** + * 地图布局系统 - 根据区域连接生成可视化地图 + */ + +import { LOCATION_CONFIG } from '@/config/locations.js' + +/** + * 构建地图图形结构 + * @returns {Object} 地图数据 + */ +export function buildMapGraph() { + const nodes = [] + const edges = [] + + // 首先创建所有节点 + for (const [id, config] of Object.entries(LOCATION_CONFIG)) { + nodes.push({ + id, + name: config.name, + type: config.type, + x: 0, + y: 0 + }) + } + + // 创建连接边 + for (const [id, config] of Object.entries(LOCATION_CONFIG)) { + if (config.connections) { + for (const connId of config.connections) { + // 避免重复边 + const edgeExists = edges.some(e => + (e.from === id && e.to === connId) || + (e.from === connId && e.to === id) + ) + if (!edgeExists) { + edges.push({ from: id, to: connId }) + } + } + } + } + + return { nodes, edges } +} + +/** + * 简单的力导向布局算法 + * 将节点分布在合理的坐标上 + * @param {Object} graph - 地图图形结构 + * @param {Object} options - 布局选项 + * @returns {Object} 带有坐标的节点 + */ +export function layoutMap(graph, options = {}) { + const { nodes, edges } = graph + const { width = 300, height = 400 } = options + + // 创建邻接表 + const adj = {} + for (const node of nodes) { + adj[node.id] = [] + } + for (const edge of edges) { + adj[edge.from].push(edge.to) + adj[edge.to].push(edge.from) + } + + // 找到中心节点(连接最多的节点作为起点) + let centerNode = nodes[0] + for (const node of nodes) { + if (adj[node.id].length > adj[centerNode.id].length) { + centerNode = node + } + } + + // BFS 分层布局 + const levels = {} + const visited = new Set() + const queue = [{ id: centerNode.id, level: 0 }] + + while (queue.length > 0) { + const { id, level } = queue.shift() + if (visited.has(id)) continue + visited.add(id) + + if (!levels[level]) levels[level] = [] + levels[level].push(id) + + for (const neighbor of adj[id]) { + if (!visited.has(neighbor)) { + queue.push({ id: neighbor, level: level + 1 }) + } + } + } + + // 计算节点位置 + const nodePositions = {} + const levelHeight = height / (Object.keys(levels).length + 1) + + for (const [level, nodeIds] of Object.entries(levels)) { + const y = (parseInt(level) + 1) * levelHeight + const levelWidth = width / (nodeIds.length + 1) + + nodeIds.forEach((nodeId, index) => { + nodePositions[nodeId] = { + x: (index + 1) * levelWidth, + y + } + }) + } + + // 更新节点坐标 + return nodes.map(node => ({ + ...node, + x: nodePositions[node.id]?.x || width / 2, + y: nodePositions[node.id]?.y || height / 2 + })) +} + +/** + * 获取从当前位置可达的区域(包括多步可达) + * @param {string} currentLocation - 当前位置ID + * @param {number} maxDistance - 最大距离(步数) + * @returns {Object} 可达区域信息,key为locationId,value为距离 + */ +export function getReachableLocations(currentLocation, maxDistance = 10) { + const distances = {} + const queue = [{ id: currentLocation, dist: 0 }] + + distances[currentLocation] = 0 + + while (queue.length > 0) { + const { id, dist } = queue.shift() + + if (dist >= maxDistance) continue + + // 找到所有连接的节点 + const connections = LOCATION_CONFIG[id]?.connections || [] + for (const connId of connections) { + if (distances[connId] === undefined) { + distances[connId] = dist + 1 + queue.push({ id: connId, dist: dist + 1 }) + } + } + } + + return distances +} + +/** + * 获取地图的路径信息 + * @param {string} from - 起点 + * @param {string} to - 终点 + * @returns {Array} 路径数组 + */ +export function getPath(from, to) { + if (from === to) return [from] + + const { edges } = buildMapGraph() + const adj = {} + for (const node of Object.keys(LOCATION_CONFIG)) { + adj[node] = LOCATION_CONFIG[node]?.connections || [] + } + + // BFS 寻找最短路径 + const queue = [[from]] + const visited = new Set([from]) + + while (queue.length > 0) { + const path = queue.shift() + const current = path[path.length - 1] + + if (current === to) { + return path + } + + for (const neighbor of adj[current]) { + if (!visited.has(neighbor)) { + visited.add(neighbor) + queue.push([...path, neighbor]) + } + } + } + + return null // 无路径 +} + +/** + * 预定义的地图布局配置 + * 为每个区域指定固定的显示位置 + */ +export const PREDEFINED_LAYOUT = { + // 营地为中心 + camp: { x: 150, y: 200 }, + // 营地周围 + market: { x: 50, y: 120 }, + blackmarket: { x: 250, y: 120 }, + wild1: { x: 150, y: 320 }, + // 更远的地方 + boss_lair: { x: 150, y: 440 }, + basement: { x: 150, y: 540 } +} + +/** + * 使用预定义布局获取节点位置 + * @param {Object} graph - 地图图形结构 + * @returns {Array} 带有坐标的节点 + */ +export function getPredefinedLayout(graph) { + const { nodes } = graph + + return nodes.map(node => ({ + ...node, + x: PREDEFINED_LAYOUT[node.id]?.x || 150, + y: PREDEFINED_LAYOUT[node.id]?.y || 200 + })) +} + +/** + * 获取可视化的地图数据 + * @param {string} currentLocation - 当前位置 + * @returns {Object} 地图数据 + */ +export function getMapData(currentLocation) { + const graph = buildMapGraph() + const layoutNodes = getPredefinedLayout(graph) + + // 计算画布大小 + let maxX = 0, maxY = 0 + for (const node of layoutNodes) { + maxX = Math.max(maxX, node.x) + maxY = Math.max(maxY, node.y) + } + + // 获取可达距离 + const reachable = getReachableLocations(currentLocation, 10) + + // 计算到每个区域的路径 + const paths = {} + for (const node of layoutNodes) { + const path = getPath(currentLocation, node.id) + if (path) { + paths[node.id] = path + } + } + + return { + nodes: layoutNodes, + edges: graph.edges, + width: maxX + 50, + height: maxY + 50, + currentLocation, + reachable, + paths + } +}