// -------------------------------------------------------------------------------------------------------------------- // // Part of: Photon Unity Networking Demos // // // Used in SlotRacer Demo // // developer@exitgames.com // -------------------------------------------------------------------------------------------------------------------- using UnityEngine; using System.Collections; using Photon.Pun.Demo.SlotRacer.Utils; using Photon.Pun.UtilityScripts; namespace Photon.Pun.Demo.SlotRacer { /// /// Player control. /// Interface the User Inputs and PUN /// Handle the Car instance /// [RequireComponent(typeof(SplineWalker))] public class PlayerControl : MonoBehaviourPun, IPunObservable { /// /// The car prefabs to pick depending on the grid position. /// public GameObject[] CarPrefabs; /// /// The maximum speed. Maximum speed is reached with a 1 unit per seconds acceleration /// public float MaximumSpeed = 20; /// /// The drag when user is not accelerating /// public float Drag = 5; /// /// The current speed. /// Only used for locaPlayer /// private float CurrentSpeed; /// /// The current distance on the spline /// Only used for locaPlayer /// private float CurrentDistance; /// /// The car instance. /// private GameObject CarInstance; /// /// The spline walker. Must be on this GameObject /// private SplineWalker SplineWalker; /// /// flag to force latest data to avoid initial drifts when player is instantiated. /// private bool m_firstTake = true; private float m_input; #region IPunObservable implementation /// /// this is where data is sent and received for this Component from the PUN Network. /// /// Stream. /// Info. void IPunObservable.OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info) { // currently there is no strategy to improve on bandwidth, just passing the current distance and speed is enough, // Input could be passed and then used to better control speed value // Data could be wrapped as a vector2 or vector3 to save a couple of bytes if (stream.IsWriting) { stream.SendNext(this.CurrentDistance); stream.SendNext(this.CurrentSpeed); stream.SendNext(this.m_input); } else { if (this.m_firstTake) { this.m_firstTake = false; } this.CurrentDistance = (float) stream.ReceiveNext(); this.CurrentSpeed = (float) stream.ReceiveNext(); this.m_input = (float) stream.ReceiveNext(); } } #endregion IPunObservable implementation #region private /// /// Setups the car on track. /// /// Grid start index. private void SetupCarOnTrack(int gridStartIndex) { // Setup the SplineWalker to be on the right starting grid position. this.SplineWalker.spline = SlotLanes.Instance.GridPositions[gridStartIndex].Spline; this.SplineWalker.currentDistance = SlotLanes.Instance.GridPositions[gridStartIndex].currentDistance; this.SplineWalker.ExecutePositioning(); // create a new car this.CarInstance = (GameObject) Instantiate(this.CarPrefabs[gridStartIndex], this.transform.position, this.transform.rotation); // We'll wait for the first serializatin to pass, else we'll have a glitch where the car is positioned at the wrong position. if (!this.photonView.IsMine) { this.CarInstance.SetActive(false); } // depending on wether we control this instance locally, we force the car to become active ( because when you are alone in the room, serialization doesn't happen, but still we want to allow the user to race around) if (PhotonNetwork.CurrentRoom.PlayerCount == 1) { this.m_firstTake = false; } this.CarInstance.transform.SetParent(this.transform); } #endregion private #region Monobehaviour /// /// Cache the SplineWalker and flag context for clean serialization when joining late. /// private void Awake() { this.SplineWalker = this.GetComponent(); this.m_firstTake = true; } /// /// Start this instance as a coroutine /// Waits for a Playernumber to be assigned and only then setup the car and put it on the right starting position on the lane. /// private IEnumerator Start() { // Wait until a Player Number is assigned // PlayerNumbering component must be in the scene. yield return new WaitUntil(() => this.photonView.Owner.GetPlayerNumber() >= 0); // now we can set it up. this.SetupCarOnTrack(this.photonView.Owner.GetPlayerNumber()); } /// /// Make sure we delete instances linked to this component, else when user is leaving the room, its car instance would remain /// private void OnDestroy() { Destroy(this.CarInstance); } // Update is called once per frame private void Update() { if (this.SplineWalker == null || this.CarInstance == null) { return; } if (this.photonView.IsMine) { this.m_input = Input.GetAxis("Vertical"); if (this.m_input == 0f) { this.CurrentSpeed -= Time.deltaTime * this.Drag; } else { this.CurrentSpeed += this.m_input; } this.CurrentSpeed = Mathf.Clamp(this.CurrentSpeed, 0f, this.MaximumSpeed); this.SplineWalker.Speed = this.CurrentSpeed; this.CurrentDistance = this.SplineWalker.currentDistance; } else { if (this.m_input == 0f) { this.CurrentSpeed -= Time.deltaTime * this.Drag; } this.CurrentSpeed = Mathf.Clamp (this.CurrentSpeed, 0f, this.MaximumSpeed); this.SplineWalker.Speed = this.CurrentSpeed; if (this.CurrentDistance != 0 && this.SplineWalker.currentDistance != this.CurrentDistance) { //Debug.Log ("SplineWalker.currentDistance=" + SplineWalker.currentDistance + " CurrentDistance=" + CurrentDistance); this.SplineWalker.Speed += (this.CurrentDistance - this.SplineWalker.currentDistance) * Time.deltaTime * 50f; } } // Only activate the car if we are sure we have the proper positioning, else it will glitch visually during the initialisation process. if (!this.m_firstTake && !this.CarInstance.activeSelf) { this.CarInstance.SetActive(true); this.SplineWalker.Speed = this.CurrentSpeed; this.SplineWalker.SetPositionOnSpline (this.CurrentDistance); } } #endregion Monobehaviour } }