1
0
forked from cgvr/DeltaVR

Initial Commit

This commit is contained in:
Toomas Tamm
2020-11-28 16:54:41 +02:00
parent 97292ee26e
commit ea967135f2
4217 changed files with 2945663 additions and 0 deletions

View File

@@ -0,0 +1 @@
SKU Name Description Currency Amount Item Type
1 SKU Name Description Currency Amount Item Type PowerballPack1 Purchase more Power Balls 1 USD 0.01 Consumable

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0b687b4832314b24db66e56bef4de820
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aa2e3f5a7dc94444f95f0774aa2bbd54
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,112 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 1000013725221134}
m_IsPrefabParent: 1
--- !u!1 &1000013725221134
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 4000011548025488}
- component: {fileID: 33000011356183730}
- component: {fileID: 65000012561974784}
- component: {fileID: 23000013314552442}
- component: {fileID: 114000011207431316}
m_Layer: 0
m_Name: PieceA
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4000011548025488
Transform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000013725221134}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.2, y: 0.2, z: 0.2}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!23 &23000013314552442
MeshRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000013725221134}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!33 &33000011356183730
MeshFilter:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000013725221134}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!65 &65000012561974784
BoxCollider:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000013725221134}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &114000011207431316
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000013725221134}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4c243c46c5f7948488696c53b4fa9786, type: 3}
m_Name:
m_EditorClassIdentifier:
m_type: 0
m_prefabA: {fileID: 1000013725221134}
m_prefabB: {fileID: 1000011610856386, guid: ff8c3ef67a52afc4a9cf330f025fdec3, type: 2}
m_prefabPower: {fileID: 1000010836483084, guid: 81a73acbe18cf784cb5184bd404c30bc,
type: 2}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 48e6308f93e461340abb975251ae824b
timeCreated: 1480479889
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,113 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 1000011610856386}
m_IsPrefabParent: 1
--- !u!1 &1000011610856386
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 4000013853064064}
- component: {fileID: 33000012696557308}
- component: {fileID: 136000012754303966}
- component: {fileID: 23000013106303838}
- component: {fileID: 114000011050251136}
m_Layer: 0
m_Name: PieceB
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4000013853064064
Transform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011610856386}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.2, y: 0.1, z: 0.2}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!23 &23000013106303838
MeshRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011610856386}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!33 &33000012696557308
MeshFilter:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011610856386}
m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
--- !u!114 &114000011050251136
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011610856386}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4c243c46c5f7948488696c53b4fa9786, type: 3}
m_Name:
m_EditorClassIdentifier:
m_type: 1
m_prefabA: {fileID: 1000013725221134, guid: 48e6308f93e461340abb975251ae824b, type: 2}
m_prefabB: {fileID: 1000011610856386}
m_prefabPower: {fileID: 1000010836483084, guid: 81a73acbe18cf784cb5184bd404c30bc,
type: 2}
--- !u!136 &136000012754303966
CapsuleCollider:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011610856386}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
m_Radius: 0.5
m_Height: 2
m_Direction: 1
m_Center: {x: 0, y: 0, z: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ff8c3ef67a52afc4a9cf330f025fdec3
timeCreated: 1480479894
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 1000010836483084}
m_IsPrefabParent: 1
--- !u!1 &1000010836483084
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 4000013490292564}
- component: {fileID: 33000012739098690}
- component: {fileID: 135000010301361876}
- component: {fileID: 23000010370066952}
- component: {fileID: 114000013731705678}
m_Layer: 0
m_Name: PowerBall
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4000013490292564
Transform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010836483084}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0.2, y: 0.2, z: 0.2}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!23 &23000010370066952
MeshRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010836483084}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_PreserveUVs: 1
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
--- !u!33 &33000012739098690
MeshFilter:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010836483084}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!114 &114000013731705678
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010836483084}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4c243c46c5f7948488696c53b4fa9786, type: 3}
m_Name:
m_EditorClassIdentifier:
m_type: 2
m_prefabA: {fileID: 1000013725221134, guid: 48e6308f93e461340abb975251ae824b, type: 2}
m_prefabB: {fileID: 1000011610856386, guid: ff8c3ef67a52afc4a9cf330f025fdec3, type: 2}
m_prefabPower: {fileID: 1000010836483084}
--- !u!135 &135000010301361876
SphereCollider:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010836483084}
m_Material: {fileID: 0}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 81a73acbe18cf784cb5184bd404c30bc
timeCreated: 1480479897
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
# Overview
This example demonstrates using the Oculus In-App-Purchase API and skill based matchmaking.
The setting is a simple boardgame (which you are encourage to chage to your creative idea!)
on a 3x3 grid with two pieces and one special 'power-piece' that can be purchased with
IAP through the Oculus Store. After an Online match is completed the ranking is sent to
the Matchmaking Service so that following match selections will take into account a user's
skill level.
# Application Setup
1. Open the Project in Unity 5.4.1p1 or later
2. Import the OculusPlatform Unity package
- Unity: Main Menu -> Assets -> Import Package -> Custom Package
- SDK Location: Unity/OculusPlatform.unitypackage
## Rift
1. Create your Rift application on the Oculus Developer Dashboard
2. Copy the Application ID into the Project (Main Menu -> Oculus Platform -> Edit Settings -> Oculus Rift App Id)
## GearVR
1. Create the GearVR application on the Oculus Developer Dashboard
2. Move the GearVR application into the Rift application's App Grouping
3. Copy the Application ID into the Project (Main Menu -> Oculus Platform -> Edit Settings -> Gear VR App Id)
4. Copy the OSIG files for the GearVR devices you are testing to Assets\Plugins\Android\Assets
# Configure Matchmaking
1. On the Oculus Dashboard, navigate to the Matchmaking section for your App Grouping
2. Change the option box from 'Pools' to 'Skill Pools'
3. Click Create Pool
4. Set the 'Skill Pool Key' to ''VR_BOARD_GAME''
5. Select ''Medium'' for the 'Luck Factor'
6. Enter ''0'' for the 'Draw Probability
7. Click 'Save & Deploy'
8. Change the option box 'Skill Pools' to 'Pools'
9. Click Create Pool
10. Set the 'Pool Key' to ''VR_BOARD_GAME_POOL''
11. Set the Mode to Quickmatch
12. Enter ''2'' for both the Min and Max Users
13. Select ''VR_BOARD_GAME'' for the 'Skill Pool'
14. Leave 'Advanced Quickmatch' set to ''No''
15. Leave 'Should Consider Ping Time?' at the default setting of ''No''
16. Click 'Save & Deploy'
# Configure IAP
1. On the Oculus Dashboard, make sure the Payment Info is setup for your Organization
2. Navigate to the IAP tab under your App Grouping
3. Select the Upload TSV button and choose the Oculus_IAP.tsv in the project root directory.
# Upload your builds
Build executables from Unity and upload them to your Application Dashboard
* Rift
1. Add the executable and data folder to a zip file
2. Upload the zip to the Alpha channel on your Dashboard
3. Set the executable name you chose in the zip file
4. Add Friends you are testing with as Subscribed Users for the Alpha channel
* GearVR
1. Create an android keystore (if you don't have one) so Unity can sign the build. (Player Settings -> Publishing Settings)
2. Upload the apk to the Alpha channel on your Dashboard
3. Each apk you upload needs a new build number (Player Settings -> Other Settings)
4. Add Friends you are testing with as Subscribed Users for the Alpha channel

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 0975c71f0ce832c489580f5059a6118a
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e865e3f9d93b4043bdf7306571e6e4c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using System.Collections;
// This behaviour is attached to GameObjects whose collision mesh
// describes a specific position on the GameBoard. The collision
// mesh doesn't need to fully cover the board position, but enough
// for eye raycasts to detect that the user is looking there.
public class BoardPosition : MonoBehaviour {
[SerializeField] [Range(0,2)] public int x = 0;
[SerializeField] [Range(0,2)] public int y = 0;
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d9244e220b0fee34c98de6ed84ee6cdd
timeCreated: 1480276073
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,99 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
// This is a helper class for selecting objects that the user is looking at.
// It will select UI objects that have an attach 3d collision volume and helps
// the GameController locate GamePieces and GamePositions.
public class EyeCamera : MonoBehaviour
{
// the EventSystem used by the UI elements
[SerializeField] private EventSystem m_eventSystem = null;
// the GameController to notify
[SerializeField] private GameController m_gameController = null;
// a tine ball in the distance to debug where the user is looking
[SerializeField] private SphereCollider m_gazeTracker = null;
// the current Button, if any, being looked at
private Button m_currentButton;
// the current GamePiece, if any, being looked at
private GamePiece m_currentPiece;
// the current BoardPosition, if any, being looked at
private BoardPosition m_boardPosition;
void Update()
{
RaycastHit hit;
Button button = null;
GamePiece piece = null;
BoardPosition pos = null;
// do a forward raycast to see if we hit a selectable object
bool hitSomething = Physics.Raycast(transform.position, transform.forward, out hit, 50f);
if (hitSomething) {
button = hit.collider.GetComponent<Button>();
piece = hit.collider.GetComponent<GamePiece>();
pos = hit.collider.GetComponent<BoardPosition>();
}
if (m_currentButton != button)
{
if (m_eventSystem != null)
{
m_eventSystem.SetSelectedGameObject(null);
}
m_currentButton = button;
if (m_currentButton != null)
{
m_currentButton.Select();
}
}
if (m_currentPiece != piece)
{
if (m_currentPiece != null)
{
m_gameController.StoppedLookingAtPiece();
}
m_currentPiece = piece;
if (m_currentPiece != null)
{
m_gameController.StartedLookingAtPiece(m_currentPiece);
}
}
if (m_boardPosition != pos)
{
m_boardPosition = pos;
if (m_boardPosition != null)
{
m_gameController.StartedLookingAtPosition(m_boardPosition);
}
}
// clear the potential move if they gaze off the board
if (hit.collider == m_gazeTracker)
{
m_gameController.ClearProposedMove();
}
// moves the camera with the mouse - very useful for debugging in to 2D mode.
if (Input.GetButton("Fire2"))
{
var v = Input.GetAxis("Mouse Y");
var h = Input.GetAxis("Mouse X");
transform.rotation *= Quaternion.AngleAxis(h, Vector3.up);
transform.rotation *= Quaternion.AngleAxis(-v, Vector3.right);
Vector3 eulers = transform.eulerAngles;
eulers.z = 0;
transform.eulerAngles = eulers;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 96a29a99957531246921ced0fac365ab
timeCreated: 1480201871
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,188 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using System;
using UnityEngine;
//
// This script describes the game board along with the game pieces that
// are in play. The rules for the game board are:
// 1) Player can place a normal GamePiece on any empty BoardPosition
// 2) Player can place a power GamePiece on top of a normal piece
// 3) The board is full when all positions have a normal piece
// Player score is calculated as:
// 1) +10 points for each normal piece on the board
// 2) +10 points for each normal piece with 1 square of one of their power pieces
// 3) -10 points for each opponent normal piece within 1 square of their power pieces
//
public class GameBoard : MonoBehaviour
{
public const int LENGTH_X = 3;
public const int LENGTH_Y = 3;
public const int MAX_PLAYERS = 2;
// the placed-piece color for each player
[SerializeField] private Color[] m_playerColors = new Color[MAX_PLAYERS];
// color for pice the player is considering moving to
[SerializeField] private Color m_proposedMoveColor = Color.white;
// the player scores that are recalcuated after a pice is placed
private int[] m_scores = new int[MAX_PLAYERS];
// GameObjects that define each of the allowed piece positions
[SerializeField] private BoardPosition[] m_positions = new BoardPosition[9];
private struct PositionInfo
{
public GameObject piece;
public int pieceOwner;
public int powerPieceOwner;
}
// pieces in play for the current game
private readonly PositionInfo[,] m_pieces = new PositionInfo[LENGTH_X, LENGTH_Y];
// removes all game pieces from the board
public void Reset()
{
for (int x = 0; x < LENGTH_X; x++)
{
for (int y = 0; y < LENGTH_Y; y++)
{
if (m_pieces[x,y].piece != null)
{
Destroy(m_pieces[x,y].piece);
m_pieces[x,y].piece = null;
m_pieces[x,y].pieceOwner = -1;
m_pieces[x,y].powerPieceOwner = -1;
}
}
}
}
#region Board status
// returns true if all the board positions have a piece in them
public bool IsFull()
{
for (int x = 0; x < LENGTH_X; x++)
{
for (int y = 0; y < LENGTH_Y; y++)
{
if (m_pieces[x,y].piece == null)
{
return false;
}
}
}
return true;
}
public bool CanPlayerMoveToPostion(int x, int y)
{
return m_pieces[x,y].piece == null;
}
public bool CanPlayerPowerUpPosition(int x, int y)
{
return m_pieces[x,y].piece != null;
}
#endregion
#region creating game pieces
public void AddPiece(int player, GameObject prefab, int x, int y)
{
var pos = m_positions[x * LENGTH_Y + y];
var piece = Create(prefab, pos.gameObject, pos, Vector3.zero);
piece.GetComponent<Renderer>().material.color = m_playerColors[player];
m_pieces[x,y].piece = piece.gameObject;
m_pieces[x,y].pieceOwner = player;
m_pieces[x,y].powerPieceOwner = -1;
UpdateScores();
}
public GamePiece AddProposedPiece(GameObject prefab, BoardPosition pos)
{
var piece = Create(prefab, pos.gameObject, pos, Vector3.zero);
piece.GetComponent<Renderer>().material.color = m_proposedMoveColor;
return piece;
}
public void AddPowerPiece(int player, GameObject prefab, int x, int y)
{
var piece = Create(prefab, m_pieces[x,y].piece, m_positions[x*LENGTH_Y+y], .2f*Vector3.up);
piece.GetComponent<Renderer>().material.color = m_playerColors[player];
m_pieces[x,y].powerPieceOwner = player;
UpdateScores();
}
public GamePiece AddProposedPowerPiece(GameObject prefab, BoardPosition pos)
{
var piece = Create(prefab, m_pieces[pos.x, pos.y].piece, pos, .2f*Vector3.up);
piece.GetComponent<Renderer>().material.color = m_proposedMoveColor;
return piece;
}
private GamePiece Create(GameObject prefab, GameObject parent, BoardPosition pos, Vector3 off)
{
var go = Instantiate(prefab, parent.transform) as GameObject;
go.transform.position = parent.transform.position + off;
go.GetComponent<GamePiece>().Position = pos;
return go.GetComponent<GamePiece>();
}
#endregion
#region scores
public int GetPlayerScore(int player)
{
return m_scores[player];
}
private void UpdateScores()
{
for (int i = 0; i < MAX_PLAYERS; i++)
{
m_scores[i] = 0;
}
for (int x = 0; x < LENGTH_X; x++)
{
for (int y = 0; y < LENGTH_Y; y++)
{
if (m_pieces[x,y].piece != null)
{
// for each piece on the board, the player gets 10 points
m_scores[m_pieces[x,y].pieceOwner] += 10;
// for each power piece, the player gains or loses 10 points
// based on the ownership of nearby pieces
if (m_pieces[x,y].powerPieceOwner >= 0)
{
for (int px = x-1; px <= x+1; px++)
{
for (int py = y-1; py <= y+1; py++)
{
if (px >= 0 && py >= 0 && px < LENGTH_X && py < LENGTH_Y)
{
var powerup =
m_pieces[x,y].pieceOwner == m_pieces[x,y].powerPieceOwner ?
+10 : -10;
m_scores[m_pieces[x, y].powerPieceOwner] += powerup;
}
}
}
}
}
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 94decd43dfca0db43a3936edb109ca2e
timeCreated: 1479840163
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,405 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using UnityEngine.UI;
// This is the primary class that implements the game logic.
public class GameController : MonoBehaviour
{
// instance of the object interfacing with the matchmaking service
[SerializeField] private MatchmakingManager m_matchmaking = null;
[SerializeField] private GameBoard m_board = null;
[SerializeField] private GamePiece m_pieceA = null;
[SerializeField] private GamePiece m_pieceB = null;
[SerializeField] private GamePiece m_powerPiece = null;
// colors for the various states of the selectable games pieces
[SerializeField] private Color m_unusableColor = Color.white;
[SerializeField] private Color m_unselectedColor = Color.white;
[SerializeField] private Color m_selectedColor = Color.white;
[SerializeField] private Color m_highlightedColor = Color.white;
[SerializeField] private Text m_ballCountText = null;
[SerializeField] private Text m_player0Text = null;
[SerializeField] private Text m_player1Text = null;
private enum GameState {
None,
PracticingMyTurn, PracticingAiTurn,
OnlineMatchMyTurn, OnlineMatchRemoteTurn
}
private GameState m_state;
// the game piece the player is currently looking at
private GamePiece m_interestedPiece;
// the piece the player selected with the Fire button
private GamePiece m_selectedPiece;
// the piece that would be placed if the player pressed the Fire button
private GamePiece m_proposedPiece;
// how many IAP power-balls the user has
private uint m_powerBallcount;
// the name of the current opponent
private string m_opponentName;
void Start()
{
TransitionToState(GameState.None);
UpdateScores();
}
void Update()
{
PerFrameStateUpdate();
}
#region Game State
private void TransitionToState(GameState state)
{
m_state = state;
UpdateGamePieceColors();
}
private void TransitionToNextState()
{
if (!m_board.IsFull())
{
switch (m_state)
{
case GameState.PracticingAiTurn:
TransitionToState(GameState.PracticingMyTurn);
break;
case GameState.PracticingMyTurn:
TransitionToState(GameState.PracticingAiTurn);
break;
case GameState.OnlineMatchRemoteTurn:
TransitionToState(GameState.OnlineMatchMyTurn);
break;
case GameState.OnlineMatchMyTurn:
TransitionToState(GameState.OnlineMatchRemoteTurn);
break;
}
}
else
{
switch (m_state)
{
case GameState.OnlineMatchRemoteTurn:
case GameState.OnlineMatchMyTurn:
m_matchmaking.EndMatch(m_board.GetPlayerScore(0), m_board.GetPlayerScore(1));
break;
}
TransitionToState(GameState.None);
}
}
private void PerFrameStateUpdate()
{
switch (m_state)
{
case GameState.PracticingAiTurn:
// don't move immediately to give the AI time to 'think'
if (Random.Range(1, 100) < 3)
{
MakeAIMove(1);
}
break;
case GameState.PracticingMyTurn:
case GameState.OnlineMatchMyTurn:
if (Input.GetButton("Fire1"))
{
TrySelectPiece();
TryPlacePiece();
}
break;
}
}
#endregion
#region Practicing with an AI Player
public void PracticeButtonPressed()
{
m_opponentName = "* AI *";
switch (m_state)
{
case GameState.OnlineMatchMyTurn:
case GameState.OnlineMatchRemoteTurn:
m_matchmaking.EndMatch(m_board.GetPlayerScore(0), m_board.GetPlayerScore(1));
break;
}
m_board.Reset();
// randomly decised whether the player or AI goes first
if (Random.Range(0, 2) == 1)
{
TransitionToState(GameState.PracticingMyTurn);
}
else
{
TransitionToState(GameState.PracticingAiTurn);
}
UpdateScores();
}
private void MakeAIMove(int player)
{
bool moved = false;
// pick a random search start position
int rx = Random.Range(0, GameBoard.LENGTH_X - 1);
int ry = Random.Range(0, GameBoard.LENGTH_Y - 1);
// from (rx,ry) search of an available move
for (int i = 0; i < GameBoard.LENGTH_X && !moved; i++)
{
for (int j = 0; j < GameBoard.LENGTH_Y && !moved; j++)
{
int x = (rx + i) % GameBoard.LENGTH_X;
int y = (ry + j) % GameBoard.LENGTH_Y;
// first try to place a piece on the current position
if (m_board.CanPlayerMoveToPostion(x, y))
{
GamePiece p = Random.Range(0, 2) == 0 ? m_pieceA : m_pieceB;
m_board.AddPiece(player, p.Prefab, x, y);
moved = true;
}
// a random percentage of the time, try to powerup this position
else if (m_board.CanPlayerPowerUpPosition(x, y) && Random.Range(0, 8) < 2)
{
m_board.AddPowerPiece(player, m_powerPiece.Prefab, x, y);
moved = true;
}
}
}
if (moved)
{
UpdateScores();
TransitionToNextState();
}
}
#endregion
#region Playing Online Match
// called from the MatchmakingManager was a successly online match is made
public void StartOnlineMatch (string opponentName, bool localUserGoesFirst)
{
m_board.Reset();
m_opponentName = opponentName;
if (localUserGoesFirst)
{
TransitionToState(GameState.OnlineMatchMyTurn);
}
else
{
TransitionToState(GameState.OnlineMatchRemoteTurn);
}
UpdateScores();
}
// called from the Matchmaking Manager when the remote users their next move
public void MakeRemoteMove(GamePiece.Piece piece, int x, int y)
{
GameObject prefab = m_pieceA.PrefabFor(piece);
if (piece == GamePiece.Piece.PowerBall)
{
m_board.AddPowerPiece(1, prefab, x, y);
}
else
{
m_board.AddPiece(1, prefab, x, y);
}
UpdateScores();
}
// called from the MatchmakingManager when the local user becomes the room
// owner and thus it's safe for the local user to make their move
public void MarkRemoteTurnComplete()
{
if (m_state == GameState.OnlineMatchRemoteTurn)
{
TransitionToNextState();
}
}
// the match ended from a player leaving before the board was complete
public void RemoteMatchEnded()
{
m_matchmaking.EndMatch(m_board.GetPlayerScore(0), m_board.GetPlayerScore(1));
}
#endregion
#region Selecting and Placing a Game Place
public void StartedLookingAtPiece(GamePiece piece)
{
m_interestedPiece = piece;
UpdateGamePieceColors();
}
public void StoppedLookingAtPiece()
{
m_interestedPiece = null;
UpdateGamePieceColors();
}
// This method is used to display an example piece where the player is looking
// so they know what to expect when they press the Fire button.
public void StartedLookingAtPosition(BoardPosition position)
{
if (m_state != GameState.OnlineMatchMyTurn && m_state != GameState.PracticingMyTurn)
return;
GamePiece newPiece = null;
if ((m_selectedPiece == m_pieceA || m_selectedPiece == m_pieceB) &&
m_board.CanPlayerMoveToPostion(position.x, position.y))
{
newPiece = m_board.AddProposedPiece(m_selectedPiece.Prefab, position);
}
else if (m_selectedPiece == m_powerPiece &&
m_board.CanPlayerPowerUpPosition(position.x, position.y))
{
newPiece = m_board.AddProposedPowerPiece(m_selectedPiece.Prefab, position);
}
if (newPiece != null)
{
if (m_proposedPiece != null)
{
Destroy(m_proposedPiece.gameObject);
}
m_proposedPiece = newPiece;
}
}
public void ClearProposedMove()
{
if (m_proposedPiece != null)
{
Destroy(m_proposedPiece.gameObject);
}
}
public void TrySelectPiece()
{
if (m_interestedPiece == m_pieceA || m_interestedPiece == m_pieceB)
{
m_selectedPiece = m_interestedPiece;
}
else if (m_interestedPiece == m_powerPiece &&
(m_powerBallcount > 0 || m_state == GameState.PracticingMyTurn))
{
m_selectedPiece = m_interestedPiece;
}
UpdateGamePieceColors();
}
public void TryPlacePiece()
{
if (m_proposedPiece == null)
return;
var position = m_proposedPiece.Position;
switch(m_proposedPiece.Type)
{
case GamePiece.Piece.A:
case GamePiece.Piece.B:
m_board.AddPiece(0, m_proposedPiece.Prefab, position.x, position.y);
break;
case GamePiece.Piece.PowerBall:
m_board.AddPowerPiece(0, m_proposedPiece.Prefab, position.x, position.y);
break;
}
Destroy(m_proposedPiece.gameObject);
if (m_state == GameState.OnlineMatchMyTurn)
{
m_matchmaking.SendLocalMove(m_proposedPiece.Type, position.x, position.y);
}
UpdateScores();
TransitionToNextState();
}
#endregion
#region UI
public void QuitButtonPressed()
{
UnityEngine.Application.Quit();
}
public void AddPowerballs(uint count)
{
m_powerBallcount += count;
m_ballCountText.text = "x" + m_powerBallcount.ToString();
}
private void UpdateScores()
{
m_player0Text.text = string.Format("{0}\n\n{1}",
PlatformManager.MyOculusID, m_board.GetPlayerScore(0));
m_player1Text.text = string.Format("{0}\n\n{1}",
m_opponentName, m_board.GetPlayerScore(1));
}
private void UpdateGamePieceColors()
{
switch (m_state)
{
case GameState.None:
case GameState.PracticingAiTurn:
case GameState.OnlineMatchRemoteTurn:
m_pieceA.GetComponent<Renderer>().material.color = m_unusableColor;
m_pieceB.GetComponent<Renderer>().material.color = m_unusableColor;
m_powerPiece.GetComponent<Renderer>().material.color = m_unusableColor;
if (m_proposedPiece != null)
{
Destroy(m_proposedPiece.gameObject);
}
break;
case GameState.PracticingMyTurn:
case GameState.OnlineMatchMyTurn:
m_pieceA.GetComponent<Renderer>().material.color = m_unselectedColor;
m_pieceB.GetComponent<Renderer>().material.color = m_unselectedColor;
m_powerPiece.GetComponent<Renderer>().material.color = m_unselectedColor;
if (m_interestedPiece == m_pieceA || m_interestedPiece == m_pieceB ||
m_interestedPiece == m_powerPiece)
{
m_interestedPiece.GetComponent<Renderer>().material.color = m_highlightedColor;
}
if (m_selectedPiece != null)
{
m_selectedPiece.GetComponent<Renderer>().material.color = m_selectedColor;
}
break;
}
}
#endregion
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e8e18544645909d4ca3288d03cc2bb95
timeCreated: 1480276241
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using System.Collections;
public class GamePiece : MonoBehaviour
{
[SerializeField] private Piece m_type = Piece.A;
// Prefab for the game pieces
[SerializeField] private GameObject m_prefabA = null;
[SerializeField] private GameObject m_prefabB = null;
[SerializeField] private GameObject m_prefabPower = null;
public enum Piece { A, B, PowerBall }
private BoardPosition m_position;
public Piece Type
{
get { return m_type; }
}
public BoardPosition Position
{
get { return m_position; }
set { m_position = value; }
}
public GameObject Prefab
{
get
{
switch (m_type)
{
case Piece.A: return m_prefabA;
case Piece.B: return m_prefabB;
default: return m_prefabPower;
}
}
}
public GameObject PrefabFor(Piece p)
{
switch (p)
{
case Piece.A: return m_prefabA;
case Piece.B: return m_prefabB;
default: return m_prefabPower;
}
}
}
}

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 4c243c46c5f7948488696c53b4fa9786
timeCreated: 1542071393
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences:
- m_prefabA: {instanceID: 0}
- m_prefabB: {instanceID: 0}
- m_prefabPower: {instanceID: 0}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,97 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using Oculus.Platform;
using Oculus.Platform.Models;
using UnityEngine.UI;
// This class coordinates In-App-Purchases (IAP) for the application. Follow the
// instructions in the Readme for setting up IAP on the Oculus Dashboard. Only
// one consumable IAP item is used is the demo: the Power-Ball!
public class IAPManager : MonoBehaviour
{
// the game controler to notify when the user purchaes more powerballs
[SerializeField] private GameController m_gameController = null;
// where to record to display the current price for the IAP item
[SerializeField] private Text m_priceText = null;
// purchasable IAP products we've configured on the Oculus Dashboard
private const string CONSUMABLE_1 = "PowerballPack1";
void Start()
{
FetchProductPrices();
FetchPurchasedProducts();
}
// get the current price for the configured IAP item
public void FetchProductPrices()
{
string[] skus = { CONSUMABLE_1 };
IAP.GetProductsBySKU(skus).OnComplete(GetProductsBySKUCallback);
}
void GetProductsBySKUCallback(Message<ProductList> msg)
{
if (msg.IsError)
{
PlatformManager.TerminateWithError(msg);
return;
}
foreach (Product p in msg.GetProductList())
{
Debug.LogFormat("Product: sku:{0} name:{1} price:{2}", p.Sku, p.Name, p.FormattedPrice);
if (p.Sku == CONSUMABLE_1)
{
m_priceText.text = p.FormattedPrice;
}
}
}
// fetches the Durable purchased IAP items. should return none unless you are expanding the
// to sample to include them.
public void FetchPurchasedProducts()
{
IAP.GetViewerPurchases().OnComplete(GetViewerPurchasesCallback);
}
void GetViewerPurchasesCallback(Message<PurchaseList> msg)
{
if (msg.IsError)
{
PlatformManager.TerminateWithError(msg);
return;
}
foreach (Purchase p in msg.GetPurchaseList())
{
Debug.LogFormat("Purchased: sku:{0} granttime:{1} id:{2}", p.Sku, p.GrantTime, p.ID);
}
}
public void BuyPowerBallsPressed()
{
#if UNITY_EDITOR
m_gameController.AddPowerballs(1);
#else
IAP.LaunchCheckoutFlow(CONSUMABLE_1).OnComplete(LaunchCheckoutFlowCallback);
#endif
}
private void LaunchCheckoutFlowCallback(Message<Purchase> msg)
{
if (msg.IsError)
{
PlatformManager.TerminateWithError(msg);
return;
}
Purchase p = msg.GetPurchase();
Debug.Log("purchased " + p.Sku);
m_gameController.AddPowerballs(3);
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3e54f4e408fb12842b72b24ac5dcbcf6
timeCreated: 1479423466
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1bcd80a3e5eb15543bd65c671c26434f
timeCreated: 1479498828
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,400 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using Oculus.Platform;
using Oculus.Platform.Models;
using UnityEngine.UI;
using System.Collections.Generic;
using System;
using UnityEngine.Assertions;
// This classes uses the Oculus Matchmaking Service to find opponents of a similar
// skill and play a match with them. A skill pool is used with the matchmaking pool
// to coordinate the skill matching. Follow the instructions in the Readme to setup
// the matchmaking pools.
// The Datastore for the Room is used to communicate between the clients. This only
// works for relatively simple games with tolerance for latency. For more complex
// or realtime requirements, you'll want to use the Oculus.Platform.Net API.
public class MatchmakingManager : MonoBehaviour
{
// GameController to notify about match completions or early endings
[SerializeField] private GameController m_gameController = null;
// Text for the button that controls matchmaking
[SerializeField] private Text m_matchButtonText = null;
// Test widget to render matmaking statistics
[SerializeField] private Text m_infoText = null;
// name of the Quckmatch Pool configured on the Oculus Developer Dashboard
// which is expected to have an associated skill pool
private const string POOL = "VR_BOARD_GAME_POOL";
// the ID of the room for the current match
private ulong m_matchRoom;
// opponent User data
private User m_remotePlayer;
// last time we've received a room update
private float m_lastUpdateTime;
// how long to wait before polling for updates
private const float POLL_FREQUENCY = 30.0f;
private enum MatchRoomState { None, Queued, Configuring, MyTurn, RemoteTurn }
private MatchRoomState m_state;
void Start()
{
Matchmaking.SetMatchFoundNotificationCallback(MatchFoundCallback);
Rooms.SetUpdateNotificationCallback(MatchmakingRoomUpdateCallback);
TransitionToState(MatchRoomState.None);
}
void Update()
{
switch (m_state)
{
case MatchRoomState.Configuring:
case MatchRoomState.MyTurn:
case MatchRoomState.RemoteTurn:
// if we're expecting an update form the remote player and we haven't
// heard from them in a while, check the datastore just-in-case
if (POLL_FREQUENCY < (Time.time - m_lastUpdateTime))
{
Debug.Log("Polling Room");
m_lastUpdateTime = Time.time;
Rooms.Get(m_matchRoom).OnComplete(MatchmakingRoomUpdateCallback);
}
break;
}
}
public void MatchButtonPressed()
{
switch (m_state)
{
case MatchRoomState.None:
TransitionToState(MatchRoomState.Queued);
break;
default:
TransitionToState(MatchRoomState.None);
break;
}
}
public void EndMatch(int localScore, int remoteScore)
{
switch (m_state)
{
case MatchRoomState.MyTurn:
case MatchRoomState.RemoteTurn:
var myID = PlatformManager.MyID.ToString();
var remoteID = m_remotePlayer.ID.ToString();
var rankings = new Dictionary<string, int>();
if (localScore > remoteScore)
{
rankings[myID] = 1;
rankings[remoteID] = 2;
}
else if (localScore < remoteScore)
{
rankings[myID] = 2;
rankings[remoteID] = 1;
}
else
{
rankings[myID] = 1;
rankings[remoteID] = 1;
}
// since there is no secure server to simulate the game and report
// verifiable results, each client needs to independently report their
// results for the service to compate for inconsistencies
Matchmaking.ReportResultsInsecure(m_matchRoom, rankings)
.OnComplete(GenericErrorCheckCallback);
break;
}
TransitionToState(MatchRoomState.None);
}
void OnApplicationQuit()
{
// be a good matchmaking citizen and leave any queue immediately
Matchmaking.Cancel();
if (m_matchRoom != 0)
{
Rooms.Leave(m_matchRoom);
}
}
private void TransitionToState(MatchRoomState state)
{
var m_oldState = m_state;
m_state = state;
switch (m_state)
{
case MatchRoomState.None:
m_matchButtonText.text = "Find Match";
// the player can abort from any of the other states to the None state
// so we need to be careful to clean up all state variables
m_remotePlayer = null;
Matchmaking.Cancel();
if (m_matchRoom != 0)
{
Rooms.Leave(m_matchRoom);
m_matchRoom = 0;
}
break;
case MatchRoomState.Queued:
Assert.AreEqual(MatchRoomState.None, m_oldState);
m_matchButtonText.text = "Leave Queue";
Matchmaking.Enqueue2(POOL).OnComplete(MatchmakingEnqueueCallback);
break;
case MatchRoomState.Configuring:
Assert.AreEqual(MatchRoomState.Queued, m_oldState);
m_matchButtonText.text = "Cancel Match";
break;
case MatchRoomState.MyTurn:
case MatchRoomState.RemoteTurn:
Assert.AreNotEqual(MatchRoomState.None, m_oldState);
Assert.AreNotEqual(MatchRoomState.Queued, m_oldState);
m_matchButtonText.text = "Cancel Match";
break;
}
}
void MatchmakingEnqueueCallback(Message untyped_msg)
{
if (untyped_msg.IsError)
{
Debug.Log(untyped_msg.GetError().Message);
TransitionToState(MatchRoomState.None);
return;
}
Message<MatchmakingEnqueueResult> msg = (Message<MatchmakingEnqueueResult>)untyped_msg;
MatchmakingEnqueueResult info = msg.Data;
m_infoText.text = string.Format(
"Avg Wait Time: {0}s\n" +
"Max Expected Wait: {1}s\n" +
"In Last Hour: {2}\n" +
"Recent Percentage: {3}%",
info.AverageWait, info.MaxExpectedWait, info.MatchesInLastHourCount,
info.RecentMatchPercentage);
}
void MatchFoundCallback(Message<Room> msg)
{
if (msg.IsError)
{
Debug.Log(msg.GetError().Message);
TransitionToState(MatchRoomState.None);
return;
}
if (m_state != MatchRoomState.Queued)
{
// ignore callback - user already cancelled
return;
}
// since this example communicates via updates to the datastore, it's vital that
// we subscribe to room updates
Matchmaking.JoinRoom(msg.Data.ID, true /* subscribe to update notifications */)
.OnComplete(MatchmakingJoinRoomCallback);
m_matchRoom = msg.Data.ID;
}
void MatchmakingJoinRoomCallback(Message<Room> msg)
{
if (msg.IsError)
{
Debug.Log(msg.GetError().Message);
TransitionToState(MatchRoomState.None);
return;
}
if (m_state != MatchRoomState.Queued)
{
// ignore callback - user already cancelled
return;
}
int numUsers = (msg.Data.UsersOptional != null) ? msg.Data.UsersOptional.Count : 0;
Debug.Log ("Match room joined: " + m_matchRoom + " count: " + numUsers);
TransitionToState(MatchRoomState.Configuring);
// only process the room data if the other user has already joined
if (msg.Data.UsersOptional != null && msg.Data.UsersOptional.Count == 2)
{
ProcessRoomData(msg.Data);
}
}
// Room Datastore updates are used to send moves between players. So if the MatchRoomState
// is RemoteTurn I'm looking for the other player's move in the Datastore. If the
// MatchRoomState is MyTurn I'm waiting for the room ownership to change so that
// I have authority to write to the datastore.
void MatchmakingRoomUpdateCallback(Message<Room> msg)
{
if (msg.IsError)
{
Debug.Log(msg.GetError().Message);
TransitionToState(MatchRoomState.None);
return;
}
string ownerOculusID = msg.Data.OwnerOptional != null ? msg.Data.OwnerOptional.OculusID : "";
int numUsers = (msg.Data.UsersOptional != null) ? msg.Data.UsersOptional.Count : 0;
Debug.LogFormat(
"Room Update {0}\n" +
" Owner {1}\n" +
" User Count {2}\n" +
" Datastore Count {3}\n",
msg.Data.ID, ownerOculusID, numUsers, msg.Data.DataStore.Count);
// check to make sure the room is valid as there are a few odd timing issues (for
// example when leaving a room) that can trigger an uninteresting update
if (msg.Data.ID != m_matchRoom)
{
Debug.Log("Unexpected room update from: " + msg.Data.ID);
return;
}
ProcessRoomData(msg.Data);
}
private void ProcessRoomData(Room room)
{
m_lastUpdateTime = Time.time;
if (m_state == MatchRoomState.Configuring)
{
// get the User info for the other player
if (room.UsersOptional != null)
{
foreach (var user in room.UsersOptional)
{
if (PlatformManager.MyID != user.ID)
{
Debug.Log("Found remote user: " + user.OculusID);
m_remotePlayer = user;
break;
}
}
}
if (m_remotePlayer == null)
return;
bool i_go_first = DoesLocalUserGoFirst();
TransitionToState(i_go_first ? MatchRoomState.MyTurn : MatchRoomState.RemoteTurn);
Matchmaking.StartMatch(m_matchRoom).OnComplete(GenericErrorCheckCallback);
m_gameController.StartOnlineMatch(m_remotePlayer.OculusID, i_go_first);
}
// if it's the remote player's turn, look for their move in the datastore
if (m_state == MatchRoomState.RemoteTurn &&
room.DataStore.ContainsKey(m_remotePlayer.OculusID) &&
room.DataStore[m_remotePlayer.OculusID] != "")
{
// process remote move
ProcessRemoteMove(room.DataStore[m_remotePlayer.OculusID]);
TransitionToState(MatchRoomState.MyTurn);
}
// If the room ownership transferred to me, we can mark the remote turn complete.
// We don't do this when the remote move comes in if we aren't yet the owner because
// the local user will not be able to write to the datastore if they aren't the
// owner of the room.
if (m_state == MatchRoomState.MyTurn && room.OwnerOptional != null && room.OwnerOptional.ID == PlatformManager.MyID)
{
m_gameController.MarkRemoteTurnComplete();
}
if (room.UsersOptional == null || (room.UsersOptional != null && room.UsersOptional.Count != 2))
{
Debug.Log("Other user quit the room");
m_gameController.RemoteMatchEnded();
}
}
private void ProcessRemoteMove(string moveString)
{
Debug.Log("Processing remote move string: " + moveString);
string[] tokens = moveString.Split(':');
GamePiece.Piece piece = (GamePiece.Piece)Enum.Parse(typeof(GamePiece.Piece), tokens[0]);
int x = Int32.Parse(tokens[1]);
int y = Int32.Parse(tokens[2]);
// swap the coordinates since each player assumes they are player 0
x = GameBoard.LENGTH_X-1 - x;
y = GameBoard.LENGTH_Y-1 - y;
m_gameController.MakeRemoteMove(piece, x, y);
}
public void SendLocalMove(GamePiece.Piece piece, int boardX, int boardY)
{
string moveString = string.Format("{0}:{1}:{2}", piece.ToString(), boardX, boardY);
Debug.Log("Sending move: " + moveString);
var dict = new Dictionary<string, string>();
dict[PlatformManager.MyOculusID] = moveString;
dict[m_remotePlayer.OculusID] = "";
Rooms.UpdateDataStore(m_matchRoom, dict).OnComplete(UpdateDataStoreCallback);
TransitionToState(MatchRoomState.RemoteTurn);
}
private void UpdateDataStoreCallback(Message<Room> msg)
{
if (m_state != MatchRoomState.RemoteTurn)
{
// ignore calback - user already quit the match
return;
}
// after I've updated the datastore with my move, change ownership so the other
// user can perform their move
Rooms.UpdateOwner(m_matchRoom, m_remotePlayer.ID);
}
// deterministic but somewhat random selection for who goes first
private bool DoesLocalUserGoFirst()
{
// if the room ID is even, the lower ID goes first
if (m_matchRoom % 2 == 0)
{
return PlatformManager.MyID < m_remotePlayer.ID;
}
// otherwise the higher ID goes first
{
return PlatformManager.MyID > m_remotePlayer.ID;
}
}
private void GenericErrorCheckCallback(Message msg)
{
if (msg.IsError)
{
Debug.Log(msg.GetError().Message);
TransitionToState(MatchRoomState.None);
return;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e4f7411db9bf50545a8b0b3c5b3c1ff8
timeCreated: 1479421035
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,113 @@
namespace Oculus.Platform.Samples.VrBoardGame
{
using UnityEngine;
using Oculus.Platform;
using Oculus.Platform.Models;
// Top level class for initializing the Oculus Platform SDK. It also performs
// and entitlement check and returns information about the logged-in user.
public class PlatformManager : MonoBehaviour
{
private static PlatformManager s_instance;
// my Application-scoped Oculus ID
private ulong m_myID;
// my Oculus user name
private string m_myOculusID;
#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);
Core.Initialize();
}
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;
Debug.Log(" I am " + m_myOculusID);
}
// In this example, for most errors, we terminate the Application. A full App would do
// something more graceful.
public static void TerminateWithError(Message msg)
{
Debug.Log("Error: " + msg.GetError().Message);
UnityEngine.Application.Quit();
}
#endregion
#region Properties
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
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 46b8fed8b150a8c4688eae89457bd466
timeCreated: 1479414194
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: