Jump to content
Sign in to follow this  
Naticus

Scripting for teleport from collision target

Recommended Posts

Hi guys,  I just recently purchased ProBuilder and ProGrids and am really impressed by these tools!  I've designed levels in the Valve Hammer Editor for years and this finally gives me a similar work flow for Unity. 

 

I'm not a programmer and am really bad at writing my own Unity scripts.  I need a little help altering a teleport script so it will work with ProBuilder objects. I've found that the teleport in the code snippet below behaves differently for standard ProBuilder objects versus standard Unity objects. 

 

The script takes the beam collision target and works out the height of that object by getting its scale and y position, then calculating the world y of the top of that object. The issue I am running into with ProBuilder objects is that the teleport is not taking the player the y position at the top of the object.  It varies based on the height of the object. Here is the code snippet in question:

 

private float GetTeleportY(Transform target, Vector3 tipPosition)

{

float newY = this.transform.position.y;

    // Check to see if the tip is on top of an object

if (target && tipPosition.Y > (target.position.y + (target.localScale.y / 2)))

{

     newY = (target.position.y + (target.localScale.y / 2));

}

return newY;

}

 

Can someone explain why the ProBuilder Object is behaving differently than the Unity Object for this script?

 

Thanks in advance!

 

Share this post


Link to post
Share on other sites

I think the problem is that this script assumes the mesh pivot point is the center, which is not always the case (for ProBuilder objects or otherwise).

 

To get the world position of a point on a mesh you could do something like this:

private Vector3 GetTeleportPoint(Transform target, Vector3 tipPosition)
{
	if(target)
	{
		// keep the x,z coordinates of the tip position
		Vector3 newPosition = tipPosition;

		// Mesh bounds are in local space
		Bounds meshBounds = target.GetComponent<MeshFilter>().sharedMesh.bounds;

		// This is world space coordinate of the top center of a mesh
		Vector3 meshBoundsUp = transform.TransformPoint(meshBounds.center + meshBounds.extents);

		// Set the height to match the top of the mesh's bounding box.
		newPosition.y = meshBoundsUp.y;

		return newPosition;
	}
	else
	{
		return tipPosition;
	}
}

Share this post


Link to post
Share on other sites

Thanks!  I will try working that into the teleport script.  I believe it was written only to work with objects that have box colliders.  Your code looks like it will be more useful to a wide variety objects, not just those with simple box colliders. 

Share this post


Link to post
Share on other sites

Yes, I absolutely need help!  :D  As I mentioned, I am horrible with scripting. 

 

Here is the original script:

 

using UnityEngine;

using System.Collections;

public class SteamVR_HeightAdjustTeleport : SteamVR_BasicTeleport {

     public bool playSpaceFalling = true;

     GameObject currentFloor = null;

     protected override void Start()

     {

          base.Start();

          adjustYForTerrain = true;

     }

     protected override void DoTeleport(object senter, WorldPointerEventArgs e)

     {

          base.DoTeleport(sender, e);

          DropToNearstFloor(false);

     }

     protected override Vector3 GetNewPosition(Vector3 tipPosition, Transform target)

     {

          Vector3 basePosition = base.GetNewPosition(tipPosition, target);

          basePosition.y = GetTeleportY(target, tipPosition);

          return basePosition;

     }

     private float GetTeleportY(Transform target, Vector3 tipPosition)

     {

          float newY = this.transform.position.y;

          //Check to see if the tip is on top of an object

          if (target && tipPosition.y > (target.position.y + (target.localScale.y / 2)))

          {

               newY = (target.position.y + (target.localScale.y / 2));

          }

          return newY;

     }

     private void DropToNearestFloor(bool withBlink)

     {

          //send a ray down to find the closest object to stand on

          Ray ray = new Ray(eyeCamera.transform.position, -transform.up);

          RaycastHit rayCollideWIth;

          bool rayHit = Physics.Raycast(ray, out rayCollidedWith);

          if (rayHit && currentFloor != rayCollidedWith.transform.gameObject)

         {

              currentFloor = rayCollidedWith.transform.gameObject;

              float floorY = (currentFloor.transform.position.y + (currentFloor.transform.localScale.y / 2));

              if (withBlink)

             {

                  Blink();

             }

             Vector3 newPosition = new Vector3(this.transform.position.x, floorY, this.transform.position.z);

             SetNewPosition(newPosition, currentFloor.transform);

        }

}

private void Update()

{

     if (playSpaceFalling)

     {

          DropToNearestFloor(true);

     }

}

 

 

I tried just replace your snippet but the script kept throwing errors I did not know how to fix.   Hope this update can be made easily.  Thanks for your help!!

 

 

 

SteamVR_HeightAdjustTeleport.zip

Share this post


Link to post
Share on other sites

Oh okay, you don't even need anything that complex.  Just get the world Y value from the raycast instead.  Check out line 49 (I left the old line commented out at the end so you can see what I changed).

using UnityEngine;
using System.Collections;

public class SteamVR_HeightAdjustTeleport : SteamVR_BasicTeleport
{
	public bool playSpaceFalling = true;
	GameObject currentFloor = null;
	protected override void Start()
	{
		base.Start();
		adjustYForTerrain = true;
	}

