Developing UI in Unity - unity3d

I am trying to develop a game using Unity, and I am in the step of developing some UI. I am going to need a player frame (to show the avatar of the active player in my turn based game), an inventory, and a settings button, with its setting screen.
The player frame thing was easy: I placed a Canvas, and an image. But, I want the inventory screen to appear when that frame is clicked. So, I created another Canvas, placed a plane inside, set a background for the inventory and a button to close it. The inventory panel is shown, but when I click the close button I receive an error. My code:
private void Awake()
{
canvas = GameObject.Find("Canvas");
inventoryPanel = GameObject.Find("InventoryPanel");
inventoryPanel.gameObject.SetActive(false);
this.raycaster = canvas.GetComponent<GraphicRaycaster>();
}
When I detect a click in UI component:
if (Input.GetMouseButtonDown(0))
{
PointerEventData pointerData = new PointerEventData(EventSystem.current);
List<RaycastResult> results = new List<RaycastResult>();
//Raycast using the Graphics Raycaster and mouse click position
pointerData.position = Input.mousePosition;
this.raycaster.Raycast(pointerData, results);
for(int i = 0; i < results.Count; i++)
{
print("Pulsada UI: "+results[i].gameObject.name);
if (results[i].gameObject.name.Equals("Playerframe"))
{
inventoryPanel.SetActive(true);
}
if(results[i].gameObject.name.Equals("Closeinventorybutton"))
{
inventoryPanel.SetActive(false);
}
}
}
Of course, the raycaster is cast from the first canvas, where the player frame is, the inventory is in ANOTHER canvas, and raycaster is null.
I tried to put the inventory screen in the first canvas, and get its reference this way:
inventoryPanel=canvas.gameObject.Find("InventoryPanel");
But seems like I cannot do that.
Do I need to create a separate canvas for any UI object in my screen, and get a separate raycaster from every canvas? The same canvas for all the UI components? if so, how can I get references to the different components?
I am having trouble finding tutorials that go further than "A button and a health bar". In other words, how to create a complex UI? A good tutorial would be fine.

You just need one canvas, you can use panels if you want to visually/hierarchically section off parts of your UI.
Edit: easiest way to get a reference to an object
public GameObject inventoryPanel;
in the inspector, you can drag and drop from the top left hierarchy into the script the inventory panel. Other options
GameObject.FindWithTag("TagName");
is a good one when you want to reference a component use
inventoryPanel.GetComponent<componentType>().variableofcomponent..

Related

Unity3d how to have a "text label" follow a game object

In my world I have a GameObject, lets say a race car.
Above the race car I want a white box with a text label that says the driver's name (let's say "Fred"). This label will follow the race car as it moves.
How can I create this "white box with a text label" in Unity? Will it be a gameobject (if so, how do I construct it).
Since the only "Text" object I find in Unity editor was a UI element. And the UI canvas is unrelated to the coordinates in my game world, so I dont know if this is appropriate use case for this type of object.
I think you have 2 options. It is fundamental to read to the different canvas modes in the documentation
1.- Overlay mode. You can figure out the screen position of your canvas at runtime with
Camera.WorldToScreenPoint. This canvas mode places UI elements on the screen rendered on top of the scene, so you would kind of "fake" the canvas is following your gameObject in the world calculatig and updating its position.
2.- World Space, make cavas children of your car and make it always face the camera with a billboard component updating the canvas rotation.
public class Billboard : MonoBehaviour {
public Transform cam;
private void Start() {
cam = Camera.main.transform;
}
void LateUpdate() {
transform.LookAt(transform.position + cam.forward);
}
}
Add the component to the canvas gameObject or do myCanvas.AddComponent<Billboard>(); where myCanvas is the Canvas component in your canvas holder gameObject.
With this option the canvas is actually in the world as the mode name indicates, thats why you would need to update its rotation to make it always look to the camera.
In option 1 you need to update the position and not the rotation because the canvas is in the screen and with option 2 you need to update the rotation and not the position because the canvas is in the world.

How to make transparent UI visible in Unity Editor?

BackGround
In Unity 2020LTS, I want to make a UI scene.
But in Game Panel, I discovered, although a animation is set at beginning (no conditions), the game will show what I see in Editor panel for a little time at first, then play the animation.
The StateMachine is Entry -> Target(Default)
I don't want show player what I see in editor, but only the first frame in animation.
I guess this is because loading level costs some time (almost 0.5 secs).
Question
So I try another way, make initial state of all objects be same as the first frame of animation.
This way work, seems just like it freeze at first frame for 0.5secs. However, I can't edit those objects visibly (Because they all are transparent in first frame).
I have tried Gizmos, but they don't work well. Besides, Gizmos makes me have to create lots of classes in C# scripts for each object, which just is component of animation and has no script.
Could there be any better way to show transparent (UI) object in editor scene only ?
Assuming you have some Game Manager script, you can add a GameObject, assigning it the UI element, and in the Start() function of the script, make it inactive, like so:
public class GameManager : MonoBehaviour
{
public GameObject menu;
void Start()
{
menu.SetActive(false);
//other statements
}
}
I'm not quite sure what you asked for but if you want to edit the UI elements that are invisible you can simply select them by using the hierarchy and edit them. I have linked an image with an example of this. Example scene with invisible panel
Image img;
// Start is called before the first frame update
void Start()
{
img = GetComponent<Image>();
img.color = new Color32(0, 0, 0, 0);
}
The code above will set the panels transparency to zero when the scene starts, make sure to use the using unity.UI namespace in order to acess the image component.

How do I make a UI button follow a 2d game object?

