forked from cgvr/DeltaVR
Initial Commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
SKU Name Description Currency Amount Item Type
|
||||
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b687b4832314b24db66e56bef4de820
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Platform/Samples/VrBoardGame/Prefabs.meta
Normal file
8
Assets/Oculus/Platform/Samples/VrBoardGame/Prefabs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa2e3f5a7dc94444f95f0774aa2bbd54
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
112
Assets/Oculus/Platform/Samples/VrBoardGame/Prefabs/PieceA.prefab
Normal file
112
Assets/Oculus/Platform/Samples/VrBoardGame/Prefabs/PieceA.prefab
Normal 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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48e6308f93e461340abb975251ae824b
|
||||
timeCreated: 1480479889
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
113
Assets/Oculus/Platform/Samples/VrBoardGame/Prefabs/PieceB.prefab
Normal file
113
Assets/Oculus/Platform/Samples/VrBoardGame/Prefabs/PieceB.prefab
Normal 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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff8c3ef67a52afc4a9cf330f025fdec3
|
||||
timeCreated: 1480479894
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81a73acbe18cf784cb5184bd404c30bc
|
||||
timeCreated: 1480479897
|
||||
licenseType: Store
|
||||
NativeFormatImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
64
Assets/Oculus/Platform/Samples/VrBoardGame/Readme.md
Normal file
64
Assets/Oculus/Platform/Samples/VrBoardGame/Readme.md
Normal 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
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0975c71f0ce832c489580f5059a6118a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Oculus/Platform/Samples/VrBoardGame/Scripts.meta
Normal file
8
Assets/Oculus/Platform/Samples/VrBoardGame/Scripts.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e865e3f9d93b4043bdf7306571e6e4c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9244e220b0fee34c98de6ed84ee6cdd
|
||||
timeCreated: 1480276073
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96a29a99957531246921ced0fac365ab
|
||||
timeCreated: 1480201871
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
188
Assets/Oculus/Platform/Samples/VrBoardGame/Scripts/GameBoard.cs
Normal file
188
Assets/Oculus/Platform/Samples/VrBoardGame/Scripts/GameBoard.cs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94decd43dfca0db43a3936edb109ca2e
|
||||
timeCreated: 1479840163
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8e18544645909d4ca3288d03cc2bb95
|
||||
timeCreated: 1480276241
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e54f4e408fb12842b72b24ac5dcbcf6
|
||||
timeCreated: 1479423466
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3481
Assets/Oculus/Platform/Samples/VrBoardGame/Scripts/Main.unity
Normal file
3481
Assets/Oculus/Platform/Samples/VrBoardGame/Scripts/Main.unity
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bcd80a3e5eb15543bd65c671c26434f
|
||||
timeCreated: 1479498828
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4f7411db9bf50545a8b0b3c5b3c1ff8
|
||||
timeCreated: 1479421035
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46b8fed8b150a8c4688eae89457bd466
|
||||
timeCreated: 1479414194
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user