	protected override void DoTeleport(object senter, WorldPointerEventArgs e)
	{
		base.DoTeleport(sender, e);
		DropToNearstFloor(false);
	}

	protected override Vector3 GetNewPosition(Vector3 tipPosition, Transform target)
	{
		Vector3 basePosition = base.GetNewPosition(tipPosition, target);
		basePosition.y = GetTeleportY(target, tipPosition);
		return basePosition;
	}

	private float GetTeleportY(Transform target, Vector3 tipPosition)
	{
		float newY = this.transform.position.y;
		//Check to see if the tip is on top of an object
		if (target && tipPosition.y > (target.position.y + (target.localScale.y / 2)))
		{
			newY = (target.position.y + (target.localScale.y / 2));
		}

		return newY;
	}

	private void DropToNearestFloor(bool withBlink)
	{
		//send a ray down to find the closest object to stand on
		Ray ray = new Ray(eyeCamera.transform.position, -transform.up);
		RaycastHit rayCollideWIth;
		bool rayHit = Physics.Raycast(ray, out rayCollidedWith);

		if (rayHit && currentFloor != rayCollidedWith.transform.gameObject)
		{
			currentFloor = rayCollidedWith.transform.gameObject;
			float floorY = rayCollidedWith.point.y; // (currentFloor.transform.position.y + (currentFloor.transform.localScale.y / 2));

			if (withBlink)
			{
				Blink();
			}

			Vector3 newPosition = new Vector3(this.transform.position.x, floorY, this.transform.position.z);
			SetNewPosition(newPosition, currentFloor.transform);
		}
	}

	private void Update()
	{
		if (playSpaceFalling)
		{
			DropToNearestFloor(true);
		}
	}
}

Share this post


Link to post
Share on other sites

Thanks again for your help working out the changes to this script.  I should have mentioned that there are two ways to teleport in the script.  The section you updated "DropToNearestFloor" teleports the player to a lower area using the raycast from the head.  This will get that part working for me.  More importantly, the section above that, "GetTeleportY" is the one that teleports the player using the end tip of a Bezier curve coming off the player controller.  Is there an easy way work out the y-axis height other than the (target.localScale.y / 2)? 

Share this post


Link to post
Share on other sites

Yes, just replace the code in GetTeleportY with the code I posted earlier:

private float GetTeleportY(Transform target, Vector3 tipPosition)
{
	// Comment out this code

	// float newY = this.transform.position.y;
	// //Check to see if the tip is on top of an object
	// if (target && tipPosition.y > (target.position.y + (target.localScale.y / 2)))
	// {
	//    newY = (target.position.y + (target.localScale.y / 2));
	// }
	// return newY;

	// Add this code
	if(target)
	{
		// keep the x,z coordinates of the tip position
		Vector3 newPosition = tipPosition;

		// Mesh bounds are in local space
		Bounds meshBounds = target.GetComponent<MeshFilter>().sharedMesh.bounds;

		// This is world space coordinate of the top center of a mesh
		Vector3 meshBoundsUp = transform.TransformPoint(meshBounds.center + meshBounds.extents);

		// Set the height to match the top of the mesh's bounding box.
		newPosition.y = meshBoundsUp.y;

		return newPosition.y;
	}
	else
	{
		return tipPosition.y;
	}
}

Share this post


Link to post
Share on other sites

Your code worked great for the location and height adjustment in teleporting to a new object however there is a problem when teleporting to a different spot on the same object the player is standing on.  The result is a huge increase in height for every new teleport on the same object. It effectively makes the player hover higher and higher above the object.  As soon as a new object is teleported to, the height in y is correct and the player is standing on top of the collision surface. 

 

Any idea why the player is being teleported way above the object when teleporting to a different spot on that same object?  Thanks in advance.  You have been a ton of help.  Hopefully we can transition to ProBuilder questions soon!  :P

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×