How would I add a persistent high score? - c#

I was following a tutorial to make a game in unity C# and I need to know how I would add a High Score which persists among game restarts. I'm new to making games so any help is apreciated, and if I need to elaborate on anything please mention that.
public class UIManager : MonoBehaviour
{
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
DestroyImmediate(this);
}
}
private static UIManager instance;
public static UIManager Instance
{
get
{
if (instance == null)
instance = new UIManager();
return instance;
}
}
protected UIManager()
{
}
private float score = 0;
public void ResetScore()
{
score = 0;
UpdateScoreText();
}
public void SetScore(float value)
{
score = value;
UpdateScoreText();
}
public void IncreaseScore(float value)
{
score += value;
UpdateScoreText();
}
private void UpdateScoreText()
{
ScoreText.text = score.ToString();
}
public void SetStatus(string text)
{
StatusText.text = text;
}
public Text ScoreText, StatusText;
}

PlayerPrefs helps you to store game data.
Usage:
void SaveHighScore(float value)
{
PlayerPrefs.SetFloat("HighScore", value);
}
void LoadHighScroe()
{
float hs = PlayerPrefs.GetFloat("HighScore", 0.0f); // 0.0f here is default value when key/value not exest in PlayerPrefs
ScoreText.text = hs.ToString();
}

Related

Unity audio bug with scripts

I am building a game on unity on real old version like 5.3. I have a main scene and a menu scene.Each scene got an audio manager script which controls the audio. My problem is with this function called DontDestroyOnLoad. More specifically the problem is that when I build the game the game runs perfectly and the menu too but the menu got sounds and the game not.The game has only some sounds that is out of audio manager and I have put them by hand.
My code of audiomanager is here
using UnityEngine;
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)]
public float volume = 0.7f;
[Range(0.5f, 1.5f)]
public float pitch = 1f;
[Range(0f, 0.5f)]
public float randomVolume = 0.1f;
[Range(0f, 0.5f)]
public float randomPitch = 0.1f;
public bool loop = false;
private AudioSource source;
public void SetSource(AudioSource _source)
{
source = _source;
source.clip = clip;
source.loop = loop;
}
public void Play()
{
source.volume = volume * (1 + Random.Range(-randomVolume / 2f, randomVolume / 2f));
source.pitch = pitch * (1 + Random.Range(-randomPitch / 2f, randomPitch / 2f));
source.Play();
}
public void Stop()
{
source.Stop();
}
}
public class AudioManager : MonoBehaviour
{
public static AudioManager instance;
[SerializeField]
Sound[] sounds;
void Awake()
{
if (instance != null)
{
if (instance != this)
{
Destroy(this.gameObject);
}
}
else
{
instance = this;
DontDestroyOnLoad(this);
}
}
void Start()
{
for (int i = 0; i < sounds.Length; i++)
{
GameObject _go = new GameObject("Sound_" + i + "_" + sounds[i].name);
_go.transform.SetParent(this.transform);
sounds[i].SetSource(_go.AddComponent<AudioSource>());
}
PlaySound("Music");
}
public void PlaySound(string _name)
{
for (int i = 0; i < sounds.Length; i++)
{
if (sounds[i].name == _name)
{
sounds[i].Play();
return;
}
}
// no sound with _name
Debug.LogWarning("AudioManager: Sound not found in list, " + _name);
}
public void StopSound(string _name)
{
for (int i = 0; i < sounds.Length; i++)
{
if (sounds[i].name == _name)
{
sounds[i].Stop();
return;
}
}
// no sound with _name
Debug.LogWarning("AudioManager: Sound not found in list, " + _name);
}
}
You need to specify that DontDestroyOnLoad will affect the gameObject itself, not only the MonoBehaviour.
void Awake()
{
if (instance != null)
{
if (instance != this)
{
Destroy(this.gameObject);
}
}
else
{
instance = this;
DontDestroyOnLoad(this.gameObject); // <-- Here
}
}

Array.Indexof confusion

