I need help instantiating a variable through a string. I need this for placing the right building based on the building number variable so I can make my code shorter and more readable than just doing many if statements.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class main : MonoBehaviour
{
public Text cashText;
public static float cash = 0f;
public float changeCash = 0f;
public GameObject building1;
public GameObject building2;
public GameObject building3;
// Start is called before the first frame update
void Start()
{
cash = changeCash;
}
// Update is called once per frame
void Update()
{
cashText.text = "cash: " + cash.ToString();
Application.targetFrameRate = 60;
if (Input.GetKeyDown("r"))
{
Destroy(GameObject.FindGameObjectWithTag("building"));
GameObject[] projectiles = GameObject.FindGameObjectsWithTag("projectile");
foreach(GameObject projectilesfind in projectiles)
{
GameObject.Destroy(projectilesfind);
}
Instantiate("building" + menu.buildingNumber);
}
}
}
As said what you are asking for is possible, it is called "reflection" and I would strongly recommend to NOT use this. Therefore I won't even tell you how to do it here. Use the link on own risk ;)
Array + index
I would rather use e.g. an array like
public GameObject[] buildings;
and then later do
// assuming 1-based User input but c# arrays are 0-based
Instantiate(buildings[menu.buildingNumber - 1]);
or if menu.buildingNumber is a string itself then do e.g.
if(int.TryParse(menu.buildingNumber, out var index))
{
Instantiate(buildings[index - 1]);
}
Dictionary + Key
Or depending on your Input you could also use a Dictionary like
public GameObject building1;
public GameObject building2;
public GameObject building3;
private readonly Dictionary<string, GameObject> buildings = new Dictionary<string, GameObject>();
private void Awake()
{
buildings[nameof(building1)] = building1;
buildings[nameof(building2)] = building2;
buildings[nameof(building3)] = building3;
}
then later you can do
if(buildings .TryGetValue("building" + menu.buildingNumber, out var building))
{
Instantiate(building);
}
Note though that this is pretty error prone and you will have to rename your fields / the string in TryGetValue according to your needs all the time
Related
I am recently new to c# and I need some help.
Essentially I have two scripts, one for spawning objects and one for moving an object along a path. I need a way to combine these two mechanics so that when a new object is instantiated it automatically joins the path and follows it.
The path is made using iTween.
![The objects the scripts are attached to] (https://i.stack.imgur.com/QPQn2.png)
I've tried changing the variable m_PlayerObj to the cube prefab and I've tried adding the Path script to the instantiation script but nothing seems to work.
The scripts attached do nkt include these attempts I made as I wanted to make the code very clear.
Spawner script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(cubeprefab, transform.position, Quaternion.identity);
}
}
}
Path script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path : MonoBehaviour
{
public GameObject m_PlayerObj;
public Transform[] positionPoint;
[Range(0, 1)]
public float value;
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
float tempTime;
// Update is called once per frame
void Update()
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
As stated above, any help would be greatly appreciated as I am really stuck on this conceot and since I am new to Unity I really can’t see a way around it // how to fix it.
Instead of only storing the player object in the Path script, store a collection of objects. That way, the path can keep track of more than one object.
//public GameObject m_PlayerObj; // Get rid of this
public List<GameObject> followers; // Add this
Then, in your Update loop, you can loop through all of them.
void Update()
{
for (var i = 0; i < followers.Length; ++i)
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
}
Of course, now, you need to make sure you pass your cube instance to the Path GameObject when you spawn it, so the path knows about the cube follower. That means your spawner also needs to know about the path.
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var cubeInst = Instantiate(cubeprefab, transform.position, Quaternion.identity);
path.followers.Add(cubeInst);
}
}
}
Now a new problem is going to be that each object is going to be at the same position on the path, because the path only stores one value - a better term might be progress. So if they're all the same, like the cube, you won't be able to tell because they'd overlap.
So you have to decide what you want to do instead. Evenly space them? You could do that with some math. Or have them all start from the beginning and keep track of their progress separately? Then you'd need to store progress for each of them. A better place to do that is probably on the cube object, which means you need to add a new script to your cube prefab:
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
And, you need to start referring to the prefab by this script, instead of just GameObject:
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
//...
Finally, you need to make sure to use the individual progress for each path follower, rather than a single progress value like your old Path script did:
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
Putting it all together (separate files of course, with their own includes!):
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
public Transform[] positionPoint;
//[Range(0, 1)]
//public float value; // Don't need this anymore either
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
// Update is called once per frame
void Update()
{
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
I picked up unity and C# a month ago so I'm still a noobie.
So far I managed to build a simple space-based arcade shooter (i have my ship, i have a way to shoot bullets). What I'm trying to achieve is a way to keep the script that takes my keyboard input to shoot separate from the possible bullet types.
The way my bullet types are currently implemented is by having a gameobject for each with its own scripts for a) taking keyboard input and b) instancing a prefab with different properties to shoot. Currently i have 2 shooting modes, and a separate script lets me swap between them with the spacebar by enabling disabling the gameobjects. An example of the scripts I'm using for one bullet type:
Script for instantiating bullet. One method simply shoots every time a button is pressed, the other "charges" an array of bullets, accompanied in the second script by a "growing aura" signifing the power increase. These two methods have the same name across different bullet classes, but are implemented differently.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletA_Basic : MonoBehaviour
{
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce = 20f;
public float altCooldown = 1f;
public float fireRate = 1f;
public float altFirePowerMultiplier = 1f;
private void Update()
{
}
public void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firingPoint);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firingPoint.up * bulletForce, ForceMode2D.Impulse);
}
public void SpecialShoot(int n)
{
StartCoroutine(Special(n));
}
public IEnumerator Special(int n)
{
for (int i = 0; i < n; i++)
{
Shoot();
yield return new WaitForSeconds(0.1f);
}
}
}
Script for taking keyboard input
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting_A : MonoBehaviour //This needs to be copied across all firing types
{
private BulletA_Basic bulletScript; //Change the class to make new projectile types with different firing modes
public Vector3 scaleChange;
private GameObject auraInstance;
private float timePassed = 0f;
private float timePassedMain = 0f;
public float timeToDetonation = 3f;
private void Start()
{
bulletScript = GetComponent<BulletA_Basic>();
}
// Update is called once per frame
void Update()
{
bool isFiring = Input.GetButtonDown("Main Cannon");
bool alternateFire = Input.GetButton("Main Cannon");
timePassedMain += Time.deltaTime;
if (isFiring && timePassedMain > bulletScript.fireRate)
{
bulletScript.Shoot();
timePassedMain = 0;
}
if (alternateFire)
{
timePassed += Time.deltaTime;
if (!auraInstance && timePassed >= bulletScript.altCooldown)
{
auraInstance = Instantiate(bulletScript.aura, bulletScript.chargingPoint);
}
if (alternateFire && auraInstance && timePassed < timeToDetonation)
{
Charge();
//Will need to add shader here
}
else if (timePassed >= timeToDetonation)
{
Destroy(auraInstance);
timePassed = 0;
}
}
else
{
if (auraInstance)
{
Destroy(auraInstance);
int powerAltFire = (int)(bulletScript.altFirePowerMultiplier * (Mathf.Pow(2 , timePassed))); //Equation returns a number of projectiles based on how long charge was held
bulletScript.SpecialShoot(powerAltFire);
}
timePassed = 0;
}
}
void Charge()
{
auraInstance.transform.localScale += scaleChange;
}
}
The key here is the bulletScript field.
Basically i'd like to make the second script general so that i don't have to implement it in a different way and copy-pasting it again and again for each type of bullet I'm going to make, and changing the bulletScript field type each time.
I tried doing it with interfaces but I'm not sure how to implement it in the general script since I need to access each field of the subclasses, which have each their own references (like bulletPrefab, or aura). In general i feel interfaces are not well integrated into unity but that might just be me.
I also tried with delegates, but i had similar problems. I simply changed the type of bulletScript to my delegate type (ShootingDelegate bulletScript), and wrote this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public delegate void ShootDelegate();
public delegate void SpecialShootDelegate(int n);
public class ShootingDelegate : MonoBehaviour
{
public ShootDelegate delShoot;
public SpecialShootDelegate delSpecialShoot;
private int weaponIndex;
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce;
public float altCooldown;
public float fireRate;
public float altFirePowerMultiplier;
// Start is called before the first frame update
void Start()
{
WeaponSwap weapon = GetComponent<WeaponSwap>();
weaponIndex = weapon.weaponIndex;
switch (weaponIndex)
{
case 1:
BulletB_Fan bulletB = GetComponent<BulletB_Fan>();
delShoot = bulletB.Shoot;
delSpecialShoot = bulletB.SpecialShoot;
bulletPrefab = bulletB.bulletPrefab;
aura = bulletB.aura;
firingPoint = bulletB.firingPoint;
chargingPoint = bulletB.chargingPoint;
bulletForce = bulletB.bulletForce;
altCooldown = bulletB.altCooldown;
fireRate = bulletB.fireRate;
altFirePowerMultiplier = bulletB.altFirePowerMultiplier;
break;
case 0:
BulletA_Basic bullet = GetComponent<BulletA_Basic>();
delShoot = bullet.Shoot;
delSpecialShoot = bullet.SpecialShoot;
bulletPrefab = bullet.bulletPrefab;
aura = bullet.aura;
firingPoint = bullet.firingPoint;
chargingPoint = bullet.chargingPoint;
bulletForce = bullet.bulletForce;
altCooldown = bullet.altCooldown;
fireRate = bullet.fireRate;
altFirePowerMultiplier = bullet.altFirePowerMultiplier;
break;
}
}
// Update is called once per frame
void Update()
{
}
}
This is the error it throws:
ArgumentException: Value does not fall within the expected range.
ShootingDelegate.Start () (at Assets/Scripts/ShootingDelegate.cs:54)
which corresponds to this line
delShoot = bullet.Shoot;
I don't really care if a solution employs either interfaces or delegates, those were just things I tried. Any thoughts?
I created a sample just for you.
Here I am creating BulletType parent class for all bullet types. And atting some variable and method for all bullets.
public class BulletType : MonoBehaviour
{
// Protected is less stric than private, more stric than public
// Protected variables only accesible to this class and its child.
protected string name;
protected int bulletDamage;
protected int bulletSpeed;
protected virtual void Start()
{
}
// Virtual means you can override this method in child classes
protected virtual void Damage() { }
public virtual void PlaySound() { }
protected virtual void ShowEffect() { }
}
Now I will add child class and inherit from parent BulletType class. Everything write in comment lines.
// This class inherits from BulletType class.
public class FireBullet : BulletType
{
// If you want to completely ignore parent class variable to by adding new keyword.
new int bulletSpeed = 3;
protected override void Start()
{
// when overriding a method automatically adds this method.
// base means parent class. with base you can access parent methods.
base.Start();
// If you remove base method, parent method won't be called in this class.
// Here I accessed parent class variable and set its value.
// This doesn't effect parent or other child classes. That's the beauty.
name = "Fire Bullet";
}
void StopSound()
{
}
protected override void PlaySound()
{
// Sound played from parent.
base.PlaySound();
// You can add your own variable and methods in parent method.
StopSound();
}
}
// FireBullet inherited from BulletType, and LavaBullet inherited from FireBullet.
// You can do this as much as you want.
public class LavaBullet : FireBullet
{
protected override void PlaySound()
{
// Here base will be FireBullet
base.PlaySound();
}
}
public class IceBullet : BulletType
{
// Add as much as you thing.
}
And for using BulletType script in your player just add this line
public class Player
{
public BulletType currentBulletType;
// You can get child from main class.
FireBullet fireBullet = currentBulletType.GetComponent<FireBullet>();
// Now you can access and use child class methods.
fireBullet.PlaySound();
}
and when you want to change bullet type, just assign new bullettype. Because all bullet type inherit from this class. You can assign every bullet type.
I don't know if it's the answer but,
delShoot = bulletB.Shoot;
delShoot is a reference to a script.
bulletB.Shoot is a function.
I don't think you can make one equal to the other.
If I understand your question, you want to make one script for two type of bullets shoot. You can create a scipts Shoot.cs who is instantiate with a value (1 for the default fire 2 for the second fire) and other scrits defaultFire.cs / secondFire.cs with they own properties.
Like this you'll juste instantiate once your Bullet like this :
public void Start(){
case 1 : Shoot shoot = new Shoot(1)
case 2 : Shoot shoot = new Shoot(2)
}
Hope this help a little..
Im making a terror game, and i want to spawn little collectibles in my scene, my code works fine, but they repeat the world location in every instantiate:
using UnityEngine;
public class objectivesGeneration : MonoBehaviour
{
GameObject[] objSpawn;
GameObject objActual;
public GameObject obj;
int index;
void Start()
{
objSpawn = GameObject.FindGameObjectsWithTag("spawnObj");
}
void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
generateNewObj();
}
}
public void generateNewObj()
{
index = Random.Range(0, objSpawn.Length);
objActual = objSpawn[index];
createObj();
}
public void createObj()
{
Instantiate(obj, objActual.transform.position, objActual.transform.rotation);
}
}
Can somebody help me?
Your question can be understood in two ways. Leoverload gave the answer to the repeating position.
In case you do not want to repeat the same type of object (so all objects are unique), then do the following:
Turn objSpawn into a List<GameObject> variable and in the start() function, add all instances from the array that's returned from FindGameObjectsWithTag to it.
Turn objSpawn.Length into objSpawn.Count (which does the same but for lists)
In that same function add: objSpawn.Remove(objActual) at the end.
If those objects are destroyed at some point and you want to create new instances of destroyed objects, ensure that in their Destroy event, you Add(gameObject) to the list again.
Am giving you instructions instead of just code so you can learn to do this yourself in the future.
Also I have the feeling you need to learn how arguments\parameters work. Then you can ommit that objActual variable and instead pass the object to createObj(GameObject theObj) directly.
You are spawning the object in the same position objActual.transform.position
You should set the limits of the spawn with 2 variables, for example:
public float startx;
public float endx;
public float starty;
public float endy;
Then you can easily Instantiate randomly in this positions:
float posX = Random.Range(startx, endx);
float posy = Random.Range(starty, endy);
Instantiate(obj, new Vector3(posx, posy, transform.position.z, Quaternion.Identity);
And so you have a random spawn!
Thanks to AlexGeorg for the answer,
this was how my final code was
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class objectivesGeneration : MonoBehaviour
{
public GameObject recipient;
public List<GameObject> objSpawns = new List<GameObject>();
int spawnSelected;
void Start()
{
//Set all the objs with the tag spawnLocation inside of the list
foreach (GameObject OBJ in GameObject.FindGameObjectsWithTag("spawnLocation"))
{
objSpawns.Add(OBJ);
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.I))
{
//Check if there are more possible spawns
if (objSpawns.Count > 0)
generateNewObj(recipient);
}
}
public void generateNewObj(GameObject frasco)
{
//Get a random number of the list which contains one obj
spawnSelected = Random.Range(0, objSpawns.Count);
//Instantiate the GameObject in the location of the random selected object
Instantiate(frasco, objSpawns[spawnSelected].transform.position, objSpawns[spawnSelected].transform.rotation);
//Delete that possible location from the list
objSpawns.RemoveAt(spawnSelected);
}
}
so i created a score that counts the player movment
using UnityEngine;
using UnityEngine.UI;
public class score : MonoBehaviour
{
public Transform player;
public Text scoreText;
void Update()
{
scoreText.text = player.position.z.ToString("0");
}
}
which works fine but when i tried to create a highscore system that save score and display it it just shows zero and doesnot get updated
enter using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HighScore : MonoBehaviour
{
public Text Score;
public Text highscore;
float highScore=0f;
public Transform player;
void Update()
{
Score.text = highScore.ToString();
if(PlayerPrefs.GetFloat("Score",0)<=highScore)
{
PlayerPrefs.GetFloat("score", highScore);
highscore.text = PlayerPrefs.GetFloat("score").ToString();
}
}
public void highnumber()
{
highscore.text = PlayerPrefs.GetFloat("score").ToString();
}
public static class HighScore
{
public Text Score;
public Text highscore;
float highScore=0f;
public Transform player;
void Update()
{
Score.text = highScore.ToString();
//care here, "score" and "Score" it's not the same
if(PlayerPrefs.GetFloat("score",0)<=highScore)
{
//HERE YOU DONT WANT TO GetFloat, YOU WANT TO SET FLOAT
//PlayerPrefs.GetFloat("score", highScore); we remove this lane
PlayerPrefs.SetFloat("score", highScore); //we add this instead
highscore.text = PlayerPrefs.GetFloat("score").ToString();
}
}
public void highnumber()
{
highscore.text = PlayerPrefs.GetFloat("score").ToString();
}
}
Think about set a var with PlayerPrefs.GetFloat("score"), instead of call GetFloat each time. It's more efficient.
Also I doubt, or at least i think you shouln't to have this Script attached to a gameObject... so remove the MonoBehaviour inheritance from HighScore class, and make it static. Cause u only want a highScore instance true? If you have any doubts about how to do it, and you're interested, just tell me.
EDIT:
Well as you said you need a script monobehaviour to control the text, okay. Lets imagine this 2 classes so:
public static class Stats
{
private static float _highScore; //your highscore it's here
private static int _maxHp, _hp; //some example values
//...someMoreValues
private static float _savedScore //this data gets the last score from PlayerPrefs
private static bool _initialized; //this var is used to know if playerPref data is loaded
//Calling this you always update your highscore
public static void GetHighScore()
{
_InitializeScores();
return _highScore;
}
//this calls playerprefs to save the highscore
public static void SaveHighScore(){
if(_savedScore < _highScore) //only if it's bigger than previuos one
{
_savedScore = _highScore; //we also update the savedscore we had from PlayerPrefs initialization
PlayerPrefs.SetFloat("score", _highScore);
}
}
//this loads your playerprefs values the 1st time it's called
private static void _InitializeScores()
{
if (!_initialized) //if not initialized
{
_initialized = true;
_savedScore= PlayerPrefs.GetFloat("score", 0f);
_highScore = _savedScore
//here you can add all the vars in a future you could recover from PlayerPrefs;
}
}
}
this 1st class manages your highscore, and after at the text you have this other one:
public class HighScoreTextController : MonoBehaviour
{
//here you drag'n'drop your text script as always
public Text score;
// Start is called before the first frame update
// Update is called once per frame
void Update()
{
score.text = Stats.GetHighScore().ToString();
}
}
As you see, at update you call Stats.highScore.ToString(). You don't need to create a var with an instance of it like
Stats stats = new Stats();
Because Stats is static (no instances, only one for all the game). GetHighScore takes care of call PlayerPrefs the 1st time you need to show the highScore and gives you it all the time you need.
When you want to save your highScore, for example when the game ends, you just call
Stats.SaveHighScore();
And only if the score is bigger, it is saved... Calling PlayerPrefs, only one time to get the data, and only one time to save it if necesary.
And why we want to use 2 scripts instead of 1?
Well, because you don't know yet, but maybe in a future you will need to get your highScore from other site... And elsewhere, you only need to call to Stats.GetHighScore(), because it's static. And maybe in a future you would need to use PlayerPrefs for other values... well you can initialize all at same time at your stats and use them whenever u wish.
Best Wishes
I'm trying to be able to respawn as well as reset the score counting as soon as i walk into my "Gold" Object.
As for now i'm not even able to respawn which was possible earlier before trying to implement the "Score-Stuff" (at first the "FoundGold" Script was only used to be able to respawn). Also i'm trying to make the lowest Score the High-Score.
Note that im new to C# and i kinda put everything together from the tutorials i needed so a answer with some actual code/stating where something went wrong would be much appreciated.
//GoldFound Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GoldFound : MonoBehaviour
{
private ScoreManager theScoreManager;
public Transform target;
[SerializeField] private Transform player;
[SerializeField] private Transform respawnpoint;
private void Start()
{
theScoreManager = FindObjectOfType<ScoreManager>();
}
private void OnTriggerEnter(Collider other)
{
theScoreManager.scoreIncreasing = false;
player.transform.position = respawnpoint.transform.position;
theScoreManager.scoreCount = 0;
theScoreManager.scoreIncreasing = true;
}
}
other code
//ScoreManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public Text scoreText;
public Text hiScoreText;
public float scoreCount;
public float hiScoreCount;
public float pointPerSecond;
public bool scoreIncreasing;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (scoreIncreasing)
{
scoreCount += pointPerSecond * Time.deltaTime;
}
if(scoreCount > hiScoreCount)
{
hiScoreCount = scoreCount;
}
scoreText.text = "Score: " + Mathf.Round (scoreCount);
hiScoreText.text = "High Score: " + Mathf.Round (hiScoreCount);
}
}
If you want to save your highscore in-between play sessions, then the easiest way to do so is to save the value to PlayerPrefs. If you want to start saving more / more complex stuff you really should save it in a file you generate yourself. But in your case, PlayerPrefs is fine.
Here's a Unity tutorial on the subject:
https://unity3d.com/learn/tutorials/topics/scripting/high-score-playerprefs
Otherwise, you can just do it like this:
public void SetHighscore (float currentScore)
{
if (PlayerPrefs.HasKey("highscore"))
{
float highscore = PlayerPrefs.GetFloat("highscore");
if (highscore > currentScore)
{
PlayerPrefs.SetFloat("highscore", currentScore);
PlayerPrefs.Save();
}
}
else
{
PlayerPrefs.SetFloat("highscore", currentScore);
PlayerPrefs.Save();
}
}
Then just write PlayerPrefs.GetKey("highscore") whenever you need it.
(Though I'd also recommend you check if it exists by using the PlayerPrefs.HasKey("highscore"))
https://docs.unity3d.com/ScriptReference/PlayerPrefs.html