Unity Multiplayer Server Client desync due to rotation from mouse input - c#

I am currently working on implementing my first multiplayer system with server authority and client side prediction with server reconciliation. But I am running into a problem that I just don't know how other's have solved. I've implemented a fixed timestep system according to a tutorial I found: https://github.com/Ajackster/ClientPredictionTutorial/blob/master/Assets/Scripts/Client.cs and in each timestep I process user keypresses and change the velocity accordingly. Which works great until I start changing my rotation with mouse input. User input and mouse input are read in the Update() function which I think is the source of my desync problems. My client is changing its rotation with each frame. but the server is only changing its input when it receives the rotation from the client in the fixed timestep "handleTick()" method. I believe this is causing a desync in the velocity of my client object and the velocity of the server object. because the velocity of my client is updating with each render (potentially, if we are receiving new mouse input in each update frame) but the velocity of my server only changes in the fixed time step call which could be called less frequently or more frequently than update()
I hope this makes some sense to you brilliant people out there. Is there anyway to handle this?? Please excuse this trash code but this is a snippet from my project
void Update()
{
timer += Time.deltaTime;
if (!isServer)
{
verticalAxisInput = 0;
horizonalAxisInput = 0;
if (inputManager.getKeyManager().getKey("w"))
{
verticalAxisInput = 1;
}
else if (inputManager.getKeyManager().getKey("s"))
{
verticalAxisInput = -1;
}
if (inputManager.getKeyManager().getKey("d"))
{
horizonalAxisInput = 1;
}
if (inputManager.getKeyManager().getKey("a"))
{
horizonalAxisInput = -1;
}
EDIT: The below line was the source of the bug. needs to be moved after transform.localRotation update
playerInput = (transform.forward * verticalAxisInput) + (transform.right * horizonalAxisInput);
pressJump = inputManager.getSingleKeyInput("space") > 0;
pressSprint = inputManager.getSingleKeyInput("left shift") > 0;
mouseX = Input.GetAxis(InputKeys.MOUSE_X_AXIS);
mouseY = Input.GetAxis(InputKeys.MOUSE_Y_AXIS);
playerCam.transform.localRotation = lookY(mouseY);
transform.localRotation = lookX(mouseX);
}
while (timer >= MIN_TIME_BETWEEN_TICKS)
{
timer -= MIN_TIME_BETWEEN_TICKS;
handleTick();
currentTick++;
}
private void handleTick()
{
if (isServer)
{
PlayerInputPacket packet = RemoteDataConnection.instance.getData();
pressJump = inputManager.getSingleKeyInput("space") > 0;
transform.localRotation = packet.playerRot;
inputManager.getKeyManager().setKeyMap(packet.keyMap);
verticalAxisInput = 0;
horizonalAxisInput = 0;
if (inputManager.getKeyManager().getKey("w"))
{
verticalAxisInput = 1;
}
else if (inputManager.getKeyManager().getKey("s"))
{
verticalAxisInput = -1;
}
if (inputManager.getKeyManager().getKey("d"))
{
horizonalAxisInput = 1;
}
if (inputManager.getKeyManager().getKey("a"))
{
horizonalAxisInput = -1;
}
playerInput = (transform.forward * verticalAxisInput) + (transform.right * horizonalAxisInput);
}
else
{
ClientDataConnection.instance.writeData(new PlayerInputPacket(inputManager.getKeyManager().getKeyMap(), transform.localRotation));
}
if (currentJumpCoolDown > 0)
{
currentJumpCoolDown--;
}
move(playerInput, pressJump, pressSprint);
}

Found the problem. The client was calculating input velocity in the update() loop but the server was doing that in the fixed timestep tick method. when made the client calculate this in the fixed timestep method my problem was fixed. I don't entirely understand how this fixes the problem because when the client was processing input it was also sending the server its rotation value so I thought the server and client would get the same result but that must of not of been the case. if anyone understands why this is I would really appreciate an answer
playerInput = (transform.forward * verticalAxisInput) + (transform.right * horizonalAxisInput);
move(playerInput, pressJump, pressSprint);
full code after fix:
void Update()
{
timer += Time.deltaTime;
if (!isServer)
{
verticalAxisInput = 0;
horizonalAxisInput = 0;
if (inputManager.getKeyManager().getKey("w"))
{
verticalAxisInput = 1;
}
else if (inputManager.getKeyManager().getKey("s"))
{
verticalAxisInput = -1;
}
if (inputManager.getKeyManager().getKey("d"))
{
horizonalAxisInput = 1;
}
if (inputManager.getKeyManager().getKey("a"))
{
horizonalAxisInput = -1;
}
pressJump = inputManager.getSingleKeyInput("space") > 0;
pressSprint = inputManager.getSingleKeyInput("left shift") > 0;
mouseX = Input.GetAxis(InputKeys.MOUSE_X_AXIS);
mouseY = Input.GetAxis(InputKeys.MOUSE_Y_AXIS);
playerCam.transform.localRotation = lookY(mouseY);
transform.localRotation = lookX(mouseX);
Debug.Log(characterController.velocity);
}
while (timer >= MIN_TIME_BETWEEN_TICKS)
{
timer -= MIN_TIME_BETWEEN_TICKS;
handleTick();
currentTick++;
}
doWeaponBounce();
}
private void handleTick()
{
if (isServer)
{
PlayerInputPacket packet = RemoteDataConnection.instance.getData();
pressJump = inputManager.getSingleKeyInput("space") > 0;
transform.localRotation = packet.playerRot;
inputManager.getKeyManager().setKeyMap(packet.keyMap);
verticalAxisInput = 0;
horizonalAxisInput = 0;
if (inputManager.getKeyManager().getKey("w"))
{
verticalAxisInput = 1;
}
else if (inputManager.getKeyManager().getKey("s"))
{
verticalAxisInput = -1;
}
if (inputManager.getKeyManager().getKey("d"))
{
horizonalAxisInput = 1;
}
if (inputManager.getKeyManager().getKey("a"))
{
horizonalAxisInput = -1;
}
}
else
{
ClientDataConnection.instance.writeData(new PlayerInputPacket(inputManager.getKeyManager().getKeyMap(), transform.localRotation));
}
if (currentJumpCoolDown > 0)
{
currentJumpCoolDown--;
}
playerInput = (transform.forward * verticalAxisInput) + (transform.right * horizonalAxisInput);
move(playerInput, pressJump, pressSprint);
}

Related

is there a way to fix this so it doesnt destroy the body

The code is a snippet of code from the torso but if you press 'f' on the torso it fires a laser. If the characters arm moves in front of the laser while it is firing it will get cut off, is there a way to fix this? PS: I'm not that good at coding. :P
laser.enabled = true;
for (int i = 0; i < iterations; i++)
{
var end = Physics2D.Raycast(BarrelPosition, BarrelDirection, 10000, bounds);
var endPos = end.transform == null ? (Vector3)BarrelPosition + (Vector3)BarrelDirection * 10000 : (Vector3)end.point;
var victimCount = Physics2D.LinecastNonAlloc(BarrelPosition, endPos, victimBuffer, layers);
laser.SetPosition(1, components.transform.InverseTransformPoint(endPos));
for (int n = 0; n < victimCount; n++)
{
var victim = victimBuffer[n];
victim.transform.SendMessage("Slice", SendMessageOptions.DontRequireReceiver);
// phys.rigidbody.AddForceAtPosition(forward * DirectionalForce * phys.rigidbody.mass, hit.point, ForceMode2D.Impulse);
}
CameraShakeBehaviour.main.Shake(1, transform.position);
physicalBehaviour.rigidbody.AddRelativeForce(new Vector2(0f * transform.localScale.normalized.x, 0), ForceMode2D.Impulse);
yield return new WaitForSeconds(interval);
}
laser.enabled = false;
yield return new WaitForSeconds(2f);
}
public override Vector2 BarrelPosition { get { return transform.TransformPoint(barrelPosition); } }
public Vector2 BarrelDirection { get { return transform.TransformDirection(barrelDirection) * transform.localScale.x; } }

