forked from cgvr/DeltaVR
Initial Commit
This commit is contained in:
213
Assets/Oculus/Platform/Samples/VrVoiceChat/Scripts/P2PManager.cs
Normal file
213
Assets/Oculus/Platform/Samples/VrVoiceChat/Scripts/P2PManager.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
namespace Oculus.Platform.Samples.VrVoiceChat
|
||||
{
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Oculus.Platform;
|
||||
using Oculus.Platform.Models;
|
||||
|
||||
// Helper class to manage a Peer-to-Peer connection to the other user.
|
||||
// The connection is used to send and received the Transforms for the
|
||||
// Avatars. The Transforms are sent via unreliable UDP at a fixed
|
||||
// frequency.
|
||||
public class P2PManager
|
||||
{
|
||||
// number of seconds to delay between transform updates
|
||||
private static readonly float UPDATE_DELAY = 0.1f;
|
||||
|
||||
// the ID of the remote player we expect to be connected to
|
||||
private ulong m_remoteID;
|
||||
|
||||
// the result of the last connection state update message
|
||||
private PeerConnectionState m_state = PeerConnectionState.Unknown;
|
||||
|
||||
// the next time to send an updated transform to the remote User
|
||||
private float m_timeForNextUpdate;
|
||||
|
||||
// the size of the packet we are sending and receiving
|
||||
private static readonly byte PACKET_SIZE = 29;
|
||||
|
||||
// packet format type just in case we want to add new future packet types
|
||||
private static readonly byte PACKET_FORMAT = 0;
|
||||
|
||||
// reusable buffer to serialize the Transform into
|
||||
private readonly byte[] sendTransformBuffer = new byte[PACKET_SIZE];
|
||||
|
||||
// reusable buffer to deserialize the Transform into
|
||||
private readonly byte[] receiveTransformBuffer = new byte[PACKET_SIZE];
|
||||
|
||||
// the last received position update
|
||||
private Vector3 receivedPosition;
|
||||
|
||||
// the previous received position to interpolate from
|
||||
private Vector3 receivedPositionPrior;
|
||||
|
||||
// the last received rotation update
|
||||
private Quaternion receivedRotation;
|
||||
|
||||
// the previous received rotation to interpolate from
|
||||
private Quaternion receivedRotationPrior;
|
||||
|
||||
// when the last transform was received
|
||||
private float receivedTime;
|
||||
|
||||
public P2PManager(Transform initialHeadTransform)
|
||||
{
|
||||
receivedPositionPrior = receivedPosition = initialHeadTransform.localPosition;
|
||||
receivedRotationPrior = receivedRotation = initialHeadTransform.localRotation;
|
||||
|
||||
Net.SetPeerConnectRequestCallback(PeerConnectRequestCallback);
|
||||
Net.SetConnectionStateChangedCallback(ConnectionStateChangedCallback);
|
||||
}
|
||||
|
||||
#region Connection Management
|
||||
|
||||
public void ConnectTo(ulong userID)
|
||||
{
|
||||
m_remoteID = userID;
|
||||
|
||||
// ID comparison is used to decide who calls Connect and who calls Accept
|
||||
if (PlatformManager.MyID < userID)
|
||||
{
|
||||
Net.Connect(userID);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (m_remoteID != 0)
|
||||
{
|
||||
Net.Close(m_remoteID);
|
||||
m_remoteID = 0;
|
||||
m_state = PeerConnectionState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Connected
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_state == PeerConnectionState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerConnectRequestCallback(Message<NetworkingPeer> msg)
|
||||
{
|
||||
Debug.LogFormat("Connection request from {0}, authorized is {1}", msg.Data.ID, m_remoteID);
|
||||
|
||||
if (msg.Data.ID == m_remoteID)
|
||||
{
|
||||
Net.Accept(msg.Data.ID);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionStateChangedCallback(Message<NetworkingPeer> msg)
|
||||
{
|
||||
Debug.LogFormat("Connection state to {0} changed to {1}", msg.Data.ID, msg.Data.State);
|
||||
|
||||
if (msg.Data.ID == m_remoteID)
|
||||
{
|
||||
m_state = msg.Data.State;
|
||||
|
||||
if (m_state == PeerConnectionState.Timeout &&
|
||||
// ID comparison is used to decide who calls Connect and who calls Accept
|
||||
PlatformManager.MyID < m_remoteID)
|
||||
{
|
||||
// keep trying until hangup!
|
||||
Net.Connect(m_remoteID);
|
||||
}
|
||||
}
|
||||
|
||||
PlatformManager.SetBackgroundColorForState();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Send Update
|
||||
|
||||
public bool ShouldSendHeadUpdate
|
||||
{
|
||||
get
|
||||
{
|
||||
return Time.time >= m_timeForNextUpdate && m_state == PeerConnectionState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendHeadTransform(Transform headTransform)
|
||||
{
|
||||
m_timeForNextUpdate = Time.time + UPDATE_DELAY;
|
||||
|
||||
sendTransformBuffer[0] = PACKET_FORMAT;
|
||||
int offset = 1;
|
||||
|
||||
PackFloat(headTransform.localPosition.x, sendTransformBuffer, ref offset);
|
||||
PackFloat(headTransform.localPosition.y, sendTransformBuffer, ref offset);
|
||||
PackFloat(headTransform.localPosition.z, sendTransformBuffer, ref offset);
|
||||
PackFloat(headTransform.localRotation.x, sendTransformBuffer, ref offset);
|
||||
PackFloat(headTransform.localRotation.y, sendTransformBuffer, ref offset);
|
||||
PackFloat(headTransform.localRotation.z, sendTransformBuffer, ref offset);
|
||||
PackFloat(headTransform.localRotation.w, sendTransformBuffer, ref offset);
|
||||
|
||||
Net.SendPacket(m_remoteID, sendTransformBuffer, SendPolicy.Unreliable);
|
||||
}
|
||||
|
||||
void PackFloat(float f, byte[] buf, ref int offset)
|
||||
{
|
||||
Buffer.BlockCopy(BitConverter.GetBytes(f), 0, buf, offset, 4);
|
||||
offset = offset + 4;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Receive Update
|
||||
|
||||
public void GetRemoteHeadTransform(Transform headTransform)
|
||||
{
|
||||
bool hasNewTransform = false;
|
||||
|
||||
Packet packet;
|
||||
while ((packet = Net.ReadPacket()) != null)
|
||||
{
|
||||
if (packet.Size != PACKET_SIZE)
|
||||
{
|
||||
Debug.Log("Invalid packet size: " + packet.Size);
|
||||
continue;
|
||||
}
|
||||
|
||||
packet.ReadBytes(receiveTransformBuffer);
|
||||
|
||||
if (receiveTransformBuffer[0] != PACKET_FORMAT)
|
||||
{
|
||||
Debug.Log("Invalid packet type: " + packet.Size);
|
||||
continue;
|
||||
}
|
||||
hasNewTransform = true;
|
||||
}
|
||||
|
||||
if (hasNewTransform)
|
||||
{
|
||||
receivedPositionPrior = receivedPosition;
|
||||
receivedPosition.x = BitConverter.ToSingle(receiveTransformBuffer, 1);
|
||||
receivedPosition.y = BitConverter.ToSingle(receiveTransformBuffer, 5);
|
||||
receivedPosition.z = BitConverter.ToSingle(receiveTransformBuffer, 9);
|
||||
|
||||
receivedRotationPrior = receivedRotation;
|
||||
receivedRotation.x = BitConverter.ToSingle(receiveTransformBuffer, 13);
|
||||
receivedRotation.y = BitConverter.ToSingle(receiveTransformBuffer, 17) * -1.0f;
|
||||
receivedRotation.z = BitConverter.ToSingle(receiveTransformBuffer, 21);
|
||||
receivedRotation.w = BitConverter.ToSingle(receiveTransformBuffer, 25) * -1.0f;
|
||||
|
||||
receivedTime = Time.time;
|
||||
}
|
||||
|
||||
// since we're receiving updates at a slower rate than we render,
|
||||
// interpolate to make the motion look smoother
|
||||
float completed = Math.Min(Time.time - receivedTime, UPDATE_DELAY) / UPDATE_DELAY;
|
||||
headTransform.localPosition =
|
||||
Vector3.Lerp(receivedPositionPrior, receivedPosition, completed);
|
||||
headTransform.localRotation =
|
||||
Quaternion.Slerp(receivedRotationPrior, receivedRotation, completed);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0b2606dc17092048b00d7388d73fbb5
|
||||
timeCreated: 1474493601
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,307 @@
|
||||
namespace Oculus.Platform.Samples.VrVoiceChat
|
||||
{
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections.Generic;
|
||||
using Oculus.Platform;
|
||||
using Oculus.Platform.Models;
|
||||
|
||||
// This class coordinates communication with the Oculus Platform
|
||||
// Service running in your device.
|
||||
public class PlatformManager : MonoBehaviour
|
||||
{
|
||||
// the game object to build the invite list in
|
||||
[SerializeField] private GameObject m_invitesList = null;
|
||||
|
||||
// Button to create for the user to answer an invite call
|
||||
[SerializeField] private GameObject m_invitePrefab = null;
|
||||
|
||||
// State transition sets the background color as a visual status indication
|
||||
[SerializeField] private Camera m_camera = null;
|
||||
|
||||
// GameObject that represents the Head of the remote Avatar
|
||||
[SerializeField] private GameObject m_remoteHead = null;
|
||||
|
||||
private State m_currentState;
|
||||
|
||||
private static PlatformManager s_instance = null;
|
||||
private RoomManager m_roomManager;
|
||||
private P2PManager m_p2pManager;
|
||||
private VoipManager m_voipManager;
|
||||
|
||||
// my Application-scoped Oculus ID
|
||||
private ulong m_myID;
|
||||
|
||||
// my Oculus user name
|
||||
private string m_myOculusID;
|
||||
|
||||
void Update()
|
||||
{
|
||||
// occasionally poll for new call invites
|
||||
if (m_roomManager.ShouldPollInviteList)
|
||||
{
|
||||
m_roomManager.UpdateActiveInvitesList();
|
||||
}
|
||||
|
||||
// occasionally send my transform to my interlocutor
|
||||
if (m_p2pManager.ShouldSendHeadUpdate)
|
||||
{
|
||||
m_p2pManager.SendHeadTransform(m_camera.transform);
|
||||
}
|
||||
|
||||
// estimate the remote avatar transforms from the most recent network update
|
||||
m_p2pManager.GetRemoteHeadTransform(m_remoteHead.transform);
|
||||
}
|
||||
|
||||
#region Initialization and Shutdown
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// make sure only one instance of this manager ever exists
|
||||
if (s_instance != null) {
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
s_instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
TransitionToState(State.INITIALIZING);
|
||||
|
||||
Core.Initialize();
|
||||
|
||||
m_roomManager = new RoomManager();
|
||||
m_p2pManager = new P2PManager(m_remoteHead.transform);
|
||||
m_voipManager = new VoipManager(m_remoteHead);
|
||||
}
|
||||
|
||||
void Start ()
|
||||
{
|
||||
// First thing we should do is perform an entitlement check to make sure
|
||||
// we successfully connected to the Oculus Platform Service.
|
||||
Entitlements.IsUserEntitledToApplication().OnComplete(IsEntitledCallback);
|
||||
}
|
||||
|
||||
void IsEntitledCallback(Message msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Next get the identity of the user that launched the Application.
|
||||
Users.GetLoggedInUser().OnComplete(GetLoggedInUserCallback);
|
||||
}
|
||||
|
||||
void GetLoggedInUserCallback(Message<User> msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
m_myID = msg.Data.ID;
|
||||
m_myOculusID = msg.Data.OculusID;
|
||||
|
||||
TransitionToState(State.WAITING_TO_CALL_OR_ANSWER);
|
||||
|
||||
// If the user launched the app by accepting the notification, then we want to
|
||||
// join that room. Otherwise, start polling for invites.
|
||||
m_roomManager.CheckForLaunchInvite();
|
||||
}
|
||||
|
||||
void OnApplicationQuit()
|
||||
{
|
||||
m_roomManager.LeaveCurrentRoom();
|
||||
m_p2pManager.Disconnect();
|
||||
m_voipManager.Disconnect();
|
||||
}
|
||||
|
||||
// For most errors we terminate the Application since this example doesn't make
|
||||
// sense if the user is disconnected.
|
||||
public static void TerminateWithError(Message msg)
|
||||
{
|
||||
Debug.Log("Error: " + msg.GetError().Message);
|
||||
UnityEngine.Application.Quit();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public static State CurrentState
|
||||
{
|
||||
get
|
||||
{
|
||||
return s_instance.m_currentState;
|
||||
}
|
||||
}
|
||||
|
||||
public static ulong MyID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance != null)
|
||||
{
|
||||
return s_instance.m_myID;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string MyOculusID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance != null && s_instance.m_myOculusID != null)
|
||||
{
|
||||
return s_instance.m_myOculusID;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Button Clicks
|
||||
|
||||
public void CallFriendOnClick()
|
||||
{
|
||||
if (CurrentState == State.WAITING_TO_CALL_OR_ANSWER)
|
||||
{
|
||||
m_roomManager.CreateRoomAndLaunchInviteMenu();
|
||||
}
|
||||
}
|
||||
|
||||
public void HangupOnClick()
|
||||
{
|
||||
m_roomManager.LeaveCurrentRoom();
|
||||
}
|
||||
|
||||
public void QuitOnClick()
|
||||
{
|
||||
UnityEngine.Application.Quit();
|
||||
}
|
||||
|
||||
public static void AnswerCallOnClick(ulong roomID)
|
||||
{
|
||||
if (s_instance)
|
||||
{
|
||||
s_instance.m_roomManager.JoinExistingRoom(roomID);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region State Management
|
||||
|
||||
public enum State
|
||||
{
|
||||
// loading platform library, checking application entitlement,
|
||||
// getting the local user info
|
||||
INITIALIZING,
|
||||
|
||||
// waiting on the user to invite a friend to chat, or
|
||||
// accept an invite sent to them
|
||||
WAITING_TO_CALL_OR_ANSWER,
|
||||
|
||||
// in this state we've create a room, and hopefully
|
||||
// sent some invites, and we're waiting for a response
|
||||
WAITING_FOR_ANSWER,
|
||||
|
||||
// we're in a room as the caller or the callee
|
||||
CONNECTED_IN_A_ROOM,
|
||||
|
||||
// shutdown any connections and leave the current room
|
||||
HANGUP,
|
||||
};
|
||||
|
||||
public static void TransitionToState(State newState)
|
||||
{
|
||||
Debug.LogFormat("State {0} -> {1}", s_instance.m_currentState, newState);
|
||||
|
||||
if (s_instance && s_instance.m_currentState != newState)
|
||||
{
|
||||
s_instance.m_currentState = newState;
|
||||
|
||||
// state transition logic
|
||||
switch (newState)
|
||||
{
|
||||
case State.HANGUP:
|
||||
s_instance.m_roomManager.LeaveCurrentRoom();
|
||||
s_instance.m_p2pManager.Disconnect();
|
||||
s_instance.m_voipManager.Disconnect();
|
||||
break;
|
||||
|
||||
case State.WAITING_TO_CALL_OR_ANSWER:
|
||||
break;
|
||||
|
||||
case State.CONNECTED_IN_A_ROOM:
|
||||
s_instance.m_p2pManager.ConnectTo(s_instance.m_roomManager.RemoteUserID);
|
||||
s_instance.m_voipManager.ConnectTo(s_instance.m_roomManager.RemoteUserID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set the background color as a visual aid to the connection status
|
||||
SetBackgroundColorForState();
|
||||
}
|
||||
|
||||
public static void SetBackgroundColorForState()
|
||||
{
|
||||
switch (s_instance.m_currentState)
|
||||
{
|
||||
case State.INITIALIZING:
|
||||
case State.HANGUP:
|
||||
s_instance.m_camera.backgroundColor = Color.black;
|
||||
break;
|
||||
|
||||
case State.WAITING_TO_CALL_OR_ANSWER:
|
||||
s_instance.m_camera.backgroundColor = new Color(0f, 0f, .3f);
|
||||
break;
|
||||
|
||||
case State.WAITING_FOR_ANSWER:
|
||||
s_instance.m_camera.backgroundColor = new Color(0, 0, .6f);
|
||||
break;
|
||||
|
||||
case State.CONNECTED_IN_A_ROOM:
|
||||
float red = s_instance.m_p2pManager.Connected ? 1.0f : 0;
|
||||
float green = s_instance.m_voipManager.Connected ? 1.0f : 0;
|
||||
s_instance.m_camera.backgroundColor = new Color(red, green, 1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetActiveInvites(List<RoomManager.Invite> invites)
|
||||
{
|
||||
if (s_instance && s_instance.m_invitesList && s_instance.m_invitePrefab)
|
||||
{
|
||||
// first remove all existing Invites
|
||||
foreach (Transform child in s_instance.m_invitesList.transform)
|
||||
{
|
||||
Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
foreach (var invite in invites)
|
||||
{
|
||||
GameObject button = Instantiate(s_instance.m_invitePrefab) as GameObject;
|
||||
button.GetComponentInChildren<Text>().text = invite.OwnerID;
|
||||
button.name = invite.RoomID.ToString();
|
||||
button.GetComponent<Button>().onClick.AddListener(
|
||||
() => PlatformManager.AnswerCallOnClick(invite.RoomID));
|
||||
button.transform.SetParent(s_instance.m_invitesList.transform, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffdbd4c8c89d94aae95321b8d5850c7a
|
||||
timeCreated: 1474236709
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,316 @@
|
||||
namespace Oculus.Platform.Samples.VrVoiceChat
|
||||
{
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Oculus.Platform;
|
||||
using Oculus.Platform.Models;
|
||||
|
||||
// Helper class to manage Room creation, membership and invites.
|
||||
// Rooms are a mechanism to help Oculus users create a shared experience.
|
||||
// Users can only be in one Room at a time. If the Owner of a room
|
||||
// leaves, then ownership is transferred to some other member.
|
||||
// Here we use rooms to create the notion of a 'call' to help us
|
||||
// invite a Friend and establish a VOIP and P2P connection.
|
||||
public class RoomManager
|
||||
{
|
||||
// the ID of the Room that I'm in
|
||||
private ulong m_roomID;
|
||||
|
||||
// the other User in the Room
|
||||
private User m_remoteUser;
|
||||
|
||||
// how often I should poll for invites
|
||||
private static readonly float INVITE_POLL_FREQ_SECONDS = 5.0f;
|
||||
|
||||
// the next time I should poll Oculus Platform for valid Room Invite requests
|
||||
private float m_nextPollTime;
|
||||
|
||||
public struct Invite
|
||||
{
|
||||
public readonly ulong RoomID;
|
||||
public readonly string OwnerID;
|
||||
|
||||
public Invite(ulong roomID, string owner)
|
||||
{
|
||||
this.RoomID = roomID;
|
||||
this.OwnerID = owner;
|
||||
}
|
||||
}
|
||||
|
||||
// cached list of rooms that I've been invited to and I'm waiting
|
||||
// for more information about
|
||||
private HashSet<ulong> m_pendingRoomRequests;
|
||||
|
||||
// accumulation list of room invites and the room owner
|
||||
private List<Invite> m_invites;
|
||||
|
||||
public RoomManager()
|
||||
{
|
||||
Rooms.SetRoomInviteAcceptedNotificationCallback(LaunchedFromAcceptingInviteCallback);
|
||||
Rooms.SetUpdateNotificationCallback(RoomUpdateCallback);
|
||||
}
|
||||
|
||||
public ulong RemoteUserID
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_remoteUser != null ? m_remoteUser.ID : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public String RemoteOculusID
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_remoteUser != null ? m_remoteUser.OculusID : String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#region Launched Application from Accepting Invite
|
||||
|
||||
// Callback to check whether the User accepted the invite as
|
||||
// a notification which caused the Application to launch. If so, then
|
||||
// we know we need to try to join that room.
|
||||
void LaunchedFromAcceptingInviteCallback(Message<string> msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
PlatformManager.TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("Launched Invite to join Room: " + msg.Data);
|
||||
|
||||
m_roomID = Convert.ToUInt64(msg.GetString());
|
||||
}
|
||||
|
||||
// Check to see if the App was launched by accepting the Notication from the main Oculus app.
|
||||
// If so, we can directly join that room. (If it's still available.)
|
||||
public bool CheckForLaunchInvite()
|
||||
{
|
||||
if (m_roomID != 0)
|
||||
{
|
||||
JoinExistingRoom(m_roomID);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Create a Room and Invite Friend(s) from the Oculus Universal Menu
|
||||
|
||||
public void CreateRoomAndLaunchInviteMenu()
|
||||
{
|
||||
Rooms.CreateAndJoinPrivate(RoomJoinPolicy.InvitedUsers, 2, true)
|
||||
.OnComplete(CreateAndJoinPrivateRoomCallback);
|
||||
}
|
||||
|
||||
void CreateAndJoinPrivateRoomCallback(Message<Room> msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
PlatformManager.TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
m_roomID = msg.Data.ID;
|
||||
m_remoteUser = null;
|
||||
PlatformManager.TransitionToState(PlatformManager.State.WAITING_FOR_ANSWER);
|
||||
|
||||
// launch the Room Invite workflow in the Oculus Univeral Menu
|
||||
Rooms.LaunchInvitableUserFlow(m_roomID).OnComplete(OnLaunchInviteWorkflowComplete);
|
||||
}
|
||||
|
||||
void OnLaunchInviteWorkflowComplete(Message msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
PlatformManager.TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Polling for Invites
|
||||
|
||||
public bool ShouldPollInviteList
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_pendingRoomRequests == null && Time.time >= m_nextPollTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateActiveInvitesList()
|
||||
{
|
||||
m_nextPollTime = Time.time + INVITE_POLL_FREQ_SECONDS;
|
||||
m_pendingRoomRequests = new HashSet<ulong>();
|
||||
m_invites = new List<Invite>();
|
||||
Notifications.GetRoomInviteNotifications().OnComplete(GetRoomInviteNotificationsCallback);
|
||||
}
|
||||
|
||||
// task 13572454: add the type to callback definition
|
||||
void GetRoomInviteNotificationsCallback(Message msg_untyped)
|
||||
{
|
||||
Message<RoomInviteNotificationList> msg = (Message<RoomInviteNotificationList>)msg_untyped;
|
||||
|
||||
if (msg.IsError)
|
||||
{
|
||||
PlatformManager.TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// loop over all the rooms we're invited to and request more info
|
||||
foreach (RoomInviteNotification invite in msg.Data)
|
||||
{
|
||||
m_pendingRoomRequests.Add(invite.RoomID);
|
||||
Rooms.Get(invite.RoomID).OnComplete(GetRoomInfoCallback);
|
||||
}
|
||||
|
||||
if (msg.Data.Count == 0)
|
||||
{
|
||||
m_pendingRoomRequests = null;
|
||||
PlatformManager.SetActiveInvites(m_invites);
|
||||
}
|
||||
}
|
||||
|
||||
void GetRoomInfoCallback(Message<Room> msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
PlatformManager.TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.Data.OwnerOptional != null)
|
||||
{
|
||||
Invite invite = new Invite(msg.Data.ID, msg.Data.OwnerOptional.OculusID);
|
||||
m_pendingRoomRequests.Remove(invite.RoomID);
|
||||
|
||||
// make sure the room still looks usable
|
||||
// (e.g. they aren't currently talking to someone)
|
||||
if (msg.Data.UsersOptional != null && msg.Data.UsersOptional.Count == 1)
|
||||
{
|
||||
m_invites.Add(invite);
|
||||
}
|
||||
}
|
||||
|
||||
// once we're received all the room info, let the platform update
|
||||
// its display
|
||||
if (m_pendingRoomRequests.Count == 0)
|
||||
{
|
||||
m_pendingRoomRequests = null;
|
||||
PlatformManager.SetActiveInvites(m_invites);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accept Invite
|
||||
|
||||
public void JoinExistingRoom(ulong roomID)
|
||||
{
|
||||
Rooms.Join(roomID, true).OnComplete(JoinRoomCallback);
|
||||
}
|
||||
|
||||
void JoinRoomCallback(Message<Room> msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
// is reasonable if caller called more than 1 person, and I didn't answer first
|
||||
return;
|
||||
}
|
||||
|
||||
string oculusOwnerID = msg.Data.OwnerOptional != null ? msg.Data.OwnerOptional.OculusID : "";
|
||||
int numUsers = msg.Data.UsersOptional != null ? msg.Data.UsersOptional.Count : 0;
|
||||
|
||||
Debug.LogFormat("Joined room: {0} owner: {1} count: ",
|
||||
msg.Data.ID, oculusOwnerID, numUsers);
|
||||
|
||||
m_roomID = msg.Data.ID;
|
||||
|
||||
// if the caller left while I was in the process of joining, just hangup
|
||||
if (msg.Data.UsersOptional == null || msg.Data.UsersOptional.Count != 2)
|
||||
{
|
||||
PlatformManager.TransitionToState(PlatformManager.State.HANGUP);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (User user in msg.Data.UsersOptional)
|
||||
{
|
||||
if (user.ID != PlatformManager.MyID)
|
||||
{
|
||||
m_remoteUser = user;
|
||||
}
|
||||
}
|
||||
|
||||
PlatformManager.TransitionToState(PlatformManager.State.CONNECTED_IN_A_ROOM);
|
||||
}
|
||||
|
||||
// update the invite list sooner
|
||||
m_nextPollTime = Time.time;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Room Updates
|
||||
|
||||
void RoomUpdateCallback(Message<Room> msg)
|
||||
{
|
||||
if (msg.IsError)
|
||||
{
|
||||
PlatformManager.TerminateWithError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
string oculusOwnerID = msg.Data.OwnerOptional != null ? msg.Data.OwnerOptional.OculusID : "";
|
||||
int numUsers = msg.Data.UsersOptional != null ? msg.Data.UsersOptional.Count : 0;
|
||||
|
||||
Debug.LogFormat("Room {0} Update: {1} owner: {2} count: ",
|
||||
msg.Data.ID, oculusOwnerID, numUsers);
|
||||
|
||||
// if the Room count is not 2 then the other party has left.
|
||||
// We'll just hangup the connection here.
|
||||
// If the other User created then room, ownership would switch to me.
|
||||
if (msg.Data.UsersOptional == null || msg.Data.UsersOptional.Count != 2)
|
||||
{
|
||||
PlatformManager.TransitionToState(PlatformManager.State.HANGUP);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (User user in msg.Data.UsersOptional)
|
||||
{
|
||||
if (user.ID != PlatformManager.MyID)
|
||||
{
|
||||
m_remoteUser = user;
|
||||
}
|
||||
}
|
||||
|
||||
PlatformManager.TransitionToState(PlatformManager.State.CONNECTED_IN_A_ROOM);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Room Exit
|
||||
|
||||
public void LeaveCurrentRoom()
|
||||
{
|
||||
if (m_roomID != 0)
|
||||
{
|
||||
Rooms.Leave(m_roomID);
|
||||
m_roomID = 0;
|
||||
m_remoteUser = null;
|
||||
}
|
||||
PlatformManager.TransitionToState(PlatformManager.State.WAITING_TO_CALL_OR_ANSWER);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea89826bdbe52462fb0102692abb3032
|
||||
timeCreated: 1474235953
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace Oculus.Platform.Samples.VrVoiceChat
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// Helper class to attach to the main camera that raycasts where the
|
||||
// user is looking to select/deselect Buttons.
|
||||
public class VREyeRaycaster : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private UnityEngine.EventSystems.EventSystem m_eventSystem = null;
|
||||
|
||||
private Button m_currentButton;
|
||||
|
||||
void Update ()
|
||||
{
|
||||
RaycastHit hit;
|
||||
Button button = null;
|
||||
|
||||
// do a forward raycast to see if we hit a Button
|
||||
if (Physics.Raycast(transform.position, transform.forward, out hit, 50f))
|
||||
{
|
||||
button = hit.collider.GetComponent<Button>();
|
||||
}
|
||||
|
||||
if (button != null)
|
||||
{
|
||||
if (m_currentButton != button)
|
||||
{
|
||||
m_currentButton = button;
|
||||
m_currentButton.Select();
|
||||
}
|
||||
}
|
||||
else if (m_currentButton != null)
|
||||
{
|
||||
m_currentButton = null;
|
||||
if (m_eventSystem != null)
|
||||
{
|
||||
m_eventSystem.SetSelectedGameObject(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a66c6bd0ba312c4b8c6fd2a08491521
|
||||
timeCreated: 1473882274
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
namespace Oculus.Platform.Samples.VrVoiceChat
|
||||
{
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
using Oculus.Platform;
|
||||
using Oculus.Platform.Models;
|
||||
|
||||
// Helper class to manage the Voice-over-IP connection to the
|
||||
// remote user
|
||||
public class VoipManager
|
||||
{
|
||||
// the ID of the remote user I expect to talk to
|
||||
private ulong m_remoteID;
|
||||
|
||||
// the last reported state of the VOIP connection
|
||||
private PeerConnectionState m_state = PeerConnectionState.Unknown;
|
||||
|
||||
// the GameObject where the remote VOIP will project from
|
||||
private readonly GameObject m_remoteHead;
|
||||
|
||||
public VoipManager(GameObject remoteHead)
|
||||
{
|
||||
m_remoteHead = remoteHead;
|
||||
|
||||
Voip.SetVoipConnectRequestCallback(VoipConnectRequestCallback);
|
||||
Voip.SetVoipStateChangeCallback(VoipStateChangedCallback);
|
||||
}
|
||||
|
||||
public void ConnectTo(ulong userID)
|
||||
{
|
||||
m_remoteID = userID;
|
||||
var audioSource = m_remoteHead.AddComponent<VoipAudioSourceHiLevel>();
|
||||
audioSource.senderID = userID;
|
||||
|
||||
// ID comparison is used to decide who initiates and who gets the Callback
|
||||
if (PlatformManager.MyID < m_remoteID)
|
||||
{
|
||||
Voip.Start(userID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (m_remoteID != 0)
|
||||
{
|
||||
Voip.Stop(m_remoteID);
|
||||
Object.Destroy(m_remoteHead.GetComponent<VoipAudioSourceHiLevel>(), 0);
|
||||
m_remoteID = 0;
|
||||
m_state = PeerConnectionState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Connected
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_state == PeerConnectionState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
void VoipConnectRequestCallback(Message<NetworkingPeer> msg)
|
||||
{
|
||||
Debug.LogFormat("Voip request from {0}, authorized is {1}", msg.Data.ID, m_remoteID);
|
||||
|
||||
if (msg.Data.ID == m_remoteID)
|
||||
{
|
||||
Voip.Accept(msg.Data.ID);
|
||||
}
|
||||
}
|
||||
|
||||
void VoipStateChangedCallback(Message<NetworkingPeer> msg)
|
||||
{
|
||||
Debug.LogFormat("Voip state to {0} changed to {1}", msg.Data.ID, msg.Data.State);
|
||||
|
||||
if (msg.Data.ID == m_remoteID)
|
||||
{
|
||||
m_state = msg.Data.State;
|
||||
|
||||
if (m_state == PeerConnectionState.Timeout &&
|
||||
// ID comparison is used to decide who initiates and who gets the Callback
|
||||
PlatformManager.MyID < m_remoteID)
|
||||
{
|
||||
// keep trying until hangup!
|
||||
Voip.Start(m_remoteID);
|
||||
}
|
||||
}
|
||||
|
||||
PlatformManager.SetBackgroundColorForState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 310e9262e28564c9d9b5c5a872a42e41
|
||||
timeCreated: 1474561098
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user