Unity - How to react to scene picking? How to force select a parent by picking its child in the sceneview - unity3d

I have the following situation I need an answer to:
I have a parent object with children. These children all have unique meshes. Whenever these children are selected in the SceneView, the parent needs to be selected in stead. The children should never have their inspectors exposed (not even a fraction of a second).
How do I do this?
There are two possible solutions that I have come up with which are not the solution I wish to go for.
First being using [SelectionBase] attribute on the parent object. This work perfectly, but only once. The first time the child is selected, the parent gets selected. However, when I click the child again, it still gets selected.
Second solution was to react on Selecten.onSelectionChanged. This however is too slow. I can set the selection to the parent if a child gets selected, but the child gets exposed for a few frames still.
Is there an instant solution to this which can guarantee me the functionality of SelectionBase, but then every time in stead of only the first time I click it?
Thanks in advance! :)

I have found a way to do exactly what i want. I combine the [SelectionBase] attribute with a piece of code in the editor OnSceneGui.
First add the [SelectionBase] attribute to your class
Second add this code to its editor class
private void OnSceneGUI()
{
HandleUtility.AddDefaultControl(0);
//Get the transform of the component with the selection base attribute
Transform selectionBaseTransform = component.transform;
//Detect mouse events
if (Event.current.type == EventType.MouseDown)
{
//get picked object at mouse position
GameObject pickedObject = HandleUtility.PickGameObject(Event.current.mousePosition, true);
//If selected null or a non child of the component gameobject
if (pickedObject == null || !pickedObject.transform.IsChildOf(selectionBaseTransform))
{
//Set selection to the picked object
Selection.activeObject = pickedObject;
}
}
}
This allows the first pick to select the component. From then on, only when you select non-child objects in the scene, selection will actually change.

Related

How do I arrange a list of buttons into rows of 4

I am trying to display the same game object in a table form of 4 columns and 2 rows so it would look like this:
GO GO GO GO
GO GO GO GO
G0 - gameObject
My gameObject is a button that can be pressed and has a text element on it to display the name of the profile on the button.
I have a List of strings of the names that i need to display on these GO buttons in the table form but i am struggling to position them correctly.
At the moment i have gotten to the point where i can Instantiate them so they all appear on the screen when the game is running, now i just need some advice on how i can position them properly in the format mentioned above.
How do i do this?
This is my code that i used to get the names and add them to the List:
private void GetProfiles()
{
List<string> profileNamesList = new List<string>();
if (Directory.Exists(filePath))
{
string[] files = Directory.GetFiles(filePath);
profileTileTemplate.SetActive(true);
foreach (var file in files)
{
string name;
name = file;
int index = name.IndexOf(filePath + "/", System.StringComparison.OrdinalIgnoreCase);
if (index >= 0)
{
int pathIndexEnd = index + filePath.Length + 1;
int stringLength = name.Length - pathIndexEnd - 5;
name = name.Substring(pathIndexEnd, stringLength);
}
profileNamesList.Add(name);
}
}
DisplayProfiles(profileNamesList);
}
private void DisplayProfiles(List<string> names)
{
}
I have tried using a for loop with in another for loop but i just end up instantiating multiple of the same game object.
This is what i get now:
And this is what i want it to look like:
This is kind of two questions, and I just realized that Unity has a built in component that will do this automatically, so I'll leave the three answers below.
How do I arrange UI gameobjects in rows?
Since it seems like you want to do this for UI elements there's actually a very easy solution. Add an empty GameObject as a child of your Canvas and add a Vertical LayoutGroup component. Add two children to that with horizontal layoutgroups. Add four placeholder prefabs of your gameobject to each horizontal layout group. Arrange them and configure the settings to get them looking the way you want (and make note of what happens if you add fewer than four items!)
Once you have it all set up, delete the placeholders. Then you can add your gameobjects to the horizontal group using Instantiate(Object original, Transform parent) (link) to parent them to the layout group, which will keep them arranged neatly. You can keep a list of those groups and each time you add four, switch to the next parent.
A neater way that seems to fit your use case (assuming there can potentially be more than 8 profiles) is to make a Scroll View that holds horizontal layout groups, which will each be a row of entries. That way instead of tracking which parent you want to add gameobjects to, you can just instantiate a new row every time you pass four entries.
If you're sure there will only ever be eight or fewer, the easiest thing to do would just be arrange eight blank buttons in the UI however you want them to appear. Then keep a list of the eight buttons and edit the text/image on them, no instantiation or looping necessary.
How do I split up a list of gameobjects into rows?
The actual code to process the list is below. This is just an example and there are plenty of different ways to do it, but I thought this demonstrated the logic clearly without depending on what UI elements you choose. As a rules of thumb, make sure to figure out the layout elements you like in Unity first using placeholders (like scroll view etc) and then figure out the code to fill them in. In other words, instantiating and laying out UI elements at runtime is a great way to give yourself a headache so it's best to only do it when you need to.
List<string> profileNamesList = new List<string>();
public int entriesPerRow; //only public so you can edit in the inspector. Otherwise, just use the number per row in your prefab.
public GameObject profileRowPrefab;
public GameObject scrollViewLayout;
private void DisplayProfiles(List<string> names)
{
int i = 0;
while( i < names.Count ) //"while the list has more names"
{
//create a new row as a child of the scroll view content area
//and save a reference for later
GameObject go = Instantiate(profileRowPrefab, scrollViewLayout);
for(j = 0; j < entriesPerRow; j++) //"add this many names"
{
if(i < names.Count)
{
//instantiate a button, or edit one in the row prefab, etc.
//depending on how you're doing it, this is where you'd use the go variable above
YourProfileButtonCreationMethod(names[i]);
i++;
}
else
{
//we've finished the list, so we're done
//you can add empty placeholders here if they aren't in the prefab
break;
}
}
}
}
YourProfileButtonCreationMethod will depend completely on how you want to implement the UI.
I wish I had thought of this an hour ago, but I've never used it myself. Unity has a built in layout feature that will do this for you (minus the scrolling, but you may be able to nest this in a scroll view).
How do I arrange UI elements in a grid?
Instead of making your own grid with horizontal and vertical layout groups, you can use the Grid Layout Group. Then just instantiate each item in the list as a button with the grid layout as their parent (see above). Here's a short video tutorial that shows what the result looks like.

