I have several in-game menus that open up when I press the Tab key (Items, Map, Lore etc..) and I want to make them move in a loop when the user is pressing the right/left buttons I've added.
For example:
If the order is Items|Map|Lore, and the player is looking at the Map menu, if they press right they reach Lore, and if they press right again they reach Items (rather than the current nothingness).
This is my left button code (same goes for right button of course but with -2560f for the x value):
public void LeftUIButton()
{
var moveMenusRight = new Vector3(2560f, 0, 0);
foreach(GameObject canvas in uICanvases.Keys)
{
Vector3 targetPosition = canvas.transform.position + canvas.transform.TransformVector(moveMenusRight);
StartCoroutine(LerpCanvas(canvas, targetPosition, uICanvasesSlidingTime));
}
}
The uICanvases is a dictionary I'm filling up in my Awake test method:
private Dictionary<GameObject, Vector3> uICanvases = new Dictionary<GameObject, Vector3>();
private void Awake()
{
gameOverScreen.SetActive(false);
// Adds all the GameObjects that are tagged as "UICanvas" to a list to be worked on.
foreach(GameObject canvas in FindObjectsOfType<GameObject>())
{
if (canvas.CompareTag("UICanvas"))
{
uICanvases.Add(canvas, canvas.transform.position);
}
}
}
I also want to keep the code as generic as possible, without committing to anything hard-coded.
Is there a way to do this?
Related
First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;
I already know how to click on 3D Objects in the scene by using the Input.GetMouseButtonDown. I'm trying to change the 3D Object position by clicking on the object. I added a Box Collider in the object and I'm calling the following method.
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
foreach (GameObject child in floorTiles) {
BoxCollider box = child.GetComponentInChildren<BoxCollider>();
if (hit.transform.name.Equals(box.name))
{
handleOnMouseDown(hit.collider);
}
}
}
}
}
floorTiles is an array of GameObjects.
If I hit one of these objects the below function is called:
void handleOnMouseDown(Collider box)
{
GameObject parent = box.transform.parent.gameObject;
Vector3 position = parent.transform.position;
positionX.GetComponent<TextMeshProUGUI>().text = position.x.ToString();
posXButtonPlus.GetComponent<Button>().onClick.AddListener(() => handleOnChangePosition("posx", parent));
}
This works, however, when I click many objects, all the last objects clicked also keep changing their positions. How can I change one position per time?
Each click on an object, adds listener to your button, but you don't ever remove listeners. You end up with multiple listeners, that's why more objects are moved than intended.
You could remove listener after each button click, but that seems like a total overkill.
Instead of adding multiple listeners, consider adding just one which will remember the last clicked object, and clicking your button will move only that object.
Also, if you want to move objects just by clicking on them, not on button click, you can simplify all these, and move the object directly in handleOnMouseDown.
Remove listeners variant:
void handleOnMouseDown(Collider box)
{
posXButtonPlus.GetComponent<Button>().onClick.AddListener(() => handleOnChangePosition("posx", box.gameObject));
}
void handleOnChangePosition(string pos, GameObject go)
{
// I don't have code for moving, but I imagine looks something like this. right * 0.5f can be anything, did it just for tests.
go.transform.position += Vector3.right * 0.5f;
posXButtonPlus.GetComponent<Button>().onClick.RemoveAllListeners();
}
Last clicked variant:
GameObject lastClicked;
void Awake()
{
posXButtonPlus.GetComponent<Button>().onClick.AddListener(() => handleOnChangePosition());
}
void handleOnMouseDown(Collider box)
{
lastClicked = box.gameObject;
}
void handleOnChangePosition()
{
lastClicked.transform.position += Vector3.right * 0.5f;
}
Without buttons and other stuff:
void handleOnMouseDown(Collider box)
{
box.transform.position += Vector3.right * 0.5f;
}
So basically I have a hand with cards. Those cards are gameobject prefab and when I hover one it scale bigger and slightly move upward. But when scaling it overlap the card at his left and stay behind the one at his right, and I want the card to get over the cards instead.
I tried to change the z index and the layer but all cards in hand are ruled by the same horizontal layout...
Should I just push the cards on each side of the hovered one ? Or someone have a solution to draw a specific child in front of the others ?
What you can do is to dynamically add a new Canvas to your object that you want to be in front, and override sorting:
In code, you can do something like this:
var newCanvas = yourGameObject.AddComponent<Canvas>();
newCanvas.overrideSorting = true;
newCanvas.sortingOrder = 1; // this has to be higher than order of parent canvas
Of course you should remember to remove this canvas when it's not needed anymore.
Edit: updated the solution with full code that I verified to be working. What I missed was adding the raycaster to the new canvas. This is the full code:
public class YourClass : MonoBehaviour, IPointerExitHandler, IPointerEnterHandler
{
private Canvas tempCanvas;
private GraphicRaycaster tempRaycaster;
public void OnPointerEnter(PointerEventData eventData)
{
// add and configure necessary components
tempCanvas = gameObject.AddComponent<Canvas>();
tempCanvas.overrideSorting = true;
tempCanvas.sortingOrder = 1;
tempRaycaster = gameObject.AddComponent<GraphicRaycaster>();
// your operations on the object
transform.localScale = new Vector2(1.1f, 1.1f);
// ...
}
public void OnPointerExit(PointerEventData eventData)
{
// remove components that are not needed anymore
Destroy(tempRaycaster);
Destroy(tempCanvas);
// your operations on the object
transform.localScale = new Vector2(1f, 1f);
// ...
}
}
I am making a 2d RTS style game in which you have the ability to select units in game with a click and drag selection box. When ever a unit is inside of this selection box and the left mouse button is let go the unit will be added to a "selected" list and their animation will be changed from the idle animation to the selected idle animation. After the selection box is draw again all the units in the selected list will be removed and their animations set back to the idle position, then all the units inside the new selection box will be added to the list and set to the selected idle animation. The problem is that if any of these units in the new selected box had been previously selected they will just switch to the idle animation and stay there instead going to the selected animation. I tested this without animations and it worked, it would stay as the selected sprite when selected again instead of switching to the idle sprite and staying there. I am pretty new at C# so this could very well be a very simple problem to fix.
Thanks
Here is the code:
ControllableUnits ControlUnitSelected;
public List<ControllableUnits> selectedControllableUnits;
private Vector3 mousePosition;
public void Awake()
{
selectedControllableUnits = new List<ControllableUnits>();
selectionAreaTransform.gameObject.SetActive(false);
ControlUnitSelected = this.gameObject.GetComponent<ControllableUnits>();
}
public void Update()
{
//Transforming Selection Area
if (Input.GetMouseButton(0))
{
Vector3 currentMousePosition = UnitControlClass.GetMouseWorldPosition();
Vector3 lowerLeft = new Vector3(
Mathf.Min(mousePosition.x, currentMousePosition.x),
Mathf.Min(mousePosition.y, currentMousePosition.y)
);
Vector3 upperRight = new Vector3(
Mathf.Max(mousePosition.x, currentMousePosition.x),
Mathf.Max(mousePosition.y, currentMousePosition.y)
);
selectionAreaTransform.position = lowerLeft;
selectionAreaTransform.localScale = upperRight - lowerLeft;
}
if (Input.GetMouseButtonDown(0))
{
mousePosition = UnitControlClass.GetMouseWorldPosition();
selectionAreaTransform.gameObject.SetActive(true);
}
//Creating the selection process
if (Input.GetMouseButtonUp(0))
{
Collider2D[] collider2DArray = Physics2D.OverlapAreaAll(mousePosition, UnitControlClass.GetMouseWorldPosition());
foreach (ControllableUnits control in selectedControllableUnits)
{
//if(ControlUnitSelected != null)
control.animPeasant.Play("Peasant_Idle");
control.isSelected = false;
}
selectedControllableUnits.Clear();
foreach (Collider2D collider2D in collider2DArray)
{
ControllableUnits controllableUnit = collider2D.GetComponent<ControllableUnits>();
if (controllableUnit != null)
{
controllableUnit.animPeasant.Play("Peasant_Idle_Selected");
controllableUnit.isSelected = true;
selectedControllableUnits.Add(controllableUnit);
}
}
selectionAreaTransform.gameObject.SetActive(false);
}
}
I guess the problem is when you call Play many times, Unity tries to merge these animations that results a random state finally.
It can be solved by giving the exact playback time.
controllableUnit.animPeasant.Play("Peasant_Idle_Selected", 0, 0f);
Well, I already know that Unity itself has the Sprite Swap function, for that, but I wanted it to click when I changed the image and returned the old one only when it closed the window that was opened, or if I clicked it again on the icon, I tried to several ways so I turned to a helpy
Video link:- https://youtu.be/-H7j3vdKl8A.
Visit github if u dont undertand it here.
https://github.com/NareshBisht/Unity-UI-Shop-Select-2-UI-object-at-same-time/blob/master/ButtonAnimationHold.cs
Thank you.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
//*************************Script to select one "Category(say Weapon)" UI Button till anathor category button(say Vehicles) is pressed.********************************//
/*
* Steps to be followed to make this script work
* Select All the Category Buttons (for example:- Suppose u r builiding a shop UI then Select all Category button Like Helics, Weapons -- refer to video link above;
* Now tag all these category button as same (say UI_CategoryButtons).
* Attach this script to all the UI_category button.
* the script will aotomatically add Button, Animator, Image component to the Ui Gameobjects.
* now select Animation in Transition tab.
* now press the Auto genrate Animation in Button component tab.
* now, set Transition from Animation to none in button Tab.
* Now, Do the above step for Sub category UI element too (for eg do for Apache, Tiger, f-22 etc which is the sub category of Helics --- refer video)
* open Animation window and Animate ur Ui elements.
* ur good to go now.
*/
[RequireComponent(typeof(Button))]
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Image))]
public class ButtonAnimationHold : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
public float TransitionTime = 0.3f; //Time taken to transition from one animation state to anathor.
private Animator MyAnim; //reference to animator attacted to the UI gameObject.
private void Start()
{
MyAnim = GetComponent<Animator>();
PlayNoramlState(); //Playing normal state at beginning.
}
public void HighlightThisButton_UnhighlightRest() //called when button is pressed (Called by OnPointerClick() defined below)
{
string TagOfPressedButton = gameObject.tag; //the tag of the button pressed by user.
foreach (Button button in gameObject.GetComponentInParent<Transform>().root.gameObject.GetComponentsInChildren<Button>()) //searching the gameobject with tag as in TagOfPressedButton inside the canvas which contain all UI element.
{ //we search inside Canvas to improve perf.
if(button.gameObject.tag == TagOfPressedButton && button.gameObject != this.gameObject) //selecting gameobjects with same tag(tag of button which was pressed) execpt the one which was pressed and then playing normal state in all selected GO.
{
button.gameObject.GetComponent<ButtonAnimationHold>().PlayNoramlState(); //Playing normal state in all UI gameObject with same tag except the one which called it.
}
}
MyAnim.Play("Pressed"); //Playing Pressed state in the UI element that was pressed.
}
public void PlayNoramlState()
{
MyAnim.CrossFadeInFixedTime("Normal", TransitionTime); //smoothly transitioning to Normal state;
}
public void OnPointerEnter(PointerEventData eventData) //Called when pointer hover over the Ui elemnt;
{
if(MyAnim.GetCurrentAnimatorStateInfo(0).IsName("Normal")) //Playing highlighted state only when it is in normal state and not when it is in Pressed state.
{
MyAnim.CrossFadeInFixedTime("Highlighted", TransitionTime); //smoothly transitioning to Highlighted state;
}
}
public void OnPointerExit(PointerEventData eventData)
{
if (MyAnim.GetCurrentAnimatorStateInfo(0).IsName("Highlighted") || MyAnim.GetCurrentAnimatorStateInfo(0).IsName("Normal")) //Playing Normal state only when it is in Highlighted state and not when it is in Pressed state
{
MyAnim.CrossFadeInFixedTime("Normal", TransitionTime); //smoothly transitioning to Normal state;
}
}
public void OnPointerClick(PointerEventData eventData) //called when button is pressed that is pointer down and pointer up both have to be done over button
{
HighlightThisButton_UnhighlightRest();
}
}
//video link
//https://youtu.be/-H7j3vdKl8A