I am making a maze game in unity 3d where the least time taken to complete the level is the highscore. Is there a way to save the highscore using Playerprefs? Scripting is done in C#.
You can make it easier using Get/Set Assessors.
public class GameManager: MonoBehavior {
...
public float HighScore {
get {
return PlayerPrefs.GetFloat("HIGHSCORE", 0);
}
set {
if(value > HighScore) {
PlayerPrefs.SetFloat("HIGHSCORE", value);
}
}
}
...
public void GameOver() {
HighScore = currentScore;
}
}
You can first put this check in your homescript in awake function:
if(!PlayerPrefs.HasKey("HighestScore"))
{
PlayerPrefs.SetString("HighestScore", "keep some big number");
}
Or alternatively can use (Preferably)
if(!PlayerPrefs.HasKey("HighestScore"))
{
PlayerPrefs.SetFloat("HighestScore", 100000f);
}
Then when the particular level ends and you need to save the score
Compare the existing PlayerPref with the current Player score.
For example for float
if(PlayerPrefs.GetFloat("HighestScore")>=current_score)
{
PlayerPrefs.SetFloat("HighestScore",current_score);
}
Hope this helps !
Thanks I got the answer! Just needed to check if the first round had already taken place or not.
// Start is called before the first frame update
void Start()
{
HighScore = score;
WinScore.text = PlayerPrefs.GetFloat("HighScore").ToString();
}
// Update is called once per frame
void Update()
{
if(PlayerPrefs.GetFloat("HighScore") == 0f)
{
PlayerPrefs.SetFloat("HighScore", (float)HighScore);
WinScore.text = HighScore.ToString();
}
if (HighScore < PlayerPrefs.GetFloat("HighScore")){
PlayerPrefs.SetFloat("HighScoreEasy", (float)HighScore);
WinScore.text = HighScore.ToString();
}
}
Related
Hey Guys I'm having some Problems with my Highscore in a Unity game. Its a 2d runner in which I save the Player Distance in a Variable and want to check when the Player is dead if distance is higher than Highscore. But I have a Error and can't figure out what the problem is. Can somebody help me, I'm pretty new to c#.
Heres my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class UIController : MonoBehaviour
{
Player player;
Text distanceText;
GameObject results;
Text finalDistanceText;
private void Awake()
{
player = GameObject.Find("Player").GetComponent<Player>();
distanceText = GameObject.Find("DistanceText").GetComponent<Text>();
results = GameObject.Find("Results");
finalDistanceText = GameObject.Find("FinalDistanceText").GetComponent<Text>();
HighscoreNumber = GameObject.Find("HighscoreNumber").GetComponent<Text>();
results.SetActive(false);
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
int distance = Mathf.FloorToInt(player.distance);
distanceText.text = distance + " m";
if (player.isDead)
{
results.SetActive(true);
finalDistanceText.text = distance + " m";
if (distance > Highscore)
{
int Highscore = distance
HighscoreNumber.text = Highscore + " m";
}
}
}
public void Quit()
{
SceneManager.LoadScene("Menu");
}
public void Retry()
{
SceneManager.LoadScene("SampleScene");
}
}
In your second if statment inside update method you use HighScore in comparison then declare variable with the same name on the next line.
Couple issues that I can see. You'll be getting an error for HighscoreNumber being assigned to before being declared.
class UIController : MonoBehaviour {
Text highScore
//...
void Awake() {
highScore = GameObject.Find...
}
}
The next issue is that you're declaring a Highscore locally after you try to use it in the if statement:
// Highscore doesn't exist yet so you can't compare distance against it.
if (distance > Highscore)
{
// The int here means you're making a new variable that only lives inside the braces and setting it to distance.
int Highscore = distance
HighscoreNumber.text = Highscore + " m";
}
If this UIComponent stays alive between scene loads, you could just move the declaration to the class level.
public class UIController : MonoBehavior {
int highScore = 0 //I get set to 0 when the UIController component is created.
//...
void Update() {
//...
// Since highScore is declared at the scope of the class, the if statement and assignment inside both have access.
if(distance > highScore) {
highScore = distance;
}
}
}
The other issue is that it looks like the UIComponent may be getting disposed and re-created when you restart the scene so will likely be reset to 0 between restarts. You'll want to look at something like the Singleton method described here:
https://www.sitepoint.com/saving-data-between-scenes-in-unity/
This is just what came up when I Googled, there's probably a bunch of examples but that should help you know what to search.
However, this will only survive as long as the game is running. If you want to retain the high score between game launches, you'll want to look at something like this:
https://blog.unity.com/technology/persistent-data-how-to-save-your-game-states-and-settings
I'm making a 2D game where you're in the middle of the screen and you move round an endless green (screen) world and white cubes spawn randomly around you, and I have finished the game mechanics and a main menu and game over screens. The one thing I'm trying to add now is a high score. I did a bit of research and found PlayerPrefs is probably the way to do it. I have a seperate scene for my main menu and my gameplay level (which includes the game over screen). I have no error messages. I have created a HSSetter (High Score Setter) script on the high score text in the main menu screen:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HSSetter : MonoBehaviour
{
public Text highScoreText;
// Start is called before the first frame update
void Start()
{
highScoreText.text = "High Score: " + PlayerPrefs.GetInt("HighScore").ToString();
}
// Update is called once per frame
void Update()
{
highScoreText.text = "High Score: " + PlayerPrefs.GetInt("HighScore").ToString();
}
}
and in my score script which is in my actual game level, here's the bit where I try to create the high score:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class score : MonoBehaviour
{
public int scoreCount = 0;
public int highScoreIFA;
void Start()
{
highScoreIFA = PlayerPrefs.GetInt("HighScore");
}
void Update()
{
if (scoreCount >= highScoreIFA)
{
PlayerPrefs.SetInt("HighScore", scoreCount);
}
}
public void AddToScore()
{
if (isHit == true) // i know this if loop works
{
scoreCount += 1; // and this, I use it to change the score text in-game.
isHit = false;
}
}
}
In AddToScore(), I increment scoreCount.
Through some debugging, I have found that everything in the HSSetter script works - when I change the highScoreText.text, the text on screen changes, which led me to believe the issue might be with the change of scenes? Thanks!
Multiple things you should do here
The first you already updated in your question afterwards: You had the condition wrong and always updated only
if(highScoreIFA > scoreCount)
which would almost always be the case.
Now you have changed it to
if(scoreCount >= highScoreIFA)
which still is not good since if the score is equal there is no reason to update it, yet.
I would rather use
if(scoreCount > highScoreIFA)
so only really update it when needed.
Secondly in both scripts do not use Update at all! That is extremely inefficient.
I would rather use event driven approach and only change and set stuff in the one single moment it actually happens.
You should only one single class (e.g. the score) be responsible and allowed to read and write the PlayerPrefs for this. I know lot of people tent to use the PlayerPrefs for quick and dirty cross access to variables. But it is exactly this: Quick but very dirty and error prone.
If you change the keyname in the future you'll have to do it in multiple scripts.
Instead rather let only the score do it but then let other scripts reference it and retrieve the values directly from that script instead
And finally you should use
PlayerPrefs.Save();
to create checkpoints. It is automatically done in OnApplicationQuit, bit in case your app is force closed or crashes the User would lose progress ;)
Might look like
public class score : MonoBehaviour
{
public int scoreCount = 0;
// Use an event so every other script that is interested
// can just register callbacks to this
public event Action<int> onHighScoreChanged;
// Use a property that simply always invoked the event whenever
// the value of the backing field is changed
public int highScoreIFA
{
get => _highScoreIFA;
set
{
_highScoreIFA = value;
onHighScoreChanged?.Invoke(value);
}
}
// backing field for the public property
private int _highScoreIFA;
private void Start()
{
highScoreIFA = PlayerPrefs.GetInt("HighScore");
}
public void AddToScore()
{
if (isHit == true) // i know this if loop works
{
scoreCount += 1; // and this, I use it to change the score text in-game.
isHit = false;
// Only update the Highscore if it is greater
// not greater or equal
if (scoreCount > highScoreIFA)
{
PlayerPrefs.SetInt("HighScore", scoreCount);
// Save is called automatically in OnApplicationQuit
// On certain checkpoints you should call it anyway to avoid data loss
// in case the app is force closed or crashes for some reason
PlayerPrefs.Save();
}
}
}
}
Then your other script only listens to the event and updates its display accordingly. It is even questionable if both scripts should not rather simply be one ;)
public class HSSetter : MonoBehaviour
{
public Text highScoreText;
// Reference your Score script here
[SerializeField] private score _score;
private void Awake ()
{
// Find it on runtime as fallback
if(!_score) _score = FindObjectOfType<score>();
// Register a callback to be invoked everytime there is a new Highscore
// Including the loaded one from Start
_score.onHighScoreChanged += OnHighScoreChanged;
}
private void OnDestroy()
{
_score.onHighScoreChanged += OnHighScoreChanged;
}
private void OnHighScoreChanged(int newHighScore)
{
highScoreText.text = $"High Score: {newHighScore}";
}
}
I'm going to enable bloody screen when got attack then disble it for two seconds.
The first way I tried is using SetActive (true/flase) .
It could work once but never worked back anymore.
I found the question by google, it seems that function will disable everything of that component.
So I found the second way which is using Image Class to do this.
but I not sure whether the way I used correctly or not.
The error msg : Object reference not set to an instance of an object
First Way (working once)
public class GameControllerScript : MonoBehaviour {
public GameObject bloodyScreen;
void Start () {
bloodyScreen.gameObject.SetActive (false); // disable at first
}
void Update () {
}
public void zombieAttack(bool zombieIsThere ){
bloodyScreen.gameObject.SetActive (true);
StartCoroutine(WaitTwoSeconds());
}
IEnumerator WaitTwoSeconds(){
yield return new WaitForSeconds (2f);
bloodyScreen.gameObject.SetActive (false);
} }
Second Way (not working)
public class GameControllerScript : MonoBehaviour {
public Image image;
void Start () {
GameObject go = GameObject.Find("Canvas");
if (!go)
return;
image = go.GetComponent<Image>();
image.enable = false;
}
void Update () {
}
public void zombieAttack(bool zombieIsThere ){
image.enabled = true;
StartCoroutine(WaitTwoSeconds());
}
IEnumerator WaitTwoSeconds(){
yield return new WaitForSeconds (2f);
image.enabled = false;
} }
Sorry for my english if soemthing is not clear.
What my object likes https://i.stack.imgur.com/DChuS.png
there are so many ways of doing the BLOODY SCREEN.
1.)
STEP 1
Make an array of texture then put all of your texture there
STEP 2
On your OnTriggerEnter or OnCollisionEnter or what ever function you want and try implementing this code
Texture[] arrayOfTexture = new Texture
foreach(Texture textures in arrayOfTexture){
//set the alpha here from 1-0/0-1
}
2.)
You can do it also in an Update() function and put it on a timer you can do it something like
float timer = 10f;
void Update(){
timer -= time.DeltaTime;
if(timer < 1){
//reset timer here
}else{
//do the alpha thing here
}
}
void ResetTimer(){
timer = 10f;
}
I have game for multiple players where each user selects their hero before game starts and that loads the selected heroes into the battle arena.
I have small issue with getting the instantiation to spawn in correct numbers of players
The method that I have for Spawning the characters:
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
This script checks for amount of selected hero cards and puts it against my list that has all the available heroes to choose from(prefabs).
The debug.log shows that only the correct heroes get called.
Instantiate ends up spawning a loot of heroes instead of the selected amount.
For clarity I attach full class:
{
private int playerSize; //amount of choices for card selection
private GameManager GM;
[Header("Lists for Spawning in Heroes")]
public List<GameObject> SelectedCards;
public List<GameObject> AvailableHeroes;
public List<Transform> PlayerSpawnLocation;
[Header("Canvas used for the game")]
public Transform GameCanvas;
public Transform CharacterCanvas;
//When scene starts it takes how many players will be picking a card.
void Start()
{
//connects this script with gamenmanager to be able to manipulate the cameras
GM = GameObject.Find("GameManager").GetComponent<GameManager>();
//gets playersize information from main menu selection
PlayerPrefs.GetInt("PlayerSize");
playerSize = PlayerPrefs.GetInt("PlayerSize");
SelectedCards = new List<GameObject>();
//enables/disables correct canvas not to cause any problems when we initiate this scene
GameCanvas.gameObject.SetActive(false);
CharacterCanvas.gameObject.SetActive(true);
}
// Update is called once per frame
void Update()
{
if (playerSize <= 0)
{
Placement();
GM.CharacterSelectionCamera.enabled = false;
GameCanvas.gameObject.SetActive(true);
CharacterCanvas.gameObject.SetActive(false);
GM.BattleCamera.enabled = true;
}
}
public void PlayerSelected(int cardPicked)
{
playerSize -= cardPicked;
}
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
}
I hope someone can explain where I am going wrong with this.
Thanks,
I got the answer, I guess I was just being tired from working and could not see the obvious.
For those who wonder what solution is
The method gets called each frame thus it continues to endlessly spawn objects
There are 2 ways to fix it
1 Make coroutine and then return after you make your initial batch
2 Use a boolean at update so not only it checks player size but also whenever it can spawn it or not, you set the boolean to false after method get called.
I did not even notice the update function part.
Just a heads up, in your start function, PlayerPrefs.GetInt("PlayerSize"); is not doing anything since the value is not saved anywhere.
I'm trying to make a GameObject appear and disappear for a finite amount of time (Lets put the time function aside for now).
Here's what I came out with:
using UnityEngine;
using System.Collections;
public class Enemy1Behavior : MonoBehaviour
{
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
this.gameObject.SetActive(false); // Making enemy 1 invisible
Debug.Log("Update called");
DisappearanceLogic(gameObject);
}
private static void DisappearanceLogic(GameObject gameObject)
{
int num = 0;
while (num >= 0)
{
if (num % 2 == 0)
{
gameObject.SetActive(false);
}
else
{
gameObject.SetActive(true);
}
num++;
}
}
}
Now when I click the play button in Unity the program just don't respond, and I can only quit it from the task manager using End Task.
(And yes I know there a infinite loop in the method).
So I guess Im doing something wrong. What is the best way for making a Gameobject Blink/Flash/appear-disappear in Unity?
Thanks guys.
You are using an infinite loop which locks your Update() completely, because num will always be greater then 0.
So you could use InvokeRepeating (http://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html)
public GameObject gameobj;
void Start()
{
InvokeRepeating("DisappearanceLogic", 0, interval);
}
void DisappearanceLogic()
{
if(gameobj.activeSelf)
{
gameobj.SetActive(false);
}
else
{
gameobj.SetActive(true);
}
}
interval is a float - something like 1f 0.5f etc.
You can make animation for blinking etc - Animations in Mecanim. Appearing and disappearing you can achieve using gameObject.SetActive(true/false);. If you want to make something with time its better to use Coroutines or just Invoke with delay parameter - Invoke Unity Docs.