started reworking collission system
This commit is contained in:
205
Entity.cs
205
Entity.cs
@@ -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, re‑resolve 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;
|
||||
|
||||
Reference in New Issue
Block a user