From 963f46e7d13eb7199ef08d0b2cdee78a8069c0fa Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 3 Nov 2015 18:15:12 +0200 Subject: [PATCH] Improved character movement lag compensation --- Subsurface/Source/Characters/AICharacter.cs | 2 -- Subsurface/Source/Characters/Character.cs | 29 +++++++++++------- .../Source/Characters/FishAnimController.cs | 4 +-- .../Characters/HumanoidAnimController.cs | 11 +++++++ Subsurface/Source/Characters/Ragdoll.cs | 19 +++++++----- .../Source/Items/Components/Machines/Radar.cs | 3 -- Subsurface/Source/Networking/NetworkEvent.cs | 4 ++- Subsurface_Solution.v12.suo | Bin 784384 -> 823808 bytes 8 files changed, 46 insertions(+), 26 deletions(-) diff --git a/Subsurface/Source/Characters/AICharacter.cs b/Subsurface/Source/Characters/AICharacter.cs index c56f4814e..e059b7ab8 100644 --- a/Subsurface/Source/Characters/AICharacter.cs +++ b/Subsurface/Source/Characters/AICharacter.cs @@ -120,8 +120,6 @@ namespace Barotrauma message.WriteRangedSingle(AnimController.RefLimb.SimPosition.X, -NetConfig.CharacterIgnoreDistance, NetConfig.CharacterIgnoreDistance, 16); message.WriteRangedSingle(AnimController.RefLimb.SimPosition.Y, -NetConfig.CharacterIgnoreDistance, NetConfig.CharacterIgnoreDistance, 16); - //message.Write(AnimController.RefLimb.LinearVelocity.X); - //message.Write(AnimController.RefLimb.LinearVelocity.Y); return true; } diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index aba1f100d..daaf5d039 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -501,16 +501,14 @@ namespace Barotrauma float findClosestTimer; - public void Control(float deltaTime, Camera cam) + public Vector2 GetTargetMovement() { - if (isDead || AnimController.StunTimer>0.0f) return; - Vector2 targetMovement = Vector2.Zero; if (IsKeyDown(InputType.Left)) targetMovement.X -= 1.0f; if (IsKeyDown(InputType.Right)) targetMovement.X += 1.0f; - if (IsKeyDown(InputType.Up)) targetMovement.Y += 1.0f; - if (IsKeyDown(InputType.Down)) targetMovement.Y -= 1.0f; - + if (IsKeyDown(InputType.Up)) targetMovement.Y += 1.0f; + if (IsKeyDown(InputType.Down)) targetMovement.Y -= 1.0f; + //the vertical component is only used for falling through platforms and climbing ladders when not in water, //so the movement can't be normalized or the character would walk slower when pressing down/up if (AnimController.InWater) @@ -525,6 +523,15 @@ namespace Barotrauma targetMovement *= SpeedMultiplier; SpeedMultiplier = 1.0f; + return targetMovement; + } + + public void Control(float deltaTime, Camera cam) + { + if (isDead || AnimController.StunTimer>0.0f) return; + + Vector2 targetMovement = GetTargetMovement(); + AnimController.TargetMovement = targetMovement; AnimController.IsStanding = true; @@ -1439,15 +1446,15 @@ namespace Barotrauma AnimController.IsStanding = true; keys[(int)InputType.Use].Held = actionKeyState; - keys[(int)InputType.Aim].Held = secondaryKeyState; + keys[(int)InputType.Aim].Held = secondaryKeyState; if (sendingTime <= LastNetworkUpdate) return; - keys[(int)InputType.Left].Held = leftKeyState; + keys[(int)InputType.Left].Held = leftKeyState; keys[(int)InputType.Right].Held = rightKeyState; - keys[(int)InputType.Up].Held = upKeyState; - keys[(int)InputType.Down].Held = downKeyState; + keys[(int)InputType.Up].Held = upKeyState; + keys[(int)InputType.Down].Held = downKeyState; keys[(int)InputType.Run].Held = runState; @@ -1496,7 +1503,7 @@ namespace Barotrauma cursorPosition = Position + new Vector2(1000.0f, 0.0f) * dir; } - AnimController.RefLimb.body.TargetPosition = pos; + AnimController.RefLimb.body.TargetPosition = AnimController.EstimateCurrPosition(pos, (float)(NetTime.Now + message.SenderConnection.RemoteTimeOffset) - sendingTime); LastNetworkUpdate = sendingTime; diff --git a/Subsurface/Source/Characters/FishAnimController.cs b/Subsurface/Source/Characters/FishAnimController.cs index e67fb4c0b..c27c42c37 100644 --- a/Subsurface/Source/Characters/FishAnimController.cs +++ b/Subsurface/Source/Characters/FishAnimController.cs @@ -131,7 +131,7 @@ namespace Barotrauma void UpdateSineAnim(float deltaTime) { - movement = MathUtils.SmoothStep(movement, TargetMovement*swimSpeed, 1.0f); + movement = TargetMovement*swimSpeed; if (movement.LengthSquared() < 0.00001f) return; if (!inWater) movement.Y = Math.Min(0.0f, movement.Y); @@ -183,7 +183,7 @@ namespace Barotrauma //current * (float)alpha + previous * (1.0f - (float)alpha); - steerForce = (movement * 50.0f - head.LinearVelocity * 30.0f); + steerForce = (movement+correctionMovement) * 50.0f - head.LinearVelocity * 30.0f; // force += (headMovement - movement) * Math.Min(head.LinearVelocity.Length()/movement.Length(), 1.0f); if (!inWater) steerForce.Y = 0.0f; diff --git a/Subsurface/Source/Characters/HumanoidAnimController.cs b/Subsurface/Source/Characters/HumanoidAnimController.cs index e77cb263d..8989b719d 100644 --- a/Subsurface/Source/Characters/HumanoidAnimController.cs +++ b/Subsurface/Source/Characters/HumanoidAnimController.cs @@ -910,6 +910,17 @@ namespace Barotrauma hand.body.SmoothRotate((ang2 + handAngle * Dir), 100.0f * force); } + public override Vector2 EstimateCurrPosition(Vector2 prevPosition, float timePassed) + { + timePassed = MathHelper.Clamp(timePassed, 0.0f, 1.0f); + + Vector2 targetMovement = character.GetTargetMovement(); + + Vector2 currPosition = prevPosition + targetMovement * timePassed/1000.0f; + + return currPosition; + } + public override void Flip() { base.Flip(); diff --git a/Subsurface/Source/Characters/Ragdoll.cs b/Subsurface/Source/Characters/Ragdoll.cs index 97287c92b..f1e9bcd7c 100644 --- a/Subsurface/Source/Characters/Ragdoll.cs +++ b/Subsurface/Source/Characters/Ragdoll.cs @@ -42,7 +42,7 @@ namespace Barotrauma //a movement vector that overrides targetmovement if trying to steer //a character to the position sent by server in multiplayer mode - private Vector2 correctionMovement; + protected Vector2 correctionMovement; protected float floorY; protected float surfaceY; @@ -692,15 +692,15 @@ namespace Barotrauma { if (inWater) { - foreach (Limb limb in Limbs) - { - //if (limb.body.TargetPosition == Vector2.Zero) continue; + //foreach (Limb limb in Limbs) + //{ + // //if (limb.body.TargetPosition == Vector2.Zero) continue; - //limb.body.SetTransform(limb.SimPosition + newMovement * 0.1f, limb.Rotation); - } + // limb.body.SetTransform(limb.SimPosition + Vector2.Normalize(diff) * 0.1f, limb.Rotation); + //} correctionMovement = - Vector2.Lerp(targetMovement, Vector2.Normalize(diff) * MathHelper.Clamp(dist * 5.0f, 0.1f, 5.0f), 0.2f); + Vector2.Lerp(targetMovement, Vector2.Normalize(diff) * MathHelper.Clamp(dist * 8.0f, 0.1f, 8.0f), 0.2f); } else { @@ -732,6 +732,11 @@ namespace Barotrauma } } + public virtual Vector2 EstimateCurrPosition(Vector2 prevPosition, float timePassed) + { + return prevPosition; + } + private Vector2 GetFlowForce() { diff --git a/Subsurface/Source/Items/Components/Machines/Radar.cs b/Subsurface/Source/Items/Components/Machines/Radar.cs index d04e4cd2e..f1e4067e6 100644 --- a/Subsurface/Source/Items/Components/Machines/Radar.cs +++ b/Subsurface/Source/Items/Components/Machines/Radar.cs @@ -105,10 +105,7 @@ namespace Barotrauma.Items.Components private void DrawRadar(SpriteBatch spriteBatch, Rectangle rect) { - Vector2 center = new Vector2(rect.Center.X, rect.Center.Y); - //lineEnd += new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Math.Min(width, height) / 2.0f; - //GUI.DrawLine(spriteBatch, GuiFrame.Center, lineEnd, Color.Green); if (!IsActive) return; diff --git a/Subsurface/Source/Networking/NetworkEvent.cs b/Subsurface/Source/Networking/NetworkEvent.cs index d4c48c1e5..2633d6903 100644 --- a/Subsurface/Source/Networking/NetworkEvent.cs +++ b/Subsurface/Source/Networking/NetworkEvent.cs @@ -170,7 +170,9 @@ namespace Barotrauma.Networking Entity e = Entity.FindEntityByID(id); if (e == null) { - //DebugConsole.ThrowError("Couldn't find an entity matching the ID ''" + id + "''"); +#if DEBUG + DebugConsole.ThrowError("Couldn't find an entity matching the ID ''" + id + "''"); +#endif return false; } diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 12ca20a9db841c5c2b738ffccd1874ec5b87edbc..6e0018820465a28c2829eb7d04b1e2077a2f3997 100644 GIT binary patch delta 13715 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 ab%c`8$Fx?)9`@Z2<;K`o^$rMq$NvSr<+3aQ