Dear communty, I’ve been programming a photon multiplayer game in unity for a few days. When I create a room I can’t move until a second player joins (but that doesn’t matter for now). The real problem comes when a second player joins. The player controller for the second player is spawned and I can move freely, but my movement change is not displayed on the other player’s screen. There I just stand still, although I have already moved a long way.
Here are 2 pictures of my player prefab:
And yes, I noticed it too. There are two transform views on the prefab (classic and normal) but one is disabled. In that respect it shouldn’t be a problem. It didn’t work even with just one.
Here is a screenshot of my first person camera (I think the error is somewhere here):
I’ve added the photon view here again because the ‘First Person Look Script’ checks ‘if (!photonView.IsMine)’ again. And if I hadn’t attached the Photon view I would of course get a NullReferenceException. I think thats the error, but how should I do it if not like this?
Here is a picture of the hierarchy of my prefab:
I got the base of the player controller from the unity asset store (https://assetstore.unity.com/packages/tools/input-management/mini-first-person-controller-174710).
Here are some of the player controller scripts that might be useful:
FirstPersonLook:
using UnityEngine;
using Photon.Pun;
public class FirstPersonLook : MonoBehaviour
{
[SerializeField]
Transform character;
public float sensitivity = 2;
public float smoothing = 1.5f;
Vector2 velocity;
Vector2 frameVelocity;
PhotonView photonView;
void Reset()
{
// Get the character from the FirstPersonMovement in parents.
character = GetComponentInParent<FirstPersonMovement>().transform;
}
void Start()
{
// Lock the mouse cursor to the game screen.
Cursor.lockState = CursorLockMode.Locked;
photonView = GetComponent<PhotonView>();
}
void Update()
{
if (!photonView.IsMine)
{
// Get smooth velocity.
Vector2 mouseDelta = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
Vector2 rawFrameVelocity = Vector2.Scale(mouseDelta, Vector2.one * sensitivity);
frameVelocity = Vector2.Lerp(frameVelocity, rawFrameVelocity, 1 / smoothing);
velocity += frameVelocity;
velocity.y = Mathf.Clamp(velocity.y, -90, 90);
// Rotate camera up-down and controller left-right from velocity.
transform.localRotation = Quaternion.AngleAxis(-velocity.y, Vector3.right);
character.localRotation = Quaternion.AngleAxis(velocity.x, Vector3.up);
}
}
}
FirstPersonMovement:
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
public class FirstPersonMovement : MonoBehaviour
{
public float speed = 5;
[Header("Running")]
public bool canRun = true;
public bool IsRunning { get; private set; }
public float runSpeed = 9;
public KeyCode runningKey = KeyCode.LeftShift;
public GameObject bullet;
public Transform firePoint;
public AudioSource shootingSound;
PhotonView photonView;
Rigidbody rigidbody;
/// <summary> Functions to override movement speed. Will use the last added override. </summary>
public List<System.Func<float>> speedOverrides = new List<System.Func<float>>();
void Awake()
{
// Get the rigidbody on this.
rigidbody = GetComponent<Rigidbody>();
}
private void Start()
{
photonView = GetComponent<PhotonView>();
}
void FixedUpdate()
{
if (!photonView.IsMine)
{
// Update IsRunning from input.
IsRunning = canRun && Input.GetKey(runningKey);
// Get targetMovingSpeed.
float targetMovingSpeed = IsRunning ? runSpeed : speed;
if (speedOverrides.Count > 0)
{
targetMovingSpeed = speedOverrides[speedOverrides.Count - 1]();
}
// Get targetVelocity from input.
Vector2 targetVelocity =new Vector2( Input.GetAxis("Horizontal") * targetMovingSpeed, Input.GetAxis("Vertical") * targetMovingSpeed);
// Apply movement.
rigidbody.velocity = transform.rotation * new Vector3(targetVelocity.x, rigidbody.velocity.y, targetVelocity.y);
// Shooting
if(Input.GetMouseButtonDown(0))
{
Instantiate(bullet, firePoint.position, firePoint.rotation);
shootingSound.Play();
}
}
/*// Update IsRunning from input.
IsRunning = canRun && Input.GetKey(runningKey);
// Get targetMovingSpeed.
float targetMovingSpeed = IsRunning ? runSpeed : speed;
if (speedOverrides.Count > 0)
{
targetMovingSpeed = speedOverrides[speedOverrides.Count - 1]();
}
// Get targetVelocity from input.
Vector2 targetVelocity =new Vector2( Input.GetAxis("Horizontal") * targetMovingSpeed, Input.GetAxis("Vertical") * targetMovingSpeed);
// Apply movement.
rigidbody.velocity = transform.rotation * new Vector3(targetVelocity.x, rigidbody.velocity.y, targetVelocity.y);
// Shooting
if(Input.GetMouseButtonDown(0))
{
Instantiate(bullet, firePoint.position, firePoint.rotation);
shootingSound.Play();
}*/
}
}
Jump:
using UnityEngine;
using Photon.Pun;
public class Jump : MonoBehaviour
{
Rigidbody rigidbody;
public float jumpStrength = 2;
public event System.Action Jumped;
[SerializeField, Tooltip("Prevents jumping when the transform is in mid-air.")]
GroundCheck groundCheck;
PhotonView photonView;
void Reset()
{
// Try to get groundCheck.
groundCheck = GetComponentInChildren<GroundCheck>();
}
void Awake()
{
// Get rigidbody.
rigidbody = GetComponent<Rigidbody>();
}
private void Start()
{
photonView = GetComponent<PhotonView>();
}
void LateUpdate()
{
if (!photonView.IsMine)
{
// Jump when the Jump button is pressed and we are on the ground.
if (Input.GetButtonDown("Jump") && (!groundCheck || groundCheck.isGrounded))
{
rigidbody.AddForce(Vector3.up * 100 * jumpStrength);
Jumped?.Invoke();
}
}
}
}
Crouch:
using UnityEngine;
using Photon.Pun;
public class Crouch : MonoBehaviour
{
public KeyCode key = KeyCode.LeftControl;
[Header("Slow Movement")]
[Tooltip("Movement to slow down when crouched.")]
public FirstPersonMovement movement;
[Tooltip("Movement speed when crouched.")]
public float movementSpeed = 2;
[Header("Low Head")]
[Tooltip("Head to lower when crouched.")]
public Transform headToLower;
[HideInInspector]
public float? defaultHeadYLocalPosition;
public float crouchYHeadPosition = 1;
[Tooltip("Collider to lower when crouched.")]
public CapsuleCollider colliderToLower;
[HideInInspector]
public float? defaultColliderHeight;
public bool IsCrouched { get; private set; }
public event System.Action CrouchStart, CrouchEnd;
PhotonView photonView;
void Reset()
{
// Try to get components.
movement = GetComponentInParent<FirstPersonMovement>();
headToLower = movement.GetComponentInChildren<Camera>().transform;
colliderToLower = movement.GetComponentInChildren<CapsuleCollider>();
}
private void Start()
{
photonView = GetComponent<PhotonView>();
}
void LateUpdate()
{
if (!photonView.IsMine)
{
if (Input.GetKey(key))
{
// Enforce a low head.
if (headToLower)
{
// If we don't have the defaultHeadYLocalPosition, get it now.
if (!defaultHeadYLocalPosition.HasValue)
{
defaultHeadYLocalPosition = headToLower.localPosition.y;
}
// Lower the head.
headToLower.localPosition = new Vector3(headToLower.localPosition.x, crouchYHeadPosition, headToLower.localPosition.z);
}
// Enforce a low colliderToLower.
if (colliderToLower)
{
// If we don't have the defaultColliderHeight, get it now.
if (!defaultColliderHeight.HasValue)
{
defaultColliderHeight = colliderToLower.height;
}
// Get lowering amount.
float loweringAmount;
if(defaultHeadYLocalPosition.HasValue)
{
loweringAmount = defaultHeadYLocalPosition.Value - crouchYHeadPosition;
}
else
{
loweringAmount = defaultColliderHeight.Value * .5f;
}
// Lower the colliderToLower.
colliderToLower.height = Mathf.Max(defaultColliderHeight.Value - loweringAmount, 0);
colliderToLower.center = Vector3.up * colliderToLower.height * .5f;
}
// Set IsCrouched state.
if (!IsCrouched)
{
IsCrouched = true;
SetSpeedOverrideActive(true);
CrouchStart?.Invoke();
}
}
else
{
if (IsCrouched)
{
// Rise the head back up.
if (headToLower)
{
headToLower.localPosition = new Vector3(headToLower.localPosition.x, defaultHeadYLocalPosition.Value, headToLower.localPosition.z);
}
// Reset the colliderToLower's height.
if (colliderToLower)
{
colliderToLower.height = defaultColliderHeight.Value;
colliderToLower.center = Vector3.up * colliderToLower.height * .5f;
}
// Reset IsCrouched.
IsCrouched = false;
SetSpeedOverrideActive(false);
CrouchEnd?.Invoke();
}
}
}
}
#region Speed override.
void SetSpeedOverrideActive(bool state)
{
// Stop if there is no movement component.
if(!movement)
{
return;
}
// Update SpeedOverride.
if (state)
{
// Try to add the SpeedOverride to the movement component.
if (!movement.speedOverrides.Contains(SpeedOverride))
{
movement.speedOverrides.Add(SpeedOverride);
}
}
else
{
// Try to remove the SpeedOverride from the movement component.
if (movement.speedOverrides.Contains(SpeedOverride))
{
movement.speedOverrides.Remove(SpeedOverride);
}
}
}
float SpeedOverride() => movementSpeed;
#endregion
}
I think that should be enough. If anything else is needed just let me know :).
Thank you for helping me!
Kind regards, jcjms.