How to Lock RectTransform's fields - c#

I'm creating custom layout group and I want to control RectTransform on the child Objects. I want to lock some fields on child's RectTransform like when using canvas or Unity's Horizontal or Vertical group so that it cannot be modified.
I need the same effect. You can see this message on top of child's RectTransform : Some values driven by HorizontalLayoutGroup
I find a halfway :
Adding [ExecuteInEditMode] then:
public void Update()
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
/* Todo => update child's positions here. */
}
#endif
}
Any other idea?

This is done with the DrivenRectTransformTracker API.
From the doc:
Driving a RectTransform means that the values of the driven
RectTransform are controlled by that component. These driven values
cannot be edited in the Inspector (they are shown as disabled). They
also won't be saved when saving a scene, which prevents undesired
scene file changes.
Whenever the component is changing values of driven RectTransforms, it
should first call the Clear method and then use the Add method to add
all RectTransforms it is driving. The Clear method should also be
called in the OnDisable callback of the component.
No example from the doc but below is how to use it:
public RectTransform targetRC;
UnityEngine.Object driver;
void Start()
{
DrivenRectTransformTracker dt = new DrivenRectTransformTracker();
dt.Clear();
//Object to drive the transform
driver = this;
dt.Add(driver, targetRC, DrivenTransformProperties.All);
}
The RectTransform linked to the targetRC variable will now be locked and cannot be modified from the Editor. It should now say something like "Some values are driven by another object". You can use DrivenTransformProperties to specify which variables to lock.
This is what it looks like after executing this code:

Related

How to assign programmatically an Interactable Event in MRTK

I am creating a map in Unity3D to be displayed in Hololens 2. I am using MRTK to code interactable functionality.
Since the map contains a lot of independent countries, whose respond onClick may vary with time, I decided to add the required components programmatically. For example:
public Transform worlmap;
public Microsoft.MixedReality.Toolkit.UI.Theme mTheme;
public Microsoft.MixedReality.Toolkit.UI.States mStates;
List<Microsoft.MixedReality.Toolkit.UI.Theme> themes;
void Start()
{
themes = new List<Microsoft.MixedReality.Toolkit.UI.Theme>();
if (mTheme)
{
themes.Add(mTheme);
}
// Looping through all the countries in worldMap
foreach (Transform country in worldMap)
{
// Adding the 4 components which make a GameObject interactable in Hololens
country.gameObject.AddComponent<Microsoft.MixedReality.Toolkit.UI.Interactable>();
country.gameObject.AddComponent<Microsoft.MixedReality.Toolkit.UI.PressableButton>();
country.gameObject.AddComponent<Microsoft.MixedReality.Toolkit.Input.NearInteractionTouchable>();
country.gameObject.AddComponent<MeshCollider>();
// Assigning State
country.gameObject.GetComponent<Microsoft.MixedReality.Toolkit.UI.Interactable>().States = mStates;
// Assigning the Profiles (Will definine the colors based on the State)
Microsoft.MixedReality.Toolkit.UI.InteractableProfileItem mProfile = new Microsoft.MixedReality.Toolkit.UI.InteractableProfileItem();
mProfile.Themes = themes;
mProfile.Target = country.gameObject;
country.gameObject.GetComponent<Microsoft.MixedReality.Toolkit.UI.Interactable>().Profiles.Add(mProfile);
// Assigning Events
// TODO
}
}
The part below is working, but I am not able to assign Events to the different countries in the same loop.
I can define these events in the Editor, for example:
But since it is a repetitive activity which may slightly change over time, I was trying to achieve the same programmatically inside the loop of the script above.
This is what I tried, but it is not working:
// Creating an Event
Microsoft.MixedReality.Toolkit.UI.InteractableEvent mEvent = new Microsoft.MixedReality.Toolkit.UI.InteractableEvent();
// Assigning the method to be executed during onClick
mEvent.Event.AddListener(country.gameObject.GetComponent<CountrySelected>().printName)
// Assigning the Event to Interactable
country.gameObject.GetComponent<Microsoft.MixedReality.Toolkit.UI.Interactable>().InteractableEvents.Add(mEvent);
What is the correct way of adding programmatically Events to an Interactable in MRTK?
To assign OnClick Event for the Interactable component at the runtime, please try the following code:
this.GetComponent<Interactable>().OnClick.AddListener(MyFun1);
The Interactable component profile in the Unity Editor may not display the new method at the runtime, but it does work in the scene.

