collision refactor
This commit is contained in:
14
Blocks.cs
14
Blocks.cs
@@ -8,7 +8,8 @@
|
|||||||
OakPlanks,
|
OakPlanks,
|
||||||
Grass,
|
Grass,
|
||||||
Bedrock,
|
Bedrock,
|
||||||
Sand
|
Sand,
|
||||||
|
TNT
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Orientation : byte
|
public enum Orientation : byte
|
||||||
@@ -88,6 +89,17 @@
|
|||||||
Voxel.Textures.GrassSide, // North
|
Voxel.Textures.GrassSide, // North
|
||||||
Voxel.Textures.GrassSide // South
|
Voxel.Textures.GrassSide // South
|
||||||
)},
|
)},
|
||||||
|
|
||||||
|
|
||||||
|
{ Voxel.Blocks.TNT, new BlockDefinition(
|
||||||
|
Voxel.Blocks.TNT,
|
||||||
|
Voxel.Textures.TntSide, // West
|
||||||
|
Voxel.Textures.TntSide, // East
|
||||||
|
Voxel.Textures.TntTop, // Top
|
||||||
|
Voxel.Textures.TntBottom, // Bottom
|
||||||
|
Voxel.Textures.TntSide, // North
|
||||||
|
Voxel.Textures.TntSide // South
|
||||||
|
)},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
251
Entity.cs
251
Entity.cs
@@ -20,12 +20,17 @@ namespace Voxel
|
|||||||
private float _gravity = 0.08f;
|
private float _gravity = 0.08f;
|
||||||
private float _terminalVelocity = -3.92f;
|
private float _terminalVelocity = -3.92f;
|
||||||
private float _airMultiplier = 0.91f;
|
private float _airMultiplier = 0.91f;
|
||||||
|
private float _yMultiplier = 0.98f;
|
||||||
private float _groundMultiplier = 0.6f;
|
private float _groundMultiplier = 0.6f;
|
||||||
|
|
||||||
private const float COLLISION_EPSILON = 0.01f;
|
private const float COLLISION_EPSILON = 0.001f;
|
||||||
|
|
||||||
protected World _world;
|
protected World _world;
|
||||||
|
|
||||||
|
// Helper methods for consistent block coordinate conversion
|
||||||
|
private static int BlockCoordMin(float coord) => (int)MathF.Floor(coord - 1e-6f);
|
||||||
|
private static int BlockCoordMax(float coord) => (int)MathF.Floor(coord + 1e-6f);
|
||||||
|
|
||||||
public Entity(Vector3 position, float width, float height, World world)
|
public Entity(Vector3 position, float width, float height, World world)
|
||||||
{
|
{
|
||||||
Position = position;
|
Position = position;
|
||||||
@@ -47,8 +52,9 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
Vector3 acceleration = new Vector3(0, -_gravity, 0);
|
Vector3 acceleration = new Vector3(0, -_gravity, 0);
|
||||||
Velocity += acceleration;
|
Velocity += acceleration;
|
||||||
Velocity *= _airMultiplier;
|
Velocity.X *= _airMultiplier;
|
||||||
|
Velocity.Z *= _airMultiplier;
|
||||||
|
Velocity.Y *= _yMultiplier;
|
||||||
|
|
||||||
if (Velocity.Y < _terminalVelocity)
|
if (Velocity.Y < _terminalVelocity)
|
||||||
{
|
{
|
||||||
@@ -77,36 +83,42 @@ namespace Voxel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collision detected, resolve each axis separately but independently
|
// Collision detected, resolve each axis independently
|
||||||
position = originalPosition;
|
position = originalPosition;
|
||||||
|
|
||||||
// Resolve Y collision (always first for ground detection)
|
// Resolve Y collision first
|
||||||
ResolveYCollisionIndependent(ref position, movement.Y);
|
ResolveYCollisionIndependent(ref position, movement.Y);
|
||||||
|
|
||||||
// Resolve X and Z collisions independently of each other
|
// Resolve X collision
|
||||||
Vector3 tempPosX = new Vector3(position.X + movement.X, position.Y, position.Z);
|
ResolveXCollisionIndependent(ref position, movement.X);
|
||||||
if (!HasCollision(GetBoundingBoxAt(tempPosX)))
|
|
||||||
{
|
|
||||||
position.X = tempPosX.X;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResolveXCollisionIndependent(ref position, movement.X);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 tempPosZ = new Vector3(position.X, position.Y, position.Z + movement.Z);
|
// Resolve Z collision
|
||||||
if (!HasCollision(GetBoundingBoxAt(tempPosZ)))
|
ResolveZCollisionIndependent(ref position, movement.Z);
|
||||||
{
|
|
||||||
position.Z = tempPosZ.Z;
|
// After moving horizontally, re‑resolve Y to handle any new vertical collisions
|
||||||
}
|
// (e.g., sliding into a ledge)
|
||||||
else
|
ResolveYCollisionIndependent(ref position, 0);
|
||||||
{
|
|
||||||
ResolveZCollisionIndependent(ref position, movement.Z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResolveYCollisionIndependent(ref Vector3 position, float velocityY)
|
private void ResolveYCollisionIndependent(ref Vector3 position, float velocityY)
|
||||||
{
|
{
|
||||||
|
// First, handle if we're already inside a block (shouldn't happen normally)
|
||||||
|
AABB currentBox = GetBoundingBoxAt(position);
|
||||||
|
if (HasCollision(currentBox))
|
||||||
|
{
|
||||||
|
// Try to push upward to escape
|
||||||
|
float step = 0.05f;
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
position.Y += step;
|
||||||
|
if (!HasCollision(GetBoundingBoxAt(position)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Velocity.Y = 0;
|
||||||
|
OnGround = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (velocityY == 0) return;
|
if (velocityY == 0) return;
|
||||||
|
|
||||||
Vector3 testPos = new Vector3(position.X, position.Y + velocityY, position.Z);
|
Vector3 testPos = new Vector3(position.X, position.Y + velocityY, position.Z);
|
||||||
@@ -117,13 +129,13 @@ namespace Voxel
|
|||||||
if (velocityY > 0) // Hitting ceiling
|
if (velocityY > 0) // Hitting ceiling
|
||||||
{
|
{
|
||||||
float ceilingY = GetCeilingHeight(testBox);
|
float ceilingY = GetCeilingHeight(testBox);
|
||||||
position.Y = ceilingY - (Height / 2) - COLLISION_EPSILON;
|
position.Y = ceilingY - (Height / 2);
|
||||||
Velocity.Y = 0;
|
Velocity.Y = 0;
|
||||||
}
|
}
|
||||||
else // Hitting floor
|
else // Hitting floor
|
||||||
{
|
{
|
||||||
float floorY = GetFloorHeight(testBox);
|
float floorY = GetFloorHeight(testBox);
|
||||||
position.Y = floorY + (Height / 2) + COLLISION_EPSILON;
|
position.Y = floorY + (Height / 2);
|
||||||
Velocity.Y = 0;
|
Velocity.Y = 0;
|
||||||
OnGround = true;
|
OnGround = true;
|
||||||
}
|
}
|
||||||
@@ -138,89 +150,75 @@ namespace Voxel
|
|||||||
{
|
{
|
||||||
if (velocityX == 0) return;
|
if (velocityX == 0) return;
|
||||||
|
|
||||||
// Check if we're already inside a block at current position
|
float halfWidth = Width / 2;
|
||||||
AABB currentBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, position.Z));
|
|
||||||
if (HasCollision(currentBox))
|
|
||||||
{
|
|
||||||
// Already inside a block, allow movement to escape
|
|
||||||
position.X += velocityX;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float direction = Math.Sign(velocityX);
|
float direction = Math.Sign(velocityX);
|
||||||
float checkDistance = Math.Abs(velocityX) + COLLISION_EPSILON;
|
float targetX = position.X + velocityX;
|
||||||
|
|
||||||
for (float offset = 0; offset <= checkDistance; offset += 0.01f)
|
// Sweep the bounding box to the target X position
|
||||||
|
Vector3 testPos = new Vector3(targetX, position.Y, position.Z);
|
||||||
|
AABB testBox = GetBoundingBoxAt(testPos);
|
||||||
|
|
||||||
|
if (HasCollision(testBox))
|
||||||
{
|
{
|
||||||
float testX = position.X + (offset * direction);
|
// Find the wall we hit
|
||||||
AABB testBox = GetBoundingBoxAt(new Vector3(testX, position.Y, position.Z));
|
float wallX;
|
||||||
|
if (direction > 0) // Moving right
|
||||||
if (HasCollision(testBox))
|
|
||||||
{
|
{
|
||||||
if (direction > 0) // Moving right
|
wallX = GetRightWallPosition(testBox) - halfWidth - COLLISION_EPSILON;
|
||||||
{
|
|
||||||
position.X = GetRightWallPosition(testBox) - (Width / 2) - COLLISION_EPSILON;
|
|
||||||
}
|
|
||||||
else // Moving left
|
|
||||||
{
|
|
||||||
position.X = GetLeftWallPosition(testBox) + (Width / 2) + COLLISION_EPSILON;
|
|
||||||
}
|
|
||||||
Velocity.X = 0;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else // Moving left
|
||||||
|
{
|
||||||
|
wallX = GetLeftWallPosition(testBox) + halfWidth + COLLISION_EPSILON;
|
||||||
|
}
|
||||||
|
position.X = wallX;
|
||||||
|
Velocity.X = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
position.X = targetX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No collision found, apply full movement
|
|
||||||
position.X += velocityX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResolveZCollisionIndependent(ref Vector3 position, float velocityZ)
|
private void ResolveZCollisionIndependent(ref Vector3 position, float velocityZ)
|
||||||
{
|
{
|
||||||
if (velocityZ == 0) return;
|
if (velocityZ == 0) return;
|
||||||
|
|
||||||
// Check if we're already inside a block at current position
|
float halfWidth = Width / 2;
|
||||||
AABB currentBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, position.Z));
|
|
||||||
if (HasCollision(currentBox))
|
|
||||||
{
|
|
||||||
// Already inside a block, allow movement to escape
|
|
||||||
position.Z += velocityZ;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float direction = Math.Sign(velocityZ);
|
float direction = Math.Sign(velocityZ);
|
||||||
float checkDistance = Math.Abs(velocityZ) + COLLISION_EPSILON;
|
float targetZ = position.Z + velocityZ;
|
||||||
|
|
||||||
for (float offset = 0; offset <= checkDistance; offset += 0.01f)
|
// Sweep the bounding box to the target Z position
|
||||||
|
Vector3 testPos = new Vector3(position.X, position.Y, targetZ);
|
||||||
|
AABB testBox = GetBoundingBoxAt(testPos);
|
||||||
|
|
||||||
|
if (HasCollision(testBox))
|
||||||
{
|
{
|
||||||
float testZ = position.Z + (offset * direction);
|
// Find the wall we hit
|
||||||
AABB testBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, testZ));
|
float wallZ;
|
||||||
|
if (direction > 0) // Moving forward (+Z)
|
||||||
if (HasCollision(testBox))
|
|
||||||
{
|
{
|
||||||
if (direction > 0) // Moving forward
|
wallZ = GetFrontWallPosition(testBox) - halfWidth - COLLISION_EPSILON;
|
||||||
{
|
|
||||||
position.Z = GetFrontWallPosition(testBox) - (Width / 2) - COLLISION_EPSILON;
|
|
||||||
}
|
|
||||||
else // Moving backward
|
|
||||||
{
|
|
||||||
position.Z = GetBackWallPosition(testBox) + (Width / 2) + COLLISION_EPSILON;
|
|
||||||
}
|
|
||||||
Velocity.Z = 0;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else // Moving backward (-Z)
|
||||||
|
{
|
||||||
|
wallZ = GetBackWallPosition(testBox) + halfWidth + COLLISION_EPSILON;
|
||||||
|
}
|
||||||
|
position.Z = wallZ;
|
||||||
|
Velocity.Z = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
position.Z = targetZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No collision found, apply full movement
|
|
||||||
position.Z += velocityZ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetFloorHeight(AABB box)
|
private float GetFloorHeight(AABB box)
|
||||||
{
|
{
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
int minX = BlockCoordMin(box.Min.X);
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
int maxX = BlockCoordMax(box.Max.X);
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
int minZ = BlockCoordMin(box.Min.Z);
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
int maxZ = BlockCoordMax(box.Max.Z);
|
||||||
int checkY = (int)MathF.Floor(box.Min.Y);
|
int checkY = BlockCoordMin(box.Min.Y);
|
||||||
|
|
||||||
float highestFloor = float.MinValue;
|
float highestFloor = float.MinValue;
|
||||||
|
|
||||||
@@ -241,11 +239,11 @@ namespace Voxel
|
|||||||
|
|
||||||
private float GetCeilingHeight(AABB box)
|
private float GetCeilingHeight(AABB box)
|
||||||
{
|
{
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
int minX = BlockCoordMin(box.Min.X);
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
int maxX = BlockCoordMax(box.Max.X);
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
int minZ = BlockCoordMin(box.Min.Z);
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
int maxZ = BlockCoordMax(box.Max.Z);
|
||||||
int checkY = (int)MathF.Floor(box.Max.Y);
|
int checkY = BlockCoordMax(box.Max.Y); // Fix: use the block above the entity
|
||||||
|
|
||||||
float lowestCeiling = float.MaxValue;
|
float lowestCeiling = float.MaxValue;
|
||||||
|
|
||||||
@@ -266,11 +264,11 @@ namespace Voxel
|
|||||||
|
|
||||||
private float GetLeftWallPosition(AABB box)
|
private float GetLeftWallPosition(AABB box)
|
||||||
{
|
{
|
||||||
int minY = (int)MathF.Floor(box.Min.Y);
|
int minY = BlockCoordMin(box.Min.Y);
|
||||||
int maxY = (int)MathF.Floor(box.Max.Y);
|
int maxY = BlockCoordMax(box.Max.Y);
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
int minZ = BlockCoordMin(box.Min.Z);
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
int maxZ = BlockCoordMax(box.Max.Z);
|
||||||
int checkX = (int)MathF.Floor(box.Min.X);
|
int checkX = BlockCoordMin(box.Min.X); // leftmost block the entity overlaps
|
||||||
|
|
||||||
float rightmostWall = float.MinValue;
|
float rightmostWall = float.MinValue;
|
||||||
|
|
||||||
@@ -291,11 +289,11 @@ namespace Voxel
|
|||||||
|
|
||||||
private float GetRightWallPosition(AABB box)
|
private float GetRightWallPosition(AABB box)
|
||||||
{
|
{
|
||||||
int minY = (int)MathF.Floor(box.Min.Y);
|
int minY = BlockCoordMin(box.Min.Y);
|
||||||
int maxY = (int)MathF.Floor(box.Max.Y);
|
int maxY = BlockCoordMax(box.Max.Y);
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
int minZ = BlockCoordMin(box.Min.Z);
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
int maxZ = BlockCoordMax(box.Max.Z);
|
||||||
int checkX = (int)MathF.Floor(box.Max.X);
|
int checkX = BlockCoordMax(box.Max.X); // rightmost block the entity overlaps
|
||||||
|
|
||||||
float leftmostWall = float.MaxValue;
|
float leftmostWall = float.MaxValue;
|
||||||
|
|
||||||
@@ -316,11 +314,11 @@ namespace Voxel
|
|||||||
|
|
||||||
private float GetBackWallPosition(AABB box)
|
private float GetBackWallPosition(AABB box)
|
||||||
{
|
{
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
int minX = BlockCoordMin(box.Min.X);
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
int maxX = BlockCoordMax(box.Max.X);
|
||||||
int minY = (int)MathF.Floor(box.Min.Y);
|
int minY = BlockCoordMin(box.Min.Y);
|
||||||
int maxY = (int)MathF.Floor(box.Max.Y);
|
int maxY = BlockCoordMax(box.Max.Y);
|
||||||
int checkZ = (int)MathF.Floor(box.Min.Z);
|
int checkZ = BlockCoordMin(box.Min.Z); // backmost block the entity overlaps
|
||||||
|
|
||||||
float frontmostWall = float.MinValue;
|
float frontmostWall = float.MinValue;
|
||||||
|
|
||||||
@@ -341,11 +339,11 @@ namespace Voxel
|
|||||||
|
|
||||||
private float GetFrontWallPosition(AABB box)
|
private float GetFrontWallPosition(AABB box)
|
||||||
{
|
{
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
int minX = BlockCoordMin(box.Min.X);
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
int maxX = BlockCoordMax(box.Max.X);
|
||||||
int minY = (int)MathF.Floor(box.Min.Y);
|
int minY = BlockCoordMin(box.Min.Y);
|
||||||
int maxY = (int)MathF.Floor(box.Max.Y);
|
int maxY = BlockCoordMax(box.Max.Y);
|
||||||
int checkZ = (int)MathF.Floor(box.Max.Z);
|
int checkZ = BlockCoordMax(box.Max.Z); // frontmost block the entity overlaps
|
||||||
|
|
||||||
float backmostWall = float.MaxValue;
|
float backmostWall = float.MaxValue;
|
||||||
|
|
||||||
@@ -366,12 +364,12 @@ namespace Voxel
|
|||||||
|
|
||||||
public bool HasCollision(AABB box)
|
public bool HasCollision(AABB box)
|
||||||
{
|
{
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
int minX = BlockCoordMin(box.Min.X);
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
int maxX = BlockCoordMax(box.Max.X);
|
||||||
int minY = (int)MathF.Floor(box.Min.Y);
|
int minY = BlockCoordMin(box.Min.Y);
|
||||||
int maxY = (int)MathF.Floor(box.Max.Y);
|
int maxY = BlockCoordMax(box.Max.Y);
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
int minZ = BlockCoordMin(box.Min.Z);
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
int maxZ = BlockCoordMax(box.Max.Z);
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++)
|
for (int x = minX; x <= maxX; x++)
|
||||||
{
|
{
|
||||||
@@ -408,13 +406,13 @@ namespace Voxel
|
|||||||
|
|
||||||
Vector3 min = new Vector3(
|
Vector3 min = new Vector3(
|
||||||
position.X - halfWidth,
|
position.X - halfWidth,
|
||||||
position.Y - halfHeight, // Center Y minus half height
|
position.Y - halfHeight,
|
||||||
position.Z - halfWidth
|
position.Z - halfWidth
|
||||||
);
|
);
|
||||||
|
|
||||||
Vector3 max = new Vector3(
|
Vector3 max = new Vector3(
|
||||||
position.X + halfWidth,
|
position.X + halfWidth,
|
||||||
position.Y + halfHeight, // Center Y plus half height
|
position.Y + halfHeight,
|
||||||
position.Z + halfWidth
|
position.Z + halfWidth
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -424,19 +422,20 @@ namespace Voxel
|
|||||||
public void UpdateOnGround()
|
public void UpdateOnGround()
|
||||||
{
|
{
|
||||||
AABB box = GetBoundingBox();
|
AABB box = GetBoundingBox();
|
||||||
float yCheck = box.Min.Y - 0.05f;
|
float yCheck = box.Min.Y - COLLISION_EPSILON;
|
||||||
|
|
||||||
int minX = (int)MathF.Floor(box.Min.X);
|
int minX = BlockCoordMin(box.Min.X);
|
||||||
int maxX = (int)MathF.Floor(box.Max.X);
|
int maxX = BlockCoordMax(box.Max.X);
|
||||||
int minZ = (int)MathF.Floor(box.Min.Z);
|
int minZ = BlockCoordMin(box.Min.Z);
|
||||||
int maxZ = (int)MathF.Floor(box.Max.Z);
|
int maxZ = BlockCoordMax(box.Max.Z);
|
||||||
|
int y = BlockCoordMin(yCheck);
|
||||||
|
|
||||||
OnGround = false;
|
OnGround = false;
|
||||||
for (int x = minX; x <= maxX; x++)
|
for (int x = minX; x <= maxX; x++)
|
||||||
{
|
{
|
||||||
for (int z = minZ; z <= maxZ; z++)
|
for (int z = minZ; z <= maxZ; z++)
|
||||||
{
|
{
|
||||||
Blocks block = _world.GetBlock(x, (int)MathF.Floor(yCheck), z);
|
Blocks block = _world.GetBlock(x, y, z);
|
||||||
if (block != Blocks.Air)
|
if (block != Blocks.Air)
|
||||||
{
|
{
|
||||||
OnGround = true;
|
OnGround = true;
|
||||||
|
|||||||
36
Player.cs
36
Player.cs
@@ -10,7 +10,7 @@ namespace Voxel
|
|||||||
public readonly float mouseCooldown = 0.2f;
|
public readonly float mouseCooldown = 0.2f;
|
||||||
|
|
||||||
private int _blockIndex = 0;
|
private int _blockIndex = 0;
|
||||||
private double _tickTime = 0;
|
private int _jumpTicks = 0;
|
||||||
|
|
||||||
private Blocks _selectedBlock = Blocks.OakPlanks;
|
private Blocks _selectedBlock = Blocks.OakPlanks;
|
||||||
private Vector3 previousPosition;
|
private Vector3 previousPosition;
|
||||||
@@ -40,8 +40,10 @@ namespace Voxel
|
|||||||
_world.SetBlock(x, y, z, Blocks.Air);
|
_world.SetBlock(x, y, z, Blocks.Air);
|
||||||
}
|
}
|
||||||
|
|
||||||
public new void Tick()
|
public void Tick()
|
||||||
{
|
{
|
||||||
|
if (_jumpTicks > 0) _jumpTicks--;
|
||||||
|
|
||||||
_world.UpdateChunkLoading(Position);
|
_world.UpdateChunkLoading(Position);
|
||||||
|
|
||||||
previousPosition = Position;
|
previousPosition = Position;
|
||||||
@@ -58,12 +60,6 @@ namespace Voxel
|
|||||||
if (Input.GetKey(Keys.D))
|
if (Input.GetKey(Keys.D))
|
||||||
sidewards = 1;
|
sidewards = 1;
|
||||||
|
|
||||||
if (Input.GetKey(Keys.Space) && OnGround)
|
|
||||||
{
|
|
||||||
Velocity = new Vector3(Velocity.X, 0.42f, Velocity.Z);
|
|
||||||
OnGround = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Input.GetMouseButton(MouseButton.Right) && lastClick == 0)
|
if (Input.GetMouseButton(MouseButton.Right) && lastClick == 0)
|
||||||
{
|
{
|
||||||
lastClick = mouseCooldown;
|
lastClick = mouseCooldown;
|
||||||
@@ -78,8 +74,30 @@ namespace Voxel
|
|||||||
BreakBlock();
|
BreakBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Position.Y < -8)
|
||||||
|
{
|
||||||
|
Position.Y = 64;
|
||||||
|
Velocity.Y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
ApplyWalkSpeed(sidewards, forwards);
|
ApplyWalkSpeed(sidewards, forwards);
|
||||||
base.Tick();
|
base.Tick();
|
||||||
|
|
||||||
|
if (Input.GetKey(Keys.Space))
|
||||||
|
{
|
||||||
|
if (OnGround && _jumpTicks == 0)
|
||||||
|
{
|
||||||
|
Jump();
|
||||||
|
_jumpTicks = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else _jumpTicks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Jump()
|
||||||
|
{
|
||||||
|
Velocity = new Vector3(Velocity.X, 0.42f, Velocity.Z);
|
||||||
|
OnGround = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(float deltaTime, float alpha)
|
public void Update(float deltaTime, float alpha)
|
||||||
@@ -125,7 +143,7 @@ namespace Voxel
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Vector3 groundAccel = worldDir * 0.1f * M_t;
|
Vector3 groundAccel = worldDir * 0.13f * M_t;
|
||||||
Velocity = new Vector3(Velocity.X * groundMultiplier + groundAccel.X,
|
Velocity = new Vector3(Velocity.X * groundMultiplier + groundAccel.X,
|
||||||
Velocity.Y,
|
Velocity.Y,
|
||||||
Velocity.Z * groundMultiplier + groundAccel.Z);
|
Velocity.Z * groundMultiplier + groundAccel.Z);
|
||||||
|
|||||||
81
World.cs
81
World.cs
@@ -47,7 +47,7 @@ namespace Voxel
|
|||||||
var neighbors = GetChunkNeighbors(chunk).ToList();
|
var neighbors = GetChunkNeighbors(chunk).ToList();
|
||||||
_chunks.Remove((chunkX, chunkZ));
|
_chunks.Remove((chunkX, chunkZ));
|
||||||
|
|
||||||
// 3. For each neighbor, remove reference to this chunk
|
// update neighbor references to this chunk
|
||||||
foreach (var (orientation, neighbor) in neighbors)
|
foreach (var (orientation, neighbor) in neighbors)
|
||||||
{
|
{
|
||||||
var opposite = GetOppositeOrientation(orientation);
|
var opposite = GetOppositeOrientation(orientation);
|
||||||
@@ -159,16 +159,26 @@ namespace Voxel
|
|||||||
return chunk.GetBlock(localX, worldY, localZ);
|
return chunk.GetBlock(localX, worldY, localZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block)
|
public void SetBlock(int worldX, int worldY, int worldZ, Blocks block, bool updateMesh = true)
|
||||||
{
|
{
|
||||||
var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ);
|
var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ);
|
||||||
|
|
||||||
Chunk chunk = GetChunk(chunkX, chunkZ);
|
Chunk chunk = GetChunk(chunkX, chunkZ);
|
||||||
if (chunk == null) return;
|
if (chunk == null) return;
|
||||||
|
|
||||||
chunk.SetBlock(localX, worldY, localZ, block);
|
chunk.SetBlock(localX, worldY, localZ, block, updateMesh: updateMesh);
|
||||||
|
|
||||||
if (block == Blocks.Air && ((localX == Chunk.Size - 1 || localX == 0) || (localZ == Chunk.Size - 1 || localZ == 0)))
|
// temporary tnt functionality
|
||||||
|
|
||||||
|
if (block == Blocks.TNT)
|
||||||
|
{
|
||||||
|
int radius = 4;
|
||||||
|
Explode(worldX, worldY, worldZ, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateMesh == false) return;
|
||||||
|
|
||||||
|
if (block == Blocks.Air && IsOnChunkBorder(worldX, worldZ))
|
||||||
{
|
{
|
||||||
foreach (var orientation in GetEdgeOrientations(localX, localZ, Chunk.Size))
|
foreach (var orientation in GetEdgeOrientations(localX, localZ, Chunk.Size))
|
||||||
{
|
{
|
||||||
@@ -180,6 +190,69 @@ namespace Voxel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsOnChunkBorder(int worldX, int worldZ)
|
||||||
|
{
|
||||||
|
var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(worldX, worldZ);
|
||||||
|
return (localX == Chunk.Size - 1 || localX == 0) || (localZ == Chunk.Size - 1 || localZ == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// temporary tnt functionality
|
||||||
|
public void Explode(int centerX, int centerY, int centerZ, int radius)
|
||||||
|
{
|
||||||
|
int radiusSq = radius * radius;
|
||||||
|
var affectedChunks = new HashSet<Chunk>(); // store chunks that will change
|
||||||
|
|
||||||
|
// bounding box
|
||||||
|
int minX = centerX - radius;
|
||||||
|
int maxX = centerX + radius;
|
||||||
|
int minY = centerY - radius;
|
||||||
|
int maxY = centerY + radius;
|
||||||
|
int minZ = centerZ - radius;
|
||||||
|
int maxZ = centerZ + radius;
|
||||||
|
|
||||||
|
for (int x = minX; x <= maxX; x++)
|
||||||
|
{
|
||||||
|
for (int y = minY; y <= maxY; y++)
|
||||||
|
{
|
||||||
|
for (int z = minZ; z <= maxZ; z++)
|
||||||
|
{
|
||||||
|
int dx = x - centerX;
|
||||||
|
int dy = y - centerY;
|
||||||
|
int dz = z - centerZ;
|
||||||
|
if (dx * dx + dy * dy + dz * dz <= radiusSq)
|
||||||
|
{
|
||||||
|
// no update
|
||||||
|
SetBlock(x, y, z, Blocks.Air, updateMesh: false);
|
||||||
|
|
||||||
|
// add chunk to update list
|
||||||
|
var (chunkX, chunkZ, localX, localZ) = WorldToChunkCoords(x, z);
|
||||||
|
Chunk chunk = GetChunk(chunkX, chunkZ);
|
||||||
|
|
||||||
|
if (chunk != null)
|
||||||
|
affectedChunks.Add(chunk);
|
||||||
|
|
||||||
|
if (IsOnChunkBorder(x, z))
|
||||||
|
{
|
||||||
|
foreach (var orientation in GetEdgeOrientations(localX, localZ, Chunk.Size))
|
||||||
|
{
|
||||||
|
if (chunk.Neighbors.TryGetValue(orientation, out var neighbor) && neighbor != null)
|
||||||
|
{
|
||||||
|
affectedChunks.Add(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now rebuild meshes for all affected chunks
|
||||||
|
foreach (var chunk in affectedChunks)
|
||||||
|
{
|
||||||
|
chunk.UpdateChunkMesh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static (int chunkX, int chunkZ, int localX, int localZ) WorldToChunkCoords(int worldX, int worldZ)
|
public static (int chunkX, int chunkZ, int localX, int localZ) WorldToChunkCoords(int worldX, int worldZ)
|
||||||
{
|
{
|
||||||
int chunkX = worldX >= 0 ? worldX / Chunk.Size : (worldX + 1) / Chunk.Size - 1;
|
int chunkX = worldX >= 0 ? worldX / Chunk.Size : (worldX + 1) / Chunk.Size - 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user