I have this scenario:
The first user that joins a lobby will have an active button on his screen. After some actions, if the player hit the button, it will disable, and then the next player in the room will have this button active, and so on.
Based on a property 'Turn' I want to change this button state like this.
In this function PassTurn I first set for the LocalPlayer the Turn property to false and then I determine the next player using some logic, and set for that player the Turn property to true.
private PassTurn()
{
PunHashtable _myCustomProperties_forCurrentPlayer = new PunHashtable();
_myCustomProperties_forCurrentPlayer["Turn"] = false;
PhotonNetwork.LocalPlayer.SetCustomProperties(_myCustomProperties_forCurrentPlayer);
foreach (Player player in PhotonNetwork.PlayerList)
{
// ... some logic to determine the next player to set the 'Turn' property to true
if (someCondition for player) {
PunHashtable _myCustomProperties_forNextPlayer = new PunHashtable();
_myCustomProperties_forNextPlayer["Turn"] = true;
player.SetCustomProperties(_myCustomProperties_forNextPlayer);
}
}
}
In OnPlayerPropertiesUpdate function, if Turn property ever change, activate or deactivate the button based on Turn property value (if true - show the button, if false don't display the button).
public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
{
base.OnPlayerPropertiesUpdate(targetPlayer, changedProps);
if (targetPlayer != null) {
if (changedProps.ContainsKey("Turn"))
{
{
foreach (Player player in PhotonNetwork.PlayerList)
{
// this print will display (1, false) and (2, false) which is not correct, because the second player doesn't have de property changed
print(player.ActorNumber + player.CustomProperties["Turn"];
if ((bool)player.CustomProperties["Turn"] == true)
{
print("HEY HEY HEY 1");
endTurnBtn.SetActive(true);
}
if ((bool)player.CustomProperties["Turn"] == false)
{
print("HEY HEY HEY 2");
endTurnBtn.SetActive(false);
}
}
}
}
}
}
This is not working properly since it will always enter on HEY HEY HEY 2., the button for the current play will disappear, but it will never appear on the next player.
Which is the right approach for this situation? How can I change ui components on other players screen? Thank you for your time!
LaterEdit based on accepted comment:
public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
{
base.OnPlayerPropertiesUpdate(targetPlayer, changedProps);
if (targetPlayer != null) {
if (changedProps.ContainsKey("Turn"))
{
{
if ((bool)PhotonNetwork.LocalPlayer.CustomProperties["Turn"] == true)
{
print("HEY HEY HEY 1");
endTurnBtn.SetActive(true);
}
if ((bool)PhotonNetwork.LocalPlayer.CustomProperties["Turn"] == false)
{
print("HEY HEY HEY 2");
endTurnBtn.SetActive(false);
}
}
}
}
}
you iterate over every player and check the variable, so as soon as the last player in your list has the turn property false every player will disable the button, while when the last players turn property is true every player will activate the button.
Either way it makes no sense to iterate over every player in the room, since you only want to change the locally shown button. Therefore you should only check the local player/players and change the button accordingly.
Related
I'm facing a really weird issue with the pause menu.
Whenever I pause and then unpause and pause again, the buttons lose their focus.
The first time I pause:
Second time I pause:
Only the first time is working as expected, all pauses after that are like the second image(no button highlighted). I couldn't figure out the reason.
Here is my code:
void Update()
{
var gamepad = Gamepad.current;
var keyboard = Keyboard.current;
if (gamepad == null && keyboard == null)
return; // No gamepad connected.
if ((gamepad != null && gamepad.startButton.wasPressedThisFrame) || (keyboard !=null && keyboard.pKey.wasPressedThisFrame))
{
if (GameIsPaused)
{
Resume();
}
else
{
Pause();
}
}
}
public void Resume()
{
player.gameObject.GetComponent<MyPlayerMovement>().enabled = true;
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
// GameObject.Find("ThePlayer").GetComponent<MyPlayerMovement>().enabled = false;
// GameObject.Find("ThePlayer").GetComponent<InteractIcons>().enabled = false;
}
private void Pause()
{
player.gameObject.GetComponent<MyPlayerMovement>().enabled = false;
pauseMenuUI.SetActive(true);
resumeBtn.Select();
Time.timeScale = 0f;
GameIsPaused = true;
// GameObject.Find("ThePlayer").GetComponent<MyPlayerMovement>().enabled = true;
// GameObject.Find("ThePlayer").GetComponent<InteractIcons>().enabled = true;
}
I tried assigning the button in the inspector to a variable and then added this variable like so resumeBtn.Select(); in Pause() but it changed nothing.
Note that when the buttons are not highlighted, they get highlighted only after I press the arrows up or down or move the gamepad's analogue stick up or down. but at first, they are not highlighted. How can I fix this?
In your event system you specified first selected to you resume button, however that seems to be not enough.
I'm not really sure how it works (this paragraph may be absolute bs), but after empirical research I think that event system sets its current selected item to the first selected reference during its awake or start. On disable, it seems to clear its current selected, but doesn't set it again on its enable.
One way to tackle this problem is to manually set current selected item when bringing up pause menu: eventSystem.SetSelectedGameObject(resumeButtonGameObject); where eventSystem is obviousely of EventSystem type.
Basically on my map I'm trying to use Physics.CheckBox detect when the player is in certain areas to determine where my enemies will spawn at. I am using a layer mask to detect when its colliding with the player and Gizmos to visualize this box in the editor. The issue I'm having is that it will return true even when the player isn't inside the box. I have verified every single other game item does not have the player layer mask causing it to return true when it hits something else. The kicker is Physics.CheckSphere works perfectly except for the fact that my map is square, not circle, so I can't use check sphere because I can't cover all of the areas I need to cover.
Code for both is as follows, note that both of these lines are not in my script at the same time I alternated them out for testing:
atNeighborhood = Physics.CheckSphere(spawnAreas[0].transform.position, neighborhoodRange, playerLayer);
atNeighborhood = Physics.CheckBox(spawnAreas[0].transform.position, neighborhoodRange, Quaternion.identity, playerLayer);
Why would the CheckBox return true when colliding with items not in the layer mask but the CheckSpere works perfectly and only returns true when colliding with the player? Anyone have any idea?
LET ME KNOW IF THERE ARE ANY PROBLEMS OR ERRORS IN COMMENTS. THANKS!
Ok. CheckBox can get kind of confusing sometimes. I would reccomend something else.
You could use Empty Game Objects with colliders on them and put them where ever you want. IsTrigger must be set to true. Imagine these as "zones", where whenever you step in one, something can happen.
All you have to do is set a certain tag to each zone to activate different things.
Note: Player does not need rigidbody, but it would be a whole lot less messy if you did.
Here is a script if your player does have a rigidbody (put this script on your player):
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.CompareTag("Zone 1"))
{
SpawnZombies();
}
}
Player doesn't have rigidbody:
If your player does not have a rigidbody, you could put a bunch a script on each one called "zone activator".
Important Notes for this version:
Your player must have a collider and a unique tag.
On each zone add a rigidbody.
Make sure detectCollisions is false!
Make sure useGravity is false!
This zone detector should have it's collider be a trigger;
(You do not want this thing to move!)
You can now create a script that goes on each zone:
public string message;
public bool inZone;
void OnTriggerEnter(Collider obj)
{
if (obj.gameObject.CompareTag("player"))
//Or set it to whatever tag the player has
{
inZone = true;
}
}
void OnTriggerExit(Collider obj)
{
if (obj.gameObject.CompareTag("player"))
//Or set it to whatever tag the player has
{
inZone = false;
}
}
You must then reference this in the player's script
public ZoneDetector[] allZones;
void Update()
{
//.....
foreach (ZoneDetector zone in allZones)
{
if (zone.inZone == true)
{
if (zone.message == "zone 1")
{
DoZone1();
}
if (zone.message == "zone 2")
{
DoZone2();
}
}
}
}
So after my player hits 0 HP, the player object (in my case a rocket ship) is set to false and the Game Over UI is set to true. Then the player has two options: either to restart or resume the game. Only resuming the game plays an ad, but after the ad is done playing the game doesn't continue and I get the error saying "The object of type 'GameObject'(The Game Over UI) has been destroyed but you are still trying to access it". Now I know that this happens when I specifically destroy an object, but not when I set the object to inactive.
Game Over function
private void GameOver()
{
Time.timeScale = 0f;
GameOverUI.SetActive(true);
finalScore.text = score.ToString() + " Meters";
PlayerPrefs.SetFloat("Score", 0);
totalCash.text = PlayerPrefs.GetFloat("Cash", 0).ToString() + " Cash";
highScoreTracker.text = PlayerPrefs.GetFloat("HighScore", 0).ToString() + " Meters";
}
Resume function called from Ads Manager after ad is played
public void ResumeGame()
{
GameOverUI.SetActive(false);
this.gameObject.SetActive(true);
PlayerPrefs.SetFloat("Score", score);
Time.timeScale = 1.0f;
}
Ads Manager function
public void OnUnityAdsDidFinish(string placementId, ShowResult showResult)
{
if (activeScene == "InitialEarth" || activeScene == "Earth" || activeScene == "Space" && showResult == ShowResult.Finished)
{
player.ResumeGame();
timesWatched++;
if (timesWatched >= 2)
{
adButton.interactable = false;
}
}
}
The error is pointing to the lines "GameOverUI.SetActive(false)" and "player.ResumeGame()".
I've tried keeping the player object active when its game over but same error. I have a similar function, like my Pause UI, where it's inactive initially and with a button click, its set to active and then inactive again and that works fine.
I'm not sure what's wrong or where to look. Is there another way to deactivate and active object? Why would an inactive object give a destroyed error, since its still in the game hierarchy? Any help or pointers would be much appreciated. If more info is needed, please let me know. Thanks.
I'm new to unity and i'm trying to load scenes based on items collected.
Problem is that the counter is not counting my acquired items.
I'm using OnTriggerEnter2D() to trigger the event; Below is the snippet:
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Player"))
{
collectionNumber += 1;
Destroy(gameObject);
if (collectionNumber == 1)
{
collision.gameObject.transform.Find("Acquired_Items").GetChild(0).gameObject.SetActive(true);
qiCollector.gameObject.transform.GetChild(0).gameObject.SetActive(true);
}
else if (collectionNumber == 2)
{
collision.gameObject.transform.Find("Acquired_Items").GetChild(1).gameObject.SetActive(true);
qiCollector.gameObject.transform.GetChild(1).gameObject.SetActive(true);
}
else if (collectionNumber == 3)
{
collision.gameObject.transform.Find("Acquired_Items").GetChild(2).gameObject.SetActive(true);
qiCollector.gameObject.transform.GetChild(2).gameObject.SetActive(true);
}
else
{
Debug.LogWarning("All Items Collected !!");
}
cN.text = "Collection Number " + collectionNumber.ToString();
}
}
Whenever a new scene is loaded this script is loaded because it is on my quest item. And for every scene there is a quest item. So what I want to do is basically keep track of my collectionNumber, but it resets to 0.
Any help is much appreciated :)
First method:
Don't allow your object to be destroyed on scene load
https://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
public static void DontDestroyOnLoad(Object target);
Above code will prevent destroying your GameObject and its components from getting destroyed when loading a new scene, thus your script values
Second Method:
Write out your only value into a player pref
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html
// How to save the value
PlayerPrefs.SetInt("CollectionNumber", collectionNumber);
// How to get that value
collectionNumber = PlayerPrefs.GetInt("CollectionNumber", 0);
Third method:
Implement a saving mechanism:
In your case i would not suggest this
Context
I am making a mobile game in which the player is required to touch objects in a specified order. The correct order is determined in a List called clickOrder. To determine the current object the player is supposed to click, currClickIndex is used.
Problem
When touching a correct object, the debug text will display "Correct" for a split second, and will then immediately change to "Wrong." What I am unsure about is why both the if and else blocks are executed when only touching a single object.
Code
void Update()
{
if (Input.touchCount == 1)
{
if (this.enabled)
{
Vector2 worldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(worldPoint, Vector2.zero);
if (hit != null && hit.collider != null)
{
// check if the touched object is the correct one
if (hit.collider.gameObject == clickOrder[MyData.currClickIndex])
{
debug.text = "Correct";
MyData.currClickIndex++;
}
else
{
debug.text = "Wrong";
}
}
}
}
}
As soon as the correct object is being touched, you do this:
MyData.currClickIndex++;
which moves you forward in the ordered sequence, and from then on, the previously correct object is not correct anymore. But you're still touching it.
If you want to avoid this, you need to move forward in the sequence after you've touched the correct object.
if (there are touches and the correct object is being touched)
{
set a flag;
}
else if (a flag has been set)
{
MyData.currClickIndex++;
reset the flag;
}