Task:
I want to create a context menu item that will work on GameObject selected in Hierarchy. This action should be available only when selected GameObject is a Prefab. I'm working on Unity 5.3.2
Code:
File MenuItems.cs is located in Assets\Editor
using UnityEditor;
using UnityEngine;
public static class MenuItems
{
[MenuItem("GameObject/Action on prefab", false, 14)]
private static void MenuActionPrefab()
{
}
[MenuItem("GameObject/Action on prefab", true, 14)]
private static bool ValidateMenuActionPrefab()
{
var isPrefab = PrefabUtility.GetPrefabParent(Selection.activeGameObject) != null;
return isPrefab;
}
}
Problem:
The problem is that the menu item is visible (and can be clicked) no matter if selected GameObject is a Prefab or not. I have tested code in debugger and value returned from ValidateMenuActionPrefab() is OK (true when I run it on Prefab, false on non-prefab gameobject).
I've read somewhere that Unity 5 have problem with validation methods but the example from UNITY EDITOR EXTENSIONS – MENU ITEMS about validation in Assets work perfectly OK.
Question:
So if this is a proper solution? Or is there another way to achieve same goal?
Additional info:
I have tried to run those methods simple way using:
[MenuItem("GameObject/Action on prefab")]
and
[MenuItem("GameObject/Action on prefab", true)]
but in this case there is no Action on prefab in context menu. Also this item isn't visible when I try not to set priority index.
Edit:
Behaviour should be simillar to Select Prefab item (visible on screenshot). When object isn't connected to prefab field should be grey and not-clickable. When object is a prefab field is black and can be clicked.
I've just set up a new unity3d project and your code works then clicking on the menu Gameobject/Action on Asset.
I'm using Unity3d 5.3.4f1. Maybe it's a bug in your specific version, however I didn't find any mention about this neither in the 5.3.3 nor the 5.3.4 changelog
However when you right-click and open the context menu over an object in the hierarchy, the ValidationMethod is not called, so the context menu element is always enabled. But if you actually click the context menu item, then the validation method is called; if it returns false then no action is performed.
Seems like a bug, but at least is safe to use it.
Prefab selected
Instance selected
Related
I have created an empty GameObject (named FileCabinet) and assigned 4 prefabs as children (CabinetFrame, Drawer01, Drawer02, and Drawer03).
I would like to be able to click on any of the drawers to open them, but everything that I have tried thus far returns the parent GameObject FileCabinet as the item clicked rather than any of the children.
What's the best/simplest approach to call a method when any of the drawer child objects are clicked?
Update:
#LoïcLeGrosFrère provided a solution that ultimately worked. I initially had problems with events not firing because my UI was blocking raycasts to my 3D file cabinet object. I resolved the issue by adding a Canvas Group component to my Canvas and unchecking Blocks Raycasts, but this then disabled all my UI components. I then added Canvas Group components to individual UI panels and checked Ignore Parent Groups. I'm not sure if this is the correct way to handle the raycast conflict but it did work for me.
The simplest approach I see is:
Add a Physics Raycaster component to your camera.
Add a Event System to your scene, with a Input System UI Input Module.
Then, add this script to your drawers:
using UnityEngine;
using UnityEngine.EventSystems;
public class Drawer : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log($"Open {gameObject.name}.");
}
}
I am creating a game project with a main menu and 3 other scenes (MyWorld, ImportWorld, Lab). I've made the buttons on the main menu clickable to go the 3 other scenes, and want to make a back button to return to the main menu from the 3 scenes. I managed to make the back button work on the Lab scene, but when I tried the same steps and scripts to the MyWorld and ImportWorld, it doesn't bring to the main menu although they are clickable and changes color when hovered and clicked.
Below is the current script I am using for the Lab scene which is working. However I had a problem when I wanted to change the name of the text, suddenly it didn't worked. But luckily I didn't save and just undo which makes the button workable again.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ReturnMainMenu : MonoBehaviour
{
public void returnMenu()
{
SceneManager.LoadScene("MainScene");
}
}
Below is the script I used for the Main Menu scene which opens up the 3 other scenes and works fine.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ButtonTrigger : MonoBehaviour
{
public void btnMyWorld() //function when clicked, open My World scene
{
SceneManager.LoadScene("MyWorld");
}
public void btnImportWorld() //function when clicked, open Import World scene
{
SceneManager.LoadScene("ImportWorld");
}
public void btnLab() //function when clicked, open Lab scene
{
SceneManager.LoadScene("Lab");
}
public void btnQuit()
{
Application.Quit();
}
public void btnReturnMainScene() //function when clicked, should return to Main Menu
{
SceneManager.LoadScene("MainScene");
}
//tried adding this function to the Back buttons but didn't work
}
I've tried various scripts (including entering scene index and scene name), changed the canvas position in hierarchy to be on top, have Event System and RayCast target but nothing seems to fix the problem. Does anyone know what might be the cause that makes the button clickable, changes color when hover but doesn't load the Main Menu scene? And as I mentioned I had problems with the workable back button at Lab scene which stops working when I changed my text name, so maybe does it have anything to do with the project settings/scripts?
Maybe you didn't put the scene in build setting.
As in my understanding from the information provided, you have established a working code, which you use in multiple areas and scenes.
Understanding from the issue you are presented:
A button has been established and is clickable Returning a colour change to prove it.
The text your tried to change is about the scene change within your script.
public class ReturnMainMenu : MonoBehaviour
{
public void returnMenu()
{
UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
//Has to be written exactly as the Scene you are aiming to load up.
//Change the steps to include SceneManagement.
}
}
Look up if it is written down correctly keeping Case sensitivity in mind.
Check if the scene is added to your Build settings Found here!
Another area to take a look at is, if you have attached the script to your button, where it gets told on what it is supposed to be doing, OnClick -> do this action.
See attached below!
(Make sure it is enabled for both: Runtime / Editor)
Make sure the button has "Interactable" as checked (Ticked on)
If you copied the button from another scene, within unity more often than not there will be issues with it that it won't work as intended, for that I recommend making a copy of the button you wish to use and make a prefab out of it. (Which can be used in every area that you desire and scenes you desire).
enter image description here
Unity has a component called Button as part of its UI system which you can use to subscribe on-click events to it through the inspector which is incredibly useful.
However, when projects get larger, I run into trouble in many situations:
events subscribed this way in the inspector are not rearrange-able which makes buttons that have lots of events difficult to manage
changing the contents of the scripts used for events can cause the button to not recognize a function that was used for an event which means you have to re-reference it
if anything happens to the GameObject or prefab that stores the Button component such as it getting corrupt then all your events that were serialized onto the button would be wiped and you would need to re-reference all of them
the above points make debugging very very difficult
What are some ways I can work around the problems I've listed above?
Inject event functions inside the code:
public Button playButton; // set button in inspector
public void Start()
{
playButton.onClick.AddListener(() =>
{
transform.position = point1;
// do something..
});
}
in an empty game object i have a selection Manger script. I'm having some difficulties with finding a panel (called: OpenSelection) I created in Canvas.
I would like to find the panel where ever it is in the hierarchy and set enabled to true.
But the code isn't finding the panel. I'm not sure why.
any help would be appreciated
//UI
private GameObject panel;
// Start is called before the first frame update
void Start()
{
panel = GameObject.Find("OpenSelection");
panel.SetActive(true);
}
Generally, Find() is never the best approach for anything.
Try to set a variable reference to your OpenSelection, like you did with your panel, then call this variable.
GameObject.Find() only returns active GameObject. Here you are trying to find the OpenSelection panel which is not active. That's why the Find() is not finding the OpenSelection panel.
I'm trying to make a tower defense game. It's 3D but mostly viewed from above (2D).
But if I right-click the mouse the camera can zoom and roam using WASD keys. This works.
However, beside the playing field I have a sidebar where I pick which towers to build and so forth.
But when in zooming/roaming the sidebar becomes useless, so I want to hide it.
I'm trying to do that from the camera-script, so I added a script-component to the sidebar to make it static (accessible from anderswo):
using UnityEngine;
public class SideBar : MonoBehaviour
{
public static SideBar Instance;
void OnEnable() { Instance = this; }
}
In the camera-controller-script I try the below to hide the sidebar (and everything inside):
SideBar.Instance.GameObject.SetActive(false);
But that won't compile: CS1061: 'SideBar' does not contain a definition for 'GameObject'
GameObject is the name of the class, the actual instance is referenced using lowercase gameObject, so change:
SideBar.Instance.GameObject.SetActive(false);
to
SideBar.Instance.gameObject.SetActive(false);
and you should be fine