I have class UnitComponent that contains references to every script of the unit. Every unit has scripts UnitUI and UnitStats. So I can access UnitUI methods from UnitStats using UnitComponent class (for example: unitComponent.unitUI.SetHealthUI()). Of course, every script has a reference to UnitComponent class as well to be able to use it. These three scripts have children (PlayerComponent, PlayerUI, PlayerStats). These classes have new methods, for example PlayerUI has SetManaUI() method. But I can't use SetManaUI() using PlayerComponent without creating new variable with PlayerUI class because UnitUI variable doesn't have this method. Is there any option to make new methods accessible through variable unitUI by changing class to PlayerUI or I have to create new PlayerUI variable and somehow get rid of unitUI.
I've tried to make virtual empty method SetManaUI() in UnitUI class and override it in PlayerUI class to make everything work, but it seems like a bad practise because UnitUI can become full of useless empty virtual methods.
Simplified version of scripts
Script that connects scripts together
using UnityEngine;
public class Collector : MonoBehaviour
{
public Stats stats;
public UI ui;
}
UI script
using UnityEngine;
public class UI : MonoBehaviour
{
Collector collector;
public void UIMethod()
{
Debug.Log("UI method");
}
}
Stats script
using UnityEngine;
public class Stats : MonoBehaviour
{
Collector collector;
public void StatsMethod()
{
Debug.Log("Stats method");
}
}
Child of collector script
public class ChildCollector : Collector
{
public string someNewData;
public string someNewData1;
public string someNewData2;
public void Start()
{
ui.ChildUIMethod(); //has to be executed and write "Child UI method"
stats.ChildStatsMethod(); //has to be executed and write "Child Stats method"
}
}
Child of UI script
using UnityEngine;
public class ChildUI : UI
{
ChildCollector childCollector;
public void ChildUIMethod()
{
Debug.Log("Child UI Method");
}
}
Child of Stats script
using UnityEngine;
public class ChildStats : Stats
{
ChildCollector childCollector;
public void ChildStatsMethod()
{
Debug.Log("Child Stats Method");
}
}
Full version of scripts
Unit Component script
using UnityEngine;
public class UnitComponent : MonoBehaviour
{
public Animator animator;
public UnitStats unitStats;
public UnitUI unitUI;
}
UnitStats script
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class UnitStats : MonoBehaviour
{
public UnitComponent unitComponent;
public float maxHealth;
public float health;
public List<GameObject> lootDrop;
protected virtual void Start()
{
if (health <= 0 || health > maxHealth)
health = maxHealth;
unitComponent.unitUI.SetHealthUI(health, maxHealth);
CheckDeath();
}
private void CreateDamagePopup(float takenDamage)
{
GameObject damagePopupCanvas = Instantiate(unitComponent.unitUI.damagePopupCanvasPref, transform.position, Quaternion.identity, transform);
damagePopupCanvas.GetComponentInChildren<Text>().text = takenDamage.ToString();
Vector3 randomPosOffset = new Vector3(Random.Range(-0.1f, 0.1f), Random.Range(-0.1f, 0.1f), 0f);
damagePopupCanvas.transform.position += randomPosOffset;
Destroy(damagePopupCanvas, 3f);
}
public float CalculateHealthPercentage()
{
return (health / maxHealth);
}
protected void CheckDeath()
{
if (health <= 0)
{
unitComponent.animator.SetTrigger("isDead");
DropLoot();
}
}
protected void CheckOverheal()
{
if (health > maxHealth)
{
health = maxHealth;
}
}
public void DealDamage(int damage)
{
unitComponent.unitUI.healthBar.SetActive(true);
health -= damage;
unitComponent.unitUI.SetHealthUI(health, maxHealth);
CheckDeath();
CreateDamagePopup(damage);
}
public void HealCharacter(int heal)
{
health += heal;
CheckOverheal();
unitComponent.unitUI.SetHealthUI(health, maxHealth);
}
public void DropLoot()
{
foreach (GameObject item in lootDrop)
Instantiate(item, transform.position, Quaternion.identity);
}
}
UnitUI script
using UnityEngine;
using UnityEngine.UI;
public class UnitUI : MonoBehaviour
{
public UnitComponent unitComponent;
public GameObject damagePopupCanvasPref;
public GameObject healthBar;
protected Slider healthBarSlider;
virtual public void Start()
{
healthBarSlider = healthBar.GetComponent<Slider>();
}
public virtual void SetHealthUI(float health, float maxHealth)
{
if (healthBarSlider)
healthBarSlider.value = health / maxHealth;
}
public virtual void SetCoinsUI(int coinAmount) { } //Necessary for PlayerUI script working
public virtual void SetManaUI(float mana, float maxMana) { } //Necessary for PlayerUI script working
}
PlayerComponent script
using UnityEngine;
public class PlayerComponent : UnitComponent
{
public PlayerMovement playerMovement;
public PlayerSlot playerSlot;
public Rigidbody2D rb;
}
PlayerStats script
using UnityEngine;
public class PlayerStats : UnitStats
{
protected int coins;
public float mana;
public float maxMana;
public float manaRegenPerSec;
protected override void Start()
{
UI = gameObject.GetComponent<PlayerUI>();
animator = gameObject.GetComponent<Animator>();
if (health <= 0 || health > maxHealth)
health = maxHealth;
UI.SetHealthUI(health, maxHealth);
UI.SetManaUI(mana, maxMana);
CheckDeath();
}
public void Update()
{
if (mana < maxMana)
{
mana += manaRegenPerSec * Time.deltaTime;
UI.SetManaUI(mana, maxMana);
}
}
private void Awake()
{
DontDestroyOnLoad(this);
}
public void AddCoins(int amount)
{
coins += amount;
UI.SetCoinsUI(amount);
}
}
PlayerUI script
using System;
using UnityEngine;
using UnityEngine.UI;
public class PlayerUI : UnitUI
{
[SerializeField] protected GameObject manaBar;
[SerializeField] protected Text manaValue;
[SerializeField] protected Text healthValue;
[SerializeField] protected Text coinValue;
protected Slider manaBarSlider;
public override void Start()
{
healthBarSlider = healthBar.GetComponent<Slider>();
manaBarSlider = manaBar.GetComponent<Slider>();
}
public override void SetHealthUI(float health, float maxHealth)
{
healthBarSlider.value = health / maxHealth;
healthValue.text = health.ToString() + "/" + maxHealth.ToString();
}
public override void SetManaUI(float mana, float maxMana)
{
manaBarSlider.value = mana / maxMana;
manaValue.text = Math.Round(mana).ToString() + "/" + Math.Round(maxMana).ToString();
}
public override void SetCoinsUI(int coinAmount)
{
coinValue.text = coinAmount.ToString();
}
}
Related
I am using UnityEvent I called ScoreEvent and I feed it a float. All works fine, except when I need it to. I tried invoking the event when an enemy dies. Nada. I tried putting it in an If statement before, and for some reason it works.
This is the main code (ignore useless variables, still seeing what I need and what I don't)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(TriggerEnter))]
public class Health : MonoBehaviour
{
private ScriptableObjectLoader _sol;
private TriggerEnter _te;
public HealthbarEvent HE;
public ScoreEvent SE;
public float ObjectHealth;
public float EnemyScore;
private float _score;
private bool _isDead;
private void Awake() {
if(_te == null)
{
_te = GetComponent<TriggerEnter>();
}
if(HE == null)
{
HE = new HealthbarEvent();
}
if(SE == null)
{
SE = new ScoreEvent();
}
}
void Start()
{
this._sol = GetComponent<ScriptableObjectLoader>();
_te.DE.AddListener(onChange);
this.ObjectHealth = _sol.Health;
this._score = _sol.Score;
this._isDead = false;
HE.Invoke(ObjectHealth);
}
void onChange(Damage damage){
ObjectHealth -= damage.damage;
if(gameObject.tag == "Player")
{
HE.Invoke(ObjectHealth);
//Works if i put SE.Invoke here
}
if(ObjectHealth <= 0)
{
SE.Invoke(EnemyScore); //doesn't work here (I WANT IT TO BE HERE, NOT UP THERE) :/
if(gameObject.tag == "Enemy")
{
EnemyScore = gameObject.GetComponent<Health>()._score;
}
Destroy(gameObject);
}
}
}
[System.Serializable]
public class ScoreEvent : UnityEvent<float>{}
public class HealthbarEvent : UnityEvent<float>{}
And this is the script that listenes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class ScoreLoader : MonoBehaviour
{
private TMP_Text _tekst;
private GameObject _player;
private Health _h;
void Awake() {
_tekst = GetComponent<TMP_Text>();
}
void Start()
{
_player = GameObject.Find("Player");
_h = _player.GetComponent<Health>();
_h.SE.AddListener(updateScore);
_tekst.text = "0";
}
void Update()
{
}
void updateScore(float score)
{
Debug.Log("Primio sam ga. Jako");
}
}
I have a unity C# script that has Some "Public static Variables" as shown below. This script is attached to the instantiated object.
public class PlaceAtLocation : MonoBehaviour
{
public static double Longitude, Latitude;
}
I want to access these static variables "Latitude" and "Longitude" in another script. That is shown below
public class Instansiate_object : MonoBehaviour
{
public GameObject GpsStageObject;
public GameObject tempbject;
public void InstansiateGpSStageObject()
{
tempbject= Instantiate(GpsStageObject, new Vector3(0, 0, 0), Quaternion.identity);
tempbject.GetComponent<PlaceAtLocation>().lat =1;
tempbject.GetComponent<PlaceAtLocation>().lon =1;
//I want to access latitude and longitude here
//=============================================
//tempbject.GetComponent<PlaceAtLocation>().Longitude =1;
//tempbject.GetComponent<PlaceAtLocation>().Latitude =1;
//=============================================
print("Instansiated");
}
}
try using this format
using UnityEngine;
using System.Collections;
public class YetAnotherScript : MonoBehaviour
{
public int numberOfPlayerDeaths = 3;
}
using UnityEngine;
using System.Collections;
public class UsingOtherComponents : MonoBehaviour
{
public GameObject otherGameObject;
private YetAnotherScript yetAnotherScript;
void Awake ()
{
yetAnotherScript = otherGameObject.GetComponent<YetAnotherScript>();
}
void Start ()
{
Debug.Log("The player has died " + yetAnotherScript.numberOfPlayerDeaths + " times");
}
}
I followed this video on how to create health bars automatically for units.
This is my HealthBar class.
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class HealthBar : MonoBehaviour
{
#region SerializeFields
[SerializeField] private Image foregroundImage;
[SerializeField] private float updateSpeedInSec = 0.5f;
[SerializeField] private float positionOffset = 1f;
#endregion
#region NonSerializeFields
private Health health;
#endregion
public void SetHealth(Health healthToSet)
{
health = healthToSet;
healthToSet.OnHealthPctChanged += HandleHealthChanged;
}
private void HandleHealthChanged(float pct)
{
StartCoroutine(ChangeToPct(pct));
}
private IEnumerator ChangeToPct(float pct)
{
float preChangedPct = foregroundImage.fillAmount;
float elapsedTime = 0f;
while (elapsedTime < updateSpeedInSec)
{
elapsedTime += Time.deltaTime;
foregroundImage.fillAmount = Mathf.Lerp(preChangedPct, pct, elapsedTime / updateSpeedInSec);
yield return null;
}
foregroundImage.fillAmount = pct;
}
private void LateUpdate()
{
var worldToScreenPoint = Camera.main.WorldToScreenPoint(health.transform.position + (Vector3) Vector2.up * positionOffset);
transform.position = worldToScreenPoint;
}
private void OnDestroy()
{
health.OnHealthPctChanged -= HandleHealthChanged;
}
}
So this is my HealthBarController class, which is where the SetHealth method is called, put on a canvas.
using System.Collections.Generic;
using UnityEngine;
public class HealthBarController : MonoBehaviour
{
#region SerializeFields
[SerializeField] private HealthBar healthBar;
#endregion
#region NonSerializeFields
private Dictionary<Health, HealthBar> healthBars = new Dictionary<Health, HealthBar>();
#endregion
private void Awake()
{
Health.OnHealthAdded += AddHealthBar;
Health.OnHealthRemoved += RemoveHealthBar;
}
private void AddHealthBar(Health health)
{
if (healthBars.ContainsKey(health)) return;
var newHealthBar = Instantiate(healthBar, transform);
healthBars.Add(health, newHealthBar);
healthBar.SetHealth(health);
}
private void RemoveHealthBar(Health health)
{
if (!healthBars.ContainsKey(health)) return;
Destroy(healthBars[health].gameObject);
healthBars.Remove(health);
}
}
And this is my Health class on a player character.
using System;
using UnityEngine;
public class Health : MonoBehaviour, IDamageable
{
#region SerializeFields
[SerializeField] protected int maxHealth = 100;
public static event Action<Health> OnHealthAdded = delegate { };
public static event Action<Health> OnHealthRemoved = delegate { };
public event Action<float> OnHealthPctChanged = delegate { };
#endregion
#region NonSerializeFields
protected int currentHealth;
#endregion
private void OnEnable()
{
currentHealth = maxHealth;
OnHealthAdded(this);
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
float currentHealthPct = (float) currentHealth / maxHealth;
OnHealthPctChanged(currentHealthPct);
if (currentHealth <= 0)
{
Die();
}
}
protected void Die()
{
OnHealthRemoved(this);
Destroy(gameObject);
}
}
The problem I'm having is in the LateUpdate method, the health field is null, even though in the SetHealth method, it was set properly.
In the AddHealthBar method of your HealthBarController.cs script you are assigning the Health class of your character to the Prefab healthBar rather than the newly created Health instance newHealthBar.
Simply replace healthBar.SetHealth(health); with newHealthBar.SetHealth(health); (line 30).
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 4 years ago.
I need help, i'am trying to add float number from a script to another script but it does not work. I'am new to c# and coding in general if somebody code fix the script and explained whats wrong with this i would be very thankful. This is the error i am getting.
"NullReferenceException: Object reference not set to an instance of an object
Resources.Die () (at Assets/Resources.cs:42)
Resources.Update () (at Assets/Resources.cs:22)"
Here is my scripts:
1st
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Resources : MonoBehaviour {
public float maxHealth = 5;
public float currentHealth;
public float ResourceCounter;
public Texture stoneIcon;
public Texture woodIcon;
void Start ()
{
currentHealth = maxHealth;
ResourceCounter = 0;
}
void Update ()
{
Die();
}
public void OnMouseOver()
{
if(Input.GetButtonDown("Fire1"))
{
Debug.Log("Loosing one health");
currentHealth = currentHealth - 1f;
}
}
public void Die()
{
if(currentHealth <= 0)
{
Destroy(gameObject);
Inventory inventory = GetComponent<Inventory>();
inventory.ResourceStone = inventory.ResourceStone + 1;
}
}
}
2nd
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour {
public float ResourceStone;
// Use this for initialization
void Start ()
{
ResourceStone = 0;
}
// Update is called once per frame
void Update () {
}
}
By looking at your code i think you never defined the inventory instance for your "resources" script. If you are using a getcomponent both the resources and the inventory script must be on the same gameobject to be found.
If what you want requires both scripts to be on other gameobjects you need a reference to the inventory in your resources script.
You can do this several way (like making a static reference and refering to it or defining inventory as a public variable and then adding the inventory reference in the unity editor)
This is how the first case would look:
First Script -
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public static Inventory instance;
private void Awake()
{
instance = this;
}
public float ResourceStone;
// Use this for initialization
void Start()
{
ResourceStone = 0;
}
}
Second Script -
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Resources : MonoBehaviour
{
public float maxHealth = 5;
public float currentHealth;
public float ResourceCounter;
public Texture stoneIcon;
public Texture woodIcon;
void Start()
{
currentHealth = maxHealth;
ResourceCounter = 0;
}
void Update()
{
Die();
}
public void OnMouseOver()
{
if (Input.GetButtonDown("Fire1"))
{
Debug.Log("Loosing one health");
currentHealth = currentHealth - 1f;
}
}
public void Die()
{
if (currentHealth <= 0)
{
Destroy(gameObject);
Inventory.instance.ResourceStone++;
}
}
}
This is how the code would look if you did the second one:
first script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Inventory : MonoBehaviour
{
public float ResourceStone;
// Use this for initialization
void Start()
{
ResourceStone = 0;
}
// Update is called once per frame
void Update()
{
}
}
second script-
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Resources : MonoBehaviour
{
public float maxHealth = 5;
public float currentHealth;
public float ResourceCounter;
public Inventory inventory;
public Texture stoneIcon;
public Texture woodIcon;
void Start()
{
currentHealth = maxHealth;
ResourceCounter = 0;
}
void Update()
{
Die();
}
public void OnMouseOver()
{
if (Input.GetButtonDown("Fire1"))
{
Debug.Log("Loosing one health");
currentHealth = currentHealth - 1f;
}
}
public void Die()
{
if (currentHealth <= 0)
{
Destroy(gameObject);
inventory.ResourceStone++;
}
}
}
Just take in account two things to decide if you will use the first or the second one. The first one is a static reference, that means that you can't make more that one inventory. In the second one you need to manually drag the reference in the editor. Just in case you don't know, writing ++ after an integer does the same as integer = integer + 1;
EDIT: There, that's better looking :D
i'm having problem in the score and high score when i play the game and eat coin its increasing the score and high score but when i die and play again its start counting from the score before i dead like i dead my score was 3 when i play again without closing the game its start counting from 3 and the high score aren't saving
Score Manager script
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class ScoreManager : MonoBehaviour
{
public Text scoreText;
public Text hiScoreText;
public static int scoreCount;
public int hiScoreCount;
public bool scoreIncreasing;
// Use this for initialization
void Start()
{
if (PlayerPrefs.HasKey("HighScore"))
{
hiScoreCount = PlayerPrefs.GetInt("HighScore");
}
}
// Update is called once per frame
void Update()
{
if (scoreIncreasing)
{
scoreCount = BallMain.getPoints();
}
if (scoreCount > hiScoreCount)
{
hiScoreCount = scoreCount;
PlayerPrefs.SetFloat("HighScore", hiScoreCount);
}
scoreText.text = "Score: " + (scoreCount);
hiScoreText.text = "High Score: " + (hiScoreCount);
}
}
BallMain Script
using UnityEngine;
using System.Collections;
public static class BallMain {
private static float ballSpeed = 1.2f;
private static int points;
private static int lives = 0;
public enum BallStateEnum { shielded,Vulnerable};
public static BallStateEnum ballState = BallStateEnum.Vulnerable;
public static float getBallSpeed()
{
return ballSpeed;
}
public static void IncreaseSpeed()
{
ballSpeed += 0.1f;
}
public static void IncreasePoints()
{
points++;
}
public static int getPoints()
{
return points;
}
public static int getLive()
{
return lives;
}
public static void addLife()
{
lives++;
}
}
CoinHandler Script
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class coinHandler : MonoBehaviour {
public Transform particles;
void OnCollisionEnter2D(Collision2D collider)
{
if (collider.gameObject.tag == "Player")
{
Instantiate(particles, new Vector3(transform.position.x, transform.position.y, -0.2f), Quaternion.identity);
BallMain.IncreaseSpeed();
BallMain.IncreasePoints();
GameObject.FindWithTag("CoinUI").GetComponent<Text>().text = BallMain.getPoints().ToString();
Destroy(gameObject);
}
}
BadCoinHandler Script
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class BadCoinHandler : MonoBehaviour {
public Transform particles;
// Use this for initialization
void Start ()
{
Destroy(gameObject, 8f);
}
void OnCollisionEnter2D(Collision2D collider)
{
if (collider.gameObject.tag == "Player")
{
Instantiate(particles, new Vector3(transform.position.x, transform.position.y, -0.2f), Quaternion.identity);
Destroy(gameObject);
if(BallMain.getLive() == 0 && BallMain.ballState == BallMain.BallStateEnum.Vulnerable)
{
SceneManager.LoadSceneAsync(0);
}
}
}
}
You have to zero the score manually. Because you are using the static keyword. So create another void in BallMain and make the point zero; Example:
public static void ResetPoints()
{
points = 0;
}
And call it on awake method on Score Manager script; Example:
void Awake()
{
scoreCount = 0; //Also the score here
BallMain.ResetPoints();
}
To save your high score use this:
PlayerPrefs.SetInt("HighScore",scoreCount);//HighScore is key and scoreCount is the number you want to save
PlayerPrefs.Save();
BTW I thing you need another void to reset ball speed as well.