From 775adf685a7f0dba2337b332cb03372fd2d94be9 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 2 Feb 2015 01:24:43 -0700 Subject: [PATCH] Significant improvements to entity physics No more jitter! Collisions actually work all the time! --- TrueCraft.API/Entities/IPhysicsEntity.cs | 1 + TrueCraft.API/Vector3.cs | 14 +++++ TrueCraft.Core/Entities/Entity.cs | 3 ++ TrueCraft.Core/Entities/ItemEntity.cs | 10 +++- TrueCraft.Core/Logic/BlockProvider.cs | 6 ++- TrueCraft/EntityManager.cs | 13 +++++ TrueCraft/PhysicsEngine.cs | 66 +++++++++++------------- TrueCraft/Program.cs | 1 - 8 files changed, 75 insertions(+), 39 deletions(-) diff --git a/TrueCraft.API/Entities/IPhysicsEntity.cs b/TrueCraft.API/Entities/IPhysicsEntity.cs index e913c2f..65fc35e 100644 --- a/TrueCraft.API/Entities/IPhysicsEntity.cs +++ b/TrueCraft.API/Entities/IPhysicsEntity.cs @@ -11,6 +11,7 @@ namespace TrueCraft.API.Entities Vector3 Velocity { get; set; } float AccelerationDueToGravity { get; } float Drag { get; } + float TerminalVelocity { get; } bool BeginUpdate(); void EndUpdate(Vector3 newPosition); diff --git a/TrueCraft.API/Vector3.cs b/TrueCraft.API/Vector3.cs index 289acee..aa3aaf9 100644 --- a/TrueCraft.API/Vector3.cs +++ b/TrueCraft.API/Vector3.cs @@ -54,6 +54,20 @@ namespace TrueCraft.API return new Vector3(Math.Floor(X), Math.Floor(Y), Math.Floor(Z)); } + /// + /// Clamps the vector to within the specified value. + /// + /// Value. + public void Clamp(double value) + { + if (Math.Abs(X) > value) + X = value * (X < 0 ? -1 : 1); + if (Math.Abs(Y) > value) + Y = value * (Y < 0 ? -1 : 1); + if (Math.Abs(Z) > value) + Z = value * (Z < 0 ? -1 : 1); + } + /// /// Calculates the distance between two Vector3 objects. /// diff --git a/TrueCraft.Core/Entities/Entity.cs b/TrueCraft.Core/Entities/Entity.cs index 0a234aa..3681fb9 100644 --- a/TrueCraft.Core/Entities/Entity.cs +++ b/TrueCraft.Core/Entities/Entity.cs @@ -99,6 +99,9 @@ namespace TrueCraft.Core.Entities public virtual void Update(IEntityManager entityManager) { + // TODO: Losing health and all that jazz + if (Position.Y < -50) + entityManager.DespawnEntity(this); } protected bool EnablePropertyChange { get; set; } diff --git a/TrueCraft.Core/Entities/ItemEntity.cs b/TrueCraft.Core/Entities/ItemEntity.cs index 65a15c2..fa31d1c 100644 --- a/TrueCraft.Core/Entities/ItemEntity.cs +++ b/TrueCraft.Core/Entities/ItemEntity.cs @@ -123,16 +123,22 @@ namespace TrueCraft.Core.Entities OnPropertyChanged("Metadata"); }*/ } + base.Update(entityManager); } public float AccelerationDueToGravity { - get { return 0.08f; } + get { return 0.4f; } } public float Drag { - get { return 0.98f; } + get { return 0.2f; } + } + + public float TerminalVelocity + { + get { return 1.96f; } } } } diff --git a/TrueCraft.Core/Logic/BlockProvider.cs b/TrueCraft.Core/Logic/BlockProvider.cs index 480a151..38a7163 100644 --- a/TrueCraft.Core/Logic/BlockProvider.cs +++ b/TrueCraft.Core/Logic/BlockProvider.cs @@ -35,7 +35,11 @@ namespace TrueCraft.Core.Logic var entityManager = server.GetEntityManagerForWorld(world); var items = GetDrop(descriptor); foreach (var item in items) - entityManager.SpawnEntity(new ItemEntity(new Vector3(descriptor.Coordinates) + new Vector3(0.5), item)); + { + var entity = new ItemEntity(new Vector3(descriptor.Coordinates) + new Vector3(0.5), item); + entity.Velocity += new Vector3(MathHelper.Random.NextDouble()); + entityManager.SpawnEntity(entity); + } } public virtual bool IsSupported(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world) diff --git a/TrueCraft/EntityManager.cs b/TrueCraft/EntityManager.cs index b2bf84d..59779e4 100644 --- a/TrueCraft/EntityManager.cs +++ b/TrueCraft/EntityManager.cs @@ -177,6 +177,17 @@ namespace TrueCraft spawnedClient = (RemoteClient)GetClientForEntity(entity as PlayerEntity); client.KnownEntities.Add(entity); client.QueuePacket(entity.SpawnPacket); + if (entity is IPhysicsEntity) + { + var pentity = entity as IPhysicsEntity; + client.QueuePacket(new EntityVelocityPacket + { + EntityID = entity.EntityID, + XVelocity = (short)(pentity.Velocity.X * 320), + YVelocity = (short)(pentity.Velocity.Y * 320), + ZVelocity = (short)(pentity.Velocity.Z * 320), + }); + } if (entity.SendMetadataToClients) client.QueuePacket(new EntityMetadataPacket(entity.EntityID, entity.Metadata)); if (spawnedClient != null) @@ -265,6 +276,8 @@ namespace TrueCraft while (PendingDespawns.Count != 0) { while (!PendingDespawns.TryTake(out entity)); + if (entity is IPhysicsEntity) + PhysicsEngine.RemoveEntity((IPhysicsEntity)entity); for (int i = 0, ServerClientsCount = Server.Clients.Count; i < ServerClientsCount; i++) { var client = (RemoteClient)Server.Clients[i]; diff --git a/TrueCraft/PhysicsEngine.cs b/TrueCraft/PhysicsEngine.cs index 42ed0a8..f419af7 100644 --- a/TrueCraft/PhysicsEngine.cs +++ b/TrueCraft/PhysicsEngine.cs @@ -40,7 +40,8 @@ namespace TrueCraft { if (!Entities.Contains(entity)) return; - Entities.Remove(entity); + lock (EntityLock) + Entities.Remove(entity); } private BoundingBox TempBoundingBox; @@ -57,6 +58,7 @@ namespace TrueCraft { entity.Velocity *= entity.Drag * multipler; entity.Velocity -= new Vector3(0, entity.AccelerationDueToGravity * multipler, 0); + entity.Velocity.Clamp(entity.TerminalVelocity); if (entity is IAABBEntity) CheckWithTerrain((IAABBEntity)entity, World); entity.EndUpdate(entity.Position + entity.Velocity); @@ -69,7 +71,7 @@ namespace TrueCraft private void CheckWithTerrain(IAABBEntity entity, IWorld world) { Vector3 collisionPoint, collisionDirection; - if (entity.Position.Y + entity.Velocity.Y >= 0 && entity.Position.Y + entity.Velocity.Y <= 255) // Don't do checks outside the map + if (entity.Position.Y > 0 && entity.Position.Y <= 127) // Don't do checks outside the map { bool fireEvent = entity.Velocity != Vector3.Zero; // Do terrain collisions @@ -80,7 +82,7 @@ namespace TrueCraft } if (AdjustVelocityY(entity, world, out collisionPoint, out collisionDirection)) { - entity.Velocity *= new Vector3(0.2, 1, 0.2); // TODO: More sophisticated friction + entity.Velocity *= new Vector3(0.1, 1, 0.1); // TODO: More sophisticated friction if (fireEvent) entity.TerrainCollision(collisionPoint, collisionDirection); } @@ -114,22 +116,19 @@ namespace TrueCraft if (entity.Velocity.X < 0) { TempBoundingBox = new BoundingBox( - new Vector3(entity.BoundingBox.Min.X + entity.Velocity.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), - new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) - ); + new Vector3(entity.BoundingBox.Min.X + entity.Velocity.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z), + entity.BoundingBox.Max); maxX = (int)(TempBoundingBox.Max.X); - minX = (int)(TempBoundingBox.Min.X + entity.Velocity.X); + minX = (int)(TempBoundingBox.Min.X + entity.Velocity.X) - 1; } else { TempBoundingBox = new BoundingBox( - entity.BoundingBox.Min - (entity.Size / 2), - new Vector3( - entity.BoundingBox.Max.X + entity.Velocity.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) - ); + entity.BoundingBox.Min, + new Vector3(entity.BoundingBox.Max.X + entity.Velocity.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z)); minX = (int)(entity.BoundingBox.Min.X); - maxX = (int)(entity.BoundingBox.Max.X + entity.Velocity.X); + maxX = (int)(entity.BoundingBox.Max.X + entity.Velocity.X) + 1; } // Do terrain checks @@ -141,8 +140,8 @@ namespace TrueCraft { for (int z = minZ; z <= maxZ; z++) { - var position = new Vector3(x, y, z); - var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); + var position = new Coordinates3D(x, y, z); + var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); @@ -213,22 +212,19 @@ namespace TrueCraft if (entity.Velocity.Y < 0) { TempBoundingBox = new BoundingBox( - new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z) - (entity.Size / 2), - new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) - ); + new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y + entity.Velocity.Y, entity.BoundingBox.Min.Z), + entity.BoundingBox.Max); maxY = (int)(TempBoundingBox.Max.Y); - minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y); + minY = (int)(TempBoundingBox.Min.Y + entity.Velocity.Y) - 1; } else { TempBoundingBox = new BoundingBox( - entity.BoundingBox.Min - (entity.Size / 2), - new Vector3( - entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) - ); + entity.BoundingBox.Min, + new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y + entity.Velocity.Y, entity.BoundingBox.Max.Z)); minY = (int)(entity.BoundingBox.Min.Y); - maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y); + maxY = (int)(entity.BoundingBox.Max.Y + entity.Velocity.Y) + 1; } // Clamp Y into map boundaries @@ -250,8 +246,10 @@ namespace TrueCraft { for (int z = minZ; z <= maxZ; z++) { - var position = new Vector3(x, y, z); - var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); + var position = new Coordinates3D(x, y, z); + if (!World.IsValidPosition(position)) + continue; + var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); @@ -323,22 +321,20 @@ namespace TrueCraft if (entity.Velocity.Z < 0) { TempBoundingBox = new BoundingBox( - new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z) - (entity.Size / 2), - new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z) - (entity.Size / 2) - ); + new Vector3(entity.BoundingBox.Min.X, entity.BoundingBox.Min.Y, entity.BoundingBox.Min.Z + entity.Velocity.Z), + entity.BoundingBox.Max); maxZ = (int)(TempBoundingBox.Max.Z); - minZ = (int)(TempBoundingBox.Min.Z + entity.Velocity.Z); + minZ = (int)(TempBoundingBox.Min.Z + entity.Velocity.Z) - 1; } else { TempBoundingBox = new BoundingBox( - entity.BoundingBox.Min - (entity.Size / 2), - new Vector3( - entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z) - (entity.Size / 2) + entity.BoundingBox.Min, + new Vector3(entity.BoundingBox.Max.X, entity.BoundingBox.Max.Y, entity.BoundingBox.Max.Z + entity.Velocity.Z) ); minZ = (int)(entity.BoundingBox.Min.Z); - maxZ = (int)(entity.BoundingBox.Max.Z + entity.Velocity.Z); + maxZ = (int)(entity.BoundingBox.Max.Z + entity.Velocity.Z) + 1; } // Do terrain checks @@ -350,8 +346,8 @@ namespace TrueCraft { for (int z = minZ; z <= maxZ; z++) { - var position = new Vector3(x, y, z); - var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, (Coordinates3D)position); + var position = new Coordinates3D(x, y, z); + var boundingBox = BlockPhysicsProvider.GetBoundingBox(world, position); if (boundingBox == null) continue; blockBox = boundingBox.Value.OffsetBy(position); diff --git a/TrueCraft/Program.cs b/TrueCraft/Program.cs index 23d450b..6eccf90 100644 --- a/TrueCraft/Program.cs +++ b/TrueCraft/Program.cs @@ -24,7 +24,6 @@ namespace TrueCraft server.AddLogProvider(new ConsoleLogProvider(LogCategory.Notice | LogCategory.Warning | LogCategory.Error | LogCategory.Debug)); #if DEBUG server.AddLogProvider(new FileLogProvider(new StreamWriter("packets.log", false), LogCategory.Packets)); - server.EnableClientLogging = true; #endif CommandManager = new CommandManager(); server.ChatMessageReceived += HandleChatMessageReceived;