For some reason the other player's movement is 'stuttering'. I know this is a common problem people run into with Photon, so was wondering if anyone knows how I can resolve it?
Here's my player movement code:
public float SmoothingDelay = 5;
public void Start()
{
GetComponent<SmoothSyncMovement>().enabled = true; //This is the name of this script
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
//We own this player: send the others our data
stream.SendNext(rb2D.position);
stream.SendNext(rb2D.rotation);
}
else
{
//Network player, receive data
correctPlayerPos = (Vector3)stream.ReceiveNext();
}
}
public void Update()
{
if (!photonView.isMine)
{
Vector2 playerMovement = rb2D.position + velocity * Time.deltaTime;
rb2D.MovePosition(playerMovement);
}
if (photonView.isMine)
{
Vector2 playerMovement = rb2D.position + velocity * Time.deltaTime;
rb2D.MovePosition(playerMovement);
}
}
Make a smooth transition between players old possible and new position, dont set position. You can use lerp for this or any other method.
This video tutorial goes into all the details of photon networking including how to deal with stuttering and other issues you might run into. Video Tutorial Play List
Exact Video That Addresses Stuttering
It would look something like this:
currentPosition = Vector3.Lerp(currentPosition, realPosition,0.1f);
Where currentPosition is the position of the networked player before it moves on your client and realPosition is the new position received from the network where you want the player to be.
Related
I am trying to set up a simple multiplayer in VR. For this I have made a scene with a vr controller and a Network Manager, spawning an avatar for each client. I am doing this so the actual VR rig dosnt have to get sent through the network but rather just an Avatar representation. I am setting the avatar to the position of the rig localy and then I only send the position of each avatar bound to the rig to each client. For some reason the avatars spawn for each client but the movement only works for the host. For all other clients the avatars neither move in local space nor on the network. When i run the Debug.Log() that is currently commented out it does print the right position of the target but just dosnt set the transform to that position but rather forces it to (0,0,0). Does anyone know why this could be the case?
FollowTarget Script:
public class FollowTarget : NetworkBehaviour
{
public Transform target;
public Vector3 offset = new Vector3(0f, 0f, 0f);
[SerializeField] private bool _isNetworkAvatar;
[SerializeField] private bool _keepYPosition;
[SerializeField] private string TagToFollow = "MainCamera";
private void Start()
{
if (_isNetworkAvatar)
{
target = GameObject.FindGameObjectWithTag("MainCamera").transform;
Debug.Log($"Initializing Network Avatar for: {target.gameObject.name}");
}
}
private void Update()
{
if(!IsOwner) return;
//Debug.Log($"My Position:{transform.position} should be {target.position}");
switch (_keepYPosition)
{
case true:
transform.position = new Vector3(target.position.x, transform.position.y, target.position.z) +
offset;
Debug.Log(new Vector3(target.position.x, transform.position.y, target.position.z) +
offset);
break;
case false:
transform.position = target.position + offset;
break;
}
}
}
Avatar Prefab:
Ok, so after many tries I found the solution.
The problem was that the client was'nt authorized to change its own position in the network, so i had to add an extra override to the NetworkTransform script which gives the client authorization.
protected override bool OnIsServerAuthoritative()
{
return false;
}
that fixed it. :)
enter image description here
enter image description here
I made this slingshot with objects in Unity3D. I know im suppose to add components but I dont know exactly which ones. The two yellows would represent as my elastics. Keep in note i have a robot arm made on the side. My claw hand will grab the object holder and pull it back so it can shoot. I need help in
what component should i add to them to make them like rubberband or elastic
how attach them to my objectHolder
how to make it shoot
please help and be patient with me :( Any advice I would appreciate it. Thank you :)
private Animation anim;
Rigidbody rb;
void Start()
{
anim = gameObject.GetComponent<Animation>();
}
void Update()
{
//********************Open pincher ********************
if (Input.GetKey(KeyCode.X))
{
anim.Play("clawopen");
//rb.AddTorque(Vector3.up * speed);
}
//*******************Close pincher ********************
if (Input.GetKey(KeyCode.Y))
{
anim.Play("clawclose");
// rb.AddTorque(Vector3.down * speed);
}
//*******************if not both ********************
else
{
rb.angularVelocity = Vector3.zero;
}
}
}
There is no magic component that will achieve what you want. You should break it down into smaller chunks.
Start with the movement of the object holder. There are different ways to achieve this. If you want to manipulate it via mouse, you can write a script that makes the objectHolder follow mouse movement while a mouse button is pressed. Once you release the mouse button you can let the object holder smoothly go back to it's start position.
For the elastic parts, I would replace the objects with LineRenderers. But also here are different ways to achieve this.
Maybe this tutorial helps you.
attach a spring component to the dark orange round shape and set connectedAnchor to the position of where it should stretch from. You can play around with soeing and damoer to make it more or less stretchy.
I would reccomend a very high spring (spring should be about 100-1000) and mass (mass should be about 5-10) on this. The ball should have a mass of 1 or less.
You can set the Rigidbody.position to the claw's position (while it is closed) Then when it opens' it will release and launch.
For example SlingShot.cs (on the dark orange object.
[HideInInspector] public bool closedClaw = false;
[HideInInspector] public Vector3 clawPos;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
if (closedClaw)
{
rb.positon = clawPos;
}
}
And then somewhere in the script that closes the claw
SlingShot ss;
GameObject slingShot;
void Start()
{
ss = slingShot.GetComponent<SlingShot>();
}
...
if (right claw.localEulerAngles.z /*or whatever axis it rotates on*/
> 90f /*or whatever threshHold*/)
{
ss.closedClaw = true;
}
else
{
ss.closedClaw = false;
}
ss.clawPos = rightClaw.transform.position +
leftClaw.transform.position;
ss.clawPos /= 2f;
All of this is untested and may have errors.
If this all works,I will come back later and do the stretchy yellow rubber bands.
Requested by comment Open closing arm
public float speed;
public Transform rightClaw;
public Transform leftClaw;
public Vector3 axis;
public Vector2 minMaxRotation;
void Update()
{
if (Input.GetKey(KeyCode.X))
{
Press(1);
}
if (Input.GetKey(KeyCode.Y))
{
Press(-1);
}
}
void Press(int open)
{
float r = rightClaw.transform.localEulerAngles * axis;
rightClaw.transform.localEulerAngles =
axis * Mathf.Clamp(r + speed * open, minMaxRotation.x, minMaxRotation.y);
float l = leftClaw.transform.localEulerAngles * axis;
leftClaw.transform.localEulerAngles =
axis * Mathf.Clamp(l - speed * open, 180f - minMaxRotation.x, 180f - minMaxRotation.y);
}
I am trying to access the Location of all the other Players who are connected to the same room. I first thought about getting the already instantiated gameobject but they all had the same name so that won't work. I wan't to to a Terrain Generator but when every Player generates Terrain its all messed up into each other. So i thought maybe just the first player has to generate the Terrain for himself and all other players to, but i now don't know how to get the other players positions. Therefore i can't check who is the first Player. So my second Question is: Is there maybe an other way to let only one generate the Terrain for all?
When i used the name to access my player i only got the player who i was already playing, i tried something where i send the player coordinates, but that only gives the coordinates when the photonView.isMine isnt True.
This is my Code so far this script is on the Player Prefab which gets instantiated when a player joins.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
public class NetworkPlayer : Photon.MonoBehaviour
{
private bool generatingTerrain;
private Vector3 correctPlayerPos;
private Quaternion correctPlayerRot;
private GameObject LevelGenerator;
private GameObject Player;
private float pos;
Rigidbody2D rb;
void Start()
{
LevelGenerator = GameObject.Find("LevelGenerator");
generatingTerrain = LevelGenerator.transform.Find("Level Generator");
Debug.Log(generatingTerrain);
if (photonView.isMine)
{
rb = GetComponent<Rigidbody2D>();
rb.simulated = true;
GetComponent<PlayerController>().enabled = true;
}
}
void Update()
{
//Debug.Log(generatingTerrain);
if (!photonView.isMine)
{
transform.position = Vector3.Lerp(transform.position, this.correctPlayerPos, Time.deltaTime * 5);
transform.rotation = Quaternion.Lerp(transform.rotation, this.correctPlayerRot, Time.deltaTime * 5);
Destroy(rb);
Debug.Log("Transform Position on !isMine: " + transform.position.x.ToString());
pos = transform.position.x;
}
Player = GameObject.Find("Player_1(Clone)");
if (photonView.isMine)
{
Debug.Log("photonView.isMine: " + transform.position.x.ToString());
Debug.Log("pos " + pos.ToString());
}
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
// We own this player: send the others our data
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}
else
{
// Network player, receive data
this.correctPlayerPos = (Vector3)stream.ReceiveNext();
this.correctPlayerRot = (Quaternion)stream.ReceiveNext();
}
}
}
Maybe i am just failing to see something, this is my first time working with unity and photon so i am really new to this topic.
If you need something just write me, i am sorry if something here is missing
Hope to get help soon :)
As far as getting the other players position you do it correctly, by looking at if the photonView is not mine.
I am not entirely sure what you are trying to do with the terrain. Right now you are trying to generate terrain on all players in the game which will make each player object in the game generate the terrain, which will probably cause chaos.
If you want to have procedurally generated terrain I would suggest doing it with a seed and then share the seed amongst all players. The terrain generation should then only be done if photonView.isMine.
If you want to change terrain, e.g. destroy or move blocks, then you need to sync this over the network , most likely with with Events or RPC calls.
I found a solution to my exact problem:
GameObject[] players = GameObject.FindGameObjectsWithTag("Player");
foreach (GameObject p in players)
{
Debug.Log(p.transform.position);
}
This lists every Gameobject which got the Player Tag and puts them into an Array, then you can access every of them to check that its not your own position you can just do:
if (p.position != gameobject.transform.position) {
//Do here what you want to do with every Player who isnt you
}
I found the solution here
I'm creating an air hockey game in Unity and I want the puck to sound like it's rolling across the canvas. I also want the sound to match the movement of the ball (get slower when the ball is slowing down, etc.) I searched for tutorials on this but I haven't found any good for C# and I've been struggling for days. I'm new to Unity and C#, so I'm not familiar with all functions and statements.
The simplest way to do it is as #akaBase said in the comments.
First, you'll want to get a good and loopable sound.
Second, you add an audio source component to the puck object and put the sound clip in.
Third, you need to make a script that looks at the current speed of the puck. If it's being moved by physics you can just request rigidbody.velocity every FixedUpdate or Update to change the audioSource.pitch.
Give it a try. Should be easy enough to figure out from here. You'll need to experiment a bit to see what sounds best, but if you still need help, just comment.
Here's a simple example script for changing pitch depending on the current speed of an object. Feel free to modify it - change the Update method to a FixedUpdate if you're gonna use physics, change how the speed is calculated/acquired etc.
using System.Collections;
using UnityEngine;
public class MovementBasedPitchAdjuster : MonoBehaviour
{
public AudioSource audioSource = null;
public float pitchAdjustingFrequency = 0.5f;
public float maxSpeed = 5f;
public float maxPitch = 1f;
private float speed = 0f;
private Vector3 lastRecordedPosition;
private float traveledDistance = 0f;
private void Start()
{
StartCoroutine(StartAdjustingPitchAndSpeed());
}
private void Update()
{
RecordTraveledDistance();
}
private IEnumerator StartAdjustingPitchAndSpeed()
{
while (true)
{
UpdateSpeed();
AdjustPitch();
yield return new WaitForSeconds(pitchAdjustingFrequency);
}
}
private void UpdateSpeed()
{
float newSpeed = Mathf.Sqrt(traveledDistance);
speed = newSpeed > maxSpeed ? maxSpeed : newSpeed;
traveledDistance = 0f;
}
private void AdjustPitch()
{
audioSource.pitch = speed / maxSpeed * maxPitch;
}
private void RecordTraveledDistance()
{
traveledDistance += Vector3.Distance(
lastRecordedPosition,
transform.position);
lastRecordedPosition = transform.position;
}
}
A test using an object that follow the cursor position.
https://streamable.com/fkw0k2
As you can see in the screenshoot I can't see prefabs in the game tab but only in the editor. I have made a simple function for shooting(not finished yet), it works fine, it spawns the prefabs but i can't see them in the game tab, I have already tried changing the Sorting Layer, move the camera, change Z position but nothing appen.
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAttack : MonoBehaviour
{
[SerializeField]
float delayBetweenShots = 0.4f;
float timePassedSinceLast = 0f;
// Start is called before the first frame update
void Start()
{
timePassedSinceLast = delayBetweenShots;
}
// Update is called once per frame
void Update()
{
Aiming();
Shooting();
}
void Aiming()
{
var objectPos = Camera.main.WorldToScreenPoint(transform.position);
var dir = Input.mousePosition - objectPos;
transform.rotation = Quaternion.Euler(new Vector3(0,0,Mathf.Atan2(-dir.x, dir.y) * Mathf.Rad2Deg));
}
void Shooting()
{
if(Input.GetMouseButton(0) && timePassedSinceLast >= delayBetweenShots)
{
GameObject bullet = (GameObject)Instantiate(Resources.Load("bullet"), transform.position, transform.rotation);
timePassedSinceLast = 0f;
}
else
{
timePassedSinceLast += Time.deltaTime;
}
}
}
The prefabs get instantiated correctly. As others suggested as well, the best way to find "lost" objects in your game is to shoot some stuff, pause the game, go into scene view, turn on 3D mode and double click one of the prefabs in the hierarchy. The camera will take you straight to your object.