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?
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
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();
}
}
}
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
}
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
}
var mySkin : GUISkin;
//attach this script to your first person controller
private var buttonOn : boolean;
private var windowOn1 : boolean;
private var windowOn2 : boolean;
private var windowOn3 : boolean;
private var windowRect1 : Rect = Rect (20, 200, 620, 470);
private var windowRect2 : Rect = Rect (340, 217, 595, 415);
private var windowRect3 : Rect = Rect (340, 217, 595, 415);//window parameters
var picture1: Texture2D;
var picture2: Texture2D;
var customButton : GUIStyle;
var customBox : GUIStyle;
var videoTexture1: MovieTexture;
var videoTexture2: Texture2D;
function OnTriggerEnter (other : Collider) {
buttonOn = true;
}
function OnTriggerExit (other : Collider) {
buttonOn = false;
}
function OnGUI () {
GUI.skin = mySkin;
if (buttonOn ==true) {
if (GUI.Button (Rect (360, 25, 100, 30), picture1, customButton)) {
windowOn2 = false;
windowOn3 = false;
}
if (GUI.Button (Rect (342,168,283,30), "THEN")) {
windowOn3 = true;
windowOn2 = false;
}
if (GUI.Button (Rect (625,168,283,30), "NOW")) {
windowOn2 = true;
windowOn3 = false;
}
if (GUI.Button (Rect (340,20,570,150), picture2, customBox))
if (windowOn2 == true) {
windowRect2 = GUI.Window (0, windowRect2, WindowFunction, videoTexture1);
}
if (windowOn3 == true) {
windowRect3 = GUI.Window (0, windowRect3, WindowFunction, videoTexture2);
}
}
//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 {
}
}
function WindowFunction (windowID : int) {
// Draw any Controls inside the window here
}
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 ();
}
}
I had the same problem and the second code work for me, but i need to put a 2d image inside a button that will apear inside the window but i cant
i start setting the var
var icon = Texture2D
then inside the window put the button
if (GUI.Button (Rect (10,50, 500, 447),icon)) {
print ("you clicked the icon");
}
and in the console i get this message "Assets/Player.js(43,23): BCE0023: No appropriate version of 'UnityEngine.GUI.Button' for the argument list '(UnityEngine.Rect, System.Type)' was found."
could somebody tell me how i can do that
sorry about my inglish but is not my native langage