x


Triggering a GUI based on proximity (and also just a click of a button)

I've been working on an educational Unity project for a while now, and I feel like I know quite a bit about design and GUI, but when it comes to coding, I'm up the creek. I would appreciate any help I can get because right now I feel like I have a nicely designed project, but if it doesn't function, there's no point.

Bottom line, I need to be able to approach an object in 3D space, then based on proximity to that object, two GUI buttons appear that then can be clicked to bring up separate GUI content windows with video.

Any suggestions for how I can trigger the GUI based on proximity? And then once that GUI appears (two buttons), they can control the visibility of a GUI window?

Thank you so much for any help you can provide!

more ▼

asked Jan 28 '10 at 02:35 AM

Melissa gravatar image

Melissa
147 4 5 11

(comments are locked)
10|3000 characters needed characters left

7 answers: sort voted first

Unity has nicely designed built in functionality for triggers. You probably want a sphereCollider, marked as a trigger. See sphere collider docs

Then, assuming you are using a characterController or other rigidbody to move, when that object enters the sphere trigger, a message will be sent calling OnTriggerEnter on all scripts attached to the characterController, and the trigger.

As for controlling the opacity, you can set the color of GUI by calling GUI.color

more ▼

answered Jan 28 '10 at 03:33 AM

Brian Kehrer gravatar image

Brian Kehrer
2.8k 9 11 50

Thanks! I still can't get the opacity to change. Right now, I have this script that works for a 3D object, but I'm not sure how to apply it to a GUI (or how to change the alpha):

//A Game Object with this script attached to it will turn blue when clicked on function OnMouseDown(){ gameObject.renderer.material.color = Color.blue; print ("Blue"); }

Jan 28 '10 at 07:41 PM Melissa
(comments are locked)
10|3000 characters needed characters left

You can use the Update() function in a script, which is called every frame, to compare the object's distance to the player with an amount you specify.

The easiest way is to just add the script to each object that could trigger the buttons (it is not the most efficient way, but if there are not that many objects running the script it won't matter).

Here's a rough idea. Add this to any of the 'approachable' objects, make sure you Tag your player object "Player", and give it a whirl.

using UnityEngine;
using System;
using System.Collections;

public class Approachable : MonoBehaviour 
{
    public float approachDistance = 5;
    public float buttonDrawOffsetHeight = 1.0f;

    [Serializable]
    public class MovieButton
    {
    	public string buttonText = "Play Movie";
    	public string movieName = "movie.mpg";
    }
    public MovieButton[] movieButtons = new MovieButton[1];

    private Transform playerTransform;
    private Transform myTransform;
    private bool drawButtons = false;

    void Start () 
    {
    	// find the player object - just do this once when we start up - assumes you've Tagged your player object "Player"
    	GameObject playerOb = GameObject.FindWithTag( "Player" );
    	if( playerOb != null )
    	{
    		playerTransform = playerOb.transform;
    	}
    	else
    	{
    		Debug.LogWarning("Could not find player object!  Turning off!");
    		this.enabled = false;
    	}

    	myTransform = transform;
    }

    // Update is called once per frame
    void Update () 
    {
    	if( !playerTransform )
    	{
    		this.enabled = false;
    		return;
    	}

    	// can compare squared magnitudes - a bit faster
    	float sqrDist = (playerTransform.position - myTransform.position).sqrMagnitude;
    	// square the distance we compare with

    	drawButtons = ( sqrDist < approachDistance*approachDistance );
    }

    void OnGUI()
    {
    	if( !drawButtons )
    		return;

    	// only draw if we're on screen
    	Vector3 buttonPos = myTransform.position + new Vector3(0, buttonDrawOffsetHeight, 0);  // adjust world Y position 
    	Vector3 screenPos = Camera.main.WorldToScreenPoint(buttonPos);

    	// render only if it's in the screen view port	
    	if (screenPos.x >= 0 && screenPos.x <= Screen.width && screenPos.y >= 0 && screenPos.y <= Screen.height && screenPos.z >= 0 ) 
    	{
    		Vector2 pos = GUIUtility.ScreenToGUIPoint(new Vector2(screenPos.x, Screen.height - screenPos.y));

    		GUILayout.BeginArea( new Rect(pos.x, pos.y, 100, 300) );
    		GUILayout.BeginVertical();
    		foreach( MovieButton mb in movieButtons )
    		{
    			if( GUILayout.Button( mb.buttonText ) )
    			{
    				Debug.Log("Play this movie: " + mb.movieName);
    			}
    		}
    		GUILayout.FlexibleSpace();
    		GUILayout.EndVertical();
    		GUILayout.EndArea();
    	}
    }

}
more ▼

answered Jan 28 '10 at 03:56 AM

Molix gravatar image

Molix
4.8k 15 27 66

(comments are locked)
10|3000 characters needed characters left

I can't test this solution, because I don't have Pro to use movie textures. Whatever you want to be displayed in teh window belongs in the WindowFunction. Thus, your button controls belong there as well (unless you want them to be drawn besides the window). I'd add the buttons to control the movie texture within the appropriate window function like this (add this excerpt into the code above):

function WindowFunction (windowID : int) {

   if (GUI.Button (Rect (10,20,100,20), "Play"))
      renderer.material.mainTexture.Play ();
   }
   if (GUI.Button (Rect (10,100,100,20), "Stop"))
      renderer.material.mainTexture.Stop ();
   }
}
more ▼

