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.
Related
I got a quick question for you. I have already everything prepared. I am making a simple launching game, where you need to kill all enemies (not time-based) to pass to the next level. I have 2 methods GoToNextLevel() and AllMonsterDied(); .
Need to make something like player have 3 attempts (3 launchings whenever he wants not based on time).
Then every launch checks if any monster left. If does just show 1 attemps less. After 3 times just restart scene.
Thanks a lot since I am new to both c# and unity that would mean the world to me.
public class LevelController: MonoBehaviour
{
[SerializeField] string _nextLevelName;
Monster[] _monsters;
void OnEnable()
{
_monsters = FindObjectsOfType<Monster>();
}
void Update()
{
int currentLevel = SceneManager.GetActiveScene().buildIndex;
if (currentLevel >= PlayerPrefs.GetInt("levelsUnlocked"))
{
PlayerPrefs.SetInt("levelsUnlocked", currentLevel + 1);
}
if (MonsterAreAllDead() )
{
GoToNextLevel();
}
Debug.Log("Level" + PlayerPrefs.GetInt("levelsUnlocked") + "UNLOCKED");
}
void GoToNextLevel()
{
Debug.Log("Go to next level" + _nextLevelName);
SceneManager.LoadScene(_nextLevelName);
}
bool MonsterAreAllDead()
{
foreach (var monster in _monsters)
{
if (monster.gameObject.activeSelf)
return false;
}
return true;
}
}
Line from Monster.cs
private void OnCollisionEnter2D(Collision2D collision)
{
if (SouldDieFromCollision(collision))
{
tickSource.Play();
StartCoroutine(Die());
}
}
IEnumerator Die()
{
_hasDied =true;
GetComponent<SpriteRenderer>().sprite=_deadsprite;
_particleSystem.Play();
yield return new WaitForSeconds(1);
gameObject.SetActive(false);
}
Let's say you want the next level launch attempt happen when the player hits N key on the keyboard.
When the player hits the key, check if all monsters are dead. If so, call GoToNextLeve(), otherwise take off 1 attempt from the attempts available and restart the current scene.
int attempts;
void OnEnable()
{
attempts = 3;
}
void Update()
{
if (Input.GetKeyUp(KeyCode.H)
{
TryGoToNextLevel();
}
}
void TryGoToNextLevel()
{
if (MonsterAreAllDead() )
{
GoToNextLevel();
}
else
{
attempts--;
}
if (attempts <= 0)
{
SceneManager.LoadScene(
SceneManager.GetActiveScene().name);
}
}
I am working on small game similar to angry birds since I am new to both unity
and C#. I want to make load and unlock next level if all enemies are dead and check if new level exist
(if not) return back to Level selection scene.
This is what I tried:
LevelScript
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelScript : MonoBehaviour
{
[SerializeField] string _nextLevelName;
Monster[] _monsters;
void OnEnable()
{
_monsters = FindObjectsOfType<Monster>();
}
private void Update()
{
int currentLevel = SceneManager.GetActiveScene().buildIndex ;
if (currentLevel >= PlayerPrefs.GetInt("levelsUnlocked"))
{
PlayerPrefs.SetInt("levelsUnlocked", currentLevel );
}
if (MonsterAreAllDead())
{
GoToNextLevel();
}
Debug.Log("Level" + PlayerPrefs.GetInt("levelsUnlocked") + "UNLOCKED");
}
public void Pass()
{
int currentLevel = SceneManager.GetActiveScene().buildIndex;
if (currentLevel >= PlayerPrefs.GetInt("levelsUnlocked") )
{
PlayerPrefs.SetInt("levelsUnlocked", currentLevel + 1);
};
}
bool MonsterAreAllDead()
{
foreach (var monster in _monsters)
{
if (monster.gameObject.activeSelf)
return false;
}
return true;
}
void GoToNextLevel()
{
Debug.Log("Go to next level" + _nextLevelName);
SceneManager.LoadScene(_nextLevelName);
}
}
and Level Manager
public class LevelManager : MonoBehaviour
{
int levelsUnlocked;
public Button[] buttons;
void Start()
{
levelsUnlocked = PlayerPrefs.GetInt("levelsUnlocked", 1);
for (int i = 0; i < buttons.Length; i++)
{
buttons[i].interactable = false;
}
for (int i = 0; i < levelsUnlocked; i++)
{
buttons[i].interactable = true;
}
}
public void LoadLevel(int levelIndex)
{
SceneManager.LoadScene(levelIndex);
}
I got these 2 scripts and I attached both canvas and my buttons and Level script to my levels.
Problem is that every level gets unlocked at begin and after completeing levels automatically
it want to go to next level whic is not exist yet.
Pls help me. Sorry if my question is stupid and for bad english.
You should make an array of scene in your LevelManager that know all your levels (in order)
and for getting next level you can get the position of your actual scene in the array and check the next one.
somethink like
pseudocode :
[Serialized]
Scene[] AllScenes
void GoToNextLevel()
{
int currentLevelPos = AllScenes.IndexOf(currentScene);
if (AllScenes[currentLevelPos + 1] != null)
Load Next Level
else
Go To Level Menu
}
I made a dialogue script so that I can display sentences on different NPC's.
I also have added a continue button so it goes from sentence to sentence.
The problem is that for some reason the last dialog gets stuck and keeps replaying if I interact after all sentences have played.
What I want is to replay all sentences that I added onto the NPC.
I also wanted to ask if there was a way to NOT use the continue button? And let the text type itself out after a certain amount of time. Like a real conversation.
As you can see I added the lines on the right
The first line printed
The second line printed
The third line printed
Starts placing the last line back to back every time I interact UNLESS I click the Button, because then it clears. But it still replays the last sentence every time instead of reverting back to the 1st.
Dialogue Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[System.Serializable]
public class Dialogue2 : MonoBehaviour
{
public GameObject dialogBox; // Attach ui image to this
public Text npcname; // Attach UI text gameObject
public Text dialogText; // Attach UI text gameObject
public string NPC; // What is he/she called?
public string[] sentences;
public bool playerInRange; // Is the Player in range?
public float typingSpeed;
private int index;
public GameObject continueButton;
public AudioSource SignUpSfx;
public AudioSource SignDownSfx;
void Update()
{
if(dialogText.text == sentences[index]){
continueButton.SetActive(true);
}
// Player in range and E is hit
if(Input.GetKeyDown(KeyCode.E) && playerInRange)
{
if(dialogBox.activeInHierarchy)
{
dialogBox.SetActive(false);
SignDownSfx.Play();
}
else
{
dialogBox.SetActive(true);
StopAllCoroutines();
StartCoroutine(Type());
npcname.text = NPC;
SignUpSfx.Play();
}
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if(other.CompareTag("Entity"))
{
playerInRange = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if(other.CompareTag("Entity") && dialogBox.activeInHierarchy == true)
{
SignDownSfx.Play();
}
if(other.CompareTag("Entity"))
{
playerInRange = false;
dialogBox.SetActive(false);
}
}
IEnumerator Type(){
foreach(char letter in sentences[index].ToCharArray()){
dialogText.text += letter;
yield return new WaitForSeconds(typingSpeed);
}
}
public void NextSentence(){
continueButton.SetActive(false);
if(index < sentences.Length - 1){
index++;
dialogText.text = "";
StartCoroutine(Type());
} else {
dialogText.text = "";
continueButton.SetActive(false);
}
}
}
Couldn't you just change:
if(index < sentences.Length - 1){
index++;
dialogText.text = "";
StartCoroutine(Type());
} else {
dialogText.text = "";
continueButton.SetActive(false);
}
To this?
if(index < sentences.Length - 1){
index++;
dialogText.text = "";
StartCoroutine(Type());
} else {
dialogText.text = "";
continueButton.SetActive(false);
index = 0;
}
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.
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.