Hey guys I have been building a shop menu that is activated when the button is pressed. I have four buttons in the menu that correspond to several buy able items in a grid. The buttons and text and Image attached are inside a GameObject. I'm trying to switch between menus with SetActive. I've successfully used SetActive to open and close the menu but when I try with the other portions they stay inactive regardless. Even when the button supposedly activating is pressed. The picture I uploaded shows the hierarchy of the shop. The buttons are under ShopSelection and the actual menu is Under Shop
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIManager : MonoBehaviour {
private static UIManager instance;
public static UIManager Instance;
public GameObject shopMenu, motherShips, resources, fleets, research;
bool menuIsOpen = false;
bool motherShipOpen = false;
bool resourcesOpen = false;
bool fleetOpen = false;
public Button shop, motherShipButton, resourceButton, fleetButton, exitButton;
void Start () {
shopMenu.SetActive(false);
//Open Shop Menu
Button btn = shop.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
//Open Motherships portion of shop
Button btn1 = shop.GetComponent<Button>();
btn1.onClick.AddListener(TaskOnClick3);
//Open resources portion of shop
Button btn2 = shop.GetComponent<Button>();
btn2.onClick.AddListener(TaskOnClick4);
//Open fleet portion of shop
Button btn3 = shop.GetComponent<Button>();
btn3.onClick.AddListener(TaskOnClick5);
//Open Research portion of shop
Button btn4 = shop.GetComponent<Button>();
btn4.onClick.AddListener(TaskOnClick6);
//Exit Menu
Button bttn = exitButton.GetComponent<Button>();
bttn.onClick.AddListener(TaskOnClick2);
}
// Update is called once per frame
void Update () {
}
void TaskOnClick()
{
shopMenu.SetActive(true);
menuIsOpen = true;
motherShips.SetActive(true);
}
void TaskOnClick2()
{
if (menuIsOpen == true)
{
shopMenu.SetActive(false);
menuIsOpen = false;
}
}
void TaskOnClick3()
{
if(menuIsOpen == true)
{
//Open Motherships Shop
motherShips.SetActive(true);
resources.SetActive(false);
fleets.SetActive(false);
}
}
void TaskOnClick4()
{
if (menuIsOpen == true)
{
//Open resources Shop
motherShips.SetActive(false);
resources.SetActive(true);
fleets.SetActive(false);
}
}
void TaskOnClick5()
{
if(menuIsOpen == true)
{
//Open Fleet Shop
motherShips.SetActive(false);
resources.SetActive(false);
fleets.SetActive(true);
}
}
void TaskOnClick6()
{
if (menuIsOpen == true)
{
//Open Research Page
motherShips.SetActive(false);
resources.SetActive(false);
fleets.SetActive(false);
}
}
}
You added all callbacks allways to the shop button e.g.
Button btn2 = shop.GetComponent<Button>();
btn2.onClick.AddListener(TaskOnClick4);
not to the supposed buttons motherShipButton, resourceButton, fleetButton
Note that you don't have to call GetComponent again if you declare the variable as Button it will automatically reference the Button component of the drag&dropped GameObject in the Inspector so instead it should simply be
shop.onClick.AddListener(TaskOnClick);
//Open Motherships portion of shop
motherShipButton.onClick.AddListener(TaskOnClick3);
//Open resources portion of shop
resourcesButton.onClick.AddListener(TaskOnClick4);
//Open fleet portion of shop
fleetButton.onClick.AddListener(TaskOnClick5);
//Open Research portion of shop
// Whatever button this belongs to
???.onClick.AddListener(TaskOnClick6);
//Exit Menu
exitButton.onClick.AddListener(TaskOnClick2);
Now also name the methods accordingly e.g. OpenMenu, CloseMenu etc. that would make it a lot easier for you and everyone else to understand the code.
I also doubt that you need the check for if(menuIsOpen). Since the other GameObjects are children of the menu anyway they will simply stay invisible even if SetActive was called. Instead I would make sure that only one panel is active by using a method with e.g. an enum instead like
enum MenuPanel
{
MotherShips,
Resources,
Fleets,
Research
}
private void ShowPanel(MenuPanel panel)
{
// First disable all
motherShips.SetActive(false);
fleets.SetActive(false);
resources.SetActive(false);
// Than only activate according panel
switch(panel)
{
case MenuPanel.MotherShips:
motherShips.SetActive(true);
break;
case MenuPanel.Reaources:
resources.SetActive(true);
break;
case MenuPanel.Fleets:
fleets.SetActive(true);
break;
}
}
And than I would still use dedicated methods for each panel like e.g.
void Start()
{
//...
motherShipButton.onClick.AddListener(ShowMotherShips);
//...
}
private void ShowMotherShips()
{
ShowPanel(MenuPanel.MotherShips);
}
you could do it also directly as lambda expression
void Start()
{
//...
motherShipButton.onClick.AddListener(() => {
ShowPanel(MenuPanel.MotherShips);
});
//...
}
I usually try to avoid it since 1. you can not reuse this expression but you might want to call it from else where as well and 2. you will not be able to use RemoveListener to eventually get rid of that call.
Related
I have created a pause screen Area2D for my game, in any case the player goes AFK. I wrote a code so whenever the key P is pressed it appears and everything pauses, and if it is clicked it disappears and everything goes back to normal. I wrote a code, but it didn't work
I tried this:
public override void _PhysicsProcess(float delta)
{
// Makes the pause screen visible when P is pressed
if (Input.IsActionPressed("Pause"))
{
Visible = true;
}
}
public override void _Ready()
{
}
public void _on_PauseScreen_mouse_entered()
{
if (Input.IsActionPressed("click"))
{
Visible = false;
}
}
But it only works when clicked on the edges, I know that's how collisions are but how do I make it so when anywhere the sprite is pressed, it disappears?
The problem in your code should be, that you are just checking if the action is pressed, when the mouse entered the area.
If you move the mouse to the middle of the area and click then, your event was already fired.
Create a variable to track, if the mouse is currently in the area by using entered and exit signals and use that to determine if something should happen when you click:
bool bMouseEntered = false
public override void _PhysicsProcess(float delta)
{
// Makes the pause screen visible when P is pressed
if (Input.IsActionPressed("Pause"))
{
Visible = true;
}
if (Input.IsActionPressed("click") && bMouseEntered)
{
Visible = false;
}
}
public override void _Ready()
{
}
public void _on_PauseScreen_mouse_entered()
{
bMouseEntered = true;
}
public void _on_PauseScreen_mouse_exited()
{
bMouseEntered = false;
}
I want it so if it is pressed anywhere on the pause screen, it dissapears.
That is probably the simplest case. And the easier way to do it is to put the check in _physics_process, where the other check is:
public override void _PhysicsProcess(float delta)
{
// Makes the pause screen visible when P is pressed
if (Input.IsActionPressed("Pause"))
{
Visible = true;
}
if (Input.IsActionPressed("click"))
{
Visible = false;
}
}
So it has nothing to do with collision or the mouse. It is just checking for the action regardless of where or how it happens.
We can argue that using _input or _unhandled_input is better, in particular if timing is critical, but I don't expect it to be an issue in this case.
Addendum:
I want it so when it is pressed anywhere ON THE PAUSE SCREEN. it disappears. it doesn't cover the full screen.
At the time of writing Bugfish already has a working solution. I'll give you an alternative.
Since you are using an Area2D it must have the property input_pickable, when it is set to true (which is the default) it allows to "pick" the Area2D with the mouse. And you would get this interaction in the input_event signal or overwriting _input_event. Which would look like this:
void _InputEvent(Viewport viewport, InputEvent ev, int shapeIdx)
{
var btn = ev as InputEventMouseButton;
if(btn != null && ev.ButtonIndex == ButtonList.Left && ev.Pressed)
{
Visible = false;
}
}
I am trying to configure this type of game where I have 6 players connected to the same room. On game start, the Master Client (first player turn) starts where the UI button should be enabled only to him and it should be disabled to all other players and after the first player click the button it will get disabled and for the 2nd player alone it should be enabled and so on to all other players.
So i have this idea where a loop should iterate through the list of players when a button is clicked by the first or master client then getNext() that is the next player as per the actor number. The problem is how do i put in code?
I was also recommended to use Custom properties but it has been a huge problem to me since i don't understand them. Watched some of the tutorials and also some documentation but I seem not to understand.
I am currently not using photonview component.
let me just write a short code of what i mean;
public class GameManager : MonoBehaviourPunCallbacks
{
private ExitGames.Client.Photon.Hashtable _myCustomProperties = new ExitGames.Client.Photon.Hashtable();
private void Start()
{
//onstart the master client should only be the one t view the button and click it
//his name should be displayed when it his turn
if (PhotonNetwork.IsMasterClient)
{
Player_Name.text = PhotonNetwork.MasterClient.NickName + " " + "it's your turn";
button.SetActive(true);
}
}
//onclcik the Button
public void TurnButton()
{
for(int i = 0; i<PhotonNetwork.Playerlist.length; i++)
{
//so every click of the button a loop should iterate and check the player if is the next to see the button and click it
//a name should also be displayed on his turn as per his nickname
}
}
}
There is no need for custom player properties here.
I would rather use room properties for that
public class GameManager : MonoBehaviourPunCallbacks
{
private const string ACTIVE_PLAYER_KEY = "ActivePlayer";
private const string ACTIVE_ME_FORMAT = "{0}, you it's your turn!!";
private const string ACTIVE_OTHER_FORMAT = "Please wait, it's {0}'s turn ...";
private Room room;
[SerializeField] private Button button;
[SerializeField] private Text Player_Name;
#region MonoBehaviourPunCallbacks
private void Awake()
{
button.onClick.AddListener(TurnButton):
button.gameObject.SetActive(false);
Player_Name.text = "Connecting ...";
// Store the current room
room = PhotonNetwork.CurrentRoom;
if (PhotonNetwork.IsMasterClient)
{
// As master go active since usually this means you are the first player in this room
// Get your own ID
var myId = PhotonNetwork.LocalPlayer.ActorNumber;
// and se it to active
SetActivePlayer(myId);
}
else
{
// Otherwise fetch the active player from the room properties
OnRoomPropertiesUpdate(room.CustomProperties);
}
}
// listen to changed room properties - this is always called if someone used "SetCustomProperties" for this room
// This is basically the RECEIVER and counter part to SetActivePlayer below
public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
// Maybe another property was changed but not the one we are interested in
if(!propertiesThatChanged.TryGetValue(ACTIVE_PLAYER_KEY, out var newActiveID)) return;
// if we got a value but it's not an int something is wrong in general
if(!(newActiveID is int newActvieIDValue))
{
Debug.LogError("For some reason \"ACTIVE_PLAYER_KEY\" is not an int!?");
return;
}
// otherwise apply the ID
ApplyActivePlayer(newActvieIDValue);
}
// Optional but might be important (?)
// In the rare case that the currently active player suddenly leaves for whatever reason
// as the master client you might want to handle this and go to the next player then
//public override void OnPlayerLeftRoom (Player otherPlayer)
//{
// if(!PhotonNetwork.IsMasterClient) return;
//
// if(!propertiesThatChanged.TryGetValue(ACTIVE_PLAYER_KEY, out var currentActiveID) && currentActiveID is int currentActiveIDValue) return;
//
// if(currentActiveIDValue != otherPlayer.ActorNumber) return;
//
// var nextPlayer = Player.GetNextFor(currentActiveIDValue);
// var nextPlayerID = nextPlayer.ActorNumber;
// SetActivePlayer(nextPlayerID);
//}
#endregion MonoBehaviourPunCallbacks
// Called via onClick
private void TurnButton()
{
// this gets the next player after you sorted by the actor number (=> order they joined the room)
// wraps around at the end
var nextPlayer = Player.GetNext();
// Get the id
var nextPlayerID = nextPlayer.ActorNumber;
// and tell everyone via the room properties that this is the new active player
SetActivePlayer(nextPlayerID);
}
// This writes the new active player ID into the room properties
// You can see this as kind of SENDER since the room properties will be updated for everyone
private void SetActivePlayer(int id)
{
var hash = new ExitGames.Client.Photon.Hashtable();
hash[ACTIVE_PLAYER_KEY] = id;
room.SetCustomProperties(hash);
}
// this applies all local changes according to the active player
private void ApplyActivePlayer(int id)
{
// get the according player
var activePlayer = Player.Get(id);
// Am I this player?
var iAmActive = PhotonNetwork.LocalPlayer.ActorNumber == id;
// Set the button active/inactive accordingly
button.gameObject.SetActive(iAmActive);
// Set the text accordingly
Player_Name.text = string.Format(iAmActive ? ACTIVE_ME_FORMAT : ACTIVE_OTHER_FORMAT, activePlayer.NickName):
}
}
I created a script where by clicking on the smartphone display, an object was activated. I used Input.GetMouseButtonDown(0) to check if a tap was made on the display. The script works fine, but I have a problem: if I insert a button in the scene, clicking on the button I receive both the click of the button and that of the Input.GetMouseButtonDown function. How could I "divide" the two touches?
// Example
void Update()
{
if (Input.GetMouseButtonDown(0))
myObject.SetActive(true);
}
public void PauseButton()
{
// Open Pause Panel
}
I was thinking of deleting "Input.GetMouseButtonDown (0)" and using an invisible button and resizing it on the portion of the display I needed.
Add Tag to your UI button like: "UI"
Below function tells whether a click was Over a UI object tagged with a UI tag, so you can check in your screen tap function if you should fire your event or not
public static class InputHandler
{
public const string CompareTagForUIElements = "UI";
public static bool IsPointerOverUIObject(int touchId)
{
var eventDataCurrentPosition = new PointerEventData(EventSystem.current);
// I'm using touch since you are talking about smartphones,
// if you still need click use Input.mousePosition
eventDataCurrentPosition.position = new Vector2(GetTouch(touchId).position.x, GetTouch(touchId).position.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
for (int i = 0; i < results.Count; i++)
{
if (results[i].gameObject.CompareTag(CompareTagForUIElements))
return true;
}
return false;
}
}
So rather than
public void ITappedOnScreen()
{
if(Input.GetMouseButtonDown(0))
Debug.Log("Screen tapped");
}
use
public void ITappedOnScreen()
{
if(Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Begin &&
!InputHandler.IsPointerOverUIObject(0))
Debug.Log("Screen tapped");
}
I have game objects which is button (Called as tiles), when the button is clicked it will be inactive and there's a condition to be tested if the condition is true the button will be destroyed but if its false the button will back to its active status.
My game Manager script (The condition):
public void checkword()
{
wordBuilded = displayer.text.ToString();
if (txtContents.Contains(wordBuilded))
{
Debug.Log(wordBuilded + " Is on the list");
wordScore++;
Debug.Log(wordScore);
}
else
{
Debug.Log("Someting is wrong..");
FindObjectOfType<LetterTiles>().tileStatus();
}
}
On my Button (Tile) script (Script attached to the buttons):
public void letterClick()
{
gameManager.currentWord += letter;
gameObject.SetActive(false);
}
public void tileStatus()
{
gameObject.SetActive(true);
}
When an object is deactivated Unity won't find it using FindObjectOfType.
either keep a local reference to the object [the button] in the game manager before you disable it or hide the button in a different way.
Since you are using a Button, you can use the interactable property of the button to make it disabled [and if you want it to be hidden then change the disabled color to transparent] like:
gameobject.GetComponent<Button>().interactable = false;
I have an object with a script that moves it along a path. On the same object is a script that, if activated, sends it back to its start point. The mover script is default active and the home is by default inactive. I want it so that if I press "2", mover deactivates and home activates, and if I press "1", the scripts go back to how they were. states 1 & 2 should be exclusive. I have created a script, on the same object, as follows:
public class activator : MonoBehaviour
{
bool mover = new bool();
bool home = new bool();
void Start ()
{
mover = true;
home = false;
}
void Update ()
{
if (Input.GetButtonDown("1"))
{
mover = true;
home = false;
}
if (Input.GetButtonDown("2"))
{
mover = false;
home = true;
}
if(mover == true)
{
GetComponent<router>().enabled = true;
GetComponent<routeRepairs>().enabled = false;
}
else if(home == true)
{
GetComponent<router>().enabled = false;
GetComponent<routeRepairs>().enabled = true;
}
}
}
note the actual scripts I'm trying to activate and deactivate from the object are router and routeRepairs. When I actually run the game in playmode nothing happens after the button presses.
There is a question that stackoverflow is telling me is similar but the solutions given aren't doing anything for me. So, any help with how to get this working would be greatly appreciated! Thanks in advance!
one more thing: I hope to add more movement scripts that should be activated and deactivated in the same way, so solutions that only apply to two states (ie moving or home) aren't ideal. probably not important but it just occurred to me that it was worth qualifying..?
Are the inputs actually recognized? Put a print inside the input if to see if it actually gets the input. If not, maybe your input is not configured correctly. Or you can try using KeyCode instead like:
if (Input.GetKeyDown(KeyCode.Alpha1))
Also, you don't need the extra booleans or the extra if statements. Just get the scripts in start and then directly enable/disable them
router router;
routerRepairs routerRepairs;
void Start ()
{
router = GetComponent<router>();
routerRepairs = GetComponent<routerRepairs>();
}
void Update ()
{
if (Input.GetKeyDown(KeyCde.Alpha1))
{
router.enabled = true;
routerRepairs.enabled = false;
}
if (Input.GetKeyDown(KeyCde.Alpha2))
{
router.enabled = false;
routerRepairs.enabled = true;
}
}