Why does Unity crash when I move a sprite horizontally?

I just started Unity and I was making a 2D game of a player (sprite) that shoots an arrow when I click a specific button. The player moves vertically and the arrow moves horizontally. The player moves fine but, when I click to shoot the arrow, Unity crashes and I have to close it from Task Manager.
The code shows 0 errors and everything else works fine except for Unity.
I don't know where the problem is so, I'll show the whole code; the function that shoots the arrows is called shootingsysytem.
void Start()
{
health = 100;
playerspeed = 2;
shotspeed = 3;
bulletcount = 3;
playerpos();
if (delay <= 0)
{
delay = 0;
}
stopbullets();
}
// Update is called once per frame
void Update()
{
playermouvement();
shootingsystem();
playerpos();
delay -= Time.deltaTime;
}
public void playermouvement()// works
{
if (Input.GetKey("z"))
{
player[0].transform.Translate(Vector2.up * Time.deltaTime * playerspeed);
}
if (Input.GetKey("s"))
{
player[0].transform.Translate(Vector2.down * Time.deltaTime * playerspeed);
}
}// works
public void shootingsystem() // this function shoots the arrows.........
{
if (Input.GetKey("k"))
{
if (bulletcount == 3 & delay <= 0.0f)
{
bullet3shot = true;
while (bullet3shot)
{
player[3].transform.Translate(Vector2.left * Time.deltaTime * shotspeed);
}
if (delay <= 0.0f)
{
bulletcount--;
delay = 1;
}
}
else if (bulletcount == 2 & delay <= 0.0f)
{
bullet2shot = true;
while (bullet2shot)
{
player[2].transform.position += goingside * Time.deltaTime;
}
if (delay <= 0.0f)
{
bulletcount--;
delay = 1;
}
}
else if (bulletcount == 1 & delay <= 0.0f)
{
bullet1shot = true;
while (bullet1shot)
{
player[1].transform.Translate(Vector2.left * Time.deltaTime * shotspeed);
}
if (delay <= 0.0f)
{
bulletcount--;
bulletcount = 3; // remove later
delay = 1;
}
}
}
}
public void playerpos()//works
{
playerposition = player[0].transform.position;
if (!bullet1shot)
{
player[1].transform.position = playerposition;
}
if (!bullet2shot)
{
player[2].transform.position = playerposition;
}
if (!bullet3shot)
{
player[3].transform.position = playerposition;
}
}//works
private void stopbullets()
{
if (player[1].transform.position.x <= -13) //stop first bullet
{
bullet1shot = false;
}
if (player[2].transform.position.x <= -13) //stop second bullet
{
bullet2shot = false;
}
if (player[3].transform.position.x <= -13) //stop third bullet
{
bullet3shot = false;
}
}
Its because you have an infinite loop.
bullet1shot = true;
while (bullet1shot)
{
player[1].transform.Translate(Vector2.left * Time.deltaTime * shotspeed);
}

