Added proper block placing and breaking!
This commit is contained in:
parent
236a421296
commit
ab69ccbe5b
|
@ -17,4 +17,12 @@ public class MathUtils {
|
|||
if (value < min) return min;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,24 @@
|
|||
package nl.andrewl.aos_core.model;
|
||||
|
||||
import nl.andrewl.aos_core.Directions;
|
||||
import nl.andrewl.aos_core.MathUtils;
|
||||
import org.joml.Math;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
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
|
||||
* that players can interact in.
|
||||
*/
|
||||
public class World {
|
||||
private static final float DELTA = 0.00001f;
|
||||
|
||||
protected final Map<Vector3ic, Chunk> chunkMap = new HashMap<>();
|
||||
protected ColorPalette palette;
|
||||
|
||||
|
@ -99,42 +105,35 @@ public class World {
|
|||
public Hit getLookingAtPos(Vector3f eyePos, Vector3f eyeDir, float limit) {
|
||||
if (eyeDir.lengthSquared() == 0 || limit <= 0) return null;
|
||||
Vector3f pos = new Vector3f(eyePos);
|
||||
Vector3f movement = new Vector3f(); // Pre-allocate this vector.
|
||||
while (pos.distance(eyePos) < limit) {
|
||||
// Find the coordinates of the next blocks on the x, y, and z axes.
|
||||
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);
|
||||
stepToNextBlock(pos, eyeDir);
|
||||
if (getBlockAt(pos) > 0) {
|
||||
Vector3f prevPos = new Vector3f(pos).sub(movement);
|
||||
Vector3i hitPos = new Vector3i(
|
||||
(int) Math.floor(pos.x),
|
||||
(int) Math.floor(pos.y),
|
||||
(int) Math.floor(pos.z)
|
||||
);
|
||||
Vector3ic hitNorm = null;
|
||||
Vector3ic hitNorm;
|
||||
|
||||
if (prevPos.y > hitPos.y + 1) hitNorm = Directions.UP;
|
||||
else if (prevPos.y < hitPos.y) hitNorm = Directions.DOWN;
|
||||
else if (prevPos.x > hitPos.x + 1) hitNorm = Directions.POSITIVE_X;
|
||||
else if (prevPos.x < hitPos.x) hitNorm = Directions.NEGATIVE_X;
|
||||
else if (prevPos.z > hitPos.z + 1) hitNorm = Directions.POSITIVE_Z;
|
||||
else if (prevPos.z < hitPos.z) hitNorm = Directions.NEGATIVE_Z;
|
||||
// Determine the face that was hit based on which face was closest to the hit point.
|
||||
float minYDist = Math.abs(pos.y - hitPos.y);
|
||||
float maxYDist = Math.abs(pos.y - (hitPos.y + 1));
|
||||
float minXDist = Math.abs(pos.x - hitPos.x);
|
||||
float maxXDist = Math.abs(pos.x - (hitPos.x + 1));
|
||||
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);
|
||||
}
|
||||
|
@ -143,27 +142,54 @@ public class World {
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper function to find the next whole number, given a current number and
|
||||
* an indication of which direction the number is increasing.
|
||||
* @param n The current number.
|
||||
* @param sign An indication of which way the number is increasing.
|
||||
* @return The next whole number up from the current number.
|
||||
* Increments the given position until it hits a new block space, moving in
|
||||
* the specified direction. Note that we move slightly into a block if
|
||||
* needed, to ensure accuracy, so the position may not be a whole number.
|
||||
* @param pos The position.
|
||||
* @param dir The direction to move in.
|
||||
*/
|
||||
private static float getNextStep(float n, float sign) {
|
||||
if (sign > 0) {
|
||||
if (Math.ceil(n) == n) {
|
||||
return n + 1;
|
||||
private static void stepToNextBlock(Vector3f pos, Vector3f dir) {
|
||||
if (dir.lengthSquared() == 0) return;
|
||||
// Find the amount we'd have to multiply dir by to get pos to move to the next block on that axis.
|
||||
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 {
|
||||
return Math.ceil(n);
|
||||
nextValue = Math.floor(pos.x) - DELTA;
|
||||
}
|
||||
} else if (sign < 0) {
|
||||
if (Math.floor(n) == n) {
|
||||
return n - 1;
|
||||
float diff = nextValue - pos.x;
|
||||
factorX = Math.abs(diff / dir.x);
|
||||
}
|
||||
|
||||
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 {
|
||||
return Math.floor(n);
|
||||
nextValue = Math.floor(pos.y) - DELTA;
|
||||
}
|
||||
float diff = nextValue - pos.y;
|
||||
factorY = Math.abs(diff / dir.y);
|
||||
}
|
||||
return n;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewl.aos_core.model;
|
||||
|
||||
import nl.andrewl.aos_core.Directions;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector3i;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -35,79 +36,22 @@ public class WorldTest {
|
|||
|
||||
@Test
|
||||
public void testGetLookingAtPos() {
|
||||
World world = Worlds.testingWorld();
|
||||
// Spawn a block high in the air.
|
||||
Vector3i blockPos = new Vector3i(20, 20, 20);
|
||||
world.setBlockAt(blockPos.x, blockPos.y, blockPos.z, (byte) 5);
|
||||
assertEquals( // Looking straight down onto block.
|
||||
blockPos,
|
||||
world.getLookingAtPos(
|
||||
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
|
||||
)
|
||||
);
|
||||
World world = new World();
|
||||
Chunk chunk = new Chunk(0, 0, 0);
|
||||
world.addChunk(chunk);
|
||||
// Spawn a block in the middle for testing.
|
||||
Vector3i blockPos = new Vector3i(7, 7, 7);
|
||||
world.setBlockAt(blockPos.x, blockPos.y, blockPos.z, (byte) 1);
|
||||
Hit hit;
|
||||
|
||||
// Looking up into the void.
|
||||
assertNull(world.getLookingAtPos(
|
||||
new Vector3f(0, 30, 0),
|
||||
new Vector3f(0, 1, 0),
|
||||
10
|
||||
));
|
||||
// Looking down.
|
||||
hit = world.getLookingAtPos(new Vector3f(7.5f, 10, 7.5f), new Vector3f(0, -1, 0), 10);
|
||||
assertEquals(blockPos, hit.pos());
|
||||
assertEquals(Directions.UP, hit.norm());
|
||||
|
||||
// Looking straight down, but too far away.
|
||||
assertNull(world.getLookingAtPos(
|
||||
new Vector3f(0.5f, 20, 0.5f),
|
||||
new Vector3f(0, -1, 0),
|
||||
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
|
||||
)
|
||||
);
|
||||
// Looking up.
|
||||
hit = world.getLookingAtPos(new Vector3f(7.5f, 5, 7.5f), new Vector3f(0, 1, 0), 10);
|
||||
assertEquals(blockPos, hit.pos());
|
||||
assertEquals(Directions.DOWN, hit.norm());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ public class ClientCommunicationHandler {
|
|||
new Thread(tcpReceiver).start();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.warn("An IOException occurred while attempting to establish a connection to a client.", e);
|
||||
}
|
||||
attempts++;
|
||||
}
|
||||
|
|
|
@ -33,9 +33,11 @@ public class ServerPlayer extends Player {
|
|||
public static final float JUMP_SPEED = 8f;
|
||||
|
||||
public static final int BLOCK_REMOVE_COOLDOWN = 250;
|
||||
public static final int BLOCK_PLACE_COOLDOWN = 100;
|
||||
|
||||
private ClientInputState lastInputState;
|
||||
private long lastBlockRemovedAt = 0;
|
||||
private long lastBlockPlacedAt = 0;
|
||||
|
||||
private boolean updated = false;
|
||||
|
||||
|
@ -59,27 +61,27 @@ public class ServerPlayer extends Player {
|
|||
|
||||
public void tick(float dt, World world, Server server) {
|
||||
long now = System.currentTimeMillis();
|
||||
// Check for breaking blocks.
|
||||
if (lastInputState.hitting() && now - lastBlockRemovedAt > BLOCK_REMOVE_COOLDOWN) {
|
||||
Vector3f eyePos = new Vector3f(position);
|
||||
eyePos.y += getEyeHeight();
|
||||
var hit = world.getLookingAtPos(eyePos, viewVector, 10);
|
||||
System.out.println(hit);
|
||||
if (hit != null) {
|
||||
world.setBlockAt(hit.pos().x, hit.pos().y, hit.pos().z, (byte) 0);
|
||||
lastBlockRemovedAt = now;
|
||||
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);
|
||||
eyePos.y += getEyeHeight();
|
||||
var hit = world.getLookingAtPos(eyePos, viewVector, 10);
|
||||
System.out.println(hit);
|
||||
if (hit != null) {
|
||||
Vector3i placePos = new Vector3i(hit.pos());
|
||||
placePos.add(hit.norm());
|
||||
world.setBlockAt(placePos.x, placePos.y, placePos.z, (byte) 1);
|
||||
lastBlockRemovedAt = now;
|
||||
lastBlockPlacedAt = now;
|
||||
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(placePos, world));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue