Skip to content
This repository has been archived by the owner on Dec 5, 2018. It is now read-only.

Commit

Permalink
initial release of the VR Starter Project for SpatialOS
Browse files Browse the repository at this point in the history
  • Loading branch information
johnprobable committed Apr 28, 2017
0 parents commit 7e82c50
Show file tree
Hide file tree
Showing 249 changed files with 17,618 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
spatial.log
spatialos_worker_packages.json
build/
logs/
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Virtual Reality starter project

![Virtual Reality starter project](vrstarterproject.jpg)

*Copyright Improbable Worlds Ltd, 2017*

- *GitHub repository*: [https://github.com/spatialos/VRStarterProject](https://github.com/spatialos/VRStarterProject)

---

## Introduction

This is a SpatialOS project that implements basic functionality for a VR game or application, intended as a starting point for developing your own. It requires SteamVR hardware (the [HTC Vive](https://www.htcvive.com/) headset, controllers, and base stations).

The main features of this project are:

* Players controlled by VR peripherals.
* Spectators controlled by mouse and keyboard.
* Multiplayer by default. Many players and spectators can share the same world.
* Position and orientation tracking of headset and controllers.
* Move by teleporting. Press the trackpad on either controller, move the target, and release the trackpad to teleport to the target location.
* Grab objects, pass them between hands, throw them, and catch them.

If you run into problems, or want to give us feedback, please visit the [SpatialOS forums](https://forums.improbable.io/).

## Running the project

To run the project locally, first build it by running `spatial worker build`, then start the server with `spatial local start`. You can connect a client by opening the Unity project and pressing the play button, or by running `spatial local worker launch UnityClient default`. See the [documentation](https://spatialos.improbable.io/docs/reference/latest/developing/local/run) for more details.

To deploy the project to the cloud, first build it by running `spatial worker build -t=deployment`, then upload the assembly with `spatial cloud upload <assembly name>`, and finally deploy it with `spatial cloud launch <assembly name> <launch configuration file> <deployment name> --snapshot=<snapshot file>`. You can obtain and share links to connect to the deployment from the [console](http://console.improbable.io/projects). See the [documentation](https://spatialos.improbable.io/docs/reference/latest/developing/deploy-an-application) for more details.


## Project structure and design overview

### Player entity and peripheral tracking

The player is represented by an entity. Its position corresponds to the centre of the Vive's play area, an abstraction that represents the physical space that is tracked by the base stations.

The position of the player's head tracks that of the Vive headset, and the player's hands track that of controllers. Those positions are stored as offsets from the player's position. Input from the headset and the controllers is handled by [`VrPeripheralHandler.cs`](workers/unity/Assets/Gamelogic/Player/VRPeripheralHandler.cs), and players are visualised by [`VrPeripheralVisualiser.cs`](workers/unity/Assets/Gamelogic/Player/VRPeripheralVisualiser.cs).

This approach to the player's position is specific to the HTC Vive with its play area. A more usual approach for SpatialOS would have the position of the player entity match the position of the player's head; however, the approach used in this project has several advantages, including a more straightforward implementation, lower bandwidth requirements (since offsets can be transmitted using smaller data types than absolute world positions), and a familiar model for experienced SteamVR developers.

The main downside is that physically moving within the play area will not cause the player entity to move when seen on the [Inspector](https://spatialos.improbable.io/docs/reference/latest/tools/inspector). This may not matter much, since the the SpatialOS world will normally be orders of magnitude bigger than the average living room, so most of the player movement will be done by teleporting. We may revisit this design tradeoff in future iterations of this project.

### Teleportation

Teleportation is implemented client-side. When the player presses the trackpad on either controller, a teleport targeter is displayed. The player can move it around to choose their desired destination, and release the trackpad to perform the teleport. All of this is implemented in [`TeleportationHandler.cs`](workers/unity/Assets/Gamelogic/Player/TeleportationHandler.cs).

### Spectator camera

Spectators are players who connect without a VR headset. They are limited to moving around and viewing the game world, but can't interact with it. The camera orientation is controlled with the mouse, and its position is controlled with the WASD keys. Spectators are visible in-game to other spectators and to players. This is done in [`SpectatorFlycam.cs`](workers/unity/Assets/Gamelogic/Player/SpectatorFlycam.cs).

### Grabbing and throwing objects

Entities can be easily marked as _grabbable_, which lets them be picked up, dropped and thrown. When the client detects a collision between a controller and a grabbable object, it adds it to a set of _reachable_ objects; when the collision ends, the object is removed (by keeping this set, we avoid having to search for nearby objects constantly). The closest reachable object is highlighted by being made blue. This is done in [`HandCollisionHandler.cs`](/workers/unity/Assets/Gamelogic/Player/HandCollisionHandler.cs), and happens entirely on the client.

When the trackpad is pressed, a `GrabRequestEvent` with the entity ID of the closest reachable object is emitted by [`GrabbingSender.cs`](workers/unity/Assets/Gamelogic/Grabbing/GrabbingSender.cs) on the client side. This event is handled by [`GrabbingReceiver.cs`](workers/unity/Assets/Gamelogic/Grabbing/GrabbingReceiver.cs) on the server side. It validates the grab attempt (that is, verifies that the hand and the object are actually colliding). If it is valid, it updates the `CurrentGrabberInfo` property of the `Grabbable` component to record which player and controller is grabbing it. When the trackpad is released, a very similar process handles dropping an object that is being held.

While an object is being held, its position and orientation track those of the controller than is holding it. This is done in every client, to ensure there's no jitter between the position and orientation of the object and the controller. When an object is released, its velocity is set to match that of the controller; this way, objects can be thrown. All of this is implemented in [`GrabbableTransformHandler.cs`](workers/unity/Assets/Gamelogic/Grabbing/GrabbableTransformHandler.cs).
43 changes: 43 additions & 0 deletions default_launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"template": "small",
"world": {
"chunkEdgeLengthMeters": 50,
"snapshots": {
"snapshotWritePeriodSeconds": 0
},
"dimensions": {
"xMeters": 1500,
"zMeters": 1500
}
},
"workers": [
{
"worker_type": "UnityWorker",
"load_balancing": {
"auto_hex_grid": {
"num_workers": 1
}
},
"permissions": [{
"all": {}
}]
},
{
"worker_type": "UnityClient",
"permissions": [{
"entity_creation": {
"allow": false
},
"entity_deletion": {
"allow": false
},
"entity_query": {
"allow": true,
"components": [
"*"
]
}
}]
}
]
}
56 changes: 56 additions & 0 deletions manual_launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"template": "small",
"world": {
"chunkEdgeLengthMeters": 50,
"snapshots": {
"snapshotWritePeriodSeconds": 0
},
"dimensions": {
"xMeters": 1500,
"zMeters": 1500
}
},
"workers": [
{
"worker_type": "UnityWorker",
"load_balancing": {
"dynamic_loadbalancer": {
"worker_scaler_config": {
"constant_config": {
"num_workers": 0
}
},
"worker_placer_config": {
"random_params": {}
},
"loadbalancer_config": {
"min_range_meters": 500.0,
"max_range_meters": 5000.0,
"speed_meters_per_second": 100.0,
"expansion_time_millis": 60000
}
}
},
"permissions": [{
"all": {}
}]
},
{
"worker_type": "UnityClient",
"permissions": [{
"entity_creation": {
"allow": false
},
"entity_deletion": {
"allow": false
},
"entity_query": {
"allow": true,
"components": [
"*"
]
}
}]
}
]
}
5 changes: 5 additions & 0 deletions schema/improbable/global/ClientAuthorityCheck.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package improbable.global;

component ClientAuthorityCheck {
id = 1002;
}
16 changes: 16 additions & 0 deletions schema/improbable/global/Grabbable.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package improbable.global;

import "improbable/global/Quaternion.schema";
import "improbable/player/Grab.schema";

type CurrentGrabberInfo {
EntityId grabber_entity = 1;
improbable.player.ControllerSide controller_side = 2;
Vector3f relative_position = 3;
Quaternion relative_orientation = 4;
}

component Grabbable {
id = 1004;
option<CurrentGrabberInfo> current_grabber_info = 1;
}
16 changes: 16 additions & 0 deletions schema/improbable/global/PlayerCreation.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package improbable.global;

enum PlayerType {
VRPLAYER = 0;
SPECTATORPLAYER = 1;
}

type CreatePlayerRequest {
PlayerType player_type = 1;
}
type CreatePlayerResponse {}

component PlayerCreation {
id = 1001;
command CreatePlayerResponse create_player(CreatePlayerRequest);
}
8 changes: 8 additions & 0 deletions schema/improbable/global/Quaternion.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package improbable.global;

type Quaternion {
float x = 1;
float y = 2;
float z = 3;
float w = 4;
}
9 changes: 9 additions & 0 deletions schema/improbable/global/WorldTransform.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package improbable.general;

import "improbable/global/Quaternion.schema";

component WorldTransform {
id = 1000;
EntityPosition position = 1;
improbable.global.Quaternion rotation = 2;
}
14 changes: 14 additions & 0 deletions schema/improbable/player/ClientConnection.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package improbable.player;

type ClientDisconnectRequest{}
type ClientDisconnectResponse{}

type HeartbeatRequest{}
type HeartbeatResponse{}

component ClientConnection {
id = 1003;
uint32 timeout_beats_remaining = 1;
command HeartbeatResponse heartbeat(HeartbeatRequest);
command ClientDisconnectResponse disconnect_client(ClientDisconnectRequest);
}
23 changes: 23 additions & 0 deletions schema/improbable/player/Grab.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package improbable.player;

enum ControllerSide {
LEFT = 0;
RIGHT = 1;
}

type GrabRequestEvent {
EntityId grabbed_entity_id = 1;
ControllerSide controller_side = 2;
}

type DropRequestEvent {
EntityId dropped_entity_id = 1;
}

component Grab {
id = 1005;
map<ControllerSide, EntityId> held_entities = 1;

event GrabRequestEvent grab_request;
event DropRequestEvent drop_request;
}
13 changes: 13 additions & 0 deletions schema/improbable/player/VRPlayerTransform.schema
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package improbable.player;

type TransformOffset {
Vector3f position = 1;
Vector3f rotation = 2;
}

component VRPeripheralOffsets {
id = 1100;
TransformOffset head = 1;
TransformOffset left_controller = 2;
TransformOffset right_controller = 3;
}
Binary file added snapshots/default.snapshot
Binary file not shown.
8 changes: 8 additions & 0 deletions spatialos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "your_project_name_here",
"project_version": "1.0.0",
"sdk_version": "10.2.0",
"dependencies": [
{"name": "WorkerSdkSchema", "version": "10.2.0"}
]
}
Binary file added vrstarterproject.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions workers/gsim/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
spatialos_nexus.sbt
spatialos_pack.sbt
.spatialos
generated
project
target
build.json
61 changes: 61 additions & 0 deletions workers/gsim/spatialos.gsim.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"tasks": [
{
"name": "Codegen",
"steps": [
{
"name": "Scala",
"arguments": [
"process_schema",
"generate",
"--input=../../schema/",
"--repository=../../build/dependencies/schema/",
"--output=generated",
"--language=scala"
]
}
]
},
{
"name": "Build",
"steps": [
{
"name": "Codegen",
"arguments": [
"invoke-task",
"Codegen"
]
},
{
"name": "scala",
"arguments": [
"invoke",
"sbt",
"workerPackage"
]
}
]
},
{
"name": "Clean",
"steps": [
{
"name": "Scala",
"arguments": [
"invoke",
"sbt",
"clean"
]
},
{
"name": "Generated code",
"arguments": [
"process_schema",
"clean",
"generated"
]
}
]
}
]
}
6 changes: 6 additions & 0 deletions workers/gsim/spatialos.gsim.worker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"build": {
"tasks_filename": "spatialos.gsim.build.json",
"generated_build_scripts_type": "gsim"
}
}
4 changes: 4 additions & 0 deletions workers/gsim/src/main/scala/DummyMain.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// The sbt-pack plugin will print a warning if there is no main class in the
// project. The sole purpose of this file is to mask this warning.

object DummyMain extends App
Loading

0 comments on commit 7e82c50

Please sign in to comment.