Is it possible to add a onClick() Event action(in GameObject dropdown) without adding new script in Unity?

I have an MRTK Slate which comes with Pressable Button to close the slate.
When the close button is pressed, the Slate is disabled but not destroyed. This is because in the Events section>Interactable script> GameObject.SetActive() is set by default as shown in fig:
I want to destroy the slate after clicking close button.
I know I can do this by making a script, attaching the slate prefab to the script(or taking the parent of the button till I get slate as parent game object), calling the function, and using GameObject.Destroy().
But I want to understand how the GameObject dropdown in the Interactable script is getting populated :
I understand that the other dropdowns like Transform, Follow me toggle, Solverhandler, etc are displayed because they are attached to the Slate.
But how does the GameObject option is available? This seems a basic thing, but I would like to be sure about how it is coded.
And if it is possible to add my new action in the Gameobject dropdown, if so, how?
I spent time looking at the scripts of Interactable and others, but I am a beginner in C# and was not able to see where is the code which does this.
Any input will help me.
No you can't because Object.Destroy is static and in UnityEvent (which Interactable.OnClick uses) via the Inspector you can only select instance methods.
You at lest need a minimal component like
public class ObjectDestroyer : MonoBehaviour
{
public void DestroyObject()
{
Destroy(this.gameObject);
}
}
or if you don't want to do it via the Inspector but automatically
public class ObjectDestroyer : MonoBehaviour
{
// store the itneractable reference
[Tootltip("If not provided uses the Interactable on this GameObject")]
[SerialzieField] private Interactable _interactable;
// Optionally provide a different target, otherwise destroys the Interactable
[Tooltip("If not provided destroys the GameObject of the Interactable")]
[SerializeField] private GameObject _targetToDestroy;
private void Awake()
{
// use get component as fallback if no Interactable was provided
if(!_interactable) _interactable = GetComponent<Interactable>();
// attach the callback on runtime (won't appear in the inspector)
_interactable.OnClick.AddListener(DestroyObject);
}
private void DestroyObject()
{
// destroy the object of the interactable if no target was provided
if(!_targetToDestroy) _targetToDestroy = _interactable.gameObject;
Destroy(_targetToDestroy);
}
}

Growing InputField list in Unity

I need to implement input fields in unity which will be added on the fly as per the elements in a list. It should increase as per the objects present in a map "m_myClassMap".
Also, is it possible to fetch the position, layout settings ( i.e. x,y,z coordinates) of the existing Inputfields? I want these info to calculate the position of new inputfield.
For e.g.
List<InputField> inputFieldList = new List<InputField>();
foreach (MyClass myClassObj in m_myClassMap.Values)
{
// code below is just statement and is syntactically wrong
InputField newInputField = new InputField();
// Add properties like size or layout
// set the text
inputFieldList.Text = myClassObj.getInputBoxString();
inputFieldList.Add(newInputField );
}
I am not sure if its even possible..Any help will be welcome :)
Thanks!
The most robust thing to do here would be to use a layout group. This method plenty of well tested functionality.
Firstly add a a parent object. And add a Vertical Layout Group component to it. I've shown the hierarchy below by adding a Red panel to the blue panel. On the red panel is the Vertical Layout Group.
In this example I've used the inspector to manually add 4 input fields. Note the layout here is all automatic. I've included the settings on this particular input field below, you'll be able to experiment in the inspector and very quickly see what impact the various options have.
Now for your particular solution, you want to add the items programatically rather than through the inspector. For this you'll want to create a GameObject with a reference to the object holding the layout group.
Programatic Creation
The below code will add a list of prefabs to a class. (In this case the parent class should contain the Vertical Layout Group component)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LayoutGroupExample : MonoBehaviour {
public GameObject layoutObject;
public GameObject inputFieldPrefab;
void AddInputFields(){
var inputFieldList = new List<string>(){"First Name", "Last Name", "Email"};
foreach(var fieldName in inputFieldList){
GameObject go = GameObject.Instantiate(inputFieldPrefab);
go.name = fieldName;
go.transform.SetParent(layoutObject.transform);
var inputField = go.AddComponent<InputField>();
}
}
}
In the inspector assign the object containing the layout group to the layoutObject field.
Assign a prefab (which should be an Input Field object) to the inputFieldPrefab field)

