Skip to content

223. 综合实战:开放世界(二十四)

Published:

前面我们实现了很多功能了。

现在开放世界里有车辆、飞机、npc、电脑等可以互动的元素。

随着开放世界越来越大,玩家可能不知道在哪里去找这些,所以我们可以做一个小地图。

在右上角标出玩家当前位置,另外几个可交互元素的位置。

首先我们把小地图画一下:

html 部分:

image.png

<!-- 小地图 -->
    <div id="miniMap">
      <canvas class="map-canvas" id="miniMapCanvas"></canvas>
      <div class="map-legend">
        <div class="legend-item">
          <span class="legend-icon player-icon"></span>
          <span>玩家</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon car-icon"></span>
          <span>车辆</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon plane-icon"></span>
          <span>飞机</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon npc-icon"></span>
          <span>NPC</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon house-icon"></span>
          <span>房屋</span>
        </div>
      </div>
      <div class="map-hint">按 M 键打开全屏地图</div>
    </div>
    <!-- 全屏地图 -->
    <div id="fullMap" style="display: none;">
      <div class="map-header">
        <h2>地图</h2>
        <button id="closeMapBtn" class="close-btn">关闭 (M)</button>
      </div>
      <canvas class="map-canvas-full" id="fullMapCanvas"></canvas>
      <div class="map-legend-full">
        <div class="legend-item">
          <span class="legend-icon player-icon"></span>
          <span>玩家</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon car-icon"></span>
          <span>车辆</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon plane-icon"></span>
          <span>飞机</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon npc-icon"></span>
          <span>NPC</span>
        </div>
        <div class="legend-item">
          <span class="legend-icon house-icon"></span>
          <span>房屋</span>
        </div>
      </div>
    </div>

就是一个 canvas 用来绘制各种元素的位置。

然后下面是图例,标出各种元素的颜色。

然后按 m 可以全屏,元素也是一样的结构。

加一下样式:

image.png

/* 小地图样式 */
#miniMap {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 250px;
  height: 250px;
  background: rgba(255, 255, 255, 0.9);
  border: 3px solid #333;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  z-index: 999;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.map-canvas {
  width: 100%;
  height: 200px;
  background: #90a955;
  position: relative;
  display: block;
}

.map-legend {
  padding: 8px;
  font-size: 10px;
  background: rgba(255, 255, 255, 0.95);
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 4px;
}

.legend-icon {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: inline-block;
}

.player-icon {
  background: #ff0000;
  border: 2px solid #fff;
  box-shadow: 0 0 4px rgba(255, 0, 0, 0.8);
}

.car-icon {
  background: #0066ff;
  border: 2px solid #fff;
}

.plane-icon {
  background: #00ff00;
  border: 2px solid #fff;
}

.npc-icon {
  background: #ffaa00;
  border: 2px solid #fff;
}

.house-icon {
  background: #8b4513;
  border: 2px solid #fff;
}

.map-hint {
  padding: 4px 8px;
  font-size: 9px;
  color: #666;
  text-align: center;
  background: rgba(240, 240, 240, 0.9);
}

/* 全屏地图样式 */
#fullMap {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.95);
  z-index: 2000;
  display: flex;
  flex-direction: column;
}

.map-header {
  padding: 20px;
  background: rgba(0, 0, 0, 0.8);
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 2px solid #333;
}

.map-header h2 {
  color: white;
  margin: 0;
  font-size: 24px;
}

.close-btn {
  padding: 10px 20px;
  background: #ff4444;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;
  font-weight: bold;
}

.close-btn:hover {
  background: #ff6666;
}

.map-canvas-full {
  flex: 1;
  background: #90a955;
  position: relative;
  display: block;
  margin: 20px auto;
  border: 3px solid #333;
  border-radius: 8px;
  max-width: 800px;
  max-height: 800px;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.map-legend-full {
  padding: 15px 20px;
  background: rgba(0, 0, 0, 0.8);
  display: flex;
  gap: 20px;
  justify-content: center;
  border-top: 2px solid #333;
}

.map-legend-full .legend-item {
  font-size: 14px;
  color: white;
}

.map-legend-full .legend-icon {
  width: 16px;
  height: 16px;
}

整个小地图固定在右上角。

写一下每种图例的颜色。

看下效果:

image.png

这样,小地图就绘制在右上角了。

然后先做一下按 M 键全屏展示的功能。

改一下提示的文案:

image.png

tipElement.textContent = '靠近车辆按 X 上车 | 靠近飞机按 C 上飞机 | 靠近电脑按 E 打电脑 | 按 M 打开地图';

加上打开地图的提示。

做一下对应处理:

image.png

else if (event.key === 'm' || event.key === 'M') {
    // M 键处理:切换全屏地图(在非电脑模式下)
    if (!isComputerView) {
        toggleFullMap();
    }
}

因为电脑模式下可能有键盘输入,所以这里单独排除下这种情况。

然后创建一个单独的文件实现这个 toggleFullMap

创建 src/map.js

// 地图系统类
class MapSystem {
  constructor() {
    this.fullMap = document.getElementById('fullMap');
    this.isFullMapOpen = false;
    
    // 绑定关闭按钮事件
    const closeBtn = document.getElementById('closeMapBtn');
    if (closeBtn) {
      closeBtn.addEventListener('click', () => this.toggleFullMap());
    }
  }
  
  // 切换全屏地图
  toggleFullMap() {
    this.isFullMapOpen = !this.isFullMapOpen;
    if (this.isFullMapOpen) {
      this.fullMap.style.display = 'flex';
    } else {
      this.fullMap.style.display = 'none';
    }
  }
}

// 创建并导出地图系统实例
export const mapSystem = new MapSystem();

// 导出切换全屏地图的函数
export function toggleFullMap() {
  mapSystem.toggleFullMap();
}

这里其实就是两种元素的显示切换。

因为前面已经绘制了两种元素了。

还要处理下关闭按钮的逻辑,也是样式切换。

试下效果:

2026-02-08 20.09.48.gif

这样,小地图就绘制加好了。

案例代码上传了小册仓库

总结

这节我们加上了小地图。

首先用 html + css 把两种模式的样式、结构写好。

然后加一下 js 的显示切换逻辑。

我们留了 canvas 用来绘制玩家、npc 等的位置,下节绘制下。

评论