implement player and movement input
This commit is contained in:
@@ -4,8 +4,13 @@ window.GameWindow = class {
|
|||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.canvasWrapperId = canvasWrapperId;
|
this.canvasWrapperId = canvasWrapperId;
|
||||||
|
|
||||||
|
this.mouseMotionX = 0;
|
||||||
|
this.mouseMotionY = 0;
|
||||||
|
this.mouseLocked = false;
|
||||||
|
|
||||||
// Add web renderer canvas to wrapper
|
// Add web renderer canvas to wrapper
|
||||||
document.getElementById(this.canvasWrapperId).appendChild(renderer.canvasElement);
|
let canvas = renderer.canvasElement;
|
||||||
|
document.getElementById(this.canvasWrapperId).appendChild(canvas);
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
this.initialize();
|
this.initialize();
|
||||||
@@ -13,10 +18,23 @@ window.GameWindow = class {
|
|||||||
// On resize
|
// On resize
|
||||||
let scope = this;
|
let scope = this;
|
||||||
window.addEventListener('resize', _ => scope.initialize(), false);
|
window.addEventListener('resize', _ => scope.initialize(), false);
|
||||||
|
|
||||||
|
// Request focus
|
||||||
|
canvas.onclick = function () {
|
||||||
|
canvas.requestPointerLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus listener
|
||||||
|
document.addEventListener('pointerlockchange', _ => this.onFocusChanged(), false);
|
||||||
|
|
||||||
|
// Mouse motion
|
||||||
|
document.addEventListener('mousemove', event => this.onMouseMove(event), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
|
// Create keyboard
|
||||||
|
Keyboard.create();
|
||||||
|
|
||||||
// Get canvas size
|
// Get canvas size
|
||||||
let canvasElement = document.getElementById(this.canvasWrapperId);
|
let canvasElement = document.getElementById(this.canvasWrapperId);
|
||||||
this.canvasWidth = canvasElement.offsetWidth;
|
this.canvasWidth = canvasElement.offsetWidth;
|
||||||
@@ -27,4 +45,13 @@ window.GameWindow = class {
|
|||||||
this.renderer.camera.updateProjectionMatrix();
|
this.renderer.camera.updateProjectionMatrix();
|
||||||
this.renderer.webRenderer.setSize(this.canvasWidth, this.canvasHeight);
|
this.renderer.webRenderer.setSize(this.canvasWidth, this.canvasHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFocusChanged() {
|
||||||
|
this.mouseLocked = document.pointerLockElement === this.renderer.canvasElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseMove(event) {
|
||||||
|
this.mouseMotionX = -event.movementX;
|
||||||
|
this.mouseMotionY = event.movementY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,9 +11,14 @@ window.Minecraft = class {
|
|||||||
this.frames = 0;
|
this.frames = 0;
|
||||||
this.lastTime = Date.now();
|
this.lastTime = Date.now();
|
||||||
|
|
||||||
|
// Create world
|
||||||
this.world = new World();
|
this.world = new World();
|
||||||
this.worldRenderer.scene.add(this.world.group);
|
this.worldRenderer.scene.add(this.world.group);
|
||||||
|
|
||||||
|
// Create player
|
||||||
|
this.player = new Player(this.world);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,12 +32,12 @@ window.Minecraft = class {
|
|||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
if (scope.running) {
|
if (scope.running) {
|
||||||
scope.requestNextFrame();
|
scope.requestNextFrame();
|
||||||
scope.onRender();
|
scope.onLoop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onRender() {
|
onLoop() {
|
||||||
// Update the timer
|
// Update the timer
|
||||||
this.timer.advanceTime();
|
this.timer.advanceTime();
|
||||||
|
|
||||||
@@ -42,22 +47,34 @@ window.Minecraft = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the game
|
// Render the game
|
||||||
this.worldRenderer.render(this.timer.partialTicks);
|
this.onRender(this.timer.partialTicks);
|
||||||
|
|
||||||
// Increase rendered frame
|
// Increase rendered frame
|
||||||
this.frames++;
|
this.frames++;
|
||||||
|
|
||||||
// Loop if a second passed
|
// Loop if a second passed
|
||||||
while (Date.now() >= this.lastTime + 1000) {
|
while (Date.now() >= this.lastTime + 1000) {
|
||||||
console.log(this.frames + " fps");
|
//console.log(this.frames + " fps");
|
||||||
|
|
||||||
this.lastTime += 1000;
|
this.lastTime += 1000;
|
||||||
this.frames = 0;
|
this.frames = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTick() {
|
onRender(partialTicks) {
|
||||||
|
if (this.window.mouseLocked) {
|
||||||
|
this.player.turn(this.window.mouseMotionX, this.window.mouseMotionY);
|
||||||
|
|
||||||
|
this.window.mouseMotionX = 0;
|
||||||
|
this.window.mouseMotionY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the game
|
||||||
|
this.worldRenderer.render(partialTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTick() {
|
||||||
|
this.player.onTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,476 @@
|
|||||||
|
window.Player = class {
|
||||||
|
|
||||||
|
constructor(world) {
|
||||||
|
this.world = world;
|
||||||
|
|
||||||
|
this.prevX = 0;
|
||||||
|
this.prevY = 0;
|
||||||
|
this.prevZ = 0;
|
||||||
|
|
||||||
|
this.x = 0;
|
||||||
|
this.y = 0;
|
||||||
|
this.z = 0;
|
||||||
|
|
||||||
|
this.motionX = 0;
|
||||||
|
this.motionY = 0;
|
||||||
|
this.motionZ = 0;
|
||||||
|
|
||||||
|
this.yaw = 0;
|
||||||
|
this.pitch = 0;
|
||||||
|
|
||||||
|
this.onGround = false;
|
||||||
|
|
||||||
|
this.collision = false;
|
||||||
|
|
||||||
|
this.jumpMovementFactor = 0.02;
|
||||||
|
this.speedInAir = 0.02;
|
||||||
|
this.flySpeed = 0.05;
|
||||||
|
this.stepHeight = 0.5;
|
||||||
|
|
||||||
|
this.moveForward = 0.0;
|
||||||
|
this.moveStrafing = 0.0;
|
||||||
|
|
||||||
|
this.jumpTicks = 0;
|
||||||
|
this.flyToggleTimer = 0;
|
||||||
|
this.sprintToggleTimer = 0;
|
||||||
|
|
||||||
|
this.jumping = false;
|
||||||
|
this.sprinting = false;
|
||||||
|
this.sneaking = false;
|
||||||
|
this.flying = false;
|
||||||
|
|
||||||
|
this.prevFovModifier = 0;
|
||||||
|
this.fovModifier = 0;
|
||||||
|
this.timeFovChanged = 0;
|
||||||
|
|
||||||
|
this.resetPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPos() {
|
||||||
|
this.setPos(0, 15, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPos(x, y, z) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
|
||||||
|
let w = 0.3;
|
||||||
|
let h = 0.9;
|
||||||
|
this.boundingBox = new BoundingBox(x - w, y - h, z - w, x + w, y + h, z + w);
|
||||||
|
}
|
||||||
|
|
||||||
|
turn(motionX, motionY) {
|
||||||
|
this.yaw = this.yaw + motionX * 0.15;
|
||||||
|
this.pitch = this.pitch - motionY * 0.15;
|
||||||
|
|
||||||
|
if (this.pitch < -90.0) {
|
||||||
|
this.pitch = -90.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pitch > 90.0) {
|
||||||
|
this.pitch = 90.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTick() {
|
||||||
|
let prevMoveForward = this.moveForward;
|
||||||
|
let prevJumping = this.jumping;
|
||||||
|
|
||||||
|
this.updateKeyboardInput();
|
||||||
|
|
||||||
|
// Toggle jumping
|
||||||
|
if (!prevJumping && this.jumping) {
|
||||||
|
if (this.flyToggleTimer === 0) {
|
||||||
|
this.flyToggleTimer = 7;
|
||||||
|
} else {
|
||||||
|
this.flying = !this.flying;
|
||||||
|
this.flyToggleTimer = 0;
|
||||||
|
|
||||||
|
this.updateFOVModifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle sprint
|
||||||
|
if (prevMoveForward === 0 && this.moveForward > 0) {
|
||||||
|
if (this.sprintToggleTimer === 0) {
|
||||||
|
this.sprintToggleTimer = 7;
|
||||||
|
} else {
|
||||||
|
this.sprinting = true;
|
||||||
|
this.sprintToggleTimer = 0;
|
||||||
|
|
||||||
|
this.updateFOVModifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.jumpTicks > 0) {
|
||||||
|
--this.jumpTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.flyToggleTimer > 0) {
|
||||||
|
--this.flyToggleTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sprintToggleTimer > 0) {
|
||||||
|
--this.sprintToggleTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prevX = this.x;
|
||||||
|
this.prevY = this.y;
|
||||||
|
this.prevZ = this.z;
|
||||||
|
|
||||||
|
// Stop if too slow
|
||||||
|
if (Math.abs(this.motionX) < 0.003) {
|
||||||
|
this.motionX = 0.0;
|
||||||
|
}
|
||||||
|
if (Math.abs(this.motionY) < 0.003) {
|
||||||
|
this.motionY = 0.0;
|
||||||
|
}
|
||||||
|
if (Math.abs(this.motionZ) < 0.003) {
|
||||||
|
this.motionZ = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump
|
||||||
|
if (this.jumping) {
|
||||||
|
if (this.isInWater()) {
|
||||||
|
this.motionY += 0.04;
|
||||||
|
} else if (this.onGround && this.jumpTicks === 0) {
|
||||||
|
this.jump();
|
||||||
|
this.jumpTicks = 10;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.jumpTicks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveStrafing *= 0.98;
|
||||||
|
this.moveForward *= 0.98;
|
||||||
|
|
||||||
|
if (this.flying) {
|
||||||
|
this.travelFlying(this.moveForward, 0, this.moveStrafing);
|
||||||
|
} else {
|
||||||
|
if (this.isInWater()) {
|
||||||
|
// Is inside of water
|
||||||
|
this.travelInWater(this.moveForward, 0, this.moveStrafing);
|
||||||
|
} else {
|
||||||
|
// Is on land
|
||||||
|
this.travel(this.moveForward, 0, this.moveStrafing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.jumpMovementFactor = this.speedInAir;
|
||||||
|
|
||||||
|
if (this.sprinting) {
|
||||||
|
this.jumpMovementFactor = this.jumpMovementFactor + this.speedInAir * 0.3;
|
||||||
|
|
||||||
|
if (this.moveForward <= 0 || this.collision || this.sneaking) {
|
||||||
|
this.sprinting = false;
|
||||||
|
|
||||||
|
this.updateFOVModifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isInWater() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isHeadInWater() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
jump() {
|
||||||
|
this.motionY = 0.42;
|
||||||
|
|
||||||
|
if (this.sprinting) {
|
||||||
|
let radiansYaw = -this.yaw * (Math.PI / 180);
|
||||||
|
this.motionX -= Math.sin(radiansYaw) * 0.2;
|
||||||
|
this.motionZ += Math.cos(radiansYaw) * 0.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
travelFlying(forward, vertical, strafe) {
|
||||||
|
// Fly move up and down
|
||||||
|
if (this.sneaking) {
|
||||||
|
this.moveStrafing = strafe / 0.3;
|
||||||
|
this.moveForward = forward / 0.3;
|
||||||
|
this.motionY -= this.flySpeed * 3.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.jumping) {
|
||||||
|
this.motionY += this.flySpeed * 3.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let prevMotionY = this.motionY;
|
||||||
|
let prevJumpMovementFactor = this.jumpMovementFactor;
|
||||||
|
this.jumpMovementFactor = this.flySpeed * (this.sprinting ? 2 : 1);
|
||||||
|
|
||||||
|
this.travel(forward, vertical, strafe);
|
||||||
|
|
||||||
|
this.motionY = prevMotionY * 0.6;
|
||||||
|
this.jumpMovementFactor = prevJumpMovementFactor;
|
||||||
|
|
||||||
|
if (this.onGround) {
|
||||||
|
this.flying = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
travelInWater(forward, vertical, strafe) {
|
||||||
|
let slipperiness = 0.8;
|
||||||
|
let friction = 0.02;
|
||||||
|
|
||||||
|
this.moveRelative(forward, vertical, strafe, friction);
|
||||||
|
this.collision = this.moveCollide(-this.motionX, this.motionY, -this.motionZ);
|
||||||
|
|
||||||
|
this.motionX *= slipperiness;
|
||||||
|
this.motionY *= 0.8;
|
||||||
|
this.motionZ *= slipperiness;
|
||||||
|
this.motionY -= 0.02;
|
||||||
|
}
|
||||||
|
|
||||||
|
travel(forward, vertical, strafe) {
|
||||||
|
let prevSlipperiness = this.getBlockSlipperiness() * 0.91;
|
||||||
|
|
||||||
|
let value = 0.16277136 / (prevSlipperiness * prevSlipperiness * prevSlipperiness);
|
||||||
|
let friction;
|
||||||
|
|
||||||
|
if (this.onGround) {
|
||||||
|
friction = this.getAIMoveSpeed() * value;
|
||||||
|
} else {
|
||||||
|
friction = this.jumpMovementFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveRelative(forward, vertical, strafe, friction);
|
||||||
|
|
||||||
|
// Get new speed
|
||||||
|
let slipperiness = this.getBlockSlipperiness() * 0.91;
|
||||||
|
|
||||||
|
// Move
|
||||||
|
this.collision = this.moveCollide(-this.motionX, this.motionY, -this.motionZ);
|
||||||
|
|
||||||
|
// Gravity
|
||||||
|
if (!this.flying) {
|
||||||
|
this.motionY -= 0.08;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrease motion
|
||||||
|
this.motionX *= slipperiness;
|
||||||
|
this.motionY *= 0.98;
|
||||||
|
this.motionZ *= slipperiness;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockSlipperiness() {
|
||||||
|
return this.onGround ? 0.6 : 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAIMoveSpeed() {
|
||||||
|
return this.sprinting ? 0.13 : 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
moveRelative(forward, up, strafe, friction) {
|
||||||
|
let distance = strafe * strafe + up * up + forward * forward;
|
||||||
|
|
||||||
|
if (distance >= 0.0001) {
|
||||||
|
distance = Math.sqrt(distance);
|
||||||
|
|
||||||
|
if (distance < 1.0) {
|
||||||
|
distance = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
distance = friction / distance;
|
||||||
|
strafe = strafe * distance;
|
||||||
|
up = up * distance;
|
||||||
|
forward = forward * distance;
|
||||||
|
|
||||||
|
let yawRadians = -this.yaw * (Math.PI / 180);
|
||||||
|
let sin = Math.sin(yawRadians);
|
||||||
|
let cos = Math.cos(yawRadians);
|
||||||
|
|
||||||
|
this.motionX += strafe * cos - forward * sin;
|
||||||
|
this.motionY += up;
|
||||||
|
this.motionZ += forward * cos + strafe * sin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateKeyboardInput() {
|
||||||
|
let moveForward = 0.0;
|
||||||
|
let moveStrafe = 0.0;
|
||||||
|
|
||||||
|
let jumping = false;
|
||||||
|
let sneaking = false;
|
||||||
|
|
||||||
|
if (Keyboard.isKeyDown("KeyR")) { // R
|
||||||
|
this.resetPos();
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("KeyW")) { // W
|
||||||
|
moveForward++;
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("KeyS")) { // S
|
||||||
|
moveForward--;
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("KeyA")) { // A
|
||||||
|
moveStrafe++;
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("KeyD")) { // D
|
||||||
|
moveStrafe--;
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("Space")) { // Space
|
||||||
|
jumping = true;
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("ShiftLeft")) { // Shift
|
||||||
|
if (this.moveForward > 0 && !this.sneaking && !this.sprinting && this.motionX !== 0 && this.motionZ !== 0) {
|
||||||
|
this.sprinting = true;
|
||||||
|
|
||||||
|
this.updateFOVModifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Keyboard.isKeyDown("KeyQ")) { // Q
|
||||||
|
sneaking = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sneaking) {
|
||||||
|
moveStrafe = moveStrafe * 0.3;
|
||||||
|
moveForward = moveForward * 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.moveForward = moveForward;
|
||||||
|
this.moveStrafing = moveStrafe;
|
||||||
|
|
||||||
|
this.jumping = jumping;
|
||||||
|
this.sneaking = sneaking;
|
||||||
|
}
|
||||||
|
|
||||||
|
moveCollide(targetX, targetY, targetZ) {
|
||||||
|
// Target position
|
||||||
|
let originalTargetX = targetX;
|
||||||
|
let originalTargetY = targetY;
|
||||||
|
let originalTargetZ = targetZ;
|
||||||
|
|
||||||
|
if (this.onGround && this.sneaking) {
|
||||||
|
for (; targetX !== 0.0 && this.world.getCollisionBoxes(this.boundingBox.offset(targetX, -this.stepHeight, 0.0)).length === 0; originalTargetX = targetX) {
|
||||||
|
if (targetX < 0.05 && targetX >= -0.05) {
|
||||||
|
targetX = 0.0;
|
||||||
|
} else if (targetX > 0.0) {
|
||||||
|
targetX -= 0.05;
|
||||||
|
} else {
|
||||||
|
targetX += 0.05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; targetZ !== 0.0 && this.world.getCollisionBoxes(this.boundingBox.offset(0.0, -this.stepHeight, targetZ)).length === 0; originalTargetZ = targetZ) {
|
||||||
|
if (targetZ < 0.05 && targetZ >= -0.05) {
|
||||||
|
targetZ = 0.0;
|
||||||
|
} else if (targetZ > 0.0) {
|
||||||
|
targetZ -= 0.05;
|
||||||
|
} else {
|
||||||
|
targetZ += 0.05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; targetX !== 0.0 && targetZ !== 0.0 && this.world.getCollisionBoxes(this.boundingBox.offset(targetX, -this.stepHeight, targetZ)).length === 0; originalTargetZ = targetZ) {
|
||||||
|
if (targetX < 0.05 && targetX >= -0.05) {
|
||||||
|
targetX = 0.0;
|
||||||
|
} else if (targetX > 0.0) {
|
||||||
|
targetX -= 0.05;
|
||||||
|
} else {
|
||||||
|
targetX += 0.05;
|
||||||
|
}
|
||||||
|
|
||||||
|
originalTargetX = targetX;
|
||||||
|
|
||||||
|
if (targetZ < 0.05 && targetZ >= -0.05) {
|
||||||
|
targetZ = 0.0;
|
||||||
|
} else if (targetZ > 0.0) {
|
||||||
|
targetZ -= 0.05;
|
||||||
|
} else {
|
||||||
|
targetZ += 0.05;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get level tiles as bounding boxes
|
||||||
|
let boundingBoxList = this.world.getCollisionBoxes(this.boundingBox.expand(targetX, targetY, targetZ));
|
||||||
|
|
||||||
|
// Move bounding box
|
||||||
|
for (let aABB in boundingBoxList) {
|
||||||
|
targetY = boundingBoxList[aABB].clipYCollide(this.boundingBox, targetY);
|
||||||
|
}
|
||||||
|
this.boundingBox.move(0.0, targetY, 0.0);
|
||||||
|
|
||||||
|
for (let aABB in boundingBoxList) {
|
||||||
|
targetX = boundingBoxList[aABB].clipXCollide(this.boundingBox, targetX);
|
||||||
|
}
|
||||||
|
this.boundingBox.move(targetX, 0.0, 0.0);
|
||||||
|
|
||||||
|
for (let aABB in boundingBoxList) {
|
||||||
|
targetZ = boundingBoxList[aABB].clipZCollide(this.boundingBox, targetZ);
|
||||||
|
}
|
||||||
|
this.boundingBox.move(0.0, 0.0, targetZ);
|
||||||
|
|
||||||
|
this.onGround = originalTargetY !== targetY && originalTargetY < 0.0;
|
||||||
|
|
||||||
|
// Stop motion on collision
|
||||||
|
if (originalTargetX !== targetX) {
|
||||||
|
this.motionX = 0.0;
|
||||||
|
}
|
||||||
|
if (originalTargetY !== targetY) {
|
||||||
|
this.motionY = 0.0;
|
||||||
|
}
|
||||||
|
if (originalTargetZ !== targetZ) {
|
||||||
|
this.motionZ = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update position
|
||||||
|
this.x = (this.boundingBox.minX + this.boundingBox.maxX) / 2.0;
|
||||||
|
this.y = this.boundingBox.minY;
|
||||||
|
this.z = (this.boundingBox.minZ + this.boundingBox.maxZ) / 2.0;
|
||||||
|
|
||||||
|
// Horizontal collision?
|
||||||
|
return originalTargetX !== targetX || originalTargetZ !== targetZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEyeHeight() {
|
||||||
|
return this.sneaking ? 1.50 : 1.62;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFOVModifier() {
|
||||||
|
let value = 1.0;
|
||||||
|
|
||||||
|
if (this.sprinting) {
|
||||||
|
value += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.flying) {
|
||||||
|
value *= 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setFOVModifier((value - 1.0) * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setFOVModifier(fov) {
|
||||||
|
this.prevFovModifier = this.fovModifier;
|
||||||
|
this.fovModifier = fov;
|
||||||
|
this.timeFovChanged = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
getFOVModifier() {
|
||||||
|
let timePassed = Date.now() - this.timeFovChanged;
|
||||||
|
let distance = this.prevFovModifier - this.fovModifier;
|
||||||
|
let duration = 100;
|
||||||
|
let progress = distance / duration * timePassed;
|
||||||
|
return timePassed > duration ? this.fovModifier : this.prevFovModifier - progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockPosX() {
|
||||||
|
return this.x - (this.x < 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockPosY() {
|
||||||
|
return this.y - (this.y < 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBlockPosZ() {
|
||||||
|
return this.z - (this.z < 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,8 +8,8 @@ window.WorldRenderer = class {
|
|||||||
|| !!document.createElement('canvas').getContext('webgl'));
|
|| !!document.createElement('canvas').getContext('webgl'));
|
||||||
|
|
||||||
// Create cameras
|
// Create cameras
|
||||||
this.camera = new THREE.PerspectiveCamera(85, 1, 1, 10000);
|
this.camera = new THREE.PerspectiveCamera(85, 1, 0.001, 10000);
|
||||||
this.camera.position.set(0, 3, 0);
|
this.camera.rotation.order = 'ZYX';
|
||||||
this.camera.up = new THREE.Vector3(0, 0, 1);
|
this.camera.up = new THREE.Vector3(0, 0, 1);
|
||||||
|
|
||||||
// Create scene
|
// Create scene
|
||||||
@@ -36,6 +36,31 @@ window.WorldRenderer = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(partialTicks) {
|
render(partialTicks) {
|
||||||
|
// Setup camera
|
||||||
|
this.orientCamera(partialTicks);
|
||||||
|
|
||||||
|
// Render chunks
|
||||||
|
this.renderChunks(partialTicks);
|
||||||
|
|
||||||
|
// Render window
|
||||||
|
this.webRenderer.render(this.scene, this.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
orientCamera(partialTicks) {
|
||||||
|
let player = this.minecraft.player;
|
||||||
|
|
||||||
|
// Rotation
|
||||||
|
this.camera.rotation.y = player.yaw * (Math.PI / 180);
|
||||||
|
this.camera.rotation.x = player.pitch * (Math.PI / 180);
|
||||||
|
|
||||||
|
// Position
|
||||||
|
let x = player.prevX + (player.x - player.prevX) * partialTicks;
|
||||||
|
let y = player.prevY + (player.y - player.prevY) * partialTicks;
|
||||||
|
let z = player.prevZ + (player.z - player.prevZ) * partialTicks;
|
||||||
|
this.camera.position.set(x, y + player.getEyeHeight(), z);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderChunks(partialTicks) {
|
||||||
let world = this.minecraft.world;
|
let world = this.minecraft.world;
|
||||||
|
|
||||||
const xKeys = Object.keys(world.chunks)
|
const xKeys = Object.keys(world.chunks)
|
||||||
@@ -56,10 +81,5 @@ window.WorldRenderer = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render window
|
|
||||||
this.webRenderer.render(this.scene, this.camera);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ window.ChunkSection = class {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let cube = new THREE.Mesh(geometry, material);
|
let cube = new THREE.Mesh(geometry, material);
|
||||||
cube.position.set(absoluteX - 0.5, absoluteY - 0.5, absoluteZ - 0.5);
|
cube.position.set(absoluteX + 0.5, absoluteY + 0.5, absoluteZ + 0.5);
|
||||||
|
|
||||||
this.group.add(cube);
|
this.group.add(cube);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,38 @@ window.World = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChunkAtBlock(x, y, z) {
|
||||||
|
let chunk = this.getChunkAt(x >> 4, z >> 4);
|
||||||
|
return y < 0 || y > World.TOTAL_HEIGHT ? null : chunk.getSection(y >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollisionBoxes(aabb) {
|
||||||
|
let boundingBoxList = [];
|
||||||
|
|
||||||
|
let minX = Math.floor(aabb.minX) - 1;
|
||||||
|
let maxX = Math.ceil(aabb.maxX) + 1;
|
||||||
|
let minY = Math.floor(aabb.minY) - 1;
|
||||||
|
let maxY = Math.ceil(aabb.maxY) + 1;
|
||||||
|
let minZ = Math.floor(aabb.minZ) - 1;
|
||||||
|
let maxZ = Math.ceil(aabb.maxZ) + 1;
|
||||||
|
|
||||||
|
for (let x = minX; x < maxX; x++) {
|
||||||
|
for (let y = minY; y < maxY; y++) {
|
||||||
|
for (let z = minZ; z < maxZ; z++) {
|
||||||
|
if (this.isSolidBlockAt(x, y, z)) {
|
||||||
|
boundingBoxList.push(new BoundingBox(x, y, z, x + 1, y + 1, z + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return boundingBoxList;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSolidBlockAt(x, y, z) {
|
||||||
|
let typeId = this.getBlockAt(x, y, z);
|
||||||
|
return typeId !== 0; /* && Block.getById(typeId).isSolid();*/
|
||||||
|
}
|
||||||
|
|
||||||
setBlockAt(x, y, z, type) {
|
setBlockAt(x, y, z, type) {
|
||||||
let chunkSection = this.getChunkAtBlock(x, y, z);
|
let chunkSection = this.getChunkAtBlock(x, y, z);
|
||||||
if (chunkSection != null) {
|
if (chunkSection != null) {
|
||||||
@@ -24,6 +56,12 @@ window.World = class {
|
|||||||
this.blockChanged(x, y, z);
|
this.blockChanged(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getBlockAt(x, y, z) {
|
||||||
|
let chunkSection = this.getChunkAtBlock(x, y, z);
|
||||||
|
return chunkSection == null ? 0 : chunkSection.getBlockAt(x & 15, y & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
getChunkAt(x, z) {
|
getChunkAt(x, z) {
|
||||||
let zArray = this.chunks[x];
|
let zArray = this.chunks[x];
|
||||||
if (typeof zArray === 'undefined') {
|
if (typeof zArray === 'undefined') {
|
||||||
@@ -65,10 +103,4 @@ window.World = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getChunkAtBlock(x, y, z) {
|
|
||||||
let chunk = this.getChunkAt(x >> 4, z >> 4);
|
|
||||||
return y < 0 || y > World.TOTAL_HEIGHT ? null : chunk.getSection(y >> 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
window.BoundingBox = class {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bounding box
|
||||||
|
*
|
||||||
|
* @param minX Minimum x side
|
||||||
|
* @param minY Minimum y side
|
||||||
|
* @param minZ Minimum z side
|
||||||
|
* @param maxX Maximum x side
|
||||||
|
* @param maxY Maximum y side
|
||||||
|
* @param maxZ Maximum z side
|
||||||
|
*/
|
||||||
|
constructor(minX, minY, minZ, maxX, maxY, maxZ) {
|
||||||
|
this.epsilon = 0.0;
|
||||||
|
|
||||||
|
this.minX = minX;
|
||||||
|
this.minY = minY;
|
||||||
|
this.minZ = minZ;
|
||||||
|
this.maxX = maxX;
|
||||||
|
this.maxY = maxY;
|
||||||
|
this.maxZ = maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy the current bounding box object
|
||||||
|
*
|
||||||
|
* @return Clone of the bounding box
|
||||||
|
*/
|
||||||
|
clone() {
|
||||||
|
return new BoundingBox(this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the bounding box. Positive and negative numbers controls which side of the box should grow.
|
||||||
|
*
|
||||||
|
* @param x Amount to expand the minX or maxX
|
||||||
|
* @param y Amount to expand the minY or maxY
|
||||||
|
* @param z Amount to expand the minZ or maxZ
|
||||||
|
* @return The expanded bounding box
|
||||||
|
*/
|
||||||
|
expand(x, y, z) {
|
||||||
|
let minX = this.minX;
|
||||||
|
let minY = this.minY;
|
||||||
|
let minZ = this.minZ;
|
||||||
|
let maxX = this.maxX;
|
||||||
|
let maxY = this.maxY;
|
||||||
|
let maxZ = this.maxZ;
|
||||||
|
|
||||||
|
// Handle expanding of min/max x
|
||||||
|
if (x < 0.0) {
|
||||||
|
minX += x;
|
||||||
|
} else {
|
||||||
|
maxX += x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle expanding of min/max y
|
||||||
|
if (y < 0.0) {
|
||||||
|
minY += y;
|
||||||
|
} else {
|
||||||
|
maxY += y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle expanding of min/max z
|
||||||
|
if (z < 0.0) {
|
||||||
|
minZ += z;
|
||||||
|
} else {
|
||||||
|
maxZ += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new bounding box
|
||||||
|
return new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand the bounding box on both sides.
|
||||||
|
* The center is always fixed when using grow.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param z
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
grow(x, y, z) {
|
||||||
|
return new BoundingBox(
|
||||||
|
this.minX - x,
|
||||||
|
this.minY - y,
|
||||||
|
this.minZ - z,
|
||||||
|
this.maxX + x,
|
||||||
|
this.maxY + y,
|
||||||
|
this.maxZ + z
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for collision on the X axis
|
||||||
|
*
|
||||||
|
* @param otherBoundingBox The other bounding box that is colliding with the this one.
|
||||||
|
* @param x Position on the X axis that is colliding
|
||||||
|
* @return Returns the corrected x position that collided.
|
||||||
|
*/
|
||||||
|
clipXCollide(otherBoundingBox, x) {
|
||||||
|
// Check if the boxes are colliding on the Y axis
|
||||||
|
if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the boxes are colliding on the Z axis
|
||||||
|
if (otherBoundingBox.maxZ <= this.minZ || otherBoundingBox.minZ >= this.maxZ) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collision if the X axis of the current box is bigger
|
||||||
|
if (x > 0.0 && otherBoundingBox.maxX <= this.minX) {
|
||||||
|
let max = this.minX - otherBoundingBox.maxX - this.epsilon;
|
||||||
|
if (max < x) {
|
||||||
|
x = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collision if the X axis of the current box is smaller
|
||||||
|
if (x < 0.0 && otherBoundingBox.minX >= this.maxX) {
|
||||||
|
let max = this.maxX - otherBoundingBox.minX + this.epsilon;
|
||||||
|
if (max > x) {
|
||||||
|
x = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for collision on the Y axis
|
||||||
|
*
|
||||||
|
* @param otherBoundingBox The other bounding box that is colliding with the this one.
|
||||||
|
* @param y Position on the X axis that is colliding
|
||||||
|
* @return Returns the corrected x position that collided.
|
||||||
|
*/
|
||||||
|
clipYCollide(otherBoundingBox, y) {
|
||||||
|
// Check if the boxes are colliding on the X axis
|
||||||
|
if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the boxes are colliding on the Z axis
|
||||||
|
if (otherBoundingBox.maxZ <= this.minZ || otherBoundingBox.minZ >= this.maxZ) {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collision if the Y axis of the current box is bigger
|
||||||
|
if (y > 0.0 && otherBoundingBox.maxY <= this.minY) {
|
||||||
|
let max = this.minY - otherBoundingBox.maxY - this.epsilon;
|
||||||
|
if (max < y) {
|
||||||
|
y = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collision if the Y axis of the current box is bigger
|
||||||
|
if (y < 0.0 && otherBoundingBox.minY >= this.maxY) {
|
||||||
|
let max = this.maxY - otherBoundingBox.minY + this.epsilon;
|
||||||
|
if (max > y) {
|
||||||
|
y = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for collision on the Y axis
|
||||||
|
*
|
||||||
|
* @param otherBoundingBox The other bounding box that is colliding with the this one.
|
||||||
|
* @param z Position on the X axis that is colliding
|
||||||
|
* @return Returns the corrected x position that collided.
|
||||||
|
*/
|
||||||
|
clipZCollide(otherBoundingBox, z) {
|
||||||
|
// Check if the boxes are colliding on the X axis
|
||||||
|
if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the boxes are colliding on the Y axis
|
||||||
|
if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collision if the Z axis of the current box is bigger
|
||||||
|
if (z > 0.0 && otherBoundingBox.maxZ <= this.minZ) {
|
||||||
|
let max = this.minZ - otherBoundingBox.maxZ - this.epsilon;
|
||||||
|
if (max < z) {
|
||||||
|
z = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for collision if the Z axis of the current box is bigger
|
||||||
|
if (z < 0.0 && otherBoundingBox.minZ >= this.maxZ) {
|
||||||
|
let max = this.maxZ - otherBoundingBox.minZ + this.epsilon;
|
||||||
|
if (max > z) {
|
||||||
|
z = max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the two boxes are intersecting/overlapping
|
||||||
|
*
|
||||||
|
* @param otherBoundingBox The other bounding box that could intersect
|
||||||
|
* @return The two boxes are overlapping
|
||||||
|
*/
|
||||||
|
intersects(otherBoundingBox) {
|
||||||
|
// Check on X axis
|
||||||
|
if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check on Y axis
|
||||||
|
if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check on Z axis
|
||||||
|
return (!(otherBoundingBox.maxZ <= this.minZ)) && (!(otherBoundingBox.minZ >= this.maxZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the bounding box relative.
|
||||||
|
*
|
||||||
|
* @param x Relative offset x
|
||||||
|
* @param y Relative offset y
|
||||||
|
* @param z Relative offset z
|
||||||
|
*/
|
||||||
|
move(x, y, z) {
|
||||||
|
this.minX += x;
|
||||||
|
this.minY += y;
|
||||||
|
this.minZ += z;
|
||||||
|
this.maxX += x;
|
||||||
|
this.maxY += y;
|
||||||
|
this.maxZ += z;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new bounding box with the given offset
|
||||||
|
*
|
||||||
|
* @param x Relative offset x
|
||||||
|
* @param y Relative offset x
|
||||||
|
* @param z Relative offset x
|
||||||
|
* @return New bounding box with the given offset relative to this bounding box
|
||||||
|
*/
|
||||||
|
offset(x, y, z) {
|
||||||
|
return new BoundingBox(
|
||||||
|
this.minX + x,
|
||||||
|
this.minY + y,
|
||||||
|
this.minZ + z,
|
||||||
|
this.maxX + x,
|
||||||
|
this.maxY + y,
|
||||||
|
this.maxZ + z
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
window.Keyboard = class {
|
||||||
|
|
||||||
|
static state = {};
|
||||||
|
|
||||||
|
static create() {
|
||||||
|
window.addEventListener('keyup', (e) => Keyboard.state[e.code] = false);
|
||||||
|
window.addEventListener('keydown', (e) => Keyboard.state[e.code] = true);
|
||||||
|
};
|
||||||
|
|
||||||
|
static isKeyDown(key) {
|
||||||
|
return Keyboard.state[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -48,10 +48,13 @@ loadScripts([
|
|||||||
|
|
||||||
// Minecraft Source
|
// Minecraft Source
|
||||||
"src/net/minecraft/util/Timer.js",
|
"src/net/minecraft/util/Timer.js",
|
||||||
|
"src/net/minecraft/util/BoundingBox.js",
|
||||||
|
"src/net/minecraft/util/Keyboard.js",
|
||||||
"src/net/minecraft/client/GameWindow.js",
|
"src/net/minecraft/client/GameWindow.js",
|
||||||
"src/net/minecraft/client/world/ChunkSection.js",
|
"src/net/minecraft/client/world/ChunkSection.js",
|
||||||
"src/net/minecraft/client/world/Chunk.js",
|
"src/net/minecraft/client/world/Chunk.js",
|
||||||
"src/net/minecraft/client/world/World.js",
|
"src/net/minecraft/client/world/World.js",
|
||||||
|
"src/net/minecraft/client/entity/Player.js",
|
||||||
"src/net/minecraft/client/Minecraft.js",
|
"src/net/minecraft/client/Minecraft.js",
|
||||||
"src/net/minecraft/client/render/WorldRenderer.js"
|
"src/net/minecraft/client/render/WorldRenderer.js"
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user