started reworking collission system

This commit is contained in:
maxwes08
2026-03-24 14:32:54 +01:00
parent 24ca4288af
commit 7429775fac
2 changed files with 283 additions and 98 deletions

176
AABB.cs
View File

@@ -24,11 +24,34 @@ namespace Voxel
return new AABB(center - half, center + new Vector3(half.X, height, half.Z));
}
public void Move(float x, float y, float z)
{
Min.X += x;
Max.X += x;
Min.Y += y;
Max.Y += y;
Min.Z += z;
Max.Z += z;
}
public bool Intersects(AABB other)
{
return (Min.X <= other.Max.X && Max.X >= other.Min.X) &&
(Min.Y <= other.Max.Y && Max.Y >= other.Min.Y) &&
(Min.Z <= other.Max.Z && Max.Z >= other.Min.Z);
return IntersectsX(other) && IntersectsY(other) && IntersectsZ(other);
}
public bool IntersectsX(AABB other)
{
return Min.X < other.Max.X && Max.X > other.Max.X;
}
public bool IntersectsY(AABB other)
{
return Min.Y < other.Max.Y && Max.Y > other.Max.Y;
}
public bool IntersectsZ(AABB other)
{
return Min.Z < other.Max.Z && Max.Z > other.Max.Z;
}
public bool Contains(Vector3 point)
@@ -37,5 +60,152 @@ namespace Voxel
(point.Y >= Min.Y && point.Y <= Max.Y) &&
(point.Z >= Min.Z && point.Z <= Max.Z);
}
public float GetClipX(AABB other, float deltaX)
{
if (IntersectsY(other) && IntersectsZ(other))
{
if (deltaX > 0 && Max.X <= other.Min.X)
{
float clip = other.Min.X - other.Min.X;
if (deltaX > clip)
deltaX = clip;
}
if (deltaX < 0 && Min.X >= other.Max.X)
{
float clip = other.Max.X - Max.X;
if (deltaX < clip)
deltaX = clip;
}
return deltaX;
}
return deltaX;
}
public float GetClipY(AABB other, float deltaY)
{
if (IntersectsX(other) && IntersectsZ(other))
{
if (deltaY > 0 && Max.Y <= other.Min.Y)
{
float clip = other.Min.Y - other.Min.Y;
if (deltaY > clip)
deltaY = clip;
}
if (deltaY < 0 && Min.X >= other.Max.X)
{
float clip = other.Max.X - Max.X;
if (deltaY < clip)
deltaY = clip;
}
return deltaY;
}
return deltaY;
}
public float GetClipZ(AABB other, float deltaZ)
{
if (IntersectsX(other) && IntersectsY(other))
{
if (deltaZ > 0 && Max.Z <= other.Min.Z)
{
float clip = other.Min.Z - other.Min.Z;
if (deltaZ > clip)
deltaZ = clip;
}
if (deltaZ < 0 && Min.Z >= other.Max.Z)
{
float clip = other.Max.Z - Max.Z;
if (deltaZ < clip)
deltaZ = clip;
}
return deltaZ;
}
return deltaZ;
}
public float GetBlockClipX(int blockX, int blockY, int blockZ, float deltaX)
{
// Use the current AABB as the collider (no new allocation)
AABB collider = this;
// Block bounds
float blockMinX = blockX;
float blockMaxX = blockX + 1;
// Quick reject: if player is not overlapping the block in Y and Z, no collision.
if (!(collider.Min.Y < blockY + 1 && collider.Max.Y > blockY) ||
!(collider.Min.Z < blockZ + 1 && collider.Max.Z > blockZ))
return deltaX;
// Moving right (positive delta)
if (deltaX > 0 && collider.Max.X <= blockMinX)
{
float clip = blockMinX - collider.Max.X;
if (deltaX > clip) deltaX = clip;
}
// Moving left (negative delta)
else if (deltaX < 0 && collider.Min.X >= blockMaxX)
{
float clip = blockMaxX - collider.Min.X; // negative distance
if (deltaX < clip) deltaX = clip;
}
return deltaX;
}
public float GetBlockClipY(int blockX, int blockY, int blockZ, float deltaY)
{
AABB collider = this;
float blockMinY = blockY;
float blockMaxY = blockY + 1;
if (!(collider.Min.X < blockX + 1 && collider.Max.X > blockX) ||
!(collider.Min.Z < blockZ + 1 && collider.Max.Z > blockZ))
return deltaY;
if (deltaY > 0 && collider.Max.Y <= blockMinY)
{
float clip = blockMinY - collider.Max.Y;
if (deltaY > clip) deltaY = clip;
}
else if (deltaY < 0 && collider.Min.Y >= blockMaxY)
{
float clip = blockMaxY - collider.Min.Y;
if (deltaY < clip) deltaY = clip;
}
return deltaY;
}
public float GetBlockClipZ(int blockX, int blockY, int blockZ, float deltaZ)
{
AABB collider = this;
float blockMinZ = blockZ;
float blockMaxZ = blockZ + 1;
if (!(collider.Min.X < blockX + 1 && collider.Max.X > blockX) ||
!(collider.Min.Y < blockY + 1 && collider.Max.Y > blockY))
return deltaZ;
if (deltaZ > 0 && collider.Max.Z <= blockMinZ)
{
float clip = blockMinZ - collider.Max.Z;
if (deltaZ > clip) deltaZ = clip;
}
else if (deltaZ < 0 && collider.Min.Z >= blockMaxZ)
{
float clip = blockMaxZ - collider.Min.Z;
if (deltaZ < clip) deltaZ = clip;
}
return deltaZ;
}
}
}

