implement player and movement input
This commit is contained in:
@@ -4,8 +4,13 @@ window.GameWindow = class {
|
||||
this.renderer = renderer;
|
||||
this.canvasWrapperId = canvasWrapperId;
|
||||
|
||||
this.mouseMotionX = 0;
|
||||
this.mouseMotionY = 0;
|
||||
this.mouseLocked = false;
|
||||
|
||||
// Add web renderer canvas to wrapper
|
||||
document.getElementById(this.canvasWrapperId).appendChild(renderer.canvasElement);
|
||||
let canvas = renderer.canvasElement;
|
||||
document.getElementById(this.canvasWrapperId).appendChild(canvas);
|
||||
|
||||
// Init
|
||||
this.initialize();
|
||||
@@ -13,10 +18,23 @@ window.GameWindow = class {
|
||||
// On resize
|
||||
let scope = this;
|
||||
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() {
|
||||
// Create keyboard
|
||||
Keyboard.create();
|
||||
|
||||
// Get canvas size
|
||||
let canvasElement = document.getElementById(this.canvasWrapperId);
|
||||
this.canvasWidth = canvasElement.offsetWidth;
|
||||
@@ -27,4 +45,13 @@ window.GameWindow = class {
|
||||
this.renderer.camera.updateProjectionMatrix();
|
||||
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.lastTime = Date.now();
|
||||
|
||||
// Create world
|
||||
this.world = new World();
|
||||
this.worldRenderer.scene.add(this.world.group);
|
||||
|
||||
// Create player
|
||||
this.player = new Player(this.world);
|
||||
|
||||
// Initialize
|
||||
this.init();
|
||||
}
|
||||
|
||||
@@ -27,12 +32,12 @@ window.Minecraft = class {
|
||||
requestAnimationFrame(function () {
|
||||
if (scope.running) {
|
||||
scope.requestNextFrame();
|
||||
scope.onRender();
|
||||
scope.onLoop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onRender() {
|
||||
onLoop() {
|
||||
// Update the timer
|
||||
this.timer.advanceTime();
|
||||
|
||||
@@ -42,22 +47,34 @@ window.Minecraft = class {
|
||||
}
|
||||
|
||||
// Render the game
|
||||
this.worldRenderer.render(this.timer.partialTicks);
|
||||
this.onRender(this.timer.partialTicks);
|
||||
|
||||
// Increase rendered frame
|
||||
this.frames++;
|
||||
|
||||
// Loop if a second passed
|
||||
while (Date.now() >= this.lastTime + 1000) {
|
||||
console.log(this.frames + " fps");
|
||||
//console.log(this.frames + " fps");
|
||||
|
||||
this.lastTime += 1000;
|
||||
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'));
|
||||
|
||||
// Create cameras
|
||||
this.camera = new THREE.PerspectiveCamera(85, 1, 1, 10000);
|
||||
this.camera.position.set(0, 3, 0);
|
||||
this.camera = new THREE.PerspectiveCamera(85, 1, 0.001, 10000);
|
||||
this.camera.rotation.order = 'ZYX';
|
||||
this.camera.up = new THREE.Vector3(0, 0, 1);
|
||||
|
||||
// Create scene
|
||||
@@ -36,6 +36,31 @@ window.WorldRenderer = class {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
let chunkSection = this.getChunkAtBlock(x, y, z);
|
||||
if (chunkSection != null) {
|
||||
@@ -24,6 +56,12 @@ window.World = class {
|
||||
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) {
|
||||
let zArray = this.chunks[x];
|
||||
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
|
||||
"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/world/ChunkSection.js",
|
||||
"src/net/minecraft/client/world/Chunk.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/render/WorldRenderer.js"
|
||||
]).then(() => {
|
||||
|
||||
Reference in New Issue
Block a user