Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

472 lines
13 KiB

namespace Oculus.Platform.Samples.VrHoops
{
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
using System.Collections.Generic;
using Oculus.Platform.Models;
// This class coordinates playing matches. It mediates being idle
// and entering a practice or online game match.
public class MatchController : MonoBehaviour
{
// Text to display when the match will start or finish
[SerializeField] private Text m_timerText = null;
// the camera is moved between the idle position and the assigned court position
[SerializeField] private Camera m_camera = null;
// where the camera will be when not in a match
[SerializeField] private Transform m_idleCameraTransform = null;
// button that toggles between matchmaking and cancel
[SerializeField] private Text m_matchmakeButtonText = null;
// this should equal the maximum number of players configured on the Oculus Dashboard
[SerializeField] private PlayerArea[] m_playerAreas = new PlayerArea[3];
// the time to wait between selecting Practice and starting
[SerializeField] private uint PRACTICE_WARMUP_TIME = 5;
// seconds to wait to coordinate P2P setup with other match players before starting
[SerializeField] private uint MATCH_WARMUP_TIME = 30;
// seconds for the match
[SerializeField] private uint MATCH_TIME = 20;
// how long to remain in position after the match to view results
[SerializeField] private uint MATCH_COOLDOWN_TIME = 10;
// panel to add most-wins leaderboard entries to
[SerializeField] private GameObject m_mostWinsLeaderboard = null;
// panel to add high-score leaderboard entries to
[SerializeField] private GameObject m_highestScoresLeaderboard = null;
// leaderboard entry Text prefab
[SerializeField] private GameObject m_leaderboardEntryPrefab = null;
// Text prefab to use for achievements fly-text
[SerializeField] private GameObject m_flytext = null;
// the current state of the match controller
private State m_currentState;
// transition time for states that automatically transition to the next state,
// for example ending the match when the timer expires
private float m_nextStateTransitionTime;
// the court the local player was assigned to
private int m_localSlot;
void Start()
{
PlatformManager.Matchmaking.EnqueueResultCallback = OnMatchFoundCallback;
PlatformManager.Matchmaking.MatchPlayerAddedCallback = MatchPlayerAddedCallback;
PlatformManager.P2P.StartTimeOfferCallback = StartTimeOfferCallback;
PlatformManager.Leaderboards.MostWinsLeaderboardUpdatedCallback = MostWinsLeaderboardCallback;
PlatformManager.Leaderboards.HighScoreLeaderboardUpdatedCallback = HighestScoreLeaderboardCallback;
TransitionToState(State.NONE);
}
void Update()
{
UpdateCheckForNextTimedTransition();
UpdateMatchTimer();
}
public float MatchStartTime
{
get
{
switch(m_currentState)
{
case State.WAITING_TO_START_PRACTICE:
case State.WAITING_TO_SETUP_MATCH:
return m_nextStateTransitionTime;
default: return 0;
}
}
private set { m_nextStateTransitionTime = value; }
}
#region State Management
private enum State
{
UNKNOWN,
// no current match, waiting for the local user to select something
NONE,
// user selected a practice match, waiting for the match timer to start
WAITING_TO_START_PRACTICE,
// playing a Practice match against AI players
PRACTICING,
// post practice match, time to view the scores
VIEWING_RESULTS_PRACTICE,
// selecting Player Online and waiting for the Matchmaking service to find and create a
// match and join the assigned match room
WAITING_FOR_MATCH,
// match room is joined, waiting to coordinate with the other players
WAITING_TO_SETUP_MATCH,
// playing a competative match against other players
PLAYING_MATCH,
// match is complete, viewing the match scores
VIEWING_MATCH_RESULTS,
}
void TransitionToState(State newState)
{
Debug.LogFormat("MatchController State {0} -> {1}", m_currentState, newState);
if (m_currentState != newState)
{
var oldState = m_currentState;
m_currentState = newState;
// state transition logic
switch (newState)
{
case State.NONE:
SetupForIdle();
MoveCameraToIdlePosition();
PlatformManager.TransitionToState(PlatformManager.State.WAITING_TO_PRACTICE_OR_MATCHMAKE);
m_matchmakeButtonText.text = "Play Online";
break;
case State.WAITING_TO_START_PRACTICE:
Assert.AreEqual(oldState, State.NONE);
SetupForPractice();
MoveCameraToMatchPosition();
PlatformManager.TransitionToState(PlatformManager.State.MATCH_TRANSITION);
m_nextStateTransitionTime = Time.time + PRACTICE_WARMUP_TIME;
break;
case State.PRACTICING:
Assert.AreEqual(oldState, State.WAITING_TO_START_PRACTICE);
PlatformManager.TransitionToState(PlatformManager.State.PLAYING_A_LOCAL_MATCH);
m_nextStateTransitionTime = Time.time + MATCH_TIME;
break;
case State.VIEWING_RESULTS_PRACTICE:
Assert.AreEqual(oldState, State.PRACTICING);
PlatformManager.TransitionToState(PlatformManager.State.MATCH_TRANSITION);
m_nextStateTransitionTime = Time.time + MATCH_COOLDOWN_TIME;
m_timerText.text = "0:00.00";
break;
case State.WAITING_FOR_MATCH:
Assert.AreEqual(oldState, State.NONE);
PlatformManager.TransitionToState(PlatformManager.State.MATCH_TRANSITION);
m_matchmakeButtonText.text = "Cancel";
break;
case State.WAITING_TO_SETUP_MATCH:
Assert.AreEqual(oldState, State.WAITING_FOR_MATCH);
m_nextStateTransitionTime = Time.time + MATCH_WARMUP_TIME;
break;
case State.PLAYING_MATCH:
Assert.AreEqual(oldState, State.WAITING_TO_SETUP_MATCH);
PlatformManager.TransitionToState(PlatformManager.State.PLAYING_A_NETWORKED_MATCH);
m_nextStateTransitionTime = Time.time + MATCH_TIME;
break;
case State.VIEWING_MATCH_RESULTS:
Assert.AreEqual(oldState, State.PLAYING_MATCH);
PlatformManager.TransitionToState(PlatformManager.State.MATCH_TRANSITION);
m_nextStateTransitionTime = Time.time + MATCH_COOLDOWN_TIME;
m_timerText.text = "0:00.00";
CalculateMatchResults();
break;
}
}
}
void UpdateCheckForNextTimedTransition()
{
if (m_currentState != State.NONE && Time.time >= m_nextStateTransitionTime)
{
switch (m_currentState)
{
case State.WAITING_TO_START_PRACTICE:
TransitionToState(State.PRACTICING);
break;
case State.PRACTICING:
TransitionToState(State.VIEWING_RESULTS_PRACTICE);
break;
case State.VIEWING_RESULTS_PRACTICE:
TransitionToState(State.NONE);
break;
case State.WAITING_TO_SETUP_MATCH:
TransitionToState(State.PLAYING_MATCH);
break;
case State.PLAYING_MATCH:
TransitionToState(State.VIEWING_MATCH_RESULTS);
break;
case State.VIEWING_MATCH_RESULTS:
PlatformManager.Matchmaking.EndMatch();
TransitionToState(State.NONE);
break;
}
}
}
void UpdateMatchTimer()
{
if (Time.time <= m_nextStateTransitionTime)
{
switch (m_currentState)
{
case State.WAITING_TO_START_PRACTICE:
case State.WAITING_TO_SETUP_MATCH:
m_timerText.text = string.Format("{0:0}", Mathf.Ceil(Time.time - MatchStartTime));
break;
case State.PRACTICING:
case State.PLAYING_MATCH:
var delta = m_nextStateTransitionTime - Time.time;
m_timerText.text = string.Format("{0:#0}:{1:#00}.{2:00}",
Mathf.Floor(delta / 60),
Mathf.Floor(delta) % 60,
Mathf.Floor(delta * 100) % 100);
break;
}
}
}
#endregion
#region Player Setup/Teardown
void SetupForIdle()
{
for (int i = 0; i < m_playerAreas.Length; i++)
{
m_playerAreas[i].SetupForPlayer<AIPlayer>("* AI *");
}
}
void SetupForPractice()
{
// randomly select a position for the local player
m_localSlot = Random.Range(0,m_playerAreas.Length-1);
for (int i=0; i < m_playerAreas.Length; i++)
{
if (i == m_localSlot)
{
m_playerAreas[i].SetupForPlayer<LocalPlayer>(PlatformManager.MyOculusID);
}
else
{
m_playerAreas[i].SetupForPlayer<AIPlayer>("* AI *");
}
}
}
Player MatchPlayerAddedCallback(int slot, User user)
{
Player player = null;
if (m_currentState == State.WAITING_TO_SETUP_MATCH && slot < m_playerAreas.Length)
{
if (user.ID == PlatformManager.MyID)
{
var localPlayer = m_playerAreas[slot].SetupForPlayer<LocalPlayer>(user.OculusID);
MoveCameraToMatchPosition();
player = localPlayer;
m_localSlot = slot;
}
else
{
var remotePlayer = m_playerAreas[slot].SetupForPlayer<RemotePlayer>(user.OculusID);
remotePlayer.User = user;
player = remotePlayer;
}
}
return player;
}
#endregion
#region Main Camera Movement
void MoveCameraToIdlePosition()
{
var ejector = m_camera.gameObject.GetComponentInChildren<BallEjector>();
if (ejector)
{
ejector.transform.SetParent(m_camera.transform.parent, false);
m_camera.transform.SetParent(m_idleCameraTransform, false);
}
}
void MoveCameraToMatchPosition()
{
foreach (var playerArea in m_playerAreas)
{
var player = playerArea.GetComponentInChildren<LocalPlayer>();
if (player)
{
var ejector = player.GetComponentInChildren<BallEjector>();
m_camera.transform.SetParent(player.transform, false);
ejector.transform.SetParent(m_camera.transform, false);
break;
}
}
DisplayAchievementFlytext();
}
#endregion
#region Match Initiation
public void StartPracticeMatch()
{
if (m_currentState == State.NONE)
{
TransitionToState(State.WAITING_TO_START_PRACTICE);
}
}
public void PlayOnlineOrCancel()
{
Debug.Log ("Play online or Cancel");
if (m_currentState == State.NONE)
{
PlatformManager.Matchmaking.QueueForMatch();
TransitionToState (State.WAITING_FOR_MATCH);
}
else if (m_currentState == State.WAITING_FOR_MATCH)
{
PlatformManager.Matchmaking.LeaveQueue();
TransitionToState (State.NONE);
}
}
// notification from the Matchmaking service if we succeeded in finding an online match
void OnMatchFoundCallback(bool success)
{
if (success)
{
TransitionToState(State.WAITING_TO_SETUP_MATCH);
}
else
{
TransitionToState(State.NONE);
}
}
// handle an offer from a remote player for a new match start time
float StartTimeOfferCallback(float remoteTime)
{
if (m_currentState == State.WAITING_TO_SETUP_MATCH)
{
// if the remote start time is later use that, as long as it's not horribly wrong
if (remoteTime > MatchStartTime && (remoteTime - 60) < MatchStartTime)
{
Debug.Log("Moving Start time by " + (remoteTime - MatchStartTime));
MatchStartTime = remoteTime;
}
}
return MatchStartTime;
}
#endregion
#region Leaderboards and Achievements
void MostWinsLeaderboardCallback(SortedDictionary<int, LeaderboardEntry> entries)
{
foreach (Transform entry in m_mostWinsLeaderboard.transform)
{
Destroy(entry.gameObject);
}
foreach (var entry in entries.Values)
{
GameObject label = Instantiate(m_leaderboardEntryPrefab);
label.transform.SetParent(m_mostWinsLeaderboard.transform, false);
label.GetComponent<Text>().text =
string.Format("{0} - {1} - {2}", entry.Rank, entry.User.OculusID, entry.Score);
}
}
void HighestScoreLeaderboardCallback(SortedDictionary<int, LeaderboardEntry> entries)
{
foreach (Transform entry in m_highestScoresLeaderboard.transform)
{
Destroy(entry.gameObject);
}
foreach (var entry in entries.Values)
{
GameObject label = Instantiate(m_leaderboardEntryPrefab);
label.transform.SetParent(m_highestScoresLeaderboard.transform, false);
label.GetComponent<Text>().text =
string.Format("{0} - {1} - {2}", entry.Rank, entry.User.OculusID, entry.Score);
}
}
void CalculateMatchResults()
{
LocalPlayer localPlayer = null;
RemotePlayer remotePlayer = null;
foreach (var court in m_playerAreas)
{
if (court.Player is LocalPlayer)
{
localPlayer = court.Player as LocalPlayer;
}
else if (court.Player is RemotePlayer &&
(remotePlayer == null || court.Player.Score > remotePlayer.Score))
{
remotePlayer = court.Player as RemotePlayer;
}
}
// ignore the match results if the player got into a session without an opponent
if (!localPlayer || !remotePlayer)
{
return;
}
bool wonMatch = localPlayer.Score > remotePlayer.Score;
PlatformManager.Leaderboards.SubmitMatchScores(wonMatch, localPlayer.Score);
if (wonMatch)
{
PlatformManager.Achievements.RecordWinForLocalUser();
}
}
void DisplayAchievementFlytext()
{
if (PlatformManager.Achievements.LikesToWin)
{
GameObject go = Instantiate(m_flytext);
go.GetComponent<Text>().text = "Likes to Win!";
go.transform.position = Vector3.up * 40;
go.transform.SetParent(m_playerAreas[m_localSlot].NameText.transform, false);
}
}
#endregion
}
}