Unity 2D Trajectory prediction for mobile

I need help with trajectory prediction on mobile.
I'm able to do that on PC using the mouse, but I'm not able to do that with touch functions on mobile.
I've the dot that I copy on the start, and while I'm dragging the ball I want to calculate my trajectory.
After touch will end I will destroy these dots.
As I said I'm able to do it using Input.GetMouseButton/Up/Down but I'm not able to do that using touch function.
Here is my code:
private void Update()
{
//if (Input.touchCount > 0)
//{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Began)
{
startPosition = Input.GetTouch(0).position;
for (int i = 0; i < number; i++)
{
trajectoryDots[i] = Instantiate(trajectoryDot, gameObject.transform);
}
}
if (Input.touchCount > 0)
{
for (int i = 0; i < number; i++)
{
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
}
if (touch.phase == TouchPhase.Ended)
{
for (int i = 0; i < number; i++)
{
Destroy(trajectoryDots[i]);
}
endPoisiton = Input.GetTouch(0).position;
force = startPosition - endPoisiton;
ballRigid.gravityScale = 1;
ballRigid.velocity = new Vector2(force.x * power, force.y * power);
}
//}
}
private Vector2 calculatePosition(float elapsedTime)
{
return new Vector2(endPoisiton.x, endPoisiton.y) +
new Vector2(force.x * power, force.y * power) * elapsedTime +
0.5f * Physics2D.gravity * elapsedTime * elapsedTime;
}
The code I used for mouse input
if (Input.GetMouseButtonDown(0))
{ //click
startPos = gameObject.transform.position;
for (int i = 0; i < number; i++)
{
trajectoryDots[i] = Instantiate(trajectoryDot, gameObject.transform);
}
}
if (Input.GetMouseButton(0))
{ //drag
endPos = Camera.main.ScreenToWorldPoint(Input.mousePosition) + new Vector3(0, 0, 10);
gameObject.transform.position = endPos;
forceAtPlayer = endPos - startPos;
for (int i = 0; i < number; i++)
{
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
}
if (Input.GetMouseButtonUp(0))
{ //leave
rigidbody.gravityScale = 1;
rigidbody.velocity = new Vector2(-forceAtPlayer.x * forceFactor, -forceAtPlayer.y * forceFactor);
for (int i = 0; i < number; i++)
{
Destroy(trajectoryDots[i]);
}
}
So after little changes it's much better, I' am getting closer to what i want there are still some glitches i guess because of touch.moved so when I' am on display with my finger and do only little moves ball is jumping on the screen
Here is the code
private void Update()
{
if (Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Began:
initPosition = gameObject.transform.position;
startPosition = cam.ScreenToWorldPoint(touch.position) + new Vector3(0, 0, 10);
for (int i = 0; i < number; i++)
{
trajectoryDots[i] = Instantiate(trajectoryDot, gameObject.transform);
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
break;
case TouchPhase.Moved:
endPosition = Camera.main.ScreenToWorldPoint(touch.position) + new Vector3(0, 0, 10);
gameObject.transform.position = initPosition;
force = startPosition - endPosition;
for (int i = 0; i < number; i++)
{
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
break;
case TouchPhase.Ended:
ballRigid.gravityScale = 1;
ballRigid.velocity = new Vector2(force.x * power, force.y * power);
for (int i = 0; i < number; i++)
{
Destroy(trajectoryDots[i]);
}
break;
}
}
}
private Vector2 calculatePosition(float elapsedTime)
{
return new Vector2(initPosition.x, initPosition.y) +
new Vector2(force.x * power, force.y * power) * elapsedTime +
0.5f * Physics2D.gravity * elapsedTime * elapsedTime;
}
Ok i have final soution and it's working correctly
private void Update()
{
if (Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
switch (touch.phase)
{
case TouchPhase.Began:
initPosition = gameObject.transform.position;
startPosition = cam.ScreenToWorldPoint(touch.position) + new Vector3(0, 0, 10);
for (int i = 0; i < number; i++)
{
trajectoryDots[i] = Instantiate(trajectoryDot, gameObject.transform);
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
break;
case TouchPhase.Ended:
ballRigid.gravityScale = 1;
ballRigid.velocity = new Vector2(force.x * power, force.y * power);
for (int i = 0; i < number; i++)
{
Destroy(trajectoryDots[i]);
}
break;
}
endPosition = Camera.main.ScreenToWorldPoint(touch.position) + new Vector3(0, 0, 10);
gameObject.transform.position = initPosition;
force = startPosition - endPosition;
for (int i = 0; i < number; i++)
{
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
}
}
private Vector2 calculatePosition(float elapsedTime)
{
return new Vector2(initPosition.x, initPosition.y) +
new Vector2(force.x * power, force.y * power) * elapsedTime +
0.5f * Physics2D.gravity * elapsedTime * elapsedTime;
}
So just taking your mouse code and convert it to touch code it might look like
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
switch(touch.phase)
{
case TouchPhase.Began:
// Note: to be more accurate you actually probably would
// rather also here use ScreenToWorldPoint on the touch.position
// same way as later. Otherwise you might allways get a force even if touch wasn't moved at all
//startPos = transform.position;
startPos = Camera.main.ScreenToWorldPoint(touch.position) + new Vector3(0, 0, 10);
for (int i = 0; i < number; i++)
{
trajectoryDots[i] = Instantiate(trajectoryDot, gameObject.transform);
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
break;
case TouchPhase.Moved:
// These were missing in your touch version
// You did it only in the end so there were no preview trajectory
// And note that also touch is in pixel screenspace so if you
// want to use world positions you need to convert them just the same as for mouse input!
endPos = Camera.main.ScreenToWorldPoint(touch.position) + new Vector3(0, 0, 10);
transform.position = endPos;
force = endPos - startPos;
for (int i = 0; i < number; i++)
{
trajectoryDots[i].transform.position = calculatePosition(i * 0.1f);
}
break;
case TouchPhase.Ended:
rigidbody.gravityScale = 1;
// Not sure why but in your mouse code you used a negative force here
// but in this case your trajectory would have been wrong since there you didn't use negative values...
rigidbody.velocity = force * forceFactor;
for (int i = 0; i < number; i++)
{
Destroy(trajectoryDots[i]);
}
break;
}
}
Btw the calculate method gets better to read like
private Vector2 calculatePosition(float elapsedTime)
{
return endPoisiton
+ force * power * elapsedTime
+ Physics2D.gravity * 0.5f * elapsedTime * elapsedTime;
}
Note: Typed on smartphone but I hope the idea gets clear

Xna Intersect is to slow

Hello i have a problem with this code:
for (int i = 0; i < map.collision_rects.Count(); i++)
{
if (player.collision_rect.Intersects(map.collision_rects[i]))
{
Debug.WriteLine("INTERSERCTED:" + player.velocity.Y);
player.velocity.Y = 0;
player.has_jumped = false;
}
}
player.Update(gameTime);
The problem is that it Detects that it intersected but the velocity is set to zero a frame later than it detected
if (has_jumped == true)
{
float i = 1;
velocity.Y += 0.15f*i;
}
The code that sets the velocity

How to stop an audioclip when the condition is over?

I'm making a game in Unity and I'm having some problems on adding audio in my enemies. I have an audio clip for walking, attacking and dying. But when I play the game the audioclips starts playing in the right condition, but they never stop like I was expecting. I want them to only play while they're in the right condition and stop playing when in another condition.
Here's my code:
public override void ActionCharacter(){
if (!isDead) {
currentTimeToChangeAction += Time.deltaTime;
if (currentDistanceToPlayer <= distanceToAttack && currentTimeToAttack < timeToAttack) {
currentTimeToAttack += Time.deltaTime;
inAttack = true;
axiVertical = 0;
audio.PlayOneShot(ZombieWalkAudio);
animationCharacter.CrossFade (attackAnimation.name);
} else {
currentTimeToAttack = 0;
inAttack = false;
}
if (inStun) {
currentTimeToStun += Time.deltaTime;
if (currentTimeToStun > timeToStun) {
currentTimeToStun = 0;
inStun = false;
}
axiVertical = 0;
animationCharacter.CrossFade (damageAnimation.name);
}
if (!inAttack && !inStun) {
if (currentTimeToChangeAction > timeToChangeAction) {
currentTimeToChangeAction = 0;
//axiHorizontal = Time.deltaTime * walkVelocity;
axiVertical = Time.deltaTime * walkVelocity;
audio.PlayOneShot(ZombieAttackAudio);
}
if (currentDistanceToPlayer <= distanceMinToRun) {
callRun = true;
} else {
callRun = false;
}
transform.LookAt (player.transform);
//currentGun.Shoot();
}
}
}

Categories

Resources