160 lines
6.5 KiB
JavaScript
160 lines
6.5 KiB
JavaScript
"use strict";
|
|
|
|
const GameControls = (function(){
|
|
|
|
const GameControls = function(hollow_bond){
|
|
|
|
const self = this,
|
|
euler = new THREE.Euler(0, 0, 0, "YXZ"),
|
|
keys = {
|
|
forward: false,
|
|
backward: false,
|
|
left: false,
|
|
right: false,
|
|
sprint: false,
|
|
crouch: false,
|
|
jump: false
|
|
};
|
|
|
|
let is_pointer_locked = false;
|
|
|
|
this.standing_height = 1.75;
|
|
this.player_width = this.standing_height / 4;
|
|
this.player_radius = this.player_width / 2;
|
|
this.crouch_height = this.standing_height / 2;
|
|
this.current_height = this.standing_height;
|
|
|
|
this.walk_speed = 3.5;
|
|
this.sprint_speed = 6.2;
|
|
this.crouch_speed = 1.8;
|
|
|
|
this.velocity_y = 0;
|
|
this.is_grounded = false;
|
|
this.is_crouching = false;
|
|
|
|
this.set_dimensions = height => {
|
|
self.standing_height = height || 1.75;
|
|
self.player_width = self.standing_height / 4;
|
|
self.player_radius = self.player_width / 2;
|
|
self.crouch_height = self.standing_height / 2;
|
|
self.current_height = self.standing_height;
|
|
};
|
|
|
|
this.setup = dungeon => {
|
|
const canvas = dungeon.renderer.domElement;
|
|
|
|
canvas.addEventListener("click", () => canvas.requestPointerLock());
|
|
document.addEventListener("pointerlockchange", () => {
|
|
is_pointer_locked = (document.pointerLockElement === canvas);
|
|
});
|
|
|
|
document.addEventListener("mousemove", evt => {
|
|
if(!is_pointer_locked) return;
|
|
euler.setFromQuaternion(dungeon.camera.quaternion);
|
|
euler.y -= evt.movementX * 0.002;
|
|
euler.x -= evt.movementY * 0.002;
|
|
euler.x = Math.max(-Math.PI / 2 + 0.01, Math.min(Math.PI / 2 - 0.01, euler.x));
|
|
dungeon.camera.quaternion.setFromEuler(euler);
|
|
});
|
|
|
|
window.addEventListener("keydown", evt => {
|
|
switch(evt.code){
|
|
case "KeyW": case "ArrowUp": keys.forward = true; break;
|
|
case "KeyS": case "ArrowDown": keys.backward = true; break;
|
|
case "KeyA": case "ArrowLeft": keys.left = true; break;
|
|
case "KeyD": case "ArrowRight": keys.right = true; break;
|
|
case "ShiftLeft": case "ShiftRight": keys.sprint = true; break;
|
|
case "KeyC": case "ControlLeft": keys.crouch = true; break;
|
|
case "Space": keys.jump = true; break;
|
|
}
|
|
});
|
|
|
|
window.addEventListener("keyup", evt => {
|
|
switch(evt.code){
|
|
case "KeyW": case "ArrowUp": keys.forward = false; break;
|
|
case "KeyS": case "ArrowDown": keys.backward = false; break;
|
|
case "KeyA": case "ArrowLeft": keys.left = false; break;
|
|
case "KeyD": case "ArrowRight": keys.right = false; break;
|
|
case "ShiftLeft": case "ShiftRight": keys.sprint = false; break;
|
|
case "KeyC": case "ControlLeft": keys.crouch = false; break;
|
|
case "Space": keys.jump = false; break;
|
|
}
|
|
});
|
|
};
|
|
|
|
this.update = (dungeon, delta) => {
|
|
if(!is_pointer_locked) return;
|
|
|
|
const gravity = hollow_bond.gravity || 9.81;
|
|
|
|
// 1. Estado de agachado
|
|
self.is_crouching = keys.crouch;
|
|
const target_height = self.is_crouching ? self.crouch_height : self.standing_height;
|
|
self.current_height += (target_height - self.current_height) * Math.min(1, 14 * delta);
|
|
|
|
// 2. Velocidad de traslación
|
|
let current_speed = self.walk_speed;
|
|
if(self.is_crouching){
|
|
current_speed = self.crouch_speed;
|
|
} else if(keys.sprint){
|
|
current_speed = self.sprint_speed;
|
|
}
|
|
|
|
// 3. Movimiento horizontal en XZ
|
|
const input_dir = new THREE.Vector3();
|
|
if(keys.forward) input_dir.z -= 1;
|
|
if(keys.backward) input_dir.z += 1;
|
|
if(keys.left) input_dir.x -= 1;
|
|
if(keys.right) input_dir.x += 1;
|
|
|
|
if(input_dir.lengthSq() > 0){
|
|
input_dir.normalize();
|
|
const move_dir = new THREE.Vector3(input_dir.x, 0, input_dir.z);
|
|
move_dir.applyAxisAngle(new THREE.Vector3(0, 1, 0), euler.y);
|
|
|
|
dungeon.camera.position.x += move_dir.x * current_speed * delta;
|
|
dungeon.camera.position.z += move_dir.z * current_speed * delta;
|
|
}
|
|
|
|
// 4. Salto
|
|
if(self.is_grounded && keys.jump){
|
|
const jump_height = self.standing_height / 2;
|
|
self.velocity_y = Math.sqrt(2 * gravity * jump_height);
|
|
self.is_grounded = false;
|
|
}
|
|
|
|
// 5. Aplicar gravedad en el aire
|
|
self.velocity_y -= gravity * delta;
|
|
dungeon.camera.position.y += self.velocity_y * delta;
|
|
|
|
// Reiniciamos apoyo; sólo un suelo bajo los pies lo activará
|
|
self.is_grounded = false;
|
|
|
|
// 6. Comprobación de apoyo en la sala actual
|
|
if(dungeon.current_room && dungeon.current_room.collider){
|
|
dungeon.current_room.collider.resolve_player(dungeon.camera.position, self, self.player_radius);
|
|
}
|
|
|
|
// 7. Comprobación de marcos (solo de la sala actual y a la misma altura)
|
|
if(dungeon.current_room && dungeon.current_room.model && dungeon.current_room.model.frames){
|
|
const feet_y = dungeon.camera.position.y - self.current_height;
|
|
|
|
for(const fid of dungeon.current_room.model.frames){
|
|
const f_obj = dungeon.objects.frame ? dungeon.objects.frame[fid] : null;
|
|
if(!f_obj || !f_obj.collider) continue;
|
|
|
|
// Comprobamos XZ y verificamos que el marco esté en este mismo piso (+- 1 metro)
|
|
const frame_y = f_obj.model ? f_obj.model.y : 0;
|
|
if(Math.abs(feet_y - frame_y) < 1.0){
|
|
if(f_obj.collider.contains(dungeon.camera.position.x, dungeon.camera.position.z)){
|
|
f_obj.collider.resolve_player(dungeon.camera.position, self, self.player_radius);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
};
|
|
|
|
return GameControls;
|
|
})(); |