Using same Boolean with multiple objects in Unity 2D - c#

I have four game objects with the same script and the same bool, when one of them get hit by ray the bool state is true, but the function will not start because the other three game objects is set to false.
What I tried:
the code works fine with the last object being instantiated
if I disabled the script on the first object and re-enabled it again the function works fine on this object only
public bool selected;
void Start(){
selected = false;
}
void Update(){
showRange ();
}
public void showRange(){
if (selected == true) {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = true;
}
} else {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = false;
}
}
}

Simply use a static variable: What is the use of static variable in C#? When to use it? Why can't I declare the static variable inside method?
You would have:
public static bool selected;

Try using gamemanager.
public static gamemanager Instance;
public bool selected;
private void Awake()
{
Instance = this;
}
Your gamemanager can have a public bool variable "selected".
New start method:
void Start() {
gamemanager.Instance.selected = false;
}
New showRange function
public void showRange(){
if (gamemanager.Instance.selected) {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = true;
}
} else {
for (int i = 0; i < tileRange.Count; i++) {
tileRange [i].GetComponent<SpriteRenderer> ().enabled = false;
}
}
}
I am sure this code can be improved. Let me know if it helps.

The problem isn't in the selected bool, but it's in showRange() method. If the ray hit one object it will be selected and the other three will remain unselected ((that's because I use a list that stores the last hitted object, then the code works only for the object inside this list))
showRange() will not work with the selected object, because the method want all the four object to be selected, then it works (stupid method, I was unable to sleep because of it).
I managed to fix showRange() problem, using a new script that turns off all game objects script component and turns on the selected one script, this will make showRange() method unable to check the bool state of the other three objects.
For (Guilherme, misher and Josep) thank you for your help, I really appreciate it, but as it is shown above the problem wasn't in the Boolean.
For (Muhammad Farhan Aqeel) I believe your code should work, but I didn't manage to get it work maybe because I'm still new to programming.

Related

Score reset on Reload

I know that there are many similar questions but none of them seem to be quite the same as mine. I have a score that increments by one every time an object is destroyed and that works fine but I want to reset it to 0 when I reload the scene... For some reason it won't reset, everything else works fine.
variables:
public Text Score;
public static int counter;
public Transform obstacle;
public GameObject Obstacle;
code to increment when the object is destroyed:
void Update()
{
if (Obstacle.transform.position.z <= -5)
{
DestroyObstacle(Obstacle);
}
Score.text = (counter / 3).ToString();
}
void DestroyObstacle(GameObject Obstacle)
{
Destroy(Obstacle);
counter++;
}
}
The following code stops everything and brings up a button. When that button is clicked, it reloads the level as you can see, yet the score does not reset:
public void OnCollisionEnter(Collision collision)
{
if (collision.collider.tag == "Obstacle")
{
movement.enabled = false;
Spawner.enabled = false;
PlayButton.enabled = true;
PlayText.enabled = true;
PlayButton.onClick.AddListener(TaskOnClick);
}
}
void TaskOnClick()
{
Score.text = 0.ToString();
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
I know that it is probably something simple but I would really appreciate the help.
Also! For some reason, if my counter is not a static int, the score will not increase? That's why it is static.
Because counter is static, it won't reset.
It won't increment if it is not because the object with the incremented value is destroyed.
You need another script that will have the counter variable and will increment it when an object is destroyed.
Replace the line
counter++;
by
Camera.main.GetComponent<ScoreCounter>().Increase();
And do another class (something like that) and add the script on your camera :
public class ScoreCounter : MonoBehaviour
{
public int counter;
public Text Score;
public void Increase()
{
counter++;
Score.text = (counter / 3).ToString();
}
}
It's because counter is static, and as such it belongs to the class and not the object. It won't reset unless there's a hard restart. Adding counter = 0 as the first line in your TaskOnClick would do it, or changing counter to not be static and be a member of the object.

Unity IsPointerOverGameObject Issue

I know it's a common problem but none worked. Maybe the button setup is wrong. I have a Panel with no image, and inside a settings button on the top left that once clicked opens another scene. I tried using those 3 methods but none works. It always detects it as a Game object.
The bool isGameStarted is checking if the player should move or no.
Please guide me through
Have tried
if (Swipe.Instance.Tap && !isGameStarted)
{
if (EventSystem.current.IsPointerOverGameObject() )
{
isGameStarted = false;
}
else
{
isGameStarted = true;
motor.StartRunning();
gameCanvas.SetTrigger("Show");
}
}
Also tried using trigger but it goes through the UI.
This is the original code.
if (Swipe.Instance.Tap && !isGameStarted)
{
isGameStarted = true;
motor.StartRunning();
gameCanvas.SetTrigger("Show");
}
Once you tap on the screen the player starts moving. I need it NOT to move or start the game if clicked on the settings button.
I had the same problem until I figured out that IsPointerOverGameObject seems to return true for any GameObject (possibly with a collider) and not only UI objects.
So I wrote a custom static class to check for UI objects only. You will need to set the layer of each panel, image, button, etc. to UI for it to work.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public static class MouseOverUILayerObject
{
public static bool IsPointerOverUIObject()
{
PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
for (int i = 0; i < results.Count; i++)
{
if (results[i].gameObject.layer == 5) //5 = UI layer
{
return true;
}
}
return false;
}
}
Use it like this:
private void OnMouseDown()
{
if (!MouseOverUILayerObject.IsPointerOverUIObject())
HandleClick();
}
The overload of IsPointerOverGameObject takes a parameter
int pointerId Pointer (touch / mouse) ID.
and
If you use IsPointerOverGameObject() without a parameter, it points to the "left mouse button" (pointerId = -1);
therefore when you use IsPointerOverGameObject for touch, you should consider passing a pointerId to it.
So from the example
if (Swipe.Instance.Tap && !isGameStarted)
{
if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
{
isGameStarted = false;
return;
}
isGameStarted = true;
motor.StartRunning();
gameCanvas.SetTrigger("Show");
}
otherwise if you have multiple possible touches you could also do the check for all of them like
if (Swipe.Instance.Tap && !isGameStarted)
{
foreach(var touch in Input.touches)
{
if (EventSystem.current.IsPointerOverGameObject(touch.fingerId))
{
isGameStarted = false;
return;
}
}
isGameStarted = true;
motor.StartRunning();
gameCanvas.SetTrigger("Show");
}
also look at this thread

Unity - How can i make a function fire only when player clicked on it 3 times?

I have a hint button on my game, but it fires up everytime you click on it. I need for it to fire only once every 3 clicks
i've tried adding a loadCount = 0 and an if statement
if (loadCount % 3 == 0) { }
but it dosen't seem to work
image for reference : https://ibb.co/L9LnNDw
Here is the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class HintScript : MonoBehaviour
{
LineRenderer line;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
line = GetComponent<LineRenderer>();
line.positionCount = transform.childCount;
for (int i = 0; i<transform.childCount; i++)
{
line.SetPosition(i, transform.GetChild(i).position);
}
}
// This is called from onClick of the Button
public void Hint()
{
FindObjectOfType<AdMobManager>().Hint = true;
FindObjectOfType<AdMobManager>().showInterstitial();
}
}
You should use a simply counter.
I also changed and commented other "issues" in your code:
public class HintScript : MonoBehaviour
{
// by adding SerializeField you can already set those references
// directly in the Unity Editor. This is better than getting them at runtime.
[SerializeField] private LineRenderer line;
[SerializeField] private AdMobManager adMobManager;
// you can still change the required clicks in the inspector
// Note: afterwards changing it here will have no effect!
[SerializeField] private int requiredClicks = 3;
// counter for your clicks
private int counter;
// Use this for initialization
void Start ()
{
// you should do this only once
line = GetComponent<LineRenderer>();
// you should also this only do once
adMobManager = FindObjectOfType<AdMobManager>();
// If instead you would drag those references directly into the now
// serialized fields you wouldn't have to get them on runtime at all.
}
// Update is called once per frame
void Update ()
{
line.positionCount = transform.childCount;
var positions = new Vector3[transform.childCount];
for (var i = 0; i < transform.childCount; i++)
{
position[i] = transform.GetChild(i).position;
}
// it is more efficient to call this only once
// see https://docs.unity3d.com/ScriptReference/LineRenderer.SetPositions.html
line.SetPositions(positions);
}
// This is called from onClick of the Button
public void Hint()
{
counter++;
if(counter < requiredClicks) return;
// Or using your solution should actually also work
if(counter % requiredClicks != 0) return;
adMobManager.Hint = true;
adMobManager.showInterstitial();
// reset the counter
counter = 0;
}
}
You can have a counter to keep track of how many times the player has clicked. Then fire when that counter is at 3.
int amountOfClicks = 0;
void clickme(){
amountOfClicks++;
if(amountOfClicks == 3){
amountOfClicks = 0;
YourFunction();
}
}
void YourFunction(){
// do something...
}
1.Keep a static/global variable.
2.You can't stop event notifications so, please add condition(if condition) upon code block inside the event handler code.

Putting references on unity button so that I can use a function from another script

I made a Button Prefab that I want to use to increase the health of my player. What I have a problem with is that my gainHealth function is in my Player Script and I tried referencing it in my addListeners. I just want the to Health Button to give my player Health when clicked, the problem is that this button is a prefab.
void AddListeners()
{
itemButton.onClick.AddListener(() => GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerHealth>().GainHealth(50));
}
void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
for (int i = 0; i < inventory.slots.Length; i++)
{
if (inventory.isFull[i] == false)
{
inventory.isFull[i] = true;
Instantiate(itemButton, inventory.slots[i].transform, false);
AddListeners ();
Destroy(gameObject);
break;
}
}
}
}
You can put a script on your button prefab, and look something like this.
public PlayerHealth P_Health;
void Start()
{
P_Health = GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerHealth>();
}
//Reference this action as Onclickevent through the Inspector inside your button
void OnMyClick()
{
P_Health.GainHealth(50);
}
Once the button is instantiated it will keep a reference of the PlayerHealth object and call the "GainHealth" function once the button is clicked.
You should put the "OnMyClick()" (or how you like to name it) function in the On Click Event of the button in your prefab editor.
Your approach is possible by changing a little thing. Currently your trying to add the listener directly to your prefab. Instead of this you should add the listener to the Button you instantiated with your Prefab.
An example that should work:
public GameObject itemButtonPrefab; // drag your Prefab to this field in the inspector
public Button itemButton; // the "real" instance of your prefab in the scene
void AddListeners()
{
itemButton.onClick.AddListener(() => GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerHealth>().GainHealth(50));
}
void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
for (int i = 0; i < inventory.slots.Length; i++)
{
if (inventory.isFull[i] == false)
{
inventory.isFull[i] = true;
itemButton = Instantiate(itemButtonPrefab, inventory.slots[i].transform, false).GetComponent<Button>();
AddListeners ();
Destroy(gameObject);
break;
}
}
}
}
PS: This example should also work an is slightly more performant due to the fact you don't have to call the GetComponent-method:
public Button itemButtonPrefab; // drag your Prefab to this field in the inspector
public Button itemButton; // the "real" instance of your prefab in the scene
void AddListeners()
{
itemButton.onClick.AddListener(() => GameObject.FindGameObjectWithTag("Player").GetComponent<PlayerHealth>().GainHealth(50));
}
void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
for (int i = 0; i < inventory.slots.Length; i++)
{
if (inventory.isFull[i] == false)
{
inventory.isFull[i] = true;
itemButton = Instantiate(itemButtonPrefab, inventory.slots[i].transform, false);
AddListeners ();
Destroy(gameObject);
break;
}
}
}
}

