inherit variable value from class to sub class c# - c#

I have 2 scenes each one have own script simply I want to get a value from script 1 and use it in script 2 which is loaded in the second scenes which happen is it always load the default value for this variable ex; if I call string variable which should have value (sas) it appears in the second script with null if integer it will be 0 although if I print it in the first script it will have the right value which "sas" .
look to method goforward () which update the variable x
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Trainbehavior : MonoBehaviour
{
public float force = (float)0.1;
private Rigidbody sph;
public GameObject button_goforward;
public GameObject button_goright;
public GameObject button_results;
public GameObject button_nextlevel;
public Text Results;
public String x ;
//public int g;
//public gamepbject panel ;
//panel.gameobject.setactive(true);
private void Start()
{
sph = GetComponent<Rigidbody>();
button_goforward.SetActive(false);
button_goright.SetActive(false);
button_results.SetActive(false);
button_nextlevel.SetActive(false);
}
private void OnTriggerEnter(Collider other)
{
if (other.name == "switchtrigger")
{
button_goforward.SetActive(true);
button_goright.SetActive(true);
button_results.SetActive(true);
Time.timeScale = 0;
}
}
public void goforward()
{
setx("sas"); // if a called print x it will be sas
// Results.text = "dzf";
}
public String getx()
{
return x;
}
public void setx(String x)
{
this.x = x;
}
second script
in the update iam trying to get the value x by
finalresults.text = trains.getx();
public class cases : Trainbehavior
{
Trainbehavior trains;
public Text finalresults;
public string cx;
// Start is called before the first frame update
void Start()
{
trains = GetComponent<Trainbehavior>();
/*var trainscript = gameObject.GetComponent<Trainbehavior>();
if (trainscript)
{
finalresults.text = Results.GetComponent<UnityEngine.UI.Text>().text;
}*/
}
// Update is called once per frame
void Update()
{
// finalresults.text = trains.x;
finalresults.text = trains.getx(); // if i called print(x) it will be null !
/*
var trainscript = gameObject.GetComponent<Trainbehavior>();
if (trainscript)
{
// GameObject x = trainscript.GameObject.Find("test");
finalresults.text = cx;
print(cx);
}*/
}
}

If I understand your question you just want to call your getx function and return the value you set to x in the first script. However Unless you call that goForward function your not setting x and I don't see where your doing that. It looks like your saying when you do call goforward you get what you want which makes sense. you just need to call that goforward function in your second script to set x to a value before you call getx.
Your not inheriting x when you do this your just setting a public variable which is fine. you can either set the value to your public variable x in that function or anywhere in that class.

Related

Money Updated In Shop Menu Only When Scene Is Started With Menu Open? Helmet Not Buying?

For some reason I need to start the scene with the menu open, then close it, then grab the coin and go to the shop menu in order for the shop ui to update my moneyAmount. If i start the scene with the shop menu closed and pick up the coin then go to my shop menu it doesnt update. And when i buy my helmet it says reference not set to an object even though all im doing is getting my player health component and adding 50 to it so why do i need to reference any kind of object? here are the scripts with my GameControl script first.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameControl : MonoBehaviour
{
public Text moneyText;
public static int moneyAmount;
int isHelmetSold;
int isBeltSold;
int isPantsSold;
int isShirtSold;
int isBootsSold;
// Start is called before the first frame update
void Start()
{
moneyAmount = PlayerPrefs.GetInt("MoneyAmount");
isHelmetSold = PlayerPrefs.GetInt("IsHelmetSold");
isBeltSold = PlayerPrefs.GetInt("IsBeltSold");
isPantsSold = PlayerPrefs.GetInt("IsPantsSold");
isShirtSold = PlayerPrefs.GetInt("IsShirtSold");
isBootsSold = PlayerPrefs.GetInt("IsBootsSold");
}
// Update is called once per frame
void Update()
{
moneyText.text = moneyAmount.ToString();
}
Here is my PageOneShop script which makes my item buyable when amount is reached (for the time being ive only finished my helmet not the rest so if you can ignore all of the public texts and buttons as i know i havent added them yet)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class PageOneShop : MonoBehaviour
{
public static int moneyAmount;
int isHelmetSold;
int isBeltSold;
int isPantsSold;
int isShirtSold;
int isBootsSold;
public Text moneyText;
public Text helmetPrice;
public Text beltPrice;
public Text pantsPrice;
public Text shirtPrice;
public Text bootsPrice;
public Button buyHelmetButton;
public Button buyBeltButton;
public Button buyPantsButton;
public Button buyShirtButton;
public Button buyBootsButton;
void Start()
{
moneyAmount = PlayerPrefs.GetInt("MoneyAmount");
isHelmetSold = PlayerPrefs.GetInt("IsHelmetSold");
isBeltSold = PlayerPrefs.GetInt("IsBeltSold");
isPantsSold = PlayerPrefs.GetInt("IsPantsSold");
isShirtSold = PlayerPrefs.GetInt("IsShirtSold");
isBootsSold = PlayerPrefs.GetInt("IsBootsSold");
}
// Update is called once per frame
void FixedUpdate()
{
moneyText.text = moneyAmount.ToString();
isHelmetSold = PlayerPrefs.GetInt("IsHelmetSold");
if (moneyAmount >= 150 && isHelmetSold == 0)
buyHelmetButton.interactable = true;
else
buyHelmetButton.interactable = false;
isBeltSold = PlayerPrefs.GetInt("IsBeltSold");
if (moneyAmount >= 120 && isBeltSold == 0)
buyBeltButton.interactable = true;
else
buyBeltButton.interactable = false;
isPantsSold = PlayerPrefs.GetInt("IsPantsSold");
if (moneyAmount >= 100 && isPantsSold == 0)
buyPantsButton.interactable = true;
else
buyPantsButton.interactable = false;
isShirtSold = PlayerPrefs.GetInt("IsShirtSold");
if (moneyAmount >= 100 && isShirtSold == 0)
buyShirtButton.interactable = true;
else
buyShirtButton.interactable = false;
isBootsSold = PlayerPrefs.GetInt("IsBootsSold");
if (moneyAmount >= 80 && isBootsSold == 0)
buyBootsButton.interactable = true;
else
buyBootsButton.interactable = false;
}
public void buyHelmet()
{
moneyAmount -= 150;
GetComponent<PlayerHealth>().maxHealth += 50;
PlayerPrefs.SetInt("IsHelmetSold", 1);
helmetPrice.text = "Sold!";
buyHelmetButton.gameObject.SetActive(false);
}
public void buyBelt()
{
moneyAmount -= 120;
GetComponent<PlayerHealth>().maxHealth += 50;
PlayerPrefs.SetInt("IsBeltSold", 1);
helmetPrice.text = "Sold!";
buyBeltButton.gameObject.SetActive(false);
}
public void buyShirt()
{
moneyAmount -= 100;
GetComponent<PlayerHealth>().maxHealth += 50;
PlayerPrefs.SetInt("IsShirtSold", 1);
helmetPrice.text = "Sold!";
buyShirtButton.gameObject.SetActive(false);
}
public void buyPants()
{
moneyAmount -= 100;
GetComponent<PlayerHealth>().maxHealth += 50;
PlayerPrefs.SetInt("IsPantsSold", 1);
helmetPrice.text = "Sold!";
buyPantsButton.gameObject.SetActive(false);
}
public void buyBoots()
{
moneyAmount -= 80;
GetComponent<PlayerHealth>().maxHealth += 50;
PlayerPrefs.SetInt("IsBootsSold", 1);
helmetPrice.text = "Sold!";
buyBootsButton.gameObject.SetActive(false);
}
And here is my coin script on my coins that i pick up.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Coin : MonoBehaviour
{
void OnTriggerEnter2D (Collider2D col)
{
PageOneShop.moneyAmount += 200;
GameControl.moneyAmount += 200;
Destroy(gameObject);
}
It's important to figure out the pieces involved. Starting from the minimum amount of code, I think this is a good start:
"GameControl"
using UnityEngine;
using UnityEngine.UI;
public class GameControl : MonoBehaviour
{
public Text moneyText;
public static int moneyAmount;
// Start is called before the first frame update
// Start is only called while this is active and enabled
void Start()
{
moneyAmount = PlayerPrefs.GetInt("MoneyAmount");
}
void Update()
{
moneyText.text = moneyAmount.ToString();
}
}
"PageOneShop"
using UnityEngine;
using UnityEngine.UI;
public class PageOneShop : MonoBehaviour
{
public static int moneyAmount;
public Text moneyAmountText;
public Text helmetPrice;
// Start is called before the first frame update
// Start is only called while this is active and enabled
void Start()
{
moneyAmount = PlayerPrefs.GetInt("MoneyAmount");
}
// Update is called once per frame
void Update()
{
moneyAmountText.text = moneyAmount.ToString();
}
public void buyHelmet()
{
moneyAmount -= 150;
helmetPrice.text = "Sold!";
}
}
"Coin"
using UnityEngine;
public class Coin : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D col)
{
PageOneShop.moneyAmount += 200;
GameControl.moneyAmount += 200;
Destroy(gameObject);
}
}
Everything in these files is used, and we can replicate our issue. Before this point, my previous answer was correct and you weren't updating the moneyAmount instance inside PageOneShop. Now there is a new issue and it's more subtle, because it's strictly speaking not in your code.
Start is a Unity called method, and is called "before the first frame update". Start is also only called if the component is enabled and the object it's attached to is active. If the object it's attached to is de-activated (like I'm guessing your menu is when you load your scene) Start will be called the first time you activate the object (open the menu). Since nowhere are you updating the "MoneyAmount" in PlayerPrefs, that value is still 0. If you pick up the coin, then open the menu for the first time, the value would be 200 but instead is re-set to 0 when this runs:
void Start()
{
moneyAmount = PlayerPrefs.GetInt("MoneyAmount");
}
Probably the quickest way to fix this is to add this line to coin:
public class Coin : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D col)
{
PageOneShop.moneyAmount += 200;
GameControl.moneyAmount += 200;
// Set the PlayerPrefs value
PlayerPrefs.SetInt("MoneyAmount", GameControl.moneyAmount);
Destroy(gameObject);
}
}
At this point even if the menu opens after the coin is picked up, the correct value is retrieved from PlayerPrefs so nothing is overwritten.
Potentially a new problem has been introduced, which is even in the editor during testing, this write to PlayerPrefs is occurring so money may accumulate as multiple play sessions are run.
There are a few other issues as well. As #derHugo accurately points out you should stop polling for changes in update to an event based approach, and the use of two static variables which are kept in sync like this is at best wasteful, and at worst can cause some real serious headaches.
Let's start by addressing the double static variable use. You could replace all the references of one with the other, but I propose introducing a third class to handle the player's "Wallet" where they keep their money. You could write something as simple as:
public static class Wallet
{
public static int MoneyAmount;
}
All the references to the other two static moneyAmount variables could be replaced with this Wallet.MoneyAmount.
The polling could now be replaced with events pretty quickly by adding an event to the Wallet class, and replacing the field with a property:
using System;
public static class Wallet
{
public static event Action OnMoneyAmountChanged;
private static int moneyAmount;
public static int MoneyAmount
{
get
{
return moneyAmount;
}
set
{
moneyAmount = value;
// maybe null check, or follow a "never null" approach
OnMoneyAmountChanged();
}
}
}
Instead of always checking moneyAmount you can now add some code like:
void Start()
{
// Listen for changes
Wallet.OnMoneyAmountChanged += handleMoneyAmountChanged;
}
private void OnDestroy()
{
// Remember to always remove your listeners when you're done with them
Wallet.OnMoneyAmountChanged -= handleMoneyAmountChanged;
}
// set the text to the value held in the wallet whenever the amount changes
void handleMoneyAmountChanged()
{
moneyText.text = Wallet.MoneyAmount.ToString();
}
Maybe you don't want the PlayerPrefs.SetInt call in Coin any more, and instead you could move it into the Wallet now. If you put it in a [Conditional] method you could also protect yourself from constantly overwriting PlayerPrefs with every play session in the editor.
using System;
using System.Diagnostics;
using UnityEngine;
public static class Wallet
{
public static event Action OnMoneyAmountChanged;
private static readonly string moneyAmountKey = "MoneyAmount";
private static int moneyAmount;
public static int MoneyAmount
{
get
{
return moneyAmount;
}
set
{
moneyAmount = value;
OnMoneyAmountChanged();
// this method call is conditional on a defined constant
saveMoneyAmount();
}
}
// This part will only run if ENABLE_PLAYERSPREFS is defined.
[Conditional("ENABLE_PLAYERPREFS")]
private static void saveMoneyAmount()
{
PlayerPrefs.SetInt(moneyAmountKey, moneyAmount);
}
}
You can type whatever you want into the scripting define symbols text box described in the "Platform custom #defines" section on this page. If you define "ENABLE_PLAYERPREFS" that method will call.
You probably now want to initialize your wallet as well, and to keep things symmetrical you could "close" it as well. This would let you cut down on a lot of reads and writes to player prefs.
public static void Open()
{
// this is another way to interact with platform define constants
#if ENABLE_PLAYERPREFS
moneyAmount = PlayerPrefs.GetInt(moneyAmountKey);
#else
moneyAmount = 0;
#endif
}
public static void Close()
{
// maybe you want to do other stuff in here
saveMoneyAmount();
}
If you called Open at the start of your application, and Close at the end you could just use the variables in memory the rest of the time and remove the saveMoneyAmount call from the MoneyAmount setter.
As for this second question you added later than my original answer, "And when i buy my helmet it says reference not set to an object even though all im doing is getting my player health component and adding 50 to it so why do i need to reference any kind of object?". You didn't post the line number, but I can guess that it originates on any/all the calls that look like this GetComponent<PlayerHealth>().maxHealth += 50; since those are all in methods on PageOneShop so unless you've added your PlayerHealth component to your menu object that GetComponent will be null, at which point you can refer to What is a NullReferenceException, and how do I fix it?

Unity code cleaning: writing general shooting script implementing same methods from different bullet classes

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..

Problem with changing the color of a game player object

I am making a small platform game and in that, I made a scene where you can change the colour of your player. I made 3 buttons that change the colour when you click on them. I also made two code files, but they are not working. I see no errors in the console either.
P.S the colour changing code and buttons are on a different scene than the game object.
This is the code for the buttons that change the colour:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColourManager : MonoBehaviour
{
public static int colour;
public void DefaultBlue()
{
colour = 0;
}
public void Green()
{
colour = 1;
}
public void Red()
{
colour = 2;
}
}
This is the code on the game object itself:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColourTarget : MonoBehaviour
{
void Start()
{
rend = GetComponent<Renderer>();
rend.sharedMaterial = materials[0];
rend.enabled = true;
}
private Renderer rend;
public int lcolour = ColourManager.colour;
public Material[] materials;
private void Update()
{
if (lcolour == 0)
{
rend.sharedMaterial = materials[0];
}
if (lcolour == 1)
{
rend.sharedMaterial = materials[1];
}
if (lcolour == 2)
{
rend.sharedMaterial = materials[2];
}
}
}
Note that changing sharedMaterial might not be what you want to do here.
If you want to modify the material of a renderer use material instead.
so rather use material. Especially once e.g. a color was changed and you are dealing with instanced materials, afterwards changing the shared material has no effect at all.
Then do not do this in Update! It is very redundant and inefficient setting this every frame!
Finally note that this
public int lcolour = ColourManager.colour;
is only assigned ONCE the moment this object is initialized and then never changed anymore ... int is a VALUE type, not a reference!
I would rather use and event and make your target listen to any changes.
So your code might look like
public class ColourManager : MonoBehaviour
{
public static int colour;
// we will invoke this event everytime the color index is changed
// and directly pass the according new index in
public static event Action<int> OnColourIndexChanged;
public void DefaultBlue()
{
colour = 0;
// The ? is a null check and only
// calls Invoke if there is at least one listener to this event
OnColourIndexChanged?.Invoke(colour);
}
public void Green()
{
colour = 1;
OnColourIndexChanged?.Invoke(colour);
}
public void Red()
{
colour = 2;
OnColourIndexChanged?.Invoke(colour);
}
}
and then
public class ColourTarget : MonoBehaviour
{
[SerializeField] private Renderer _renderer;
public Material[] materials;
private void Awake()
{
if(!_renderer) _renderer = GetComponent<Renderer>();
_renderer.enabled = true;
// Add a callback to the event
// Removing it first is save also if it wasn't added so far
// This just makes sure it is always added only exactly once
ColourManager.OnColourIndexChanged -= UpdateMaterial;
ColourManager.OnColourIndexChanged += UpdateMaterial;
// do the first update now with the current state
UpdateMaterial(ColourManager.colour);
}
// Now this is called only when the value is changed in the manager
// script and once at the beginning with the initial state
private void UpdateMaterial(int index)
{
// check for validity
if(index < 0 || index >= materials.Length) return;
_renderer.material = materials[index];
}
private void OnDestroy()
{
// Always make sure to clean up listeners once not needed anymore
// otherwise you get NullReferencExceptions
ColourManager.OnColourIndexChanged -= UpdateMaterial;
}
}

My first C# game in Unity (introducing playerprefs)

In my game, there is a profit count which is controlled by the addMoney variable in my money script ex. profitcount = addMoney.
When I add the player pref regarding my addMoney Variable it defaults the profitCount to 0 when it should in fact be 1. This is my first game, so it could be be very easily a small thing that I have misunderstood or overlooked.
moneyCount
public class moneyCount : MonoBehaviour
{
float timeTillAdd = 1;
public int addMoney = 1;
public int money;
public Text txt;
// Start is called before the first frame update
void Start()
{
money = PlayerPrefs.GetInt("Money");
addMoney = PlayerPrefs.GetInt("addmoney");
}
// Update is called once per frame
void Update()
{
PlayerPrefs.SetInt("addmoney", addMoney);
if (Time.time >= timeTillAdd)
{
money += addMoney;
timeTillAdd++;
}
txt.text = money.ToString();
PlayerPrefs.SetInt("Money", money);
}
}
profit count
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class profitCount : MonoBehaviour
{
public int profitAmount;
public GameObject moneyManagerObj;
moneyCount mc;
public Text txt;
// Start is called before the first frame update
void Start()
{
mc = moneyManagerObj.GetComponent<moneyCount>();
// profitAmount = PlayerPrefs.GetInt("profitamount");
}
// Update is called once per frame
void Update()
{
profitAmount = mc.addMoney;
txt.text = profitAmount.ToString();
// PlayerPrefs.SetInt("profitamount", profitAmount);
}
}
shopManager
public class ShopManager : MonoBehaviour
{
public int item1_cost = 50;
public int item1_upgrade = 5;
public int item1_tier = 1;
public int item2_cost = 50;
public int item2_upgrade = 5;
public GameObject moneyManagerObj;
moneyCount mc;
public Text txt;
public Text item1;
public Text item1_text;
// Start is called before the first frame update
void Start()
{
mc = moneyManagerObj.GetComponent<moneyCount>();
item1_cost = PlayerPrefs.GetInt("Item1_cost");
//item1_upgrade = PlayerPrefs.GetInt("Item1_upgrade");
item1_tier = PlayerPrefs.GetInt("Item1_tier");
}
// Update is called once per frame
void Update()
{
item1.text = item1_tier.ToString();
PlayerPrefs.SetInt("Item1_cost", item1_cost);
// PlayerPrefs.SetInt("Item1_upgrade", item1_upgrade);
PlayerPrefs.SetInt("Item1_tier", item1_tier);
if (item1_tier > 0)
{
item1_text.text = ("Upgrade");
}
}
public void on_click()
{
{
if (mc.money >= item1_cost)
{
mc.money -= item1_cost;
mc.addMoney += item1_upgrade;
item1_tier += 1;
item1.text = item1_tier.ToString();
item1_cost += 50 * item1_tier;
}
}
}
}
Two things:
Don't use PlayerPrefs to store save data. It isn't meant for that. Its meant for saving player preferences such as volume, full screen mode, or input type. The file used by PlayerPrefs is plain text and can't support complex data types.
If no save data exists, the values read out are zero (at least from PlayerPrefs). You need to account for this and currently you are not. When you move to a different method of saving, you'll get other errors, like null pointers or file not founds. You have to determine if a save exists and only if it does, should you read from it.
Ok so you were assigning default value to addMoney during initialization i.e public int addMoney = 1;
But then in your start you were again assigning it a value that has not been saved yet addMoney = PlayerPrefs.GetInt("addmoney");
If you want to create a record do it like this PlayerPrefs.SetInt("addmoney",addmoney);

calling a child class method in OnMouseDown() of sprite in unity

Good Day all, I am trying to make a simple 2D game and here is how it works. Assume there is a sprite "tent", and whenever i clicked the tent, the console will print out a message. Sounds straightforward is it? However I'm like stuck in the middle of it and I know I am gonna facepalmed myself so hard after this. What I did in my script is:
public abstract class Player : MonoBehaviour {
protected string _name;
// Use this for initialization
public Player(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
set { _name = value; }
}
void Start () {
}
// Update is called once per frame
void Update () {
}}
Next I have a child class called Civilian
public class Civilian : Player {
// Use this for initialization
public Civilian(string name):base(name)
{
}
void Start () {
}
// Update is called once per frame
void Update () {
}
}
Then in my script "Tentclicked", i want to called the function of sleep when the sprite is clicked.
public class tentclicked : MonoBehaviour {
void OnMouseDown()
{
// c.Sleep();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
And you are wondering what is c and what it is point to? I instantiated a new class in another script called testing1.
public class testing1 : MonoBehaviour
{
public Text p1name;
public void Awake()
{
//p1name.text = "Hello";
//Civilian C = Scriptss.AddComponent<Civilian>();
//GameObject theCivilian = GameObject.Find("Scriptss");
// Civilian c = theCivilian.GetComponent<Civilian>();
//c.Name = Testingsavename.playernames.username;
//c.Name = "Hello";
}
// Use this for initialization
void Start()
{
if (Choosechar.choice.choicecount == 1)
{
Civilian c = new Civilian(Testingsavename.playernames.username);
p1name = GetComponent<Text>();
p1name.text = c.Name;
}
I have already add a circle collider to my sprite. The error is that in tentclicked, c does not exist in context. So i suspect it may be that unity does not know what c is. I am thinking of making civilian class a static one, and just called Civilian.civilian.Sleep() in tentclicked script....
"c" does not exist in this context because your tentclicked class has no reference to it. Find "c" with GameObject.Find(string name) and assign the gameobject value to a variable in tentclicked
You could raycast from the current mouse position towards the object (just make sure that the object has a collider.
void OnMouseDown()
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if ( Physics.Raycast (ray,out hit,100.0f))
{
Civilian c = hit.collider.gameObject.GetComponent<Civilian>();
c.Sleep();
}
}

Categories

Resources