Hellos guys I am a beginner and have 2 classes. Class A has an Array that I need to access in Class B. I've searched through here for the past 36 hours trying different methods and this is what I ended up with any help?
This is the first Class with the Array
[System.Serializable]
public class Cli
{
public string name;
public int money;
};
public class BankManager : MonoBehaviour
{
private static BankManager _instance;
public static BankManager Instance
{
get
{
if (_instance == null)
{
GameObject go = new GameObject("BankManager");
go.AddComponent<BankManager>();
}
return _instance;
}
}
public GameObject player;
private string NaMe;
public Bank[] banks = new Bank[1];
public Cli[] clients = new Cli[1];
void Awake()
{
_instance = this;
}
void Start()
{
banks[0] = new Bank("In But Not Out", 10000);
player = GameObject.Find("Player");
clients[0].money = 1000;
}
void Update()
{
PlayerController pName = player.GetComponent<PlayerController>();
clients[0].name = pName.name;
}
}
The array I am trying to access is the clients[]
public class AccessAtm : MonoBehaviour
{
public Bank bank;
public int clams;
private void OnTriggerEnter2D(Collider2D other)
{
bank = BankManager.Instance.banks[0];
if (other.tag == "Player")
{
clams = Array.IndexOf(BankManager.Instance.clients,
other.GetComponent<PlayerController>().name);
Debug.Log(other.GetComponent<PlayerController>().name);
}
}
}
The debug.log properly tells me I'm getting the player.name I believe my error is in either getting that instance of the array or in the indexof command
The problem is IndexOf expects you to pass a instance of Bank for the 2nd parameter of
clams = Array.IndexOf(BankManager.Instance.clients,
other.GetComponent<PlayerController>().name);
you are passing it a string.
You need to use something else to search for it, the easist solution would be use a for loop.
var clients = BankManager.Instance.clients;
var otherName = other.GetComponent<PlayerController>().name;
int foundAt = -1;
for(int i = 0; i < clients.Length; i++)
{
if(clients[i].name == otherName)
{
foundAt = i;
break;
}
}
claims = foundAt;

Handle Time.timeScale onChange event

I'm started with Unity, and I need (before game is in paused) trigger an avent. Is there any way to trigger any event when the Time.timeScale is changed to 0? Something like: Time.timeScale.onBeforeChange()...
Thanks a lot.
Make the thing that changes the time scale a controller, then make that controller raise the event.
[System.Serializable]
public class BeforeTimeChangedData
{
public bool canceled;
public float oldValue;
public float newValue;
}
[System.Serializable]
public class BeforeTimeChangedEvent : UnityEvent<BeforeTimeChangedData>
{
}
//Attach this to a game object that gets loaded in your pre-load scene with the tag "Singletons"
public class TimeController : MonoBehaviour
{
void Awake()
{
DontDestroyOnLoad(gameObject);
if(BeforeTimeChanged == null)
BeforeTimeChanged = new BeforeTimeChangedEvent();
}
public BeforeTimeChangedEvent BeforeTimeChanged;
public bool ChangeTimeScale(float newValue)
{
var args = new BeforeTimeChangedData();
args.oldValue = Time.timeScale;
args.newValue = Time.timeScale;
BeforeTimeChanged.Invoke(args);
if(!args.canceled)
{
Time.timeScale = newValue;
}
return args.canceled;
}
}
Elsewhere you can change the timescale by doing
public class TimeSlower : MonoBehaviour
{
private TimeController _timeController;
public Text TimeChanged;
void Start()
{
var singletons = GameObject.FindWithTag("Singletons");
_timeController = singletons.GetComponent<TimeController>();
if(_timeController == null)
throw new System.ArgumentNullException("Could not find a TimeController on the Singletons object");
}
void Update()
{
if(Input.GetButton("SlowTime"))
{
var changed = _timeController.ChangeTimeScale(0.5f);
if(changed)
{
TimeChanged.text = "Time Changed!";
}
}
}
}
And here is another component that listens for the change and cancels the change if the change has happened too recently;
public class TimeChangeLimiter : MonoBehaviour
{
private float lastTimeChange = 0;
private TimeController _timeController;
public Text TimeChanged;
[Range(0, float.MaxValue)]
public float Cooldown;
void Start()
{
var singletons = GameObject.FindWithTag("Singletons");
_timeController = singletons.GetComponent<TimeController>();
if(_timeController == null)
throw new System.ArgumentNullException("Could not find a TimeController on the Singletons object");
_timeController.BeforeTimeChanged.AddListener(OnBeforeTimeChanged);
}
void OnDestroy()
{
_timeController.BeforeTimeChanged.RemoveListener(OnBeforeTimeChanged);
}
void OnBeforeTimeChanged(BeforeTimeChangedData args)
{
if(Time.time - lastTimeChange < Cooldown)
{
args.canceled = true;
return;
}
lastTimeChange = Time.time;
}
}

unity turn base script for enemy

