Unity: Onmouseup while dragging UI element over it? - c#

In my 2D project I can't seem to get OnMouseUp working while dragging a UI object over it. I want to be able to detect that I dropped the UI object on a non-UI gameobject so I can activate the script that lets the position of the UI object be the same position as the non-UI object each Update.
Currenty I use the IOnDragHandler and IEndDragHandler on the UI object for the dragging process. When I want to drop the UI object on another UI object then IDropHandler helps me, but when I want to drop it on a non-UI GameObject, I want to be able to use the OnMouseUp event, but it doesn't fire.
Whenever I start dragging the object I use a CanvasGroup on the UI object and I make sure that it doesn't get hit by any raycasts.
I also tried making the collider of the non-UI object a trigger but it has no effect. Also, OnMouseOver and OnMouseExit do work when I am dragging the UI element over the non-UI element.
Thanks very much for looking into this. Here comes the code:
So on the UI object (A card) I use this:
public void OnDrag(PointerEventData eventData)
{
if (CanDrag)
{
if (!isDragging)
{
isDragging = true;
if (Slot != null)
{
Slot.GetComponent<FamilyTreePlacementScript>().CurrentFamilyCard = null;
}
if (card is FamilyCard || card is EquipmentCard) UIManagerScript.Instance.ToggleFamilyTree(null, true);
GetComponent<CanvasGroup>().blocksRaycasts = false;
CardManagerScript.CardBeingDragged = gameObject;
DataKeeperScript.Instance.MayDragCamera = false;
CardManagerScript.Instance.DeletePreview();
transform.SetParent(canvas);
}
transform.position = Input.mousePosition;
}
}
public void OnEndDrag(PointerEventData eventData)
{
if (CanDrag)
{
isDragging = false;
GetComponent<CanvasGroup>().blocksRaycasts = true;
CardManagerScript.CardBeingDragged = null;
DataKeeperScript.Instance.MayDragCamera = true;
if (Slot == null)
{
transform.SetParent(defaultParent);
}
else
{
Slot.GetComponent<FamilyTreePlacementScript>().CurrentFamilyCard = card as FamilyCard;
transform.SetParent(Slot);
transform.position = Slot.position;
}
}
}
And on the non-UI object that I want to "drop" the card on, I use:
private void OnMouseUp()
{
if (CardManagerScript.CardBeingDragged)
{
Card card = CardManagerScript.CardBeingDragged.GetComponent<CardObjectScript>().card;
if (card is TakeOverCard)
{
//Check if the card can actually be played
if (Business.Allegiance != CardManagerScript.Instance.PlayerFamily)
{
CurrentTakeOverCard = card;
CardManagerScript.CardBeingDragged.GetComponent<CardObjectScript>().SetWorldObject(transform);
}
}
}
}

Check the 'Queries Hit Triggers' (Project Settings > Physics). It should be set to 'true'

Related

Unity check all objects of same tag

I'm developing a search game, where players must look for certain objects. Whenever the targeted object is found and has been picked up, the player wins and go to the next level. I tagged the targeted objects as "TargetObj". I successfully implemented this when there is only one object to look for. I want to modify my code to include cases where there is more than one object to look for. Here is my code :
public void someFunction() {
//if we press the button of choice
if (Input.GetKeyDown(KeyCode.Space)) {
//and we're not holding anything
if (currentlyPickedUpObject == null) {
//and we are looking an interactable object
if (lookObject != null) {
PickUpObject();
}
} else { //if we press the pickup button and have something, we drop it
BreakConnection();
}
}
}
/* ommitted lines */
public void PickUpObject() {
if (GameObject.FindGameObjectsWithTag("TargetObj").Length == 1 & lookObject.tag == "TargetObj") {
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
winUI.SetActive(true);
Time.timeScale = 0f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
Time.timeScale = 1f;
} else if (GameObject.FindGameObjectsWithTag("TargetObj").Length > 1) {
} else {
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
}
I added this line, to check if there is more than one object to look for.
else if (GameObject.FindGameObjectsWithTag("TargetObj").Length > 1)
How to implement this (if the player picked up all objects of tag "TargetObj", go to next level.)?
A fast way of doing this is to keep a counter of picked objects. Then, if the counter is equal with the number of objects with the "TargetObj" tag, then the player wins. As snippet you can have something like this:
Gameobject[] targetObjects; // an array where you will keep your objects with "TargetObj" tag
List<GameObject> targetObjectsList;
void Start()
{
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
targetObjectsList = new List<GameObject>();
}
.
.
.
// In your method (You didn't put all your code so I will use your snippet)
if (Input.GetKeyDown(KeyCode.Space))
{
//and we're not holding anything
if (currentlyPickedUpObject == null)
{
//and we are looking an interactable object
if (lookObject != null )
{
PickUpObject();
// I suppose that "lookObject" is the gameobject that you want to pickup. If not, replase this variable with the right gameobject.
if(!targetObjectsList.Contains(lookObject.gameObject))
{
targetObjectsList.Add(lookObject.gameObject);
if (targetObjectsList.Count == targetObjects.Length)
{
//Finish the game
winUI.SetActive(true);
Time.timeScale = 0f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
Time.timeScale = 1f;
}
}
}
}
}
//if we press the pickup button and have something, we drop it
else
{
BreakConnection();
}
}
Then you modify your PickUpObject method, just to pick and drop objects.
I'm sorry if I miss something. I wrote this without an editor and I didn't test the code, so please tell me if is something that I miss.

