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;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
float diff = nextValue - pos.x;
|
||||||
if (Math.floor(n) == n) {
|
factorX = Math.abs(diff / dir.x);
|
||||||
return n - 1;
|
}
|
||||||
|
|
||||||
|
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 {
|
} 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;
|
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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue