1 / 28

Re-making a Classic in VR

Re-making a Classic in VR. Chris Zaharia. @ chrisjz # SydVR. Project. Engine - Unity 3D (v4.5) View + head tracking - Oculus Rift DK1 Hand tracking - Razer Hydra C# Language Re-create game up to first mission in first world. To Do. Integrate Rift with player

eadoin
Download Presentation

Re-making a Classic in VR

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Re-making a Classic in VR Chris Zaharia @chrisjz #SydVR

  2. Project • Engine - Unity 3D (v4.5) • View + head tracking - Oculus Rift DK1 • Hand tracking - Razer Hydra • C# Language • Re-create game up to first mission in first world

  3. To Do • Integrate Rift with player • Integrate Razer Hydra with player’s hands • Player avatar • Player movement • Hand interaction with objects

  4. Setup World • Start a new project • Create a scene • Import or create an environment • Create a Directional Light to light up the environment

  5. Create Player & Integrate Rift • Create a GameObject called Player • Attach the component Character Controller • Attach the scripts Character Motor and FPSInput Controller • Attach OVRCameraController prefab as child of Player GameObject • In script FPSInput Controller set “Ovr Camera” variable as CameraRight • Drag OVRCameraControllerbehind player

  6. FPSInputController.cs using UnityEngine; using System.Collections; // Require a character controller to be attached to the same game object [RequireComponent(typeof(CharacterMotor))] [AddComponentMenu("Character/FPS Input Controller")] public class FPSInputController : MonoBehaviour { public GameObjectovrCamera; private CharacterMotor motor; private boolinputEnabled; // If input is enabled/disabled // Use this for initialization void Awake (){ motor = GetComponent<CharacterMotor>(); } void Start() { inputEnabled = true; }

  7. FPSInputController.cs (2) // Update is called once per frame void Update (){ // Get the input vector from keyboard or analog stick Vector3 directionVector= new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); motor.inputJump = Input.GetButton ("Jump"); // Play jumping audio clips if (initialJumpAudioClips.Length > 0 && motor.inputJump && motor.grounded && !audio.isPlaying) { audio.clip = initialJumpAudioClips[Random.Range(0, initialJumpAudioClips.Length)]; audio.Play(); } if (directionVector != Vector3.zero) { // Get the length of the directon vector and then normalize it // Dividing by the length is cheaper than normalizing when we already have the length anyway float directionLength= directionVector.magnitude; directionVector = directionVector / directionLength;

  8. FPSInputController.cs (3) // Make sure the length is no bigger than 1 directionLength = Mathf.Min(1, directionLength); // Make the input vector more sensitive towards the extremes and less sensitive in the middle // This makes it easier to control slow speeds when using analog sticks directionLength = directionLength * directionLength; // Multiply the normalized direction vector by the modified length directionVector = directionVector * directionLength; } // Apply the direction to the CharacterMotor motor.inputMoveDirection = ovrCamera.transform.rotation * directionVector; } public void SetInputEnabled (bool status) { inputEnabled = status; } }

  9. Attach avatar to player • Place player 3D model as child of Player object and name it “Avatar” • Move the OVR camera object behind the model • Make the model’s head rotate with the Rift’s head tracker by moving the head model inside the CameraRight object

  10. Hydra Integration for Hands • Import Sixense Unity plugin for Razer integration • Create GameObject “Hands” as child of OVRCameraController • Place SixenseInput prefab as child of OVRCamera Controller

  11. Hydra Integration for Hands • Create GameObjects “Left Hand” + “Right Hand” as children of “Hands” • Place the player’s hand models within each of those 2 hand objects respectively • Attach script SixenseHandController to each hand GameObject • Set the “Hand” variable to either LEFT or RIGHT, as appropriate

  12. Control Player using Hydra • Modify FPSInputController script to map Hydra’s left joystick and a button to moving and jumping • Create PlayerLook script in project to handle looking in environment • Create HydraLook script to map Hydra’s right joystick to looking, inheriting PlayerLookclass in its script

  13. FPSInputController.cs void Start() { IgnorePlayerColliders (); inputEnabled = true; } // Update is called once per frame void Update (){ // Get the input vector from keyboard or analog stick Vector3 directionVector= new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); // Get the input vector from hydra SixenseInput.ControllerhydraLeftController = SixenseInput.GetController (SixenseHands.LEFT); SixenseInput.ControllerhydraRightController = SixenseInput.GetController (SixenseHands.RIGHT); if (hydraLeftController != null) { directionVector= new Vector3(hydraLeftController.JoystickX, 0, hydraLeftController.JoystickY); } if (hydraRightController != null) { motor.inputJump = hydraRightController.GetButton (SixenseButtons.BUMPER); } else { motor.inputJump = Input.GetButton ("Jump"); }

  14. FPSInputController.cs (2) … } … // Prevent colliders on player from colliding with each other i.e. hand colliders with body collider void IgnorePlayerColliders () { Collider[] cols = GetComponentsInChildren<Collider>(); foreach (Collider col in cols) { if (col != collider) { Physics.IgnoreCollision(col, collider); } } } }

  15. PlayerLook.cs using UnityEngine; using System.Collections; /// PlayerLook rotates the transform based on the input device's delta. /// Minimum and Maximum values can be used to constrain the possible rotation. /// Based on Unity's MouseLook script. [AddComponentMenu("Camera-Control/Player Look")] public class PlayerLook : MonoBehaviour { public enumRotationAxes { XAndY = 0, X = 1, Y = 2 } public RotationAxes axes = RotationAxes.XAndY; public float sensitivityX = 15F; public float sensitivityY = 15F; public float minimumX = -360F; public float maximumX = 360F; public float minimumY = -60F; public float maximumY = 60F; protected float rotationY = 0F;

  16. PlayerLook.cs (2) protected float axisX, axisY; void Start () { // Make the rigid body not change rotation if (rigidbody) rigidbody.freezeRotation = true; } protected virtual void Update () { if (axes == RotationAxes.XAndY) { float rotationX = transform.localEulerAngles.y + axisX * sensitivityX; rotationY += axisY * sensitivityY; rotationY = Mathf.Clamp (rotationY, minimumY, maximumY); transform.localEulerAngles = new Vector3(-rotationY, rotationX, 0); } else if (axes == RotationAxes.X) { transform.Rotate(0, axisX * sensitivityX, 0); }

  17. PlayerLook.cs (3) else { rotationY += axisY * sensitivityY; rotationY = Mathf.Clamp (rotationY, minimumY, maximumY); transform.localEulerAngles = new Vector3(-rotationY, transform.localEulerAngles.y, 0); } } }

  18. HydraLook.cs using UnityEngine; using System.Collections; public class HydraLook : PlayerLook { protected override void Update () { // Get the input vector from hydra SixenseInput.ControllerhydraRightController = SixenseInput.GetController (SixenseHands.RIGHT); if (hydraRightController != null) { axisX = hydraRightController.JoystickX; axisY = hydraRightController.JoystickY; } base.Update (); } }

  19. Hand Interactions using Hydra • Attach invisible rectangles to each hand to use as colliders for hands • Name the objects LeftHandCollider and RightHandCollider respectively • Modify the SixenseHandController with functions to grab objects and throw based on hand velocity • Tag any grabbable objects with “Grabbable” and give them a rigidbody for physics

  20. SixenseHandController.cs public class SixenseHandController : SixenseObjectController { public float minGrabDistance = 1.0f; // Grabbable object must be within this distance from hand colliders to be picked up public float throwForce = 30.0f; // Force multiplyer for throwing objects private boolisHoldingObject = false; private GameObjectclosestObject = null; private GrabObjectgrabObject; // Script attached to grabbed object with grappling data on that object private float handVelocity; private Vector3 handVector; private Vector3 handPrevious; …

  21. SixenseHandController.cs (2) protected override void UpdateObject( SixenseInput.Controller controller ) { … if ( controller.Enabled ) { // Animation update UpdateAnimationInput( controller ); // Action update UpdateActionInput ( controller ); } base.UpdateObject(controller); } …

  22. SixenseHandController.cs (3) protected void UpdateActionInput( SixenseInput.Controller controller) { Vector3 currentPosition = new Vector3(); Quaternion currentRotation = new Quaternion(); Velocity(); if (isHoldingObject && !controller.GetButton(SixenseButtons.TRIGGER)) { Throw(); isHoldingObject = false; } if (Hand == SixenseHands.LEFT) { currentPosition = GameObject.Find("LeftHandCollider").transform.position; currentRotation = GameObject.Find("LeftHandCollider").transform.rotation; } if (Hand == SixenseHands.RIGHT) { currentPosition = GameObject.Find("RightHandCollider").transform.position; currentRotation = GameObject.Find("RightHandCollider").transform.rotation; }

  23. SixenseHandController.cs (4) if (!isHoldingObject) { foreach (GameObject o in GameObject.FindGameObjectsWithTag ("Grabbable")) { float dist = Vector3.Distance(o.transform.position, currentPosition); if (dist < minGrabDistance) { closestObject = o; } } }

  24. SixenseHandController.cs (5) if (closestObject != null && Vector3.Distance(closestObject.transform.position, currentPosition) < minGrabDistance && controller.GetButton(SixenseButtons.TRIGGER)) { if (closestObject.rigidbody && closestObject.rigidbody.isKinematic) { return; } grabObject = closestObject.GetComponent<GrabObject>(); if (grabObject && grabObject.isEnabled) { closestObject.transform.position = currentPosition + grabObject.GetPosition(Hand); closestObject.transform.rotation = currentRotation * Quaternion.Euler(grabObject.GetRotation(Hand)); } else { closestObject.transform.position = currentPosition; closestObject.transform.rotation = currentRotation; } isHoldingObject = true; } }

  25. SixenseHandController.cs (6) // Calculate velocity of hand protected void Velocity () { if (Time.deltaTime != 0) { handVector = (transform.position - handPrevious) / Time.deltaTime; handPrevious = transform.position; } handVelocity = Vector3.Magnitude(handVector); } // Throw the held object once player lets go based on hand velocity protected void Throw () { if (closestObject.rigidbody) { Vector3 dir = (closestObject.transform.position - transform.position).normalized; closestObject.rigidbody.AddForce(dir * handVelocity * throwForce); } }

  26. Next • Presentation slides will be up online • Project will be open sourced once beta is completed • YouTube video showing a play through

  27. Future • Integrate alternative and supporting devices i.e. STEM, Leap Motion, MYO, Omni + • Re-make further (complete?) • Boilerplate? • Re-create other classics (Super Smash Bros?)

More Related