Pause Menu in FPS GAME buttons problem using Unity

I am trying to create a pause menu within my FPS game. However, when the game is paused, whenever I mouse over any of the buttons that appear I cannot click on them. I've tried disabling my FPS controller (I'm using the unity fps controller provided) script and checked my canvas has an event system etc.
Any suggestions would be a huge help! Here is my code for the pause menu I have :
public Transform menu;
public GameObject Player;
public GameObject Gun;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
Pause();
}
}
public void Pause()
{
if (menu.gameObject.activeInHierarchy == false)
{
menu.gameObject.SetActive(true);
Time.timeScale = 0;
Gun.GetComponent<Gun>().enabled = false;
Player.GetComponent<FirstPersonController>().enabled = false;
Cursor.lockState = CursorLockMode.None;
Cursor.lockState = CursorLockMode.Confined;
Cursor.visible = true;
}
else
{
menu.gameObject.SetActive(false);
Time.timeScale = 1;
Player.GetComponent<FirstPersonController>().enabled = true;
Gun.GetComponent<Gun>().enabled = true;
}
}
public void QuitToMain()
{
SceneManager.LoadScene("Menu 3D");
}
} ```
Does the pause menu work outside of the games pause state? Have you checked whether the buttons are obstructed by other UI objects that are in the way?
This link might help you to debug the UI issues:
https://answers.unity.com/questions/1148727/ui-button-not-working-2.html

Trying to program two buttons to appear when the player in my game dies

I am writing to ask for some assistance please. I am trying to program two buttons a Main menu and Exit button in my game to appear only when my player dies. I have been trying to define the gameobject list with,
GameManager Script.
//Awake is always called before any Start functions
void Awake()
{
//Check if instance already exists
if (instance == null)
//if not, set instance to this
instance = this;
//If instance already exists and it's not this:
else if (instance != this)
//Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
Destroy(gameObject);
//Sets this to not be destroyed when reloading scene
DontDestroyOnLoad(gameObject);
//Assign enemies to a new List of Enemy objects.
enemies = new List<Enemy>();
//Get a component reference to the attached BoardManager script
boardScript = GetComponent<BoardManager>();
//Call the InitGame function to initialize the first level
//InitGame();
}
// TODO: Uncommented Code
/// <summary>
/// Called when the level / scene has finished loading
/// </summary>
/// <param name="scene"></param>
/// <param name="mode"></param>
void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
// Don't initialize game on main menu. That would be silly
if (scene.buildIndex > 0)
{
InitGame();
}
level++; // Increment after we have loaded level. Might be a better place to put this
}
// TODO: Uncomemnted code
void OnEnable()
{
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
void OnDisable()
{
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
}
//Initializes the game for each level.
void InitGame()
{
//While doingSetup is true the player can't move, prevent player from moving while title card is up.
doingSetup = true;
//Get a reference to our image LevelImage by finding it by name.
levelImage = GameObject.Find("LevelImage");
//Get a reference to our text LevelText's text component by finding it by name and calling GetComponent.
levelText = GameObject.Find("LevelText").GetComponent<Text>();
//Set the text of levelText to the string "Day" and append the current level number.
levelText.text = "Day " + level;
//Set levelImage to active blocking player's view of the game board during setup.
levelImage.SetActive(true);
//Call the HideLevelImage function with a delay in seconds of levelStartDelay.
Invoke("HideLevelImage", levelStartDelay);
//Clear any Enemy objects in our List to prepare for next level.
enemies.Clear();
//Call the SetupScene function of the BoardManager script, pass it current level number.
boardScript.SetupScene(level);
// TODO: Removed initGame() from here. No need to call it twice
}
//Hides black image used between levels
void HideLevelImage()
{
//Disable the levelImage gameObject.
levelImage.SetActive(false);
//Set doingSetup to false allowing player to move again.
doingSetup = false;
}
//Update is called every frame.
void Update()
{
//Check that playersTurn or enemiesMoving or doingSetup are not currently true.
if (playersTurn || enemiesMoving || doingSetup)
//If any of these are true, return and do not start MoveEnemies.
return;
//Start moving enemies.
StartCoroutine(MoveEnemies());
}
//Call this to add the passed in Enemy to the List of Enemy objects.
public void AddEnemyToList(Enemy script)
{
//Add Enemy to List enemies.
enemies.Add(script);
}
public void ShowButtons()
{
foreach (var Button in buttons)
{
var gameOver = GetComponent<Exit2>();
Button.gameObject.SetActive(true);
}
}
//GameOver is called when the player reaches 0 food points
public void GameOver()
{
//Set levelText to display number of levels passed and game over message
levelText.text = "After " + level + " days, you starved.";
//Shows buttons on player's death.
var gameOver = GetComponent<Exit2>();
gameOver.ShowButtons("Main menu 2", "Exit2");
//Enable black background image gameObject.
levelImage.SetActive(true);
//Disable this GameManager.
enabled = false;
}
//Coroutine to move enemies in sequence.
IEnumerator MoveEnemies()
{
//While enemiesMoving is true player is unable to move.
enemiesMoving = true;
//Wait for turnDelay seconds, defaults to .1 (100 ms).
yield return new WaitForSeconds(turnDelay);
//If there are no enemies spawned (IE in first level):
if (enemies.Count == 0)
{
//Wait for turnDelay seconds between moves, replaces delay caused by enemies moving when there are none.
yield return new WaitForSeconds(turnDelay);
}
//Loop through List of Enemy objects.
for (int i = 0; i < enemies.Count; i++)
{
//Call the MoveEnemy function of Enemy at index i in the enemies List.
enemies[i].MoveEnemy();
//Wait for Enemy's moveTime before moving next Enemy,
yield return new WaitForSeconds(enemies[i].moveTime);
}
//Once Enemies are done moving, set playersTurn to true so player can move.
playersTurn = true;
//Enemies are done moving, set enemiesMoving to false.
enemiesMoving = false;
}
void Awake()
{
// Get the buttons
menuButtonList();
//buttons = GetComponentsInChildren<Button>();
// Disable them
HideButtons();
}
public void HideButtons()
{
foreach (var Button in buttons)
{
Button.gameObject.SetActive(false);
}
}
public void LoadByIndex(int sceneIndex)
{
SceneManager.LoadScene(sceneIndex);
}
public void Quit()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
I just want to make it so in my GameManager script under GameOver function is called the two buttons appear with what s already defined in the game PLEASE CHECK the lines in GameManager 162-194 and all of Exit2.
First of all you need a Canvas. Then you create the two buttons inside the canvas. Have a reference to the instance of this canvas in your GameManager script.
Deactivate it via .SetActive(false) when the game starts, then Activate it again when the GameOver happens via .SetActive(true).
Add 2 methods to the GameManager script, one for Exit and one for MainMenu. And have the two buttons you created have their OnClick event trigger those 2 methods.

Tango/Unity - UI not blocking touches on screen

We are building an example that is similar to the "Kitten - Placing Virtual objects in AR" as shown here:
https://developers.google.com/tango/apis/unity/unity-howto-placing-objects.
Basically when you touch the screen, a kitten appears on the real world plane (floor).
In our app we have a side menu, with a few buttons and each shows a different game object. We want to detect touch anywhere on the screen except where there is UI. We want the UI to block touches in Tango, and only allow touches to instantiate the related game objects on areas of the screen without UI elements.
The touch specific code is here:
void Update() {
if (Input.touchCount == 1) {
// Trigger placepictureframe function when single touch ended.
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Ended) {
PlacePictureFrame(t.position);
}
}
}
(The PlacePictureFrame() places a picture frame object at the touch position.)
I can't find any Tango examples which has touch and UI combined. I've tried an asset called LeanTouch to block touches behind UI elements but it doesn't seem to work with Tango specifically. Please help!
I have tried using method 5 from this:
How to detect events on UI and GameObjects with the new EventSystem API
and while it does add a PhysicsRaycaster to the TangoARCamera (which is tagged as MainCamera), the OnPointerDown method produces no debug logs no matter where you touch the screen. Tango is a special case so this is not a duplicate question. See below:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class PictureFrameUIController : MonoBehaviour, IPointerClickHandler {
public GameObject m_pictureFrame;
private TangoPointCloud m_pointCloud;
void Start() {
m_pointCloud = FindObjectOfType<TangoPointCloud>();
addPhysicsRaycaster();
}
void addPhysicsRaycaster() {
PhysicsRaycaster physicsRaycaster = GameObject.FindObjectOfType<PhysicsRaycaster>();
if (physicsRaycaster == null) {
Camera.main.gameObject.AddComponent<PhysicsRaycaster>();
}
}
public void OnPointerClick(PointerEventData eventData) {
Debug.Log("Clicked: " + eventData.pointerCurrentRaycast.gameObject.name);
PlacePictureFrame(eventData.pointerCurrentRaycast.screenPosition);
}
//void Update() {
// if (Input.touchCount == 1) {
// // Trigger placepictureframe function when single touch ended.
// Touch t = Input.GetTouch(0);
// if (t.phase == TouchPhase.Ended) {
// PlacePictureFrame(t.position);
// }
// }
//}
void PlacePictureFrame(Vector2 touchPosition) {
// Find the plane.
Camera cam = Camera.main;
Vector3 planeCenter;
Plane plane;
if (!m_pointCloud.FindPlane(cam, touchPosition, out planeCenter, out plane)) {
Debug.Log("cannot find plane.");
return;
}
// Place picture frame on the surface, and make it always face the camera.
if (Vector3.Angle(plane.normal, Vector3.up) > 60.0f && Vector3.Angle(plane.normal, Vector3.up) < 140.0f) {
Vector3 forward = plane.normal;
// Vector3 right = Vector3.Cross(plane.normal, cam.transform.forward).normalized;
// Vector3 forward = Vector3.Cross(right, plane.normal).normalized;
Instantiate(m_pictureFrame, planeCenter, Quaternion.LookRotation(forward, Vector3.up));
} else {
Debug.Log("surface is not steep enough for picture frame to be placed on.");
}
}
public void DeleteAllFrames() {
GameObject[] frames = GameObject.FindGameObjectsWithTag("Frame");
if (frames == null) {
return;
}
foreach (GameObject frame in frames) {
Destroy(frame);
}
}
}
If you want to detect a click anywhere on the screen except for where there is a UI control/component, you have to check if the pointer is over the UI with EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId).
If on desktop, use EventSystem.current.IsPointerOverGameObject(). You are using Tango so EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId). should be used.
void Update()
{
if (Input.touchCount == 1)
{
//Trigger placepictureframe function when single touch ended.
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Ended)
{
//Make sure that pointer is not over UI before calling PlacePictureFrame
if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
{
PlacePictureFrame(t.position);
}
}
}
}
Edit:
It seems like this works with TouchPhase.Began only.
Change t.phase == TouchPhase.Ended to t.phase == TouchPhase.Began and this should work as expected. Make sure to test with a mobile device/tango instead of your mouse.

Deleting previous added Component of a GameObject

I have this script where it would add the SelectMove script at runtime when a character was touched by the user.
public GameObject target;
void Start () {
}
// Update is called once per frame
void Update () {
if(Input.touchCount > 0 || Input.GetTouch(0).phase == TouchPhase.Began){
ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(ray.origin,ray.direction * 20,Color.red);
if(Physics.Raycast(ray, out hit,Mathf.Infinity)){
Debug.Log(hit.transform.gameObject.name);
target = hit.transform.gameObject;
//Destroy(hit.transform.gameObject);
selectedPlayer();
}
}
}
void selectedPlayer(){
target.AddComponent(Type.GetType("SelectMove"));
}
In the code above, if the user will click Player A, Player A will move using accelerometer. What I need to do is if I click another character, say Player B, I need Player A to stop moving and it is now Player B's time to move. But I seemed not to get what I want. I tried destroying the script by using Destroy(this) or by this code:
if (target != null)
{
var sphereMesh = target.GetComponent<SelectMove>();
Destroy(sphereMesh);
}
But it is still not working.
If I don't destroy the previous added script, if the user clicks for another character, the previous selected player still moves along with the new one.
Is there another way that I could achieve what I needed to do?
public GameObject target = null;
void Update ()
{
if(Input.touchCount > 0 || Input.GetTouch(0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay(Input.GetTouch(0).position);
Debug.DrawRay(ray.origin,ray.direction * 20,Color.red);
RaycastHit hit;
if(Physics.Raycast(ray, out hit,Mathf.Infinity))
{
Debug.Log(hit.transform.gameObject.name);
if(this.target != null){
SelectMove sm = this.target.GetComponent<SelectMove>()
if(sm != null){ sm.enabled = false; }
}
target = hit.transform.gameObject;
//Destroy(hit.transform.gameObject);
selectedPlayer();
}
}
}
void selectedPlayer(){
SelectMove sm = this.target.GetComponent<SelectMove>();
if(sm == null){
target.AddComponent<SelectMove>();
}
sm.enabled = true;
}
The code reuses your base, the new part is that you use the previous selected object to disable the SelectMove script (I assume it is the one moving things). Instead of destroying it, it enables/disables so you save that memory consumption.
I do not think Adding and destroying scripts is the convenient method here.
Try this:
1. Add the SelectMove script to all movable players.
2. Add a public property bool IsMoving = false; to the SelectMove script and make an if statement to move if IsMoving is true.
3. Change selectedPlayer() method to instead change the target player IsMoving to true, and set the previous one (or simply all other players) to false.

Categories

Resources