Added proper block placing and breaking!

This commit is contained in:
Andrew Lalis 2022-07-16 10:47:06 +02:00
parent 236a421296
commit ab69ccbe5b
5 changed files with 103 additions and 123 deletions

View File

@ -17,4 +17,12 @@ public class MathUtils {
if (value < min) return min; if (value < min) return min;
return Math.min(value, max); return Math.min(value, max);
} }
public static float min(float... values) {
float m = Float.MAX_VALUE;
for (float v : values) {
if (v < m) m = v;
}
return m;
}
} }

View File

@ -1,18 +1,24 @@
package nl.andrewl.aos_core.model; package nl.andrewl.aos_core.model;
import nl.andrewl.aos_core.Directions; import nl.andrewl.aos_core.Directions;
import nl.andrewl.aos_core.MathUtils;
import org.joml.Math; import org.joml.Math;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector3i; import org.joml.Vector3i;
import org.joml.Vector3ic; import org.joml.Vector3ic;
import java.util.*; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/** /**
* A world is just a collection of chunks that together form the environment * A world is just a collection of chunks that together form the environment
* that players can interact in. * that players can interact in.
*/ */
public class World { public class World {
private static final float DELTA = 0.00001f;
protected final Map<Vector3ic, Chunk> chunkMap = new HashMap<>(); protected final Map<Vector3ic, Chunk> chunkMap = new HashMap<>();
protected ColorPalette palette; protected ColorPalette palette;
@ -99,42 +105,35 @@ public class World {
public Hit getLookingAtPos(Vector3f eyePos, Vector3f eyeDir, float limit) { public Hit getLookingAtPos(Vector3f eyePos, Vector3f eyeDir, float limit) {
if (eyeDir.lengthSquared() == 0 || limit <= 0) return null; if (eyeDir.lengthSquared() == 0 || limit <= 0) return null;
Vector3f pos = new Vector3f(eyePos); Vector3f pos = new Vector3f(eyePos);
Vector3f movement = new Vector3f(); // Pre-allocate this vector.
while (pos.distance(eyePos) < limit) { while (pos.distance(eyePos) < limit) {
// Find the coordinates of the next blocks on the x, y, and z axes. stepToNextBlock(pos, eyeDir);
float stepX = getNextStep(pos.x, eyeDir.x);
float stepY = getNextStep(pos.y, eyeDir.y);
float stepZ = getNextStep(pos.z, eyeDir.z);
// Get the distance from our current position to the next block on the x, y, and z axes.
float distX = Math.abs(pos.x - stepX);
float distY = Math.abs(pos.y - stepY);
float distZ = Math.abs(pos.z - stepZ);
// Get the factor required to multiply each component by to get to its next step.
float factorX = Math.abs(distX / eyeDir.x);
float factorY = Math.abs(distY / eyeDir.y);
float factorZ = Math.abs(distZ / eyeDir.z);
float minFactor = Float.MAX_VALUE;
if (factorX > 0 && factorX < minFactor) minFactor = factorX;
if (factorY > 0 && factorY < minFactor) minFactor = factorY;
if (factorZ > 0 && factorZ < minFactor) minFactor = factorZ;
// We should add dir * lowest factor to step to the first next block.
movement.set(eyeDir).mul(minFactor);
pos.add(movement);
if (getBlockAt(pos) > 0) { if (getBlockAt(pos) > 0) {
Vector3f prevPos = new Vector3f(pos).sub(movement);
Vector3i hitPos = new Vector3i( Vector3i hitPos = new Vector3i(
(int) Math.floor(pos.x), (int) Math.floor(pos.x),
(int) Math.floor(pos.y), (int) Math.floor(pos.y),
(int) Math.floor(pos.z) (int) Math.floor(pos.z)
); );
Vector3ic hitNorm = null; Vector3ic hitNorm;
if (prevPos.y > hitPos.y + 1) hitNorm = Directions.UP; // Determine the face that was hit based on which face was closest to the hit point.
else if (prevPos.y < hitPos.y) hitNorm = Directions.DOWN; float minYDist = Math.abs(pos.y - hitPos.y);
else if (prevPos.x > hitPos.x + 1) hitNorm = Directions.POSITIVE_X; float maxYDist = Math.abs(pos.y - (hitPos.y + 1));
else if (prevPos.x < hitPos.x) hitNorm = Directions.NEGATIVE_X; float minXDist = Math.abs(pos.x - hitPos.x);
else if (prevPos.z > hitPos.z + 1) hitNorm = Directions.POSITIVE_Z; float maxXDist = Math.abs(pos.x - (hitPos.x + 1));
else if (prevPos.z < hitPos.z) hitNorm = Directions.NEGATIVE_Z; float minZDist = Math.abs(pos.z - hitPos.z);
float maxZDist = Math.abs(pos.z - (hitPos.z + 1));
float minDist = MathUtils.min(minYDist, maxYDist, minXDist, maxXDist, minZDist, maxZDist);
if (minDist == maxYDist) hitNorm = Directions.UP;
else if (minDist == minYDist) hitNorm = Directions.DOWN;
else if (minDist == maxXDist) hitNorm = Directions.POSITIVE_X;
else if (minDist == minXDist) hitNorm = Directions.NEGATIVE_X;
else if (minDist == maxZDist) hitNorm = Directions.POSITIVE_Z;
else if (minDist == minZDist) hitNorm = Directions.NEGATIVE_Z;
else {
hitNorm = Directions.UP;
System.err.println("Invalid hit!");
}
return new Hit(hitPos, hitNorm); return new Hit(hitPos, hitNorm);
} }
@ -143,27 +142,54 @@ public class World {
} }
/** /**
* Helper function to find the next whole number, given a current number and * Increments the given position until it hits a new block space, moving in
* an indication of which direction the number is increasing. * the specified direction. Note that we move slightly into a block if
* @param n The current number. * needed, to ensure accuracy, so the position may not be a whole number.
* @param sign An indication of which way the number is increasing. * @param pos The position.
* @return The next whole number up from the current number. * @param dir The direction to move in.
*/ */
private static float getNextStep(float n, float sign) { private static void stepToNextBlock(Vector3f pos, Vector3f dir) {
if (sign > 0) { if (dir.lengthSquared() == 0) return;
if (Math.ceil(n) == n) { // Find the amount we'd have to multiply dir by to get pos to move to the next block on that axis.
return n + 1; float factorX = Float.MAX_VALUE;
float factorY = Float.MAX_VALUE;
float factorZ = Float.MAX_VALUE;
if (dir.x != 0) {
float nextValue;
if (dir.x > 0) {
nextValue = (Math.ceil(pos.x) == pos.x) ? pos.x + 1 : Math.ceil(pos.x);
} else { } else {
return Math.ceil(n); nextValue = Math.floor(pos.x) - DELTA;
}
} else if (sign < 0) {
if (Math.floor(n) == n) {
return n - 1;
} else {
return Math.floor(n);
} }
float diff = nextValue - pos.x;
factorX = Math.abs(diff / dir.x);
} }
return n;
if (dir.y != 0) {
float nextValue;
if (dir.y > 0) {
nextValue = (Math.ceil(pos.y) == pos.y) ? pos.y + 1 : Math.ceil(pos.y);
} else {
nextValue = Math.floor(pos.y) - DELTA;
}
float diff = nextValue - pos.y;
factorY = Math.abs(diff / dir.y);
}
if (dir.z != 0) {
float nextValue;
if (dir.z > 0) {
nextValue = (Math.ceil(pos.z) == pos.z) ? pos.z + 1 : Math.ceil(pos.z);
} else {
nextValue = Math.floor(pos.z) - DELTA;
}
float diff = nextValue - pos.z;
factorZ = Math.abs(diff / dir.z);
}
float minFactor = Math.min(factorX, Math.min(factorY, factorZ));
dir.mulAdd(minFactor, pos, pos);
} }
/** /**

View File

@ -1,5 +1,6 @@
package nl.andrewl.aos_core.model; package nl.andrewl.aos_core.model;
import nl.andrewl.aos_core.Directions;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector3i; import org.joml.Vector3i;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -35,79 +36,22 @@ public class WorldTest {
@Test @Test
public void testGetLookingAtPos() { public void testGetLookingAtPos() {
World world = Worlds.testingWorld(); World world = new World();
// Spawn a block high in the air. Chunk chunk = new Chunk(0, 0, 0);
Vector3i blockPos = new Vector3i(20, 20, 20); world.addChunk(chunk);
world.setBlockAt(blockPos.x, blockPos.y, blockPos.z, (byte) 5); // Spawn a block in the middle for testing.
assertEquals( // Looking straight down onto block. Vector3i blockPos = new Vector3i(7, 7, 7);
blockPos, world.setBlockAt(blockPos.x, blockPos.y, blockPos.z, (byte) 1);
world.getLookingAtPos( Hit hit;
new Vector3f(20.5f, 25, 20.5f),
new Vector3f(0, -1, 0),
15
)
);
assertEquals( // Looking straight up.
blockPos,
world.getLookingAtPos(
new Vector3f(20.5f, 15, 20.5f),
new Vector3f(0, 1, 0),
15
)
);
assertEquals( // Looking towards -Z
blockPos,
world.getLookingAtPos(
new Vector3f(20.5f, 20.5f, 26f),
new Vector3f(0, 0, -1),
15
)
);
assertEquals( // Looking towards +Z
blockPos,
world.getLookingAtPos(
new Vector3f(20.5f, 20.5f, 15f),
new Vector3f(0, 0, 1),
15
)
);
assertEquals( // Looking towards -X
blockPos,
world.getLookingAtPos(
new Vector3f(26f, 20.5f, 20.5f),
new Vector3f(-1, 0, 0),
15
)
);
assertEquals( // Looking towards +X
blockPos,
world.getLookingAtPos(
new Vector3f(15f, 20.5f, 20.5f),
new Vector3f(1, 0, 0),
15
)
);
// Looking up into the void. // Looking down.
assertNull(world.getLookingAtPos( hit = world.getLookingAtPos(new Vector3f(7.5f, 10, 7.5f), new Vector3f(0, -1, 0), 10);
new Vector3f(0, 30, 0), assertEquals(blockPos, hit.pos());
new Vector3f(0, 1, 0), assertEquals(Directions.UP, hit.norm());
10
));
// Looking straight down, but too far away. // Looking up.
assertNull(world.getLookingAtPos( hit = world.getLookingAtPos(new Vector3f(7.5f, 5, 7.5f), new Vector3f(0, 1, 0), 10);
new Vector3f(0.5f, 20, 0.5f), assertEquals(blockPos, hit.pos());
new Vector3f(0, -1, 0), assertEquals(Directions.DOWN, hit.norm());
15
));
assertEquals( // Standing in the corner, looking into center of block.
blockPos,
world.getLookingAtPos(
new Vector3f(20f, 21f, 20f),
new Vector3f(1, -2, 1).normalize(),
15
)
);
} }
} }

View File

@ -101,7 +101,7 @@ public class ClientCommunicationHandler {
new Thread(tcpReceiver).start(); new Thread(tcpReceiver).start();
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); log.warn("An IOException occurred while attempting to establish a connection to a client.", e);
} }
attempts++; attempts++;
} }

View File

@ -33,9 +33,11 @@ public class ServerPlayer extends Player {
public static final float JUMP_SPEED = 8f; public static final float JUMP_SPEED = 8f;
public static final int BLOCK_REMOVE_COOLDOWN = 250; public static final int BLOCK_REMOVE_COOLDOWN = 250;
public static final int BLOCK_PLACE_COOLDOWN = 100;
private ClientInputState lastInputState; private ClientInputState lastInputState;
private long lastBlockRemovedAt = 0; private long lastBlockRemovedAt = 0;
private long lastBlockPlacedAt = 0;
private boolean updated = false; private boolean updated = false;
@ -59,27 +61,27 @@ public class ServerPlayer extends Player {
public void tick(float dt, World world, Server server) { public void tick(float dt, World world, Server server) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Check for breaking blocks.
if (lastInputState.hitting() && now - lastBlockRemovedAt > BLOCK_REMOVE_COOLDOWN) { if (lastInputState.hitting() && now - lastBlockRemovedAt > BLOCK_REMOVE_COOLDOWN) {
Vector3f eyePos = new Vector3f(position); Vector3f eyePos = new Vector3f(position);
eyePos.y += getEyeHeight(); eyePos.y += getEyeHeight();
var hit = world.getLookingAtPos(eyePos, viewVector, 10); var hit = world.getLookingAtPos(eyePos, viewVector, 10);
System.out.println(hit);
if (hit != null) { if (hit != null) {
world.setBlockAt(hit.pos().x, hit.pos().y, hit.pos().z, (byte) 0); world.setBlockAt(hit.pos().x, hit.pos().y, hit.pos().z, (byte) 0);
lastBlockRemovedAt = now; lastBlockRemovedAt = now;
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(hit.pos(), world)); server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(hit.pos(), world));
} }
} }
if (lastInputState.interacting() && now - lastBlockRemovedAt > BLOCK_REMOVE_COOLDOWN) { // Check for placing blocks.
if (lastInputState.interacting() && now - lastBlockPlacedAt > BLOCK_PLACE_COOLDOWN) {
Vector3f eyePos = new Vector3f(position); Vector3f eyePos = new Vector3f(position);
eyePos.y += getEyeHeight(); eyePos.y += getEyeHeight();
var hit = world.getLookingAtPos(eyePos, viewVector, 10); var hit = world.getLookingAtPos(eyePos, viewVector, 10);
System.out.println(hit);
if (hit != null) { if (hit != null) {
Vector3i placePos = new Vector3i(hit.pos()); Vector3i placePos = new Vector3i(hit.pos());
placePos.add(hit.norm()); placePos.add(hit.norm());
world.setBlockAt(placePos.x, placePos.y, placePos.z, (byte) 1); world.setBlockAt(placePos.x, placePos.y, placePos.z, (byte) 1);
lastBlockRemovedAt = now; lastBlockPlacedAt = now;
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(placePos, world)); server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(placePos, world));
} }
} }