205
Entity.cs
View File

@@ -1,6 +1,7 @@
using OpenTK.Mathematics;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -23,14 +24,13 @@ namespace Voxel
private float _yMultiplier = 0.98f;
private float _groundMultiplier = 0.6f;
private const float COLLISION_EPSILON = 0.001f;
private const float COLLISION_EPSILON = 0.1f;
private static int BlockCoordMin(float coord) => (int)MathF.Floor(coord - 1e-2f);
private static int BlockCoordMax(float coord) => (int)MathF.Floor(coord + 1e-2f);
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)
{
Position = position;
@@ -41,13 +41,13 @@ namespace Voxel
public void Tick()
{
Vector3 desiredMovement = Velocity;
Vector3 newPosition = Position;
var newPosition = Position;
var desiredMovement = Velocity;
CheckAndResolveCollisions(ref newPosition, desiredMovement);
Position = newPosition;
//Move();
if (!OnGround)
{
Vector3 acceleration = new Vector3(0, -_gravity, 0);
@@ -56,6 +56,7 @@ namespace Voxel
Velocity.Z *= _airMultiplier;
Velocity.Y *= _yMultiplier;
if (Velocity.Y < _terminalVelocity)
{
Velocity.Y = _terminalVelocity;
@@ -67,58 +68,59 @@ namespace Voxel
}
UpdateOnGround();
Console.WriteLine(Position.Y);
}
public void Move()
{
Vector3 moveVector = Velocity;
Vector3 original = moveVector;
AABB collider = new AABB(
Position - new Vector3(Position.X - Width / 2, 0, Position.Z - Width / 2),
Position - new Vector3(Position.X + Width / 2, Height, Position.Z + Width / 2)
);
}
public void CheckAndResolveCollisions(ref Vector3 position, Vector3 movement)
{
Vector3 originalPosition = position;
// Try full movement first
AABB futureBox = GetBoundingBoxAt(originalPosition + movement);
if (!HasCollision(futureBox))
{
// No collision, apply full movement
position = originalPosition + movement;
return;
}
// Collision detected, resolve each axis independently
position = originalPosition;
// Resolve Y collision first
ResolveYCollisionIndependent(ref position, movement.Y);
// Resolve X collision
ResolveXCollisionIndependent(ref position, movement.X);
Vector3 tempPosX = new Vector3(position.X + movement.X, position.Y, position.Z);
if (!HasCollision(GetBoundingBoxAt(tempPosX)))
{
position.X = tempPosX.X;
}
else
{
ResolveXCollisionIndependent(ref position, movement.X);
}
// Resolve Z collision
ResolveZCollisionIndependent(ref position, movement.Z);
// After moving horizontally, reresolve Y to handle any new vertical collisions
// (e.g., sliding into a ledge)
ResolveYCollisionIndependent(ref position, 0);
Vector3 tempPosZ = new Vector3(position.X, position.Y, position.Z + movement.Z);
if (!HasCollision(GetBoundingBoxAt(tempPosZ)))
{
position.Z = tempPosZ.Z;
}
else
{
ResolveZCollisionIndependent(ref position, movement.Z);
}
}
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;
Vector3 testPos = new Vector3(position.X, position.Y + velocityY, position.Z);
@@ -126,7 +128,7 @@ namespace Voxel
if (HasCollision(testBox))
{
if (velocityY > 0) // Hitting ceiling
if (velocityY > 0)
{
float ceilingY = GetCeilingHeight(testBox);
position.Y = ceilingY - (Height / 2);
@@ -150,66 +152,80 @@ namespace Voxel
{
if (velocityX == 0) return;
float halfWidth = Width / 2;
// Check if we're already inside a block at current position
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 targetX = position.X + velocityX;
float checkDistance = Math.Abs(velocityX) + COLLISION_EPSILON;
// Sweep the bounding box to the target X position
Vector3 testPos = new Vector3(targetX, position.Y, position.Z);
AABB testBox = GetBoundingBoxAt(testPos);
for (float offset = 0; offset <= checkDistance; offset += 0.1f)
{
float testX = position.X + (offset * direction);
AABB testBox = GetBoundingBoxAt(new Vector3(testX, position.Y, position.Z));
if (HasCollision(testBox))
{
// Find the wall we hit
float wallX;
if (direction > 0) // Moving right
if (HasCollision(testBox))
{
wallX = GetRightWallPosition(testBox) - halfWidth - COLLISION_EPSILON;
if (direction > 0)
{
position.X = GetRightWallPosition(testBox) - (Width / 2) - COLLISION_EPSILON;
}
else
{
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)
{
if (velocityZ == 0) return;
float halfWidth = Width / 2;
// Check if we're already inside a block at current position
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 targetZ = position.Z + velocityZ;
float checkDistance = Math.Abs(velocityZ) + COLLISION_EPSILON;
// Sweep the bounding box to the target Z position
Vector3 testPos = new Vector3(position.X, position.Y, targetZ);
AABB testBox = GetBoundingBoxAt(testPos);
for (float offset = 0; offset <= checkDistance; offset += 0.01f)
{
float testZ = position.Z + (offset * direction);
AABB testBox = GetBoundingBoxAt(new Vector3(position.X, position.Y, testZ));
if (HasCollision(testBox))
{
// Find the wall we hit
float wallZ;
if (direction > 0) // Moving forward (+Z)
if (HasCollision(testBox))
{
wallZ = GetFrontWallPosition(testBox) - halfWidth - COLLISION_EPSILON;
if (direction > 0) // Moving forward
{
position.Z = GetFrontWallPosition(testBox) - (Width / 2);
}
else // Moving backward
{
position.Z = GetBackWallPosition(testBox) + (Width / 2);
}
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)
@@ -243,7 +259,7 @@ namespace Voxel
int maxX = BlockCoordMax(box.Max.X);
int minZ = BlockCoordMin(box.Min.Z);
int maxZ = BlockCoordMax(box.Max.Z);
int checkY = BlockCoordMax(box.Max.Y); // Fix: use the block above the entity
int checkY = BlockCoordMax(box.Max.Y);
float lowestCeiling = float.MaxValue;
@@ -268,7 +284,7 @@ namespace Voxel
int maxY = BlockCoordMax(box.Max.Y);
int minZ = BlockCoordMin(box.Min.Z);
int maxZ = BlockCoordMax(box.Max.Z);
int checkX = BlockCoordMin(box.Min.X); // leftmost block the entity overlaps
int checkX = BlockCoordMin(box.Min.X);
float rightmostWall = float.MinValue;
@@ -293,7 +309,7 @@ namespace Voxel
int maxY = BlockCoordMax(box.Max.Y);
int minZ = BlockCoordMin(box.Min.Z);
int maxZ = BlockCoordMax(box.Max.Z);
int checkX = BlockCoordMax(box.Max.X); // rightmost block the entity overlaps
int checkX = BlockCoordMax(box.Max.X);
float leftmostWall = float.MaxValue;
@@ -318,7 +334,7 @@ namespace Voxel
int maxX = BlockCoordMax(box.Max.X);
int minY = BlockCoordMin(box.Min.Y);
int maxY = BlockCoordMax(box.Max.Y);
int checkZ = BlockCoordMin(box.Min.Z); // backmost block the entity overlaps
int checkZ = BlockCoordMin(box.Min.Z);
float frontmostWall = float.MinValue;
@@ -343,7 +359,7 @@ namespace Voxel
int maxX = BlockCoordMax(box.Max.X);
int minY = BlockCoordMin(box.Min.Y);
int maxY = BlockCoordMax(box.Max.Y);
int checkZ = BlockCoordMax(box.Max.Z); // frontmost block the entity overlaps
int checkZ = BlockCoordMax(box.Max.Z);
float backmostWall = float.MaxValue;
@@ -406,13 +422,13 @@ namespace Voxel
Vector3 min = new Vector3(
position.X - halfWidth,
position.Y - halfHeight,
position.Y - halfHeight, // Center Y minus half height
position.Z - halfWidth
);
Vector3 max = new Vector3(
position.X + halfWidth,
position.Y + halfHeight,
position.Y + halfHeight, // Center Y plus half height
position.Z + halfWidth
);
@@ -424,18 +440,17 @@ namespace Voxel
AABB box = GetBoundingBox();
float yCheck = box.Min.Y - COLLISION_EPSILON;
int minX = BlockCoordMin(box.Min.X);
int maxX = BlockCoordMax(box.Max.X);
int minZ = BlockCoordMin(box.Min.Z);
int maxZ = BlockCoordMax(box.Max.Z);
int y = BlockCoordMin(yCheck);
int minX = (int)MathF.Floor(box.Min.X);
int maxX = (int)MathF.Floor(box.Max.X);
int minZ = (int)MathF.Floor(box.Min.Z);
int maxZ = (int)MathF.Floor(box.Max.Z);
OnGround = false;
for (int x = minX; x <= maxX; x++)
{
for (int z = minZ; z <= maxZ; z++)
{
Blocks block = _world.GetBlock(x, y, z);
Blocks block = _world.GetBlock(x, (int)MathF.Floor(yCheck), z);
if (block != Blocks.Air)
{
OnGround = true;