Skip to content

225. 综合实战:开放世界(二十六)

Published:

上节加上了小地图:

2026-02-08 21.09.00.gif

可以实时显示玩家位置以及车辆、NPC、电脑等的位置。

这节我们加一下行走方向的箭头。

首先之前是获取 rotation.y 这样不如直接拿到相机获取 camera.getWorldPosition(worldPos) 更好。

我们改一下:

image.png

// 导出更新标记的函数
export function updateMapMarkers(characterModel, carModel, planeModel, personModel, camera) {
  // 更新玩家位置
  if (characterModel) {
    const worldPos = new THREE.Vector3();
    characterModel.getWorldPosition(worldPos);
    
    // 使用相机的实际朝向来计算方向,因为玩家移动是基于相机方向的
    let rot = characterModel.rotation.y;
    if (camera) {
      const forward = new THREE.Vector3();
      camera.getWorldDirection(forward);
      forward.y = 0;
      forward.normalize();
      // 计算相机朝向的角度(相对于+Z方向)
      rot = Math.atan2(forward.x, forward.z);
    }
    
    mapSystem.updateMarker('player', worldPos.x, worldPos.z, rot);
  }
  
  // 更新车辆位置
  if (carModel) {
    const worldPos = new THREE.Vector3();
    carModel.getWorldPosition(worldPos);
    mapSystem.updateMarker('car', worldPos.x, worldPos.z);
  }
  
  // 更新飞机位置
  if (planeModel) {
    const worldPos = new THREE.Vector3();
    planeModel.getWorldPosition(worldPos);
    mapSystem.updateMarker('plane', worldPos.x, worldPos.z);
  }
  
  // 更新NPC位置
  if (personModel) {
    const worldPos = new THREE.Vector3();
    personModel.getWorldPosition(worldPos);
    mapSystem.updateMarker('npc', worldPos.x, worldPos.z);
  }
}

用到的地方也改一下:

image.png

然后加一下绘制箭头的逻辑:

image.png

// 绘制玩家方向指示
  drawPlayerDirection(ctx, mapX, mapZ, rotationY) {
    const length = 15;

    // 计算箭头终点位置
    const endX = mapX + Math.sin(rotationY) * length;
    const endZ = mapZ - Math.cos(rotationY) * length;
    
    // 绘制箭头线
    ctx.strokeStyle = '#ff0000';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(mapX, mapZ);
    ctx.lineTo(endX, endZ);
    ctx.stroke();
    
    // 绘制箭头头部
    const arrowSize = 5;
    const angle = Math.atan2(endZ - mapZ, endX - mapX);
    ctx.beginPath();
    ctx.moveTo(endX, endZ);
    ctx.lineTo(
      endX - arrowSize * Math.cos(angle - Math.PI / 6),
      endZ - arrowSize * Math.sin(angle - Math.PI / 6)
    );
    ctx.lineTo(
      endX - arrowSize * Math.cos(angle + Math.PI / 6),
      endZ - arrowSize * Math.sin(angle + Math.PI / 6)
    );
    ctx.closePath();
    ctx.fillStyle = '#ff0000';
    ctx.fill();
  }

就是基于 rotationY 计算下 cos、sin 的边的长度。

然后画箭头,也就是 3 个 lineTo

看下效果:

2026-02-08 21.25.28.gif

这样,箭头就绘制完成了。

案例代码上传了小册仓库

总结

这节我们加上了箭头的绘制。

首先用 camera.getWorldDirection 拿到方向,然后基于这个信息计算箭头的方向来画出箭头。

不断的拿到最新的方向绘制一下就可以了。

评论