Unity object reference + serialized field. What am I missing?

I have a ui Button called "Attack Button". I have a script called HeroBattleController attached to my Gameobject. I have an attack button set as a serialized field and the attack button object dropped on the script in the editor.
[SerializeField]
private Button AttackButton;
public void SetButtonStatus(bool status) {
AttackButton.interactable=status;
}
Trying to access that gives me the error NullReferenceException: Object reference not set to an instance of an object
I thought putting the object in the editor would allow me to access it without having to "Find" the object. Can anyone point me in the right direction?
The error is on the line
AttackButton.interactable=status;
Complete HeroBattleController script
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class HeroBattleController: MonoBehaviour {
public static string SelectedHero;
[SerializeField]
private Button AttackButton;
public void SetButtonStatus(bool status) {
AttackButton.interactable=status;
}
public void HeroTouch() {
Debug.Log("Hero was touched: "+this.name);
SetButtonStatus(false);
}
// Use this for initialization
void Start() {
}
// Update is called once per frame
void Update() {
}
}
HeroTouch is called via onClick from the heroprefab object.
Update: I had the herobattlecontroller script attached to two objects, the gameobject at the root of the scene and the heroprefab object. I removed it from the gameobject and only have it on the prefab. However now when I drag the attack button to the script section on the prefab it stays bold and when I run the game the attack button reference is gone. I can drag the button to the running object and it functions as I expected. I'm clearly missing something on the connection between the object hierarchy and where the objects are usable. Putting the hero battle controller script on the root gameobject only acts the same way meaning it's bold and when ran it's missing the link.
hierarchy during edit
hierarchy during runtime
Does HeroBattleController attached to a prefab? and does the AttackButton is on the same prefab ? if not then there is a problem.

Unity3D - Turning off a GameObject's guiTexture fails to respond

I've been struggling with what appears to be a simple task for the past few hours. I have been successfully able to turn a GameObject's guiTexture on an off using GameObject.guiTexture.enabled as I have read in other similar questions. I do this in a script that is attached to the GameObject I wish to toggle.
The problem arises when I try to preform this same command from another script that is attached to an empty game object in my scene. When I interact with the button, simply nothing happens.
To give this some context, I am trying to create functionality to switch menus on the screen. When a user taps a certain UI button, I want to call unloadMenu() so I can wipe everything off the screen and draw the textures that belong to the appropriate menu.
//When the user lifts their finger
void OnTouchEnded ()
{
switch (this.gameObject.name)
{
//If they have selected the quiz button
case "QuizButton":
//this.guiTexture.enabled = false;
//Call the unloadMenu function which will clear the GUITextures
menuMgr.unloadMenu();
break;
The commented out section is what works fine. But now, I wish to perform this code in a function that is located in another script - menuMgr.unloadMenu()
This is what the MenuManager script looks like:
public GameObject quizButton;
void unloadMenu()
{
quizButton.guiTexture.enabled = false;
}
I have ensured to drag the quizButton gameobject from my scene into the inspector and connect it properly with the variable I have created yet nothing happens when this function is called. Does anyone have any idea why? Thanks in advanced.
How you declare MenuManager creates a new script while the reference to quizButton is in your MenuManager GameObject. That is why your menuMgr.UnloadMenu didn't do anything.
I have created something similar to yours and I found it worked fine :
Quizbutton Script:
public GameObject menuMgr; // reference to the empty game object (menu manager)
void OnMouseDown() // In your case, this is OnTouchEnded
{
// get the script of menu manager here
MenuMgrScript menuscript = menuMgr.GetComponent<MenuMgrScript>();
// and execute the function you want
menuscript.UnloadMenu();
}
Menu Manager Script:
// this script is exactly the same as yours, nothing's wrong here
// oh except add public to function UnloadMenu, so it can be called
// in the quizbutton script
public GameObject quizButton;
public void UnloadMenu() {
quizButton.guiTexture.enabled = false;
}

Categories

Resources