I have a list of enemys. so i want each enemy have their turn.
First of all :
Player turn --> enemys turn ("here each enemy move one by one untill the end then player move again"). how do i making some waiting time here and forcus on enemy turn?
Any help would be appreciated.
void Start()
{
// find list enemy
enemy = GameObject.FindGameObjectsWithTag("Enemy");
}
void Update()
{
//enemy turn reference to player. after move all enemy we change it to false to change the player turn.
if(StaticClass.enemyTurn == true )
{
for(int i=0;i<enemy.length;i++)
{
// how do i making some waiting time here and forcus on enemy turn?
EnemyTurn(i);
}
}
}
public void EnemyTurn(int id)
{
ChessMoveMent chessMoveScript = enemy[id].GetComponent<ChessMoveMent>();
chessMoveScript.ProcessMove();
id++;
if(id>=enemy.Length)
{
isMove = false;
}
}
I usually use StartCoroutine in this case.
Please try the below code:
public IEnumerator EnemyTurn(int id)
{
yield return null;
ChessMoveMent chessMoveScript = enemy[id].GetComponent<ChessMoveMent>();
chessMoveScript.ProcessMove();
id++;
if(id>=enemy.Length)
{
isMove = false;
}
}
When you want to use it, please use with "StartCoroutine()"
StartCoroutine(EnemyTurn(i));
More details here
You might have a coordinator, who tells the participants when it's their turn.
public class GameCoordinator : MonoBehaviour
{
public List<Participant> participants;
private int currentParticipantIdx = -1;
private Participant CurrentParticipant
{
get { return participants[currentParticipantIdx]; }
}
private void Start()
{
PlayNextMove();
}
private void PlayNextMove()
{
ProceedToNextParticipant();
CurrentParticipant.OnMoveCompleted += OnMoveCompleted;
CurrentParticipant.BeginMove();
}
private void OnMoveCompleted()
{
CurrentParticipant.OnMoveCompleted -= OnMoveCompleted;
StartCoroutine(PlayNextMoveIn(2.0f));
}
private IEnumerator PlayNextMoveIn(float countdown)
{
yield return new WaitForSeconds(countdown);
PlayNextMove();
}
private void ProceedToNextParticipant()
{
++currentParticipantIdx;
if (currentParticipantIdx == participants.Count)
currentParticipantIdx = 0;
}
}
public class Participant : MonoBehaviour
{
public event Action OnMoveCompleted;
public void BeginMove()
{
//
}
}

Need to a Instantiate a prefab in canvas which is in another scene

