我有一个OrbitControls摄像头,它围绕点旋转:target = player.position.x,y和z。没关系,但是播放器位于屏幕中央。我需要一台相机绕环旋转...first picture: target is in the centre, second: target is always placed to the left of the centre
让我解释一下:这是我的代码:
controls.target.x = player.position.x;
controls.target.y = player.position.y+3;
controls.target.z = player.position.z;
因此,我们绕固定点旋转。
但是我需要一个可以根据controls.getAzimuthalAngle()或其他方式更改其位置的点。相机必须像在GTA V或其他游戏中一样,其中玩家不在屏幕中央,而是在屏幕左侧偏左,因此例如在瞄准时,玩家更方便,不会干扰瞄准中心。
帮助...
通常,对于第三人称相机,您经常将相机对准播放器(或与播放器有一定偏移量,或连接到播放器的某些物体)
然后您可以让摄像机随时间跟随播放器。
在下面的代码中,播放器后面有一个cameraRig
。摄像机装备在那里,因此我们无需进行任何数学运算即可将摄像机保持在地面上方。同样,有一个camTarget
附在播放器上。这样相机就可以看到肩膀的高度。
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const keys = {};
const scene = new THREE.Scene();
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 500;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// put the camera 5 units above the rig
const cameraRig = new THREE.Object3D();
cameraRig.add(camera);
camera.position.y = 5;
scene.add(cameraRig);
cameraRig.position.z = 5;
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const size = 200;
const divisions = 100;
const gridHelper = new THREE.GridHelper(size, divisions);
scene.add(gridHelper);
}
const boxWidth = 0.5;
const boxHeight = 2;
const boxDepth = 0.5;
const geometry = new THREE.CylinderGeometry(boxWidth, boxDepth, boxHeight);
const material = new THREE.MeshPhongMaterial({color:'red'});
const cube = new THREE.Mesh(geometry, material);
const player = new THREE.Object3D();
const camTarget = new THREE.Object3D();
cube.position.y = boxHeight / 2; // move cube above ground
player.add(cube);
camTarget.position.y = boxHeight * 3 / 2; // target 2/3ds up the player
player.add(camTarget);
scene.add(player);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
const moveDir = new THREE.Vector3();
const camTargetPos = new THREE.Vector3();
let then = 0;
function render(now) {
now *= 0.001;
deltaTime = now - then;
then = now;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
// left, right, a, d
const dx = ((keys[37] || keys[65]) ? 1 : 0) +
((keys[39] || keys[68]) ? -1 : 0);
// up, down, w, s
const dy = ((keys[38] || keys[87]) ? 1 : 0) +
((keys[40] || keys[83]) ? -1 : 0);
const playerMoveSpeed = 10; // units per second
camera.getWorldDirection(moveDir);
moveDir.y = 0; // no up down movement
moveDir.normalize();
// move player forward/back
player.position.addScaledVector(moveDir, dy * playerMoveSpeed * deltaTime);
// rotate direction 90 degrees
const t = moveDir.x;
moveDir.x = moveDir.z;
moveDir.z = -t;
// move player left/right
player.position.addScaledVector(moveDir, dx * playerMoveSpeed * deltaTime);
// if the cameraRig is too far from
// player then move it
const maxDistance = 6;
const maxCamMoveSpeed = 0.015;
const distance = cameraRig.position.distanceTo(player.position);
if (distance > maxDistance) {
const amount = maxCamMoveSpeed;
cameraRig.position.lerp(player.position, amount);
}
camTarget.getWorldPosition(camTargetPos);
camera.lookAt(camTargetPos);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
window.addEventListener('keydown', (e) => {
e.preventDefault();
keys[e.keyCode] = true;
});
window.addEventListener('keyup', (e) => {
e.preventDefault();
keys[e.keyCode] = false;
});
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.min.js"></script>
<canvas id="c" tabindex="0"></canvas>
第三人称摄影机可能很难。上面的一个太简单了。您可以将其想像成是将cameraRig拖到播放器后面的字符串上。如果备份,相机不会移动。仅在摄像机离开maxDistance时才移动。
通常将照相机放在摇杆上,所以如果您向后走,摇杆会将照相机推回原位。
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const keys = {};
const scene = new THREE.Scene();
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 500;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// put the camera 5 units above the rig
const cameraRig = new THREE.Object3D();
cameraRig.add(camera);
camera.position.y = 5;
scene.add(cameraRig);
cameraRig.position.z = 5;
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const size = 200;
const divisions = 100;
const gridHelper = new THREE.GridHelper(size, divisions);
scene.add(gridHelper);
}
const boxWidth = 0.5;
const boxHeight = 2;
const boxDepth = 0.5;
const geometry = new THREE.CylinderGeometry(boxWidth, boxDepth, boxHeight);
const material = new THREE.MeshPhongMaterial({color:'red'});
const cube = new THREE.Mesh(geometry, material);
const player = new THREE.Object3D();
const camTarget = new THREE.Object3D();
cube.position.y = boxHeight / 2; // move cube above ground
player.add(cube);
camTarget.position.y = boxHeight * 3 / 2; // target 2/3ds up the player
player.add(camTarget);
scene.add(player);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
const moveDir = new THREE.Vector3();
const camTargetPos = new THREE.Vector3();
const rigTargetPos = new THREE.Vector3();
function moveCameraToDistance(desiredDistance) {
const maxCamMoveSpeed = 0.02;
// delta from player to rig
rigTargetPos.subVectors(cameraRig.position, player.position);
// remove up/down
rigTargetPos.y = 0; // no up/down
// make unit vector
rigTargetPos.normalize();
// make desiredDistance long
rigTargetPos.multiplyScalar(desiredDistance);
// add player position
rigTargetPos.add(player.position);
// move rig toward that position
const curDistance = cameraRig.position.distanceTo(player.position);
cameraRig.position.lerp(
rigTargetPos,
THREE.MathUtils.lerp(
maxCamMoveSpeed,
1,
THREE.MathUtils.clamp(1 - curDistance / desiredDistance, 0, 1)));
}
let then = 0;
function render(now) {
now *= 0.001;
deltaTime = now - then;
then = now;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
// left, right, a, d
const dx = ((keys[37] || keys[65]) ? 1 : 0) +
((keys[39] || keys[68]) ? -1 : 0);
// up, down, w, s
const dy = ((keys[38] || keys[87]) ? 1 : 0) +
((keys[40] || keys[83]) ? -1 : 0);
const playerMoveSpeed = 10; // units per second
camera.getWorldDirection(moveDir);
moveDir.y = 0; // no up down movement
moveDir.normalize();
// move player forward/back
player.position.addScaledVector(moveDir, dy * playerMoveSpeed * deltaTime);
// rotate direction 90 degrees
const t = moveDir.x;
moveDir.x = moveDir.z;
moveDir.z = -t;
// move player left/right
player.position.addScaledVector(moveDir, dx * playerMoveSpeed * deltaTime);
// keep camera at distance
const minDistance = 4;
const maxDistance = 6;
const distance = cameraRig.position.distanceTo(player.position);
if (distance > maxDistance) {
moveCameraToDistance(maxDistance);
} else if (distance < minDistance) {
moveCameraToDistance(minDistance);
}
// compute point from player in direction of rig
// at desired distance
camTarget.getWorldPosition(camTargetPos);
camera.lookAt(camTargetPos);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
window.addEventListener('keydown', (e) => {
e.preventDefault();
keys[e.keyCode] = true;
});
window.addEventListener('keyup', (e) => {
e.preventDefault();
keys[e.keyCode] = false;
});
}
main();
body {
margin: 0;
}
#c {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.js"></script>
<canvas id="c" tabindex="0"></canvas>
然后,您需要处理诸如照相机和播放器之间的东西之类的事情。 (某些游戏会将这些内容淡出)。您还需要处理说走进一扇门的问题。有一会儿,玩家将在门的一侧,而摄像机则在门的另一侧(或者恰好在门口的相对侧),所以摄像机会掉下来,以便可以透过门看到它吗?墙壁会褪色吗?摄像机会跳吗?穿过门,从其他位置开始看。谣言是在《马里奥64》中,一个程序员整年都在照像机上工作,没别的。)
[请注意,上面的某些代码仅在以下特殊情况下有效:someObject.position
是世界空间位置(该对象没有父对象,或者如果所有父对象都具有position = 0,0,0 rotation = 0 ,0,0,比例尺= 1,1,1)。如果对象确实有父母,那么您需要使用
const wp = new THREE.Vector3();
someObject.getWorldPosition(wp);
并且,如果您想应用世界位置,则需要做更多的工作才能使该位置成为父母的相对位置。现在,为了简单起见,我只使用世界位置就是其位置的对象。