how GetComponentInChildren in unity

I have a problem:
How to assign [] status to rend[]
public GameObject[] Obj;
private bool[] Status;
private MeshRenderer[] rend;
private void Start ()
{
for (int i = 0; i < Obj.Length; i++)
{
rend[i] = GetComponentInChildren<MeshRenderer>();
Status[i] = rend[i];
}
}
Accorting to Object.bool you can try this way:
public GameObject[] Obj ;
private bool[] Status;
private MeshRenderer[] rend;
void Start () {
for (int i = 0; i < Obj.Length; i++)
{
rend[i] = Obj[i].GetComponentInChildren<MeshRenderer>();
Status[i] = rend[i] != null;
}
}
Your question is very unclear so I can just guess what you are trying to archieve:
I'ld say you want to find the first MeshRenderer under your GameObjects in Obj and get an array Status saying whether they are enabled or not.
You are currently allways searching for MeshRenderer in the GameObject this script is attached to instead of looking in the Obj GameObjects. You have to get it using
Obj[i].GetComponentInChildren<MeshRenderer>();
you probably don't initailize your arrays -> you cannot just assign values using Status[i] = and rend[i] = if the arrays were never initialized using new. You can either do that in start once you have the size of Obj
public GameObject[] Obj;
private bool[] Status;
private MeshRenderer[] rend;
private void start()
{
Status = new bool[Obj.Length];
rend = new MeshRenderer[Obj.Length];
// ...
}
but I would prefer using a List<bool> and List<MeshRenderer> instead so you can simply add and remove values later (while an array has a fixed length).
Finally I guess you want to know whether the component MeshRenderer is enabled so you can assign rend[i].enabled
all together
something like
public GameObject[] Obj ;
// Using List here you can already initialize it here
// which would be possible using an array
private List<bool> Status = new List<bool>();
private List<MeshRenderer> rend = new List<MeshRenderer>();
private void Start(){
for (int i = 0; i < Obj.Length; i++)
{
rend.Add(Obj[i].GetComponentInChildren<MeshRenderer>());
bool existsAndEnabled = rend[i] != null && rend[i].enabled;
Status.Add(existsAndEnabled);
}
}
Note that it is still possible that for one of the GameObjects in Obj there is no MeshRenderer found so I say Status shall also be false if there was none.

Categories

Resources