new to unity and have stumbled into a problem. I am unsure about how to make a UI button on a canvas follow a rigidbody game object in the 2D screen space. I want to make the button's x and y position match the x and y of the RB, so that in theory if I clicked on the RB it would activate the button. However, I am unsure how to implement such a thing into my project.
I tried using transform.position and just equating the ui button pos and RB pos together but it remained static. However, I did script the RB to move to a designated x point and only then did the button move as well despite the RB moving beforehand.
This is literally the example in the documentation for Camera.WorldToScreenPoint(). Attach this script to your button:
public class ObjectFollow : MonoBehaviour
{
public Transform Follow;
private Camera MainCamera;
// Start is called before the first frame update
void Start()
{
MainCamera = Camera.main;
}
// Update is called once per frame
void Update()
{
var screenPos = MainCamera.WorldToScreenPoint(Follow.position);
transform.position = screenPos;
}
}
I have an Idea To Implement that You can use a world space canvas and as a child of that object "that you mentioned has rigid body " now when it moves the canvas and its content will move watch this to
Understand Canvas
Now press on your object at hierarchy window Right Click then UI then Button
this will create new canvas and attach button to will look like this
hierarchy
Now Select Canvas and reset its width and height , set scale to small value 0.01 ,0.01 0.01 for example
then change its Rendering Mode to Space World and assign camera
you should insure that you have event system it should created automatically but events will never work if you don't have a one in hierarchy Window
canvas will like this
Canvas Image
Now You can Select this Button and add a function to call using onClick or you can pass it to another script to add this function dynamically like this
Add Event to UI Button From Script

Merge cameras to one

I have two cameras in my scene - one to follow 2D objects and the other for 3D Models (the second camera has other angle of view than first).
I want to create third camera that combines views from that 2 cameras (so, that camera should "see" what the user "see the game"). Is that possbile?
Why I want such a camera? I would like to make a screenshot. I know I can user Application.CaptureScreenshot but it also captures UI elements. Why can't I disable UI elements for a few miliseconds only when screen is captured? Because the effect is ugly (user sees the moment when UI disappears for a while). So my idea is to create third camera that ignores UI layers (but sees exactly what users sees). make render a frame to file and destroy that camera.
My code so far:
private IEnumerator CaptureScreenCoroutine()
{
// Wait till the last possible moment before screen rendering to hide the UI
yield return null;
this.EnableUi(false);
// Wait for screen rendering to complete
yield return new WaitForEndOfFrame();
// Take screenshot
Application.CaptureScreenshot("SomeScreenShor.png");
this.EnableUi(true);
}
With a Monobehaviour that is attached to the last rendered camera you will need something like this.
void LateUpdate(){
Texture2D tex = new Texture2D(Screen.width, Screen.height);
tex.ReadPixels(new Rect(0,0,Screen.width,Screen.height),0,0);
tex.Apply();
}
this will dump all pixels on the screen into "tex" then you can do what you will with it. This might cause a hitch depending on the resolution that you are rendering the cameras at.

Unity3d with vuforia showing 2d image when targed is detected

I have a question about the way how to show simple 2d image on top of detected marker. I have followed some tutorial to show 3d model and it works fine. there is no problem with 3d.
The problem starts when I want to add normal 2d object->sprite . When I add simple sprite I can't add texture and when I insert UI image it's added together with canvas and it is not showed when target is
detected. The original image on editor is placed then so far that it's difficult to find it.
I would be grateful if somebody can highlight me the right direction.
I need to make this image touch sensitive like a button. Clicking into it must show new scene ( I have it but under GUI.Button). The best way is to replace original marker but I can also make new sprite bigger to hide marker under it.
To help understand the answer, here's a quick rundown on how Vuforia handles marker detection. If you take a look at the DefaultTrackableEventHandler script that's attached to the ImageTarget prefab, you'll see that there are events that fire when the when the tracking system finds or loses an Image.
These are OnTrackingFound (line 67) & OnTrackingLost (line 88) in DefaultTrackableEventHandler.cs
If you want to show a Sprite when tracking, all you need to do is put the Image Target prefab (or any other) and make the Sprite a child of the prefab. The enabling and disabling should happen automatically.
However, in case you want to do something more, here's some edited code.
DefaultTrackableEventHandler.cs
//Assign this in the inspector. This is the GameObject that
//has a SpriteRenderer and Collider2D component attached to it
public GameObject spriteGameObject ;
Add the below lines to OnTrackingFound
//Enable both the Sprite Renderer, and the Collider for the sprite
//upon Tracking Found. Note that you can change the type of
//collider to be more specific (such as BoxCollider2D)
spriteGameObject.GetComponent<SpriteRenderer>().enabled = true;
spriteGameObject.GetComponent<Collider2D>().enabled = true;
//EDIT 1
//Have the sprite inherit the position and rotation of the Image
spriteGameObject.transform.position = transform.position;
spriteGameObject.transform.rotation = transform.rotation;
And the below to OnTrackingLost
//Disable both the Sprite Renderer, and the Collider for the sprite
//upon Tracking Lost.
spriteGameObject.GetComponent<SpriteRenderer>().enabled = false;
spriteGameObject.GetComponent<Collider2D>().enabled = false;
Next, your question about detecting clicks on this Sprite. Unity's Monobehaviour fires events for a lot of mouse events, such as OnMouseUp, OnMouseDown etc.
Link to Monobehaviour on Unity's API docs
What you will need is an event called OnMouseUpAsButton
Create a new script called HandleClicks.cs and add the below code to it. Attach this script as a component to the spriteGameObject that you assigned for the above.
public class HandleClicks : MonoBehaviour {
//Event fired when a collider receives a mouse down
//and mouse up event, like the interaction with a button
void OnMouseUpAsButton () {
//Do whatever you want to
Application.LoadLevel("myOtherLevel");
}
}