MRTK Grid Object Collection UpdateCollection() not working if called from script

I am using the GridObjectCollection Script from MRTK to display a number of buttons. I am instantiating the buttons dynamically through a script and place them as children of the GridObjectCollection GameObject. Now you can Update the collection through the UpdateCollection() function. Everything works as expected except the following case:
If i want to display a set of different buttons in the same GridObjectCollection, i destroy all children of the collection and instantiate the new buttons as before and call the UpdateCollection() function. But the collection is not updating properly. The old buttons were destroyed and the new added, but the placement is shifted. If i click the UpdateCollection button in inspector, the collection is correctly updated.
Why UpdateCollection() is not working as expected after modifying the content of the collection?
I just fixed the problem. If the collection is updated in the next frame, the update works as expected.
So i am starting a Coroutine and call the update function there.
private IEnumerator InvokeUpdateCollection()
{
yield return null;
gridObjectCollection.UpdateCollection();
}

Unity: transform child's position and leave parent where it is

I want to move the itemChild away from the parent, but the parent always move to the new position with the child. Why the parent follow the child's position? How can I move the child and leave the parent where it is.
My code:
GameObject go = new GameObject("go");
go.transform.parent = GameObject.FindGameObjectWithTag("Canvas").transform;
itemChild.transform.SetParent(go.transform);
itemChild.transform.localPosition = new Vector2(itemChild.transform.position.x, itemChild.transform.position.y + 250);
Parent should stand still. Are you sure parent is moving with the child ? I mean you are just creating an empty GameObject, can share with us the x,y,z of parent and child ? I tried your code with a bit of change and it is working for me. Here is my code : https://imgur.com/khXrZ07 If you just create an empty game object and click on it in the canvas, since your parent("go" in your case) is empty it will just focus on your child object and it might confuse you.

A panel to change 3d object properties when right click on it