I am making an achievement system.I have to 2 scene in my game.The first scene is my Main Menu and the other scene is my game scene. I have designed and coded the achievement system in the main menu scene and added a EarnCanvas to my game scene. EarnCanvas will display the achievement that the player has unlocked.
Code i have is only allowing me to link the achievements to a EarnCanvas on the Main Menu scene.i need help to modifier the code or link the two scene so that i can instantiate the unlocked achievement prefab in the EarnCanvas in the game Scene. At the moment no prefab is instantiated or displayed in the EarnCanvas when the player has unlocked an achievement.
Main Menu scene
achievement Manager script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
public class AchievementManager : MonoBehaviour {
public GameObject achievementPraf;
public Sprite[] sprites;
private AchievementButton activeButton;
public ScrollRect scrollRect;
public GameObject achievementMenu;
public GameObject visualAchievement;
public Dictionary<string, Achievement> achievements = new Dictionary<string, Achievement>();
public Sprite UnlockedSprite;
private static AchievementManager instance;
public static AchievementManager Instance
{
get {
if(instance == null)
{
instance = GameObject.FindObjectOfType<AchievementManager>();
}
return AchievementManager.instance;
}
}
// Use this for initialization
void Start ()
{
//remove before deploy
//PlayerPrefs.DeleteAll();
activeButton = GameObject.Find("Streakbtn").GetComponent<AchievementButton>();
CreateAchievement("Streak", "Press W", "Press W to unlock this achievement", 5, 0);
CreateAchievement("Streak", "Press R", "Press R to unlock this achievement", 5, 0);
CreateAchievement("Streak", "Press All Keys", "Press All Keys to unlock", 5, 0, new string[] { "Press W", "Press R" });
foreach (GameObject achievementList in GameObject.FindGameObjectsWithTag("achievementList"))
{
achievementList.SetActive(false);
}
activeButton.click();
achievementMenu.SetActive(false);
}
// Update is called once per frame
void Update ()
{
if (Input.GetKeyDown(KeyCode.S))
{
achievementMenu.SetActive(!achievementMenu.activeSelf);
}
if(Input.GetKeyDown(KeyCode.W))
{
EarnAchievemnent("Press W");
}
if (Input.GetKeyDown(KeyCode.R))
{
EarnAchievemnent("Press R");
}
}
public void EarnAchievemnent(string title)
{
if(achievements[title].EarnAchievement())
{
GameObject achievement = (GameObject) Instantiate(visualAchievement);
StartCoroutine(HideAchievement(achievement));
//this is where is its giving the parent the gameobject name to find
SetAchievementInfo("EarnCanvas", achievement, title);
}
}
public IEnumerator HideAchievement(GameObject achievement)
{
yield return new WaitForSeconds(3);
Destroy(achievement);
}
public void CreateAchievement(string parent,string title,string description,int points,int spriteIndex,string[] dependencies = null)
{
GameObject achievement = (GameObject)Instantiate(achievementPraf);
Achievement newAchievement = new Achievement(name, description, points, spriteIndex,achievement);
achievements.Add(title, newAchievement);
SetAchievementInfo(parent, achievement,title);
if(dependencies != null)
{
foreach(string achievementTitle in dependencies)
{
Achievement dependency = achievements[achievementTitle];
dependency.Child = title;
newAchievement.AddDependency(dependency);
//Dependency = Press Space <-- Child = Press W
//NewAchievement = Press W --> Press Space
}
}
}
public void SetAchievementInfo(string parent, GameObject achievement, string title)
{
//this where its trying to find a gameobject-(EarnCanvas) to instantiate the unlocked achievement prefab in the Main Menu Scene
achievement.transform.SetParent(GameObject.Find(parent).transform);
achievement.transform.localScale = new Vector3(1, 1, 1);
achievement.transform.localPosition = new Vector3(0, 0, 0);
achievement.transform.GetChild(0).GetComponent<Text>().text = title;
achievement.transform.GetChild(1).GetComponent<Text>().text = achievements[title].Description;
achievement.transform.GetChild(2).GetComponent<Text>().text = achievements[title].Points.ToString();
achievement.transform.GetChild(3).GetComponent<Image>().sprite = sprites[achievements[title].SprintIndex];
}
public void ChangeCategory(GameObject button)
{
AchievementButton achievementButton = button.GetComponent<AchievementButton>();
scrollRect.content = achievementButton.achievementList.GetComponent<RectTransform>();
achievementButton.click();
activeButton.click();
activeButton = achievementButton;
}
}
Main Menu Scene
Achievement Script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Collections.Generic;
public class Achievement
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
private bool unlocked;
public bool Unlocked
{
get { return unlocked; }
set { unlocked = value; }
}
private int points;
public int Points
{
get { return points; }
set { points = value; }
}
private int sprintIndex;
public int SprintIndex
{
get { return sprintIndex; }
set { sprintIndex = value; }
}
private GameObject achievementRef;
private List<Achievement> dependencies = new List<Achievement>();
private string child;
public string Child
{
get { return child; }
set { child = value; }
}
public Achievement(string name,string description, int points, int sprintIndex, GameObject achievementRef)
{
this.name = name;
this.description = description;
this.unlocked = false;
this.points = points;
this.sprintIndex = sprintIndex;
this.achievementRef = achievementRef;
LoadAchievement();
}
public void AddDependency(Achievement dependency)
{
dependencies.Add(dependency);
}
public bool EarnAchievement()
{
if(!unlocked && !dependencies.Exists(x=> x.unlocked == false))
{
achievementRef.GetComponent<Image>().sprite = AchievementManager.Instance.UnlockedSprite;
SaveAchievement(true);
if(child != null)
{
AchievementManager.Instance.EarnAchievemnent(child);
}
return true;
}
return false;
}
public void SaveAchievement(bool value)
{
unlocked = value;
PlayerPrefs.SetInt(name,value? 1 : 0);
PlayerPrefs.Save();
}
public void LoadAchievement()
{
unlocked = PlayerPrefs.GetInt(name) == 1 ? true : false;
if (unlocked)
{
achievementRef.GetComponent<Image>().sprite = AchievementManager.Instance.UnlockedSprite;
}
}
}
You should use DontDestroyOnLoad method it is very simple way to retain gameobjects over scene changes you just need to add following code to your AchievementManager.
void Awake ()
{
DontDestroyOnLoad (transform.gameObject);
}
You can add this for any gameObject which you do not want to be destroyed upon changing scene.

Categories

Resources