Well, i'm pretty new with unity. I got a problem to display highscore. The score was display everytime enemy were shot. I want to display the highscore everytime the game end and be able to update everytime i got a new highscore. The score system using the GUI text. The example below.
Score:
Highscore:
To display the score, i'm using this script
using UnityEngine;
public class HealthScript : MonoBehaviour
{
public int hp = 1;
private GUIText scoreReference;
public bool isEnemy = true;
public void Damage(int damageCount)
{
hp -= damageCount;
if (hp <= 0)
{
// Dead!
Destroy(gameObject);
scoreReference.text = (int.Parse(scoreReference.text) + 1).ToString();
}
}
void Start()
{
scoreReference = GameObject.Find("Score").guiText;
}
// . . .
}
i got some idea to retrieve the value of the score, but it won't display. Please help me.. Thanks
we put a isDead boolaen and make it true when the player dies and when isDead is true we will show the score
int score;
int highscore;
bool isDead=false;
//initilizing
void OnGUI () {
if(isDead) //make this true when player dies
GUI.Label (new Rect (0,0,100,50),score.ToString());
}
void Awake(){
highscore=PlayerPrefs.GetInt("highscore");
}
public void Damage(int damageCount)
{
hp -= damageCount;
if (hp <= 0)
{
// Dead!
Destroy(gameObject);
score++; //increase score
if(score>highscore)
highscore=score;
}
}
public void onGameEnds(){
PlayerPrefs.SetInt("highscore",highscore);
}
this will be good
get { if (_highscore == -1)
_highscore = PlayerPrefs.GetInt("Highscore", 0);
return _highscore;
}
set {
if (value > _highscore) {
_highscore = value;
highscoreReference.text = _highscore.ToString();
PlayerPrefs.SetInt("Highscore", _highscore);
}
}
}
Related
I am new to Unity and c#. I am trying to create a dice game that rolls two dices and the total values of the two dice faces is added to the player's score. I am intending the game to continue until the player rolls two 1's or the total score reached or exceeds 50, and at the end display win or lose message and the score. I somehow managed to implement most of it. However, I can't manage to update score after rolling the dice. I tried on my own to do so, but now it's keep adding the dice rolls without break and prints win message right away.
This is the Dice code
public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded;
bool thrown;
Vector3 initPosition; //Initial Position
public int diceValue;
public DiceSide[] diceSides;
void Start()
{
rb = GetComponent<Rigidbody>();
initPosition = transform.position;
rb.useGravity = false;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
RollDice();
}
if(rb.IsSleeping() && !hasLanded && thrown)
{
hasLanded = true;
rb.useGravity = false;
//rb.isKinematic = true;
SideValueCheck();
}
else if(rb.IsSleeping() && hasLanded && diceValue == 0)
{
RollAgain();
}
}
void RollDice()
{
if(!thrown && !hasLanded)
{
thrown = true;
rb.useGravity = true;
rb.AddTorque(Random.Range(0, 500), Random.Range(0, 500), Random.Range(0, 500));
}
else if(thrown && hasLanded)
{
Reset();
}
}
void Reset()
{
transform.position = initPosition;
thrown = false;
hasLanded = false;
rb.useGravity = false;
//rb.isKinematic = false;
}
void RollAgain()
{
Reset();
thrown = true;
rb.useGravity = true;
rb.AddTorque(Random.Range(0, 500), Random.Range(0, 500), Random.Range(0, 500));
}
void SideValueCheck()
{
diceValue = 0;
foreach (DiceSide side in diceSides)
{
if (side.OnGround())
{
diceValue = side.sideValue;
Debug.Log(diceValue + " has been rolled!");
}
}
}
}
and this is the sides code(just in case. not really relevant to the problem I am having but I am still attaching just in case)
public class DiceSide : MonoBehaviour
{
bool onGround;
public int sideValue;
void OnTriggerStay(Collider col)
{
if(col.tag == "Ground")
{
onGround = true;
}
}
void OnTriggerExit(Collider col)
{
if(col.tag == "Ground")
{
onGround = false;
}
}
public bool OnGround()
{
return onGround;
}
}
and here's the code I am having the hardest time with...
public class Check : MonoBehaviour
{
public GameObject DiceGameObject1;
public GameObject DiceGameObject2;
private Dice dice;
private Dice dice2;
public int dicevalue1;
public int dicevalue2;
private int score;
private int totalScore;
public TMP_Text Score;
public TMP_Text TotalScore;
public TMP_Text Win;
public TMP_Text Lose;
public TMP_Text Player;
void Start()
{
totalScore = 0;
score = 0;
Win.text = "";
Lose.text = "";
}
void Awake()
{
dice = DiceGameObject1.GetComponent<Dice>();
dice2 = DiceGameObject2.GetComponent<Dice>();
}
void Update()
{
UpdateScore();
}
void SetScoreText()
{
if (dicevalue1 == 1 && dicevalue2 == 1)
{
Lose.text = "You Lose:(";
}
else if (totalScore >= 50)
{
Win.text = "You win!";
}
else
{
TotalScore.text = totalScore.ToString();
}
}
public void UpdateScore()
{
dicevalue1 = dice.diceValue;
dicevalue2 = dice2.diceValue;
score = dicevalue1 + dicevalue2;
Debug.Log(dicevalue1 + dicevalue2);
// how do I import diceValue variables from Dice class and use the values here?
totalScore += score;
SetScoreText();
}
}
So I first had problem using the diceValue variable(value) from Dice script at Check script, but I somehow managed to. I am quite not understanding how, but I tried GetComponent from Dice file to make it somehow work. I was wondering if I should do that with UpdateScore method as well, but I am stuck...
Eventually I am trying to make this a turn-based two-player game, where player take turns. So far I barely managed it to work as one player game...
Please help!!
You are calling UpdateScore() in Update(). Update() is called every frame, so when your SideValueCheck() has evaluated a dice number, the score will rise very quickly to 50. What you need is a condition to query when the dice numbers have been evaluated such that you update the score only once. You could add in your Check class a new flag bool scoreUpdated = false; and change UpdateScore() to
public void UpdateScore()
{
dicevalue1 = dice.diceValue;
dicevalue2 = dice2.diceValue;
// only update if not done already and the dice have meaningful side values
if (!scoreUpdated && dicevalue1 != 0 && dicevalue2 != 0) {
score = dicevalue1 + dicevalue2;
Debug.Log(dicevalue1 + dicevalue2);
// how do I import diceValue variables from Dice class and use the values here?
// (Edit: you did that already)
totalScore += score;
scoreUpdated = true; // track the score update
SetScoreText();
}
}
Now, if you do a new dice roll, you need to first reset the dice value of the rolling dice. Reset() would be edited to:
void Reset()
{
transform.position = initPosition;
thrown = false;
hasLanded = false;
diceValue = 0; // the dice is rolling again, so no valid side value
rb.useGravity = false;
//rb.isKinematic = false;
}
and of course you need to reset the score tracking which you could do maybe in Update() of Dice class, because you located the input code there:
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
RollDice();
check.scoreUpdated = false;
}
How to get check? I dont know where you attached it, but you need to get a reference to that game object and then get the script component on it with Check check = go.GetComponent<Check>(); just like you did with the dice variables.
I don't know why the sound is not playing when the game is over. I am able to play sounds on the game menu and when the game start but when the game is over the sound doesn't play.
I am sure I am making some mistake, but I am not able to find where i am doing wrong.
Any help will be appreciated.
Here is the code
public static GameManager instance;
public GameObject PlatformSpawner;
public GameObject GameTapTextPanel;
public GameObject GameIntroPanel;
public GameObject scoreManagerUI;
public bool gameStarted;
public bool gameEnd;
// Start is called before the first frame update
public Text scoreText;
public Text highScoreText;
int score = 0;
int highScore;
AudioSource audioSource;
public AudioClip[] GameMusic;
void Awake()
{
if (instance == null)
{
instance = this;
}
audioSource = GetComponent<AudioSource>();
}
void Start()
{
highScore = PlayerPrefs.GetInt("HighScore");
highScoreText.text = "Best Score : " + highScore;
}
// Update is called once per frame
void Update()
{
if (!gameStarted)
{
if (Input.GetMouseButtonDown(0))
{
GameStart();
audioSource.clip = GameMusic[1];
audioSource.Play();
}
}
}
public void GameStart()
{
gameStarted = true;
PlatformSpawner.SetActive(true);
StartCoroutine(ScoreUp()); // to update the score we need to add this code;
GameTapTextPanel.SetActive(false);
scoreManagerUI.SetActive(true);
GameIntroPanel.GetComponent<Animator>().Play("TwistTurnAnim");
}
public void GameOver()
{
audioSource.clip = GameMusic[3]; //Here is the problem the sound doesn't play
audioSource.Play();
gameStarted = false;
gameEnd = true;
PlatformSpawner.SetActive(false);
scoreManagerUI.SetActive(true);
GameIntroPanel.SetActive(false);
SaveHighScore();
Invoke("ReloadLevel",1f);
StopCoroutine(ScoreUp());
}
void ReloadLevel()
{
SceneManager.LoadScene("Game");
}
//this one is used to increase score per second of the game;
IEnumerator ScoreUp()
{
while (true)
{
yield return new WaitForSeconds(1.5f);
score ++;
scoreText.text = score.ToString();
}
}
public void IncrementScore()
{
audioSource.PlayOneShot(GameMusic[2],0.4f);
score += 4; //Bonus points for game
scoreText.text = score.ToString();
}
void SaveHighScore()
{
if (PlayerPrefs.HasKey("HighScore"))
{
if (score > PlayerPrefs.GetInt("HighScore"))
{
PlayerPrefs.SetInt("HighScore", score);
}
}
else
{
PlayerPrefs.SetInt("HighScore",score);
}
}
Is the audio source part of an object that is deleted upon the Game Over condition?
The scripting API mentions that AudioSource is attached to an object.
In your method, GameOver, you are calling Invoke("ReloadLevel",1f) which will reload the scene in 1.0 seconds. Your GameMusic[3] is not playing because it doesn't have a chance to play before the scene is reloaded.
I would like to istantiate multiple dices (you should be able to add and substract dices) and roll them.
For now I can roll a dice and get the readout in the console.
My problem: I can't get multiple dice to work...
These are the scripts:
the dice controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiceController : MonoBehaviour
{
public Dice dice;
public GameObject dicePre;
public int count = 1;
void Update()
{
GameObject[] dices = GameObject.FindGameObjectsWithTag("Dice");
if(count - 1 == dices.Length){
for (int i = 0; i < count; i++)
{
Instantiate(dicePre, new Vector3(i * 1.1F, 0, 0), Quaternion.identity);
}
}
else if(count -1 < dices.Length){
return;
}
}
public void Throw()
{
GameObject[] dices = GameObject.FindGameObjectsWithTag("Dice");
foreach(GameObject dic in dices){
dice = dic.GetComponent<Dice>();
dice.RollDice();
}
}
public void Plus(){ //add dice
count++;
}
public void Minus(){ //substract dice
count--;
}
}
the dice sides:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiceSide : MonoBehaviour
{
bool onGround;
public int sideValue;
void OnTriggerStay(Collider col) {
if(col.tag == "ground"){
onGround = true;
}
}
void OnTriggerExit(Collider col) {
if(col.tag == "ground"){
onGround = false;
}
}
public bool OnGround(){
return onGround;
}
}
the main dice script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Dice : MonoBehaviour
{
Rigidbody rb;
bool hasLanded;
bool thrown;
Vector3 initPos;
public int diceValue;
public DiceSide[] diceSides;
private void Start(){
rb = GetComponent<Rigidbody>();
initPos = transform.position;
rb.useGravity = false;
}
private void Update(){
if(Input.GetKeyDown(KeyCode.T)){
RollDice();
}
if(rb.IsSleeping() && !hasLanded && thrown){
hasLanded = true;
rb.useGravity = false;
rb.isKinematic = true;
SideValueCheck();
}
else if(rb.IsSleeping() && hasLanded && diceValue == 0){
RollAgain();
}
}
public void RollDice(){
if(!thrown && !hasLanded){
thrown = true;
rb.useGravity = true;
rb.AddTorque(Random.Range(0,500), Random.Range(0,500), Random.Range(0,500));
}
else if(thrown && hasLanded){
Reset();
}
}
void Reset(){
transform.position = initPos;
thrown = false;
hasLanded = false;
rb.useGravity = false;
rb.isKinematic = false;
}
void RollAgain(){
Reset();
thrown = true;
rb.useGravity = true;
rb.AddTorque(Random.Range(0,500), Random.Range(0,500), Random.Range(0,500));
}
void SideValueCheck(){
diceValue = 0;
foreach(DiceSide side in diceSides){
if(side.OnGround()){
diceValue = side.sideValue;
Debug.Log("Eine " + diceValue + " wurde gewürfelt!");
}
}
}
}
How can I get this to work?
also here you can download the unitypackage with everything i got right now:
https://workupload.com/file/7brN4gTCeLu
First as said I would directly make the prefab field of type
public Dice dicePre;
then I would not use FindGameObjectsWithTag all the time to get current instances.
I would rather keep track of them in a List like e.g.
public class Dice : MonoBehaviour
{
// every instance will add itself to this list
private static List<Dice> instances = new List<Dice>();
// public read only access
public static ReadOnlyCollection<Dice> Instances => instances.AsReadOnly();
// Add yourself to the instances
private void Awake()
{
instances.Add(this);
}
// Remove yourself from the instances
private void OnDestroy()
{
instances.Remove(this);
}
}
So later you can simply use
foreach(var dice in Dice.Instances)
{
dice.RollDice();
}
The main issue
Then currently you are checking
if(count - 1 == dices.Length)
and if so you instantiate count dices.
So what if your dices is empty and your count is 3? -> nothing would happen
Or what if you already have 2 dices but count is 3 -> you spawn 3 dices and end up with 5!
You would need to actually check the difference between the dices amount and count and either add or remove only the difference.
In order to fix this I would not do this in Update but rather using a property like
[SerializeField] private int _count;
public int Count
{
get => _count;
set
{
// Count can not be negative
_count = Mathf.Max(0, value);
// Now do something with this new value
// check difference
var dif = Dice.Instances.Count - _count;
// if 0 -> nothing to do
if(dif == 0)
{
return;
}
// if smaller then 0 -> need more dices
if(dif < 0)
{
for(var i = dif; i < 0; i++)
{
Instantiate(dicePre, Vector3.right * Dice.Instances.Count, Quaternion.identity);
}
}
// dif bigger then 0 -> have to many dices
else
{
for(var i = 0; i < dif; i++)
{
DestroyImmediate(Dice.Instances[Dice.Instances.Count - 1]);
}
}
}
}
[ContextMenu(nameof(Plus))]
public void Plus()
{
Count++;
}
[ContextMenu(nameof(Minus))]
public void Minus()
{
Count--;
}
i do not know unity... so if this is off base with regards to that then i will gladly delete.
public class DiceController : MonoBehaviour
{
public List<Dice> dices;
void DiceController()
{
dices = new list<Dice>();
dices.add(new Dice); //ensure always have at least 1 on start
}
public void Plus()
{
dices.Add(new Dice);
}
//caller can decided, could be random which one get removed.
//assume they all equal
public void Minus(Dice dice){
dices.Remove(dice);
}
public void Throw()
{
foreach(Dice dice in dices){
dice.RollDice();
}
}
}
I have made a game manager making sure my data gets passed on from the first scene on to the next scene. Within the game manager, I have added certain scripts to ensure it gets passed. As you can see in the picture underneath.
The problem is that the score adds up at the first level, let's say I have 5 points. I go to level 2 and the UI shows my score as 0 (even tho I have nothing put as text within the score text) I kill 1 monster and the UI shows 6. So how can I put the UI to be showing it at all times? (Constant refresh?)
The second problem is that while the score manager does work. The health script cancels everything out when switching levels.
The user starts with 10 health. Takes damage in the first scene, but in the second scene, the user still has 10 health for some reason?
Game Manager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public ActionButton PopupPrefab;
private ActionButton currentlySpawnedPopup;
public static GameManager instance = null;
void Awake () {
if (instance == null) {
instance = this;
} else if (instance != this) {
Destroy (gameObject);
}
Application.targetFrameRate = 60;
}
void Update () {
//if (Input.GetKeyDown(KeyCode.R)) {
// RestartScene ();
//}
}
public void InvokeRestartScene (float time) {
Invoke ("RestartScene", time);
}
public void RestartScene () {
SceneManager.LoadScene (0);
}
public void SpawnPopup (Vector2 position) {
DespawnPopup ();
currentlySpawnedPopup = Instantiate (PopupPrefab, position, Quaternion.identity) as ActionButton;
}
public void DespawnPopup () {
if (currentlySpawnedPopup != null) {
currentlySpawnedPopup.DestroySelf();
currentlySpawnedPopup = null;
}
}
public void FadePopup () {
if (currentlySpawnedPopup != null) {
currentlySpawnedPopup.FadeMe ();
currentlySpawnedPopup = null;
}
}
}
Score Manager
using UnityEngine;
using System;
using System.Collections;
public class ScoreManager : MonoBehaviour
{
public static ScoreManager Instance { get; private set; }
public int Score { get; private set; }
public int HighScore { get; private set; }
public bool HasNewHighScore { get; private set; }
public static event Action<int> ScoreUpdated = delegate {};
public static event Action<int> HighscoreUpdated = delegate {};
private const string HIGHSCORE = "HIGHSCORE";
// key name to store high score in PlayerPrefs
void Awake()
{
if (Instance)
{
DestroyImmediate(gameObject);
}
else
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
}
void Start()
{
Reset();
}
public void Reset()
{
// Initialize score
Score = 0;
// Initialize highscore
HighScore = PlayerPrefs.GetInt(HIGHSCORE, 0);
HasNewHighScore = false;
}
public void AddScore(int amount)
{
Score += amount;
// Fire event
ScoreUpdated(Score);
if (Score > HighScore)
{
UpdateHighScore(Score);
HasNewHighScore = true;
}
else
{
HasNewHighScore = false;
}
}
public void UpdateHighScore(int newHighScore)
{
// Update highscore if player has made a new one
if (newHighScore > HighScore)
{
HighScore = newHighScore;
PlayerPrefs.SetInt(HIGHSCORE, HighScore);
HighscoreUpdated(HighScore);
}
}
}
Health Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Health : MonoBehaviour {
public UnityEvent OnTakeDamageEvent;
public UnityEvent OnTakeHealEvent;
public UnityEvent OnDeathEvent;
[Header ("Max/Starting Health")]
public int maxHealth;
[Header ("Current Health")]
public int health;
[Header ("IsDeathOrNot")]
public bool dead = false;
[Header ("Invincible")]
public bool invincible = false;
public bool becomeInvincibleOnHit = false;
public float invincibleTimeOnHit = 1f;
private float invincibleTimer = 0f;
[Header ("Perform Dead Events after x time")]
public float DieEventsAfterTime = 1f;
void Start () {
health = maxHealth;
}
void Update () {
if (invincibleTimer > 0f) {
invincibleTimer -= Time.deltaTime;
if (invincibleTimer <= 0f) {
if (invincible)
invincible = false;
}
}
}
public bool TakeDamage (int amount) {
if (dead || invincible)
return false;
health = Mathf.Max (0, health - amount);
if (OnTakeDamageEvent != null)
OnTakeDamageEvent.Invoke();
if (health <= 0) {
Die ();
} else {
if (becomeInvincibleOnHit) {
invincible = true;
invincibleTimer = invincibleTimeOnHit;
}
}
return true;
}
public bool TakeHeal (int amount) {
if (dead || health == maxHealth)
return false;
health = Mathf.Min (maxHealth, health + amount);
if (OnTakeHealEvent != null)
OnTakeHealEvent.Invoke();
return true;
}
public void Die () {
dead = true;
if (CameraShaker.instance != null) {
CameraShaker.instance.InitShake(0.2f, 1f);
}
StartCoroutine (DeathEventsRoutine (DieEventsAfterTime));
}
IEnumerator DeathEventsRoutine (float time) {
yield return new WaitForSeconds (time);
if (OnDeathEvent != null)
OnDeathEvent.Invoke();
}
public void SetUIHealthBar () {
if (UIHeartsHealthBar.instance != null) {
UIHeartsHealthBar.instance.SetHearts (health);
}
}
}
I have thought of adding the following script on to my Health Script
But then I get the following error messages:
" Cannot implicitly convert type 'int' to 'bool'"
"The left-hand side of an assignment must be a variable, property or indexer"
void Awake()
{
if (health)
{
DestroyImmediate(gameObject);
}
else
{
(int)health = this;
DontDestroyOnLoad(gameObject);
}
}
The problem is that the score adds up at the first level, let's say I have 5 points. I go to level 2 and the UI shows my score as 0 (even tho I have nothing put as text within the score text) I kill 1 monster and the UI shows 6. So how can I put the UI to be showing it at all times? (Constant refresh?)
You can make one of the scripts set the UI text score when the scene is loaded.
void Start(){
// Loads the scoreText on start
scoreText.text = yourCurrentScore.ToString();
// Will work unless it has a "DontDestroyOnLoad" applied to it
}
The second problem is that while the score manager does work. The
health script cancels everything out when switching levels. The user
starts with 10 health. Takes damage in the first scene, but in the
second scene, the user still has 10 health for some reason?
In your health script, you had this:
void Start () {
health = maxHealth;
}
This resets your health everytime the scene loads and starts (Aka when you load to the next level). This causes the issue.
" Cannot implicitly convert type 'int' to 'bool'"
if (health)
The () for the if statement should be a condition (a question).
For example, doing health < 0 is valid since its saying "Is health less than 0?"
Doing health is not, since its just saying "10" (or some number).
"The left-hand side of an assignment must be a variable, property or
indexer"
(int)health = this;
If you wanted to change the value of health, just do health = 10 or health = some_Variable_That_Is_An_Integer
A quick question. I am playing with a small game from some tutorials but wanted to add a score that reduces each second. I managed to do so by the code provided below, but the score only lowers with 1 seconds at a time. I want to speed it up somehow, so it can drop each second by let's say 150. I tried a few things, but they didn't work and most of them didn't even record a change on the GUI inside the game. Any help is appreciated!
Code:
public class GameOver : MonoBehaviour {
public GameObject gameOverScreen;
public Text Score;
public Text Highscore;
bool gameOver;
private int score = 12000;
void Start () {
FindObjectOfType<PlayerController>().OnPlayerDeath += OnGameOver;
}
public void Update () {
Score.text = Mathf.Round(score - Time.timeSinceLevelLoad).ToString();
if (gameOver)
{
if (Input.GetKeyDown (KeyCode.Space))
{
SceneManager.LoadScene(1);
}
}
}
void OnGameOver()
{
gameOverScreen.SetActive (true);
Highscore.text = Mathf.Round(score - Time.timeSinceLevelLoad ).ToString();
gameOver = true;
}
}
Don't repeat yourself. Make a function to get the score.
int GetScore() {
return score - (int)Time.timeSinceLevelLoad * 150;
}
And then use it.
void Update() {
Score.text = GetScore().ToString();
}
Change this part
Score.text = Mathf.Round(score- Time.deltaTime*150).ToString();
--
public void Update () {
Score.text = Mathf.Round(score- Time.deltaTime*150).ToString();
if (gameOver)
{
if (Input.GetKeyDown (KeyCode.Space))
{
SceneManager.LoadScene(1);
}
}
}