answered Feb 15 '10 at 04:43 AM

Sebas gravatar image

Sebas
4k 12 18 45

Thanks! How can I apply this to multiple textures? I have this proximity code on about seven objects, and I seem to be having trouble distinguishing between the different video textures.

Feb 17 '10 at 10:04 PM Melissa

You already implemented the check for your GUItype (the script attached to each of your seven objects which gives it a unique ID; that's from my comment above). You can also use this check here to determine which texture is displayed. I suppose you have 1 video texture per object. Put another if-statement in your WindowFunction to check for the GUItype. You could also create different windows for each video texture and you put the check for your GUItype in your OnGUI function. In short, it's just another check for your GUItype (the variable which determines which object you collided with).

Feb 17 '10 at 11:44 PM Sebas
(comments are locked)
10|3000 characters needed characters left

It seems like triggering the buttons based on Brian's answer seems to work for you. So I modified my script to reflect the trigger functionality. I basically replaced the Update function with OnTriggerEnter and OnTriggerExit functions. Rest stays the same.

//attach this script to your first person controller
//make sure your target has a sphere collider attached which is set to "isTrigger"
//set the radius of the sphere collider to the distance which you want as your proximity-check

private var buttonOn : boolean;
private var windowOn : boolean;
private var windowRect : Rect = Rect (150, 150, 120, 50);//window parameters


function OnTriggerEnter (other : Collider) {
    	buttonOn = true;
    }

function OnTriggerExit (other : Collider) {
    	buttonOn = false;
    }


function OnGUI () {

        if (buttonOn == true) {

                if (GUI.Button (Rect (25, 25, 100, 30), "WindowOn")) {
                        windowOn = true;
                }

                if (GUI.Button (Rect (25, 100, 100, 30), "WindowOff")) {
                        windowOn = false;
                }

                if (windowOn == true) {
                        windowRect = GUI.Window (0, windowRect, WindowFunction, "My Window");
                }
        }       
        //take this "else-statement out if you want the activation of the window to be remembered, even if you leave the proximity of the object
        else {
                windowOn = false;
        }

    }

function WindowFunction (windowID : int) {
        // Draw any Controls inside the window here
    }
more ▼

answered Jan 29 '10 at 06:11 AM

Sebas gravatar image

Sebas
4k 12 18 45

This is awesome. It works, but my only problem is that it doesn't go away once you are no longer in the proximity. It does if you go back in the direction you came, but if you try to go around the object, it stays... no matter how far in the other direction you go. Perhaps my collider is not set up correctly...

Feb 02 '10 at 02:25 AM Melissa

That's odd. The collider shouldn't be much of a problem. If it's a sphere collider which is set to isTrigger, it should work. The OnTriggerExit function is responsible for making sure the buttons/window disappear. As soon as you leave the sphere collider, buttonOn is set to false which means the stuff in the OnGUI function is not called anymore. Doublecheck the OnTriggerExit. Not sure why it's causing such strange behavior.

Feb 03 '10 at 06:27 AM Sebas

I think what is happening is that the distance number when leaving the object (pushing past it through space) becomes less than what I've specified as proximity (if that makes sense). Do I need to do two functions-- one for each direction?

Feb 07 '10 at 05:17 PM Melissa

Did you use the first solution I posted? If so, I'd check whether the correct object is attached as targetObj in the inspector. The second answer is solely based on the sphere collider. What you describe sounds like your "distance" is based on the wrong object. If you push past it, distance should increase again. If you like, hit me up on Skype (psychosebas). It's hard to say what the problem is. If you use the above posted code as it is, the problem shouldn't be big.

Feb 07 '10 at 09:33 PM Sebas

I would like to use the second code you provided because I feel like it requires less work in terms of calculating the necessary proximity for each building, but it looks like this can only work for one building, not multiple ones. How do I specify that I want one GUI to appear for one building (say, a header with a description), and a different GUI for a different building (trigger)?

Feb 10 '10 at 05:05 PM Melissa
(comments are locked)
10|3000 characters needed characters left

I'm rather new to scripting, so it might not be optimized or clean, but it should give you an idea of what you could do. It works for me. Attach the script to your first person controller and drag a target GameObject on it in your editor.

//attach this script to your first person controller

var targetObj : GameObject;//drag your target into this slot in the editor
var proximity : float = 3;//change trigger proximity here or in editor
private var buttonOn : boolean;
private var windowOn : boolean;
private var windowRect : Rect = Rect (150, 150, 120, 50);//window parameters


function Update() {

    	var dist = Vector3.Distance(targetObj.transform.position, transform.position);

    	//check whether you are within target proximity
    	if (dist < proximity) {
    		buttonOn = true;
    	}
    	else {
    		buttonOn = false;
    	}	
    }

function OnGUI () {

    	if (buttonOn ==true) {

    		if (GUI.Button (Rect (25, 25, 100, 30), "WindowOn")) {
    			windowOn = true;
    		}

    		if (GUI.Button (Rect (25, 100, 100, 30), "WindowOff")) {
    			windowOn = false;
    		}

    		if (windowOn == true) {
    			windowRect = GUI.Window (0, windowRect, WindowFunction, "My Window");
    		}
    	}	
    	//take this "else-statement out if you want the activation of the window to be remembered, even if you leave the proximity of the object
    	else {
    		windowOn = false;
    	}

    }

function WindowFunction (windowID : int) {
    	// Draw any Controls inside the window here
    }
more ▼

answered Jan 28 '10 at 03:43 AM

Sebas gravatar image

Sebas
4k 12 18 45

Thank you for your response! So, a GUI.Window is supposed to appear when I approach the Game Object? That doesn't happen... Hmmm. Did I need to add anything to this script?

Jan 28 '10 at 06:56 PM Melissa

Not really, As long as you drag your target onto the script into the editor it should work as is. I'll check again. You approach the target, two buttons appear to turn window on/off. I just copied the script again as I posted it here and used it. No problems. The window function has also been copied directly from the documentation --> http://unity3d.com/support/documentation/ScriptReference/GUI.Window.html

Jan 28 '10 at 08:05 PM Sebas

maybe the scale of your scene is different. try increasing the proximity range. do the buttons appear at all and only the window doesn't shop up?

Jan 28 '10 at 08:41 PM Sebas

None of it appears, but I will try increasing the proximity and let you know how that worked out. Thanks again!

Jan 29 '10 at 04:10 AM Melissa

This is a great solution and works well. Thank you! The only question I have is how can I open up a GUI window using nothing but the keyboard (As in "Press A To Open Window"). With this script attached to the camera, in creates a bad interface in my case. Suggestions?

Sep 15 '10 at 04:22 AM Erin
(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x3669
x39

asked: Jan 28 '10 at 02:35 AM

Seen: 3254 times

Last Updated: Jan 28 '10 at 02:35 AM