Skip to content

192. 玩家漫游(一)

Published:

我们在玩一些 3D 游戏的时候,可以通过键盘、鼠标控制人物移动:

image.png

并且视野也会跟着改变。

那如果我们也要实现这样的效果呢?

如何实现键盘、鼠标控制人物行走方向,如何实现镜头跟随呢?

这节开始我们就来实现一下。

创建项目:

npx create-vite player-roaming

image.png

进入项目,安装依赖:

pnpm install
pnpm install --save three
pnpm install --save-dev @types/three

先写下基础代码:

改下 src/main.js

import './style.css';
import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';

const scene = new THREE.Scene();

scene.add(mesh);

const light = new THREE.DirectionalLight(0xffffff);
light.position.set(500, 300, 600);
scene.add(light);

const light2 = new THREE.AmbientLight();
scene.add(light2);

const axesHelper = new THREE.AxesHelper(1000);
scene.add(axesHelper);

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
camera.position.set(0, 500, 500);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(width, height)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

创建 Scene、Light、Camera、Renderer

改下 style.css

body {
    margin: 0;
}

然后创建 mesh.js

import * as THREE from 'three';

const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshPhongMaterial({
    color: 'orange'
})
const mesh = new THREE.Mesh(geometry, material);

export default mesh;

先画个立方体

跑一下:

npm run dev

image.png

2025-08-10 09.03.47.gif

然后我们找个人物行走的模型。

用 three.js 官方仓库里这个士兵的模型:

https://github.com/mrdoob/three.js/blob/dev/examples/models/gltf/Soldier.glb

image.png

image.png

代码里加载下:

image.png

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = new GLTFLoader();

const mesh = new THREE.Group();

loader.load("./Soldier.glb", function (gltf) {
    console.log(gltf);
    mesh.add(gltf.scene);
    gltf.scene.scale.set(100, 100, 100);
})

export default mesh;

2025-09-21 09.23.23.gif

我们播放下它的行走动画:

image.png

image.png

const mixer = new THREE.AnimationMixer(mesh);
const clipAction = mixer.clipAction(gltf.animations[3]);
clipAction.play();

const clock = new THREE.Clock();
function render() {
    const delta = clock.getDelta();
    mixer.update(delta);

    requestAnimationFrame(render);
}

render();

2025-09-21 09.24.26.gif

然后来实现人物行走。

玩游戏的时候是按下 W、A、S、D 控制人物行走。

这里我们先实现按 W 向前行走:

image.png

document.addEventListener('keydown', e => {
  console.log(e.code + ' down');
});

document.addEventListener('keyup', e => {
  console.log(e.code + ' up');
});

试一下:

2025-09-21 09.32.32.gif

我们用一个对象来记录每个键的按下状态:


let keyPressed = {
  w: false,
  a: false,
  s: false,
  d: false
};

document.addEventListener('keydown', e => {
  switch(e.code) {
    case 'KeyA':
      keyPressed.a = true;
      break;
    case 'KeyW':
      keyPressed.w = true;
      break;
    case 'KeyS':
      keyPressed.s = true;
      break;
    case 'KeyD':
      keyPressed.d = true;
      break;
  }
});

document.addEventListener('keyup', e => {
  switch(e.code) {
    case 'KeyA':
      keyPressed.a = false;
      break;
    case 'KeyW':
      keyPressed.w = false;
      break;
    case 'KeyS':
      keyPressed.s = false;
      break;
    case 'KeyD':
      keyPressed.d = false;
      break;
  }
});

把渲染循环移到后面,这样我们可以在渲染循环里读取出来

如果按下 w 键,人物就往前走,否则不动:

image.png

const clock = new THREE.Clock();
const v = new THREE.Vector3(0, 0, -200);
function render() {
  const deltaTime = clock.getDelta();
  if(keyPressed.w) {
    const movePos = v.clone().multiplyScalar(deltaTime);
    mesh.position.add(movePos);
  }

  renderer.render(scene, camera);
  requestAnimationFrame(render);
}

render();

v 是速度,这里方向是 z 轴负半轴方向走。

每次移动 v * t 速度 * 时间的距离。

试下效果:

2025-09-21 09.42.36.gif

这样,控制人物行走的基础功能就完成了。

案例代码上传了小册仓库

总结

这节我们开始做玩家漫游的效果,也就是射击游戏里那种第一人称感觉。

我们监听了 wasd 的按下状态,如果是按下,就在速度的方向上移动一段距离。

下节我们加上更多的加速、减速、后退等功能。

评论