//*********************************************************** // Filename: BallTeleport.cs // Author: Moritz Kolvenbach, Marco Fendrich // Last changes: Thursday, 9th of August 2018 // Content: Teleportation with a thrown ball //*********************************************************** using System.Collections.Generic; using UnityEngine; /// /// Ball teleport by throwing an instantiated object from the player's hand /// public class BallTeleport : MonoBehaviour, IButton { // general reference classes for access private GameObject gameManager; private Visualiser renderScript; private Teleport teleportScript; private ViveNavMesh navMesh; // list of points within parabola or line being shown to player as indicator for his teleportation protected List projectionPoints; // distance in meters between the single points of the optical representation protected float pointSpacing; // maximum number of points being calculated until parabola is being stopped protected int pointCount; // prefab to be thrown public GameObject ballPrefab; // instantiated object while throwing private GameObject ballObject; // current state of teleportation private BallState ballState; // additional information needed for calculations; details below private Vector3 lastPosition; private bool isBallAboveMesh; private Vector3 teleportDestination; private Vector3 normalVector; // reference to controller private SteamVR_Controller.Device controller; // for rendering only protected Vector3 normalisedVelocity = Vector3.down; protected Vector3 normalisedHitPoint = Vector3.up; void Start() { // get references for variables declared above gameManager = GameObject.FindWithTag("GameController"); teleportScript = (Teleport)gameManager.GetComponent(typeof(Teleport)); renderScript = (Visualiser)gameManager.GetComponent(typeof(Visualiser)); navMesh = (ViveNavMesh)gameManager.GetComponent(typeof(ViveNavMesh)); projectionPoints = new List(pointCount); pointSpacing = renderScript.PointSpacing; pointCount = renderScript.PointCount; } void Update() { // only running when ball is currently being thrown if (ballState != BallState.THROWING) return; // calculate a line downwards as preparation to show the current teleport target position UpdateProjectionPoints(); // display the calculated points renderScript.updateRendering(isBallAboveMesh, projectionPoints, normalisedHitPoint, Vector3.down * 10.0F); } /// /// Function being called on press by player; checks the state the teleportation is in and either creates a new ball or /// destroys the old one. Teleports if the ball was above a valid destination /// /// gameObject of controller /// ID of controller currently using this function public void OnButtonDown(GameObject controllerObject, int controllerIdentificator) { // if no ball is instantiated, create new one if (ballState == BallState.NONE) { // create ball at controller position controller = SteamVR_Controller.Input(controllerIdentificator); ballObject = Instantiate(ballPrefab, controllerObject.transform.position, controllerObject.transform.rotation); // set the ball to stay static at front of controller ballObject.GetComponent().isKinematic = true; ballObject.transform.SetParent(controllerObject.transform); // set ballstate to a ball being held ballState = BallState.HELD; } // if a ball is currently instantied and was thrown, destroy it and teleport if the position was valid else if (ballState == BallState.THROWING) { // check whether the ball was above a valid destination navMesh.Linecast(ballObject.transform.position, new Vector3(ballObject.transform.position.x, ballObject.transform.position.y - 5.0f, ballObject.transform.position.z), out isBallAboveMesh, out teleportDestination, out normalVector); // if so, teleport there if (isBallAboveMesh) { teleportScript.CallTeleport(teleportDestination); } // if not, give haptic feedback that input was registered as no other feedback would be given else { // vibrate controller } // destroy ball, stop rendering, reset ball state Destroy(ballObject); ballState = BallState.NONE; renderScript.enabled = false; } } /// /// Function being called on release of button by player; releases ball if one is currently being held /// public void OnButtonUp() { // check if an instantiated ball is being held if (ballState != BallState.HELD) return; // if so, decouple from controller ballObject.GetComponent().isKinematic = false; ballObject.transform.SetParent(null); // give the ball the controller's speed increased by factor ballObject.GetComponent().velocity = controller.velocity * 2.0F; // set ball state to throwing and start rendering position being targeted ballState = BallState.THROWING; renderScript.enabled = true; } /// /// Sample a bunch of points along a line until you hit gnd. At that point, cut off the parabola /// Entire calculation is analogue to except with downward velocity /// private void UpdateProjectionPoints() { // turn velocity to work straight down normalisedVelocity = transform.TransformDirection(Vector3.down); projectionPoints.Clear(); projectionPoints.Add(ballObject.transform.position); Vector3 last = ballObject.transform.position; for (int i = 0; i < pointCount; i++) { Vector3 next = new Vector3(last.x, last.y - pointSpacing, last.z); Vector3 castHit; Vector3 norm; bool endOnNavmesh; if (navMesh.Linecast(last, next, out endOnNavmesh, out castHit, out norm)) { projectionPoints.Add(castHit); normalisedHitPoint = norm; isBallAboveMesh = endOnNavmesh; return; } else { projectionPoints.Add(next); } last = next; } normalisedHitPoint = Vector3.up; isBallAboveMesh = false; } } /// /// Represents the player's current use of the ball teleport machanic. /// public enum BallState { // The player is not using teleportation right now NONE, // The player is holding an instance of the ball prefab HELD, // The ball is currently flying and therefore "selecting" a destination THROWING }