I'm building a UI system to edit selected 3D objects. I have spheres on the screen and I want to be able to edit their proprieties (radius for example). In order to do that I want the user to be able to click on a sphere and then it show a panel next to the object. When the user changes slider value the radius of the sphere which as been clicked on change. Using the new unity event system, I think it's easy to achieve, but I'm a new to unity and even if I know the basic I don't know how to organize this properly.
At the end my goal would be to select multiple sphere, right click and edit the radius to all selected spheres.!
Any tips on how to do that ? Sorry for my poor english. Thanks
Here's how you can do it:
Keep a bool for tracking if the object is selected, say, bool _isSelected
Toggle this boolean in the OnMouseUpAsButton() function. This function is called when you click on an object and release that click on the same object. Unity's OnMouseXXX() interfaces are very good for handling mouse events. You just need to define this function in the sphere objects' script.
Put together a Slider and arrange its value interval from editor. Define a function that will edit the scale value of the spheres and tie that function to that slider.
Below you can see an example of how I did that in a game of mine.
Unity simply catches the slider's value if you provide it with a public void _f(float val). It will appear on the top of the list as "Dynamic float". So, in your case, your slider update function should look like this:
public void SphereScaleSliderUpdate(float val)
{
foreach (GameObject sphere in GameObject.FindGameObjectsWithTag("your tag here"))
{
if (sphere.IsSelected) // don't forget to provide a public getter for _isSphere variable
{
sphere.transform.localScale = new Vector3(val, val, val);
}
}
}
Also, you need to create another script for toggling the menu(Canvas/Panel(optional)) that contains the slider, and you may add it to another object, like a UIManager. Assign the Canvas in the game hierarchy to a variable in that script, and then scan for RClick in the Update() function of the UIManager.
.
void Update()
{
if (Input.GetMouseButtonDown(1) // RClick event
{
// makes the slider appear through enabling canvas
_canvasHandle.enabled = true;
}
}

Show/ hide Unity 3D elements

I hope I am posting in the correct section. I know that it is possible to write a custom code in Unity so I have the following questions:
Imagine a house model in Unity. Would it be possible to have a code which helps to hide/unhide certain objects? For example, letter "W" would hide/unhide all windows, letter "C" would hide/unhide all columns etc.
If it would be possible to develop a code for that, what would be the workflow? How would Unity know what is window and what is door?
Taking one step further.. Would it be possible to have a code that unhides the next step of the project. For example, the first step would be building foundations. Would it be possible to have a code that would unhide the next step, say, 1st floor floor element, with a klick of a keyboard key? And then with the same key unhide the next step which might be 1st floor walls. And would it be possible with another key go backwards?
If such code would be possible, what do you think would be the workflow? How would Unity know which element is in which step?
Yes just have a class with an array of objects to show/hide and another property for what button will do it. Then just have a method that will hide/unhide each object in that class. In the update method of a behavior just check the input and call the method of the class based on what button was pressed.
Would it be possible to have a code which helps to hide/unhide?
Yes, you can do that by calling GameObject.SetActive() function
door.SetActive(true); // door is a game object;
How would Unity know what is window and what is door?
You can give the window/door a name, then access the game object by name
var door = GameObject.Find("MyDoor") as GameObject;
Would it be possible to have a code that would show the next step, say, 1st floor floor element, with a klick of a keyboard key?
Yes, you can do that with code snippet below:
int step = 1;
void Update() {
if (Input.GetKeyDown("space")){
print("space key was pressed");
step++;
}
if(step == 1) {
// do sth
}
else if(step == 2) {
// do sth
}
}
Given the above snippet, your last answer can be easily deduced.
I see 2 ways of doing what you asked.
1st way
GameObject[] walls;
walls = GameObject.FindGameObjectsWithTag("Wall");
foreach (GameObject wall in walls)
{
wall.setActive(true);
}
What you will have to do, besides the code, is to assign to those object a correct tag (like "Wall" "Window" "WatheverYouWillNeed"). So you can easily find all objects by giving them some kind of order (with tags).
Best practice tip
You may want to create a static class "Tags", and set public constants string inside the class, with all tag names.
public static class Tags
{
public static const WINDOW_TAG = "Window";
// ... more tags
}
2nd way
If what you want to set is only "visibility", you may want to modify only camera tag rendering. It doesn't even require any cycle at all. When you want to view windows, you just tell your camera to render the tag "Window".
// Turns on Windows Layer on mask using an OR operation
private void Show()
{
camera.cullingMask |= 1 << LayerMask.NameToLayer("Window");
}
// Turn off the bit using an AND operation with the complement of the shifted int:
private void Hide()
{
camera.cullingMask &= ~(1 << LayerMask.NameToLayer("Window"));
}
It may look odd the second option, but keep in mind that the cullingMask is composed of bits, and every bit is a Layer defined by your tags. The "Everything" culling mask is 111111. If you want to see only Window, for example, and window is the last element, your culling mask would look something like 0000001
Hope it helped! :)