I am working on a multiplayer FPS using Tom Weiland's Riptide networking solution. I followed his three part tutorial on setting up basic networking and movement, but ran into an error after implementing interpolation (This video).
When only one player is connected to the server it works fine, but when two players are connected the following error occurs (client side):
transform.position assign attempt for 'Player 2 (editor)' is not valid. Input position is { Infinity, Infinity, -Infinity }.
UnityEngine.Transform:set_position (UnityEngine.Vector3)
Interpolator:InterpolatePosition (single) (at Assets/Scripts/Interpolator.cs:56)
Interpolator:Update () (at Assets/Scripts/Interpolator.cs:43)
The Update and InterpolatePosition functions in which the error occurs are:
private void Update()
{
for (int i = 0; i < futureTransformUpdates.Count; i++)
{
if (NetworkManager.Singleton.ServerTick >= futureTransformUpdates[i].Tick)
{
previous = to;
to = futureTransformUpdates[i];
from = new TransformUpdate(NetworkManager.Singleton.InterpolationTick, transform.position);
futureTransformUpdates.RemoveAt(i);
i--;
timeElapsed = 0f;
timeToReachTarget = (to.Tick - from.Tick) * Time.fixedDeltaTime;
}
}
timeElapsed += Time.deltaTime;
InterpolatePosition(timeElapsed / timeToReachTarget); // Line 43
}
private void InterpolatePosition(float lerpAmount)
{
if ((to.Position - previous.Position).sqrMagnitude < squareMovementThreshold)
{
if (to.Position != from.Position)
transform.position = Vector3.Lerp(from.Position, to.Position, lerpAmount);
return;
}
transform.position = Vector3.LerpUnclamped(from.Position, to.Position, lerpAmount); // Line 56
}
I have double checked and this is how it appears in the tutorial. I haven't used Lerp or LerpUnclamped before, and don't really know how to diagnose the issue. The error results in very choppy movement, often with several seconds between position updates, and no smoothing between them, making the game unplayable.
I am running the server and one client in the Unity editor, and the other client as a build, all on one computer. It doesn't matter which order I connect the clients in, both experience the issue when both are connected.
Related
I am working on an endless runner game for Android by Unity. I dont want to use a kinematic rigidbody. So physics is involved but the rigidbody is supposed to run along a predefined path by default. (and jumps or changes lanes by user actions). Moving straight is easy. I have done that but I want to have the next stage in the game where there are turns. It seems to work but it sometimes gets jittery and the turning isnt as smooth as I want it to be. And if I increase the speed, the player gets wonky. Could you please help me to optimize the code to get a smoother turns no matter what the speed is.
As far as I searched I couldnt find an answer on internet probably people are using kinematic rigidbodies more often in order not to deal with physics. So I use .AddForce and .AddTorque. I now use prefabs with predefined turns (road pieces). So it is spawned as the player moves along. Each road prefab has a spline (a free asset based on Unity 2015 procedural spline generation video I suppose) for the moving path. So the player is picking up a node along the spline and sets it as target and uses its rotation to turn towards using the AddTorque.
Maybe it is easier if I switch to kinematic rigidbody. Maybe that is ideal but I insist on doing this for the sake of learning physics and some people might find it useful for another project as there isnt enough resources on this.
void FixedUpdate()
{
if (!jump)
{
//maxangle = Mathf.Clamp(r.velocity.magnitude * 2f,3,15f);
maxangle = r.velocity.magnitude;
r.constraints = RigidbodyConstraints.None;
r.constraints = RigidbodyConstraints.FreezeRotationZ | RigidbodyConstraints.FreezeRotationX;
TurnToTarget(transform, sample.Rotation,target, maxangle);
r.constraints = RigidbodyConstraints.None;
r.constraints = RigidbodyConstraints.FreezeRotationZ | RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationY;
}
//Debug.Log(currentroad.transform.name + maxangle);
if (!GameManager.gameManager.dead && running)
{
r.isKinematic = false;
//Debug.Log(transform.position.y);
var speed = r.velocity.magnitude;
Vector3 directionOfTarget = (target - transform.position).normalized;
if (speed < runspeed)
{
//r.velocity += Vector3.forward * 1f;
Debug.Log(r.velocity.z+ " " + r.velocity.magnitude);
Debug.Log(directionOfTarget);
r.AddForce(directionOfTarget* (runspeed-speed), ForceMode.VelocityChange);
}
if (transform.position.y > 2.7f)
{
r.mass = 50000f;
Physics.gravity = new Vector3(0, -100f, 0);
}
if (grounded)
{
r.mass = 10f;
Physics.gravity = new Vector3(0, -10f, 0);
}
private void TurnToTarget(Transform transform, Quaternion targetrot, Vector3 movePoint, float maxTurnAccel)
{
Vector3 directionOfTarget = (movePoint -transform.position).normalized;
Vector3 directionInEulers = targetrot.eulerAngles;
Vector3 offsetInEulers = ClampHeading(directionInEulers) - ClampHeading(transform.eulerAngles);
offsetInEulers = ClampHeading(offsetInEulers);
//optional
Vector3 angularVelocity = r.angularVelocity / Time.fixedDeltaTime;
if (offsetInEulers.sqrMagnitude < Mathf.Pow(maxTurnAccel, 2))
{
if (offsetInEulers.y < 0)
{
if (angularVelocity.y < offsetInEulers.y)
{
offsetInEulers.y = -offsetInEulers.y;
}
}
else
{
if (angularVelocity.y > offsetInEulers.y)
{
offsetInEulers.y = -offsetInEulers.y;
}
}
if (offsetInEulers.x > 0)
{
if (angularVelocity.x < -offsetInEulers.x)
{
offsetInEulers.x = -offsetInEulers.x * 2;
}
}
else
{
if (angularVelocity.x > -offsetInEulers.x)
{
offsetInEulers.x = -offsetInEulers.x * 2;
}
}
if (offsetInEulers.z > 0)
{
if (angularVelocity.z < -offsetInEulers.z)
offsetInEulers.z = -offsetInEulers.z * 2;
}
else
{
if (angularVelocity.z > -offsetInEulers.z)
offsetInEulers.z = -offsetInEulers.z * 2;
}
}
offsetInEulers = ClampVector(offsetInEulers, -maxTurnAccel, maxTurnAccel);
//Debug.Log(currentroad + " " + offsetInEulers + " " + r.angularVelocity + " " + directionOfTarget + " " + ClampHeading(directionInEulers)+" " +transform.eulerAngles);
r.AddRelativeTorque(transform.up * offsetInEulers.y);
//r.AddTorque(offsetInEulers*r.velocity.magnitude);
}
You could look into splines. You can reduce the amount of computation to move a character along a path by calculating how many points along that path are needed for movement to appear smooth as the character is moved from point to point.
Sometimes blurring effects are used to reduce the number of polygons that need to be drawn, when a character is moving fast.
First
First thing to note is in this code:
if (transform.position.y > 2.7f)
{
r.mass = 50000f;
Physics.gravity = new Vector3(0, -100f, 0);
}
if (grounded)
{
r.mass = 10f;
Physics.gravity = new Vector3(0, -10f, 0);
}
If looks like you're manipulating mass to achieve the affect of "stopping". If the player is in the air, you slam them into the ground with a high gravity value to slow them down rapidly. Manipulating mass of an object in motion can cause a great deal of issues, especially if you're applying a force in the same physics frame. I see you use ForceMode.VelocityChange to counter this problem, so kudos to you on that. However, when you AddRelativeTorque to an object, its impact depends heavily on the mass.
Changing the mass of an object directly does not automatically scale the current linear and angular momentum that an object has. Instead, when you increase the mass to 50,000f you are increasing the momentum by:
50,000 / (prior mass).
Changing mass on-the-fly is usually bound to cause problems. I would recommend finding a different solution to force your player to the ground that does not involve manipulating mass (and to a lesser concern, gravity). Perhaps lerping the rigidbody.position downard, or applying a downward impulse force may achieve the same effect without the risk for bugs.
Second
All of this logic is occurring in the FixedUpdate() cycle. This is separate from the Update() cycle that runs every frame. I see that you are accessing some frame-specific data, notably transform.position and transform.eulerAngles. It is possible that 2 physics cycles occur before the next frame cycle occurs, resulting in something like the following:
Update() - transform is updated
FixedUpdate() - reads most recent transform data
Update() - transform is updated
FixedUpdate() - reads most recent transform data
FixedUpdate() - reads most recent transform data
Update() - transform is updated
Since some of your game logic is based on the transform data, the FixedUpdate() can sometimes double-act before it can be updated, resulting in jittery movements.
I would recommend avoiding any transform reads in FixedUpdate(), and instead using RigidBody.position. Changing the rotation is a bit more nuanced, but RigidBody.MoveRotation may serve useful here as well.
I'm still working on "perfect" ladder movement, and what I was able to come up with is a way to calculate the exact, specific distance the character needs to move while it is on the ladder to reach the point with which it will collide perfectly with the ground above the ladder.
I know this value is exactly specific because I allow my character to land on the ladder landing when the game starts (position.y = 3.6235), and when I print the movement value in the console I get exactly 3.6235.
I'm not sure that I'm implementing this correctly in code, though, as I've noticed that this value is still barely above "0" in the console once my character is all the way up the ladder. Is Mathf.Clamp() not the correct function to limit movement, or maybe I'm using it incorrectly?
public void ClimbUpLadder(ref Vector3 deltaMovement)
{
float rayLength = raycastOrigins.centerRayLength * 2;
RaycastHit2D[] hits = Physics2D.RaycastAll(new Vector2(raycastOrigins.center.x + deltaMovement.x,
(raycastOrigins.center.y - raycastOrigins.centerRayLength + skinWidth) + deltaMovement.y), Vector2.up,
rayLength, climbMask);
Debug.DrawRay(new Vector2(raycastOrigins.center.x + deltaMovement.x,
(raycastOrigins.center.y - raycastOrigins.centerRayLength + skinWidth) + deltaMovement.y), Vector2.up * rayLength, Color.green);
for (int i = 0; i < hits.Length; i++)
{
if (hits[i])
{
if (hits[i].collider.tag == "Ladder")
{
IsClimbingLadder = true;
}
if (i >= 1 && hits[i].collider.tag == "platformOneWay")
{
//This gives us the exact distance needed to finish climbing
GameObject platform = hits[i].collider.gameObject;
Transform platformTransform = platform.GetComponent<Transform>();
float finalMoveDistance = (platformTransform.position.y - characterTransform.position.y) + platformTransform.position.y;
deltaMovement.y = Mathf.Clamp(deltaMovement.y, 0, finalMoveDistance);
print(finalMoveDistance);
}
}
}
}
Unfortunately, after I set deltaMovement.y to this value it says that finalMoveDistance is around .9 or 1.0, so I still move slightly too far up the ladder. Do you think setting the character's transform.position.y directly is the best way to smooth out the movement? My goal is to eliminate any bounce when transitioning from climbing the ladder to walking on the ground again.
I think you're making a mistake here:
float finalMoveDistance = (platformTransform.position.y - characterTransform.position.y) + platformTransform.position.y;
Why do you add the platformTransform twice?
Try this:
float finalMoveDistance = (platformTransform.position.y - characterTransform.position.y);
Ive been working on this school project about Neural Networks for a while and i got about everything to work except for the fitness, here i Thought i made a simple loop that will record the Distance for the Float ReaverOld, wait one Second (with the Invoke), then record the distance for the float ReaverNew. It should record the fitness with the fitness float; fitdistance = (ReaverNew - ReaverOld); (the FitDistance is correctly connected to the Neural Network, i did check that)
My program runs fine, but when i use the Debug log command to check the variables for Fitdistance and Transform.LocalPosition.y or x, i just get back Zero. I went based off this tutorial https://docs.unity3d.com/ScriptReference/Transform-localPosition.html and was told that a loop using a Invoke is possible. I don't understand why its not tracking the position at all, Ive also tried both Vector2 and 3.
public void Point() // use for calling on a Distance Function function
//already in a loop, dont double loop for whatever reason
{
if(ReaverPos = true) {
transform.localPosition = new Vector3(0, 0, 0);
Debug.Log (transform.localPosition.y);
float ReaverOld = transform.localPosition.y + transform.localPosition.x;
Invoke("Shine", 1f);
}
}
public void Shine() //Second call on from Point, Endless loop for Checking Distance based on the Invoke since it last 1 second
{
float ReaverNew = transform.localPosition.y + transform.localPosition.x;
fitDistance = (ReaverNew - ReaverOld);
//Debug.Log (fitDistance);
}
I'm experiencing an odd issue with my collision detection. I'm using the Update method to move the player (I don't want to use FixedUpdate because that creates an undesired weird movement). The fixed timestep is set at the default 0.02 (I tried playing with time setting but that didn't work either) . I set the collision detection of the rigidbodies of both objects to "continuous dynamic". Also, I set the target frame rate to 300 and that didn't change anything...
When the framerate is low or the device itself is slow, the collision detection doesn't always work. The player can easily fall through the object it's supposed to collide with, though sometimes it doesn't.
Please tell me what I can do to fix this because I've published a game and many users are reporting this (serious) bug. Thank you for your support.
This is what is supposed to happen:
This is what actually happens:
(as you can see, the cube gets out of the wall and to the other side)
I move the player when the user releases the mouse button:
Script 1:
public Script2 Jumper;
public float TimeToJump;
public void Update()
{
if (Input.GetMouseButtonUp(0))
{
StartCoroutine (Delay (1f/50f)); //Don't mind the time.
}
}
IEnumerator Delay(float waitTime)
{
yield return new WaitForSeconds (waitTime);
if (Jumper != null)
{
Jumper.SetVelocityToJump (gameObject, TimeToJump);
}
}
Script 2 attached to player (cube):
public class Script2 : MonoBehaviour {
GameObject target;
private float timeToJump;
public bool isJumping = false;
public void SetVelocityToJump(GameObject goToJumpTo, float timeToJump)
{
StartCoroutine(jumpAndFollow(goToJumpTo, timeToJump));
this.timeToJump = timeToJump;
this.target = goToJumpTo;
}
private IEnumerator jumpAndFollow(GameObject goToJumpTo, float timeToJump)
{
var startPosition = transform.position;
var targetTransform = goToJumpTo.transform;
var lastTargetPosition = targetTransform.position;
var initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
var progress = 0f;
while (progress < timeToJump)
{
progress += Time.deltaTime;
if (targetTransform.position != lastTargetPosition)
{
lastTargetPosition = targetTransform.position;
initialVelocity = getInitialVelocity(lastTargetPosition - startPosition, timeToJump);
}
float percentage = progress * 100 / timeToJump;
GetComponent<Rigidbody>().isKinematic = percentage < 100.0f;
transform.position = startPosition + (progress * initialVelocity) + (0.5f * Mathf.Pow(progress, 2) * _gravity);
yield return null;
}
OnFinishJump (goToJumpTo, timeToJump);
}
private void OnFinishJump(GameObject target, float timeToJump)
{
if (stillJumping)
{
this.isJumping = false;
}
}
private Vector3 getInitialVelocity(Vector3 toTarget, float timeToJump)
{
return (toTarget - (0.5f * Mathf.Pow(timeToJump, 2) * _gravity)) / timeToJump;
}
}
The target of the cube is a child of the bigger cube (the wall).
If you require clarification, please leave a comment below. I might give the link to my game if you need more details.
Quote from here (found thanks to #Logman): "The problem exists even if you use continuous dynamic collision detection because fast moving objects can move so fast that they are too far apart from itself from one frame to the next immediate frame. It's like they teleported and no collision detection would ever be triggered because no collision existed, from each frame perspective, and thus from all calculations processed."
In my case, the cube is not going fast, but you get the concept.
There are several issues with your code.
You are asking a Coroutine to yield for 1/50th of a second. The minimum time a yield must occur for is one frame. If Time.deltaTime > 0.02f this is already one of the problems.
You are using Coroutines and yield return null to compute physics calculations. Essentially, you're computing physics in Update(), which is only called once per frame (null is equivalent to new WaitForEndOfFrame(): as mentioned in (1), a running Coroutine cannot be yielding between frames). Under low frame-rate, the amount of motion an object undertook between two frames might exceed the collision range of the target trigger. Assuming linear, non-accelerating motion: ∆S = v∆t where v = velocity, ∆S is movement to cover in the current frame, ∆t is Time.deltaTime. As you can see, ∆S scales proportionally with ∆t.
You have GetComponent<T>() calls inside loops. Always avoid doing this: store a reference as a member variable instead (initialise it in Start()).
My suggestion for the quickest working hack would be to not worry too much about "being clean", and instead create subroutines that you call from FixedUpdate(), and (create and) use member bools to conditionally test which subroutine to "execute" and which to "skip". You can also use member bools or enums as triggers to switch between various "states".
A better solution would be to let Unity handle the kinematics and you instead work with rigidbody mutators (and not transform.positions), but that may be totally unnecessary for an arcade situation, which yours might be. In that case stick to the hack above.
If you really want to control kinematics by hand, use an engine like SFML. A Particle System tutorial would be a good place to start.
It's your float percentage, among other things.
"If isKinematic is enabled, Forces, collisions or joints will not affect the rigidbody anymore."
That's from the isKinematic page of Unity's documentation. You're setting it to true when progress hits 100. So at lower framerates, there'll be a sudden jump due to Time.deltaTime steps being a lot higher, progress is suddenly >= 100, isKinematic is set to true and the player is no longer affected by collisions.
I think you're going to have to rethink a lot of the code here and do some heavy optimisations. But the other posters have laid those out already, so I don't need to.
EDIT: Misunderstood the initial question, thought that it meant you were trying to detect collisions but your code wasn't always detecting them. Didn't realise it actually meant getting the collisions to occur in the first place.
So I'm trying to get this game working right offline before I implement the server, and I've had mostly positive results. However, an interesting situation is occuring when the player runs into a collider. It will stutter for a half-second, and unless I pull back in time, it will then freeze. I'm using forces to move, which probably accounts for the reason why. However, I'd like a definite reason.
Yahoo Games Network is my basic service for multiplayer as it lets me write a C# .dll for the server and manage things.
void FixedUpdate () {
RunTime = GetTime();
mInput.y = Input.GetAxis("Vertical");
mInput.x = Input.GetAxis("Horizontal");
if (Ready)
{
SelfTick(RunTime);
UpdatePlayerInput();
foreach (KeyValuePair<int, PlayerController> Target in Targets)
{
}
}
}
void SelfTick(int T)
{
Player.FixedStep(T);
}
void UpdatePlayerInput()
{
if (mInput.x != 0 || mInput.y != 0)
{
Player.UpdateInput(mInput);
// Send Connection Data Here //
}
}
This code runs the main loop, which connects to this on the first player. Everyone else is run in a foreach loop check that sends respective commands:
public void FixedStep(int _T)
{
if (LastTimeStep == 0)
{
LastTimeStep = _T;
}
int Offset = System.Math.Min(_T - LastTimeStep, 1000);
for (int i = 0; i < Offset; i++)
{
Vector3 actionPoint = transform.position + transform.TransformDirection(buoyancyCentreOffset);
float forceFactor = 1f - ((actionPoint.y - waterLevel) / floatHeight);
if (forceFactor > 0f)
{
Vector3 uplift = -Physics.gravity * (forceFactor - GetComponent<Rigidbody>().velocity.y * bounceDamp);
rigidBody.AddForceAtPosition(uplift, actionPoint);
}
rigidBody.AddRelativeForce(0f, 0f, mInput.y * Speed);
rigidBody.AddRelativeTorque(0f, mInput.x * TurnSpeed, 0f);
}
LastTimeStep = _T;
Debug.Log(rigidBody.velocity);
}
I was able to see one debug error in one crash instance, which noted that the force applied was too high - Infinite. Most of the time when it crashes, it will crash with no errors. This is the time code that I have:
public int GetTime()
{
System.DateTime EpochStart = new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
int Current_Time = (int)(System.DateTime.UtcNow - EpochStart).TotalMilliseconds;
return Current_Time;
}
Can anyone offer any advice? I'm using Unity3D as my primary game development IDE, with Visual Studio instead of MonoDevelop.
Set a Min and MAX limit on your uplift variable...to keep it in range and see if it stops crashing. Set a CLAMP function on it before AddForceAtPosition.
Ryan