So I wanted to make a counter that counts how many times the player hitted a "player_grower". But the problem I'm getting that it isn't counting futher then 1.
public class Collision_Player_grower : MonoBehaviour
{
public GameObject Player_grower;
public float times_player_grower;
void Start()
{
times_player_grower = 0;
}
void OnTriggerEnter(Collider collision)
{
if (collision.gameObject.tag == "Player")
{
print("we hit an playergrower");
Destroy(Player_grower);
times_player_grower = times_player_grower + 1;
print("times hit =" + times_player_grower);
}
}
void Update ()
{
}
}
The result this gives me is that it prints: "times hit = 1", even when I hit two or more of those objects. I believe this happens because it doesn't save the value but I'm not sure.
How can I fix this?
Each of your Collision_Player_grower instances have their own respective field
public float times_player_grower;
so this is an individual counter for each of them.
You probably would make it static so it is "shared" among them all
// I also think an int would be probably more appropriate for your use case
public static int times_player_grower;
Or as an alternative - and cleaner in my eyes - would be to rather let the player itself track the collisions and count.
You either keep your current code but let the player store according value:
// Script on your player object
public class PlayerHitCountController : MonoBehaviour
{
public int times_player_grower;
}
and then do
public class Collision_Player_grower : MonoBehaviour
{
public GameObject Player_grower;
void OnTriggerEnter(Collider collision)
{
if (collision.TryGetComponent<PlayerHitCountController>(out var hitCounter))
{
print("we hit an playergrower");
Destroy(Player_grower);
hitCounter.times_player_grower += 1;
print("times hit =" + hitCounter.times_player_grower);
}
}
}
Or and that would be my actual approach go the total other way round.
// If Player_grower is the same as the object this script is attached to than probably you wouldn't need it
public class Collision_Player_grower : MonoBehaviour
{
// does nothing but holding the information
public GameObject Player_grower;
}
// Script on your player object
public class GrowCollisionChecker : MonoBehaviour
{
public int times_player_grower;
void OnTriggerEnter(Collider other)
{
if (other.TryGetComponent<Collision_Player_grower>(out var grower))
{
print("we hit an playergrower");
Destroy(grower.Player_grower);
// Or if Player_grower is the same object as this is attached to anyway
//Destroy(grower.gameObject);
times_player_grower += 1;
print("times hit =" + times_player_grower);
}
}
}
This way your player has the full control over the counter and can e.g. also reset it where needed.
Most probably you are trying to count the triggers per Player_grower. What is happening is the following: let's say you have 5 Player_grower objects. 1 of them gets triggered. It is calling the OnTriggerEnter() on that particular object setting times_player_grower to 1 and then immediately destroying the object. Even though the Destroy statement is before the incremente and print statments Unity still executes everyting before destroying the object (calling Destroy only marks the object to get destroyed at the end of the frame, until then everything runs). So this is why it always prints 1.
Now to answer your problem - times_player_grower variable exists on every grower but you need to have it declared only 1 time, like a "global" variable instead of "local" for every object so that it will keep counting and not get destroyed together with the GameObject. Now there is 2 ways of achieving this:
The first one is to make the variable static like this: public static float times_player_grower; what static is doing here is declaring a shared variable across all instances of the Collision_Player_grower. All the objects of class Collision_Player_grower will have a common variable called times_player_grower. This way the variable does not get destroyed when the object gets destroyed and you can do your count there. You MUST research static variables if you don't know what they are.
Second one is keeping a count in a separate GameObject that does not get destroyed duing the trigger event.
Good luck
Related
So for some reason when I try to access variables from another script the text is showing what I put but when I change things in the editor the accrual values don't change. even if in the code I put playerPoints to 9 when I run it its 0 during a collision. I have all the scripts and objects connected up, and when I try to flip where the variables are, make them in the destroy script, for some reason, it doesn't work. it wont let me use public BallsText bt; it will return an error even though it works fine for the other. I'm sorry if all of these are really basic questions but I've looked as far as I can on the internet and I cant find anything, so any help would be appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BallsText : MonoBehaviour
{
public Destroy destroy;
// public int playerPoints = 0;
//public int enemyPoints = 0;
//int playerPoints = 0;
//int enemyPoints = 0;
public Text playerPointsText;
public Text enemyPointsText;
void Update()
{
playerPointsText.text = destroy.playerPoints.ToString();
enemyPointsText.text = destroy.enemyPoints.ToString();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Destroy : MonoBehaviour
{
public int playerPoints = 9;
public int enemyPoints = 0;
void OnCollisionEnter2D(Collision2D col)
{
if (this.name == "Destroy.cs")
{
Debug.Log("i have a doughnut");
}
if (col.gameObject.name == "Player")
{
Debug.Log(playerPoints);
playerPoints++;
Debug.Log(playerPoints);
Debug.Log("at least im a dounut");
Destroy(this.gameObject);
}
else if (col.gameObject.name == "Enemy Zone")
{
enemyPoints++;
Destroy(this.gameObject);
}
}
}
You can hide them from the inspector's view with HideInInspector while still being public.
[HideInInspector] public int playerPoints = 9;
You can also use the reset script to return the setting of public numbers to the first state.
Like Everts said, you had the value at zero and created an instance of the script. Now when you change the script Unity will serialize (save) all the public/serialized fields, then reload the script, then deserialize (load) all the public/serialized fields it previously saved. This means your values are locked into the instance that you previously used. If you make a new instance, you get the current values, and if you reset the script you'll get the current values.
This has also bitten me enough times that I don't set default values when variables are declared anymore. If you have a value that you want to use, set those values in Start() or Awake().
The advantage here is that whatever is in the script will get overwritten when play mode starts. The disadvantage is that you can't customize those values on a per-instance basis anymore, because all instances of the script will all load the same default values when play mode starts. If this matters to you, and you want to be able to customize those values, then unfortunately you'll need to go to each script and change those values manually.
If you use a property with an automatic backing field then you won't be able to see it in the editor
public class Destroy : MonoBehaviour
{
public int playerPoints{get; set;} = 9; // Can't see this in the editor
public int enemyPoints{get; set;} = 0; // Can't see this in the editor
If you use a property with an explicit backing field then you can expose the backing field to the editor with the [SerializeField] tag, but then you've got the same problem you've got now - the editor will serialize that field and subsequent changes to the script won't affect instances:
public class Destroy : MonoBehaviour
{
[SerializeField]
private int playerPoints_ = 9; // Instances will "lock in" values and later changes to the script here won't take effect
[SerializeField]
private int enemyPoints_ = 0; // Instances will "lock in" values and later changes to the script here won't take effect
public int playerPoints
{
get=>playerPoints_;
set{playerPoints_ = value;}
}
public int enemyPoints
{
get=>enemyPoints_;
set{enemyPoints_ = value;}
}
If you keep the fields public (and thus exposed to the editor) but set the values at runtime in Awake() or Start() then you can see the values in the editor but the editor values for all instances will be overridden when play mode starts:
public class Destroy : MonoBehaviour
{
public int playerPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
public int enemyPoints; // Doesn't matter what the value is here on instantiation becuase you'll override it on Awake()
public void Awake()
{
playerPoints = 9; // Will override *every* instance's values with this.
enemyPoints = 0; // Will override *every* instance's values with this.
}
:EDIT:
I'll add too that repeatedly polling is wasteful. If you use events then you can subscribe and get notifications when there's something to see. Consider instead:
public class Destroy : MonoBehaviour
{
private int playerPoints = 9;
private int enemyPoints = 0;
public System.EventHandler<int> OnPlayerPointsChanged;
public System.EventHandler<int> OnEnemyPointsChanged;
void OnCollisionEnter2D(Collision2D col)
{
if (this.name == "Destroy.cs")
{
Debug.Log("i have a doughnut");
}
if (col.gameObject.name == "Player")
{
Debug.Log(playerPoints);
playerPoints++;
OnPlayerPointsChanged?.Invoke(this, playerPoints);
Debug.Log(playerPoints);
Debug.Log("at least im a dounut");
Destroy(this.gameObject);
}
else if (col.gameObject.name == "Enemy Zone")
{
enemyPoints++;
OnEnemyPointsChanged?.Invoke(this, enemyPoints);
Destroy(this.gameObject);
}
}
}
Now Destroy has two public events that fire when the public or enemy points change. Anyone subscribing to those events will get notified when the points change, and part of the event notification is the current point value.
Then your other script subscribes to the events and does whatever they need to when they receive that event. Here they'll convert the points .ToString() and update the Text values:
public class BallsText : MonoBehaviour
{
public Destroy destroy;
public Text playerPointsText;
public Text enemyPointsText;
private void Start()
{
destroy.OnPlayerPointsChanged += PlayerPointsChanged;
destroy.OnEnemyPointsChanged += EnemyPointsChanged;
}
public void PlayerPointsChanged(object sender, int points)
{
playerPointsText.text = points.ToString;
}
public void EnemyPointsChanged(object sender, int points)
{
enemyPointsText.text = points.ToString();
}
}
Last note here is that your Destroy script increments enemyPoints but then also immediately destroys the gameObject, so I don't see the point in incrementing enemyPoints unless there's something else accumulating enemy points. With the subscription model that's totally doable - you could have something else subscribing to the Destroy script models and they'll get notifications before the script self-destructs.
So im trying to change a variable in another script by touching a cube.
Current setup
1x Player
1x Enemy
Each with their own script Enemy_Stats & Character_Stats
As you can see in this little snippet it's quite a workaround to access the variable from another script.
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Enemy")
{
collision.gameObject.GetComponent<Enemy_Stats>().Health =
collision.gameObject.GetComponent<Enemy_Stats>().Health
- gameObject.GetComponent<Character_Stats>().AttackDamage;
if (collision.gameObject.GetComponent<Enemy_Stats>().Health <= 0)
{
Destroy(collision.gameObject);
}
}
}
Iam new to Unity, but isn't there a way to just refer it with something like:
collision.Health?
How to access variables/functions from another Class. The variable or function you want to access or called must be public not private.
public class ScriptA : MonoBehaviour{
public int playerScore = 0;
void Start()
{
}
public void doSomething()
{
}
}
Access variable playerScore in ScriptA from ScriptB. First, find the GameObject that the script or component is attached to with the GameObject.Find function then use the GetComponent function to retrieve that script or component that is attached to it.
public class ScriptB : MonoBehaviour{
ScriptA scriptInstance = null;
void Start()
{
GameObject tempObj = GameObject.Find("NameOfGameObjectScriptAIsAttachedTo");
scriptInstance = tempObj.GetComponent<ScriptA>();
//Access playerScore variable from ScriptA
scriptInstance.playerScore = 5;
//Call doSomething() function from ScriptA
scriptInstance.doSomething();
}
}
No since Health is not part of the collision object, but Enemy_Stats. You can cache a Component (that's what Enemy_Stats is) if you use it multiple times to save you some typing (and some performance, but that is rather marginal for this example). Also you can cache "known" components like in this case Player_Stats. You can do this e.g. in Start or with a public variable and the inspector.
What you should probably do though is to make the enemy be responsible for his life and not the player, so move the Destroy-part to Enemy_Stats (into the Health property to be exact).
The first thing to make this shorter (and eventually faster) would be to store this: gameObject.GetComponent<Character_Stats>() on Start() in a private variable (you should avoid calling GetComponent on a frequent basis if you can avoid it).
For the Health variable, a way of avoiding GetComponent calls could be caching: you create a Dictionary<GameObject, Enemy_Stats> and read from that as soon as this gameobject collided once
At the very beginning i mean in Awake() method you can find a game-object with tag
and get it's Heath after that in Collision() method you should just decrease the health but, here the condition is there is only one enemy and only one player.
I'm making a game in Unity3D with C# for mobile devices and can't figure out how to check which scene was loaded before the current scene. I need to check this to change the spawn point from the player gameobject. First I added a simple script to my buttons (loadnextscene and loadprevscene)
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Application.LoadLevel(sceneNumber);
}
}
A second scripts handles the touch input from the user and changes the movement of the player object.
So, for example: If the player clicks on the "load previous scene" button in the second Level to switch to the first level again, I want to set the spawn point of the player object on the right half on the screen and not on the left side like when the game was started the first time.
I tried it with Singleton and PlayerPrefs, but it did not work out.
You need to save the scene number to some variable before LoadScene, then check it after the scene loaded.
The only problem is that this variable will be destroyed after the new scene is loaded. So, to prevent it, you can use DontDestroyOnLoad. Here is what you do:
First, create a new empty game object, and attach the following script to it:
using UnityEngine;
using System.Collections;
public class Indestructable : MonoBehaviour {
public static Indestructable instance = null;
// For sake of example, assume -1 indicates first scene
public int prevScene = -1;
void Awake() {
// If we don't have an instance set - set it now
if(!instance )
instance = this;
// Otherwise, its a double, we dont need it - destroy
else {
Destroy(this.gameObject) ;
return;
}
DontDestroyOnLoad(this.gameObject) ;
}
}
And now, before you load, save the scene number in the Indestructable object:
public class SwitchScene : MonoBehaviour {
public int sceneNumber;
public void LoadScene(int sceneNumber) {
Indestructable.instance.prevScene = Application.loadedLevel;
Application.LoadLevel(sceneNumber);
}
}
And last, in your scene Start() check Indestructable.instance.prevScene and do your magic accordingly.
More info here:
http://docs.unity3d.com/ScriptReference/Object.DontDestroyOnLoad.html
*I did not compile the code, so there may be some errors, but this is the general idea.
Why did the PlayerPrefs approach did not work?
I think its the easiest way to solve your problem.
public class FirstLevel : MonoBehaviour {
public void Start() {
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
}
And then in the second scene simply read the saved PlayerPrefs
public class SecondLevel : MonoBehaviour {
string PrevScene;
public void Start() {
PrevScene = PlayerPrefs.GetString("SceneNumber");
// if there will be a third scene, etc.
PlayerPrefs.SetString("SceneNumber", SceneManager.GetActiveScene().name);
}
public void GoToPrevScene() {
SceneManager.LoadScene(PrevScene);
}
}
You can solve this problem with a single static member variable in the SwitchScene class. No need for the singleton pattern or DontDestroyOnLoad.
public class SwitchScene : MonoBehaviour
{
public int sceneNumber;
private static int previousScene;
private int oldPreviousScene;
void Start()
{
oldPreviousScene = previousScene;
previousScene = sceneNumber;
}
public void HandleLoadPrevButtonClick()
{
SceneManager.LoadScene(oldPreviousScene);
}
}
In Unity3D I've got a script that adds the variable 'eaten' as a component.
using UnityEngine;
[AddComponentMenu("My game/IsEaten")]
public class IsEaten : MonoBehaviour
{
public bool Eaten;
}
Yay! I can then add another script to access 'Eaten'
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private Eaten someScript;
// Use this for initialization
void Start ()
{
someScript = GetComponent<IsEaten>();
bool temp = someScript.Eaten;
print(temp); // false
}
}
Which works fine. What do I have to do to access the variable with dot notation from another script? ie
if (myCube.eaten == true)
{
// do something
}
You know, in Unity one does rarely create the whole script to add a single property to some object. The common approach is to think of scripts as 'components' (which they are, in fact). Let me explain this, a component is a single piece of code that add certain functionality to your GameObject, like ability to animate, or to behave the laws of physics. So, maybe, it would be better to reform your IsEaten class to form a true component, like Pickup (I'm assuming that you need Eaten property for the pickup of some sort) that will have functionality to be eaten by a player, or something.
// You will actually need to attach a collider (and check the 'IsTrigger' checkbox on it) to your GameObject
// Then this method will be called whenewer some game object with a collider (e. g. player, npc's, monsters) will enter the trigger of this object
void OnTriggerEnter(Collider other)
{
// Check whether this object was eaten before and if the actor entering our trigger is actually the player
if (!Eaten && other.tag == "Player")
{
// Make shure that we will not get eaten twice
Eaten = true;
// Apply some effect to the player that has devoured us
other.GetComponent<Player>().AddHp(25);
}
}
Other than that, I'm personally thinking, that getting out of your way to simply enable a little sweeter syntax is not worth the hassle, but, if you provide some insight of what are you actually trying to implement, I may try to help you with it :)
One way to do it might be using Get/Set:
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private Eaten someScript;
// start new!
public bool eaten
{
get
{
return someScript.Eaten;
}
set
{
someScript.Eaten = value;
}
}
// end new!
// Use this for initialization
void Start ()
{
someScript = GetComponent<IsEaten>();
bool temp = someScript.Eaten;
print(temp); // false
}
}
Then you can access the Test class property:
Test t = GetComponent<Test>();
t.eaten = true;
ath.
I have to access variable ammoMagazine from this class
public class Pistol : MonoBehaviour {
public int ammoMagazine = 7;
}
then i tried this code :
public class AmmoCounter : MonoBehaviour {
public int ammo;
private Pistol _pistol;
void Start () {
_pistol = GetComponentInChildren<Pistol>();
}
void Update () {
ammo = _pistol.ammoMagazine;
guiText.text = "Pistol: " + ammo + "/7";
}
}
Why there's NullReferenceException: Object reference not set to an instance of an object ?
Thanks, im new in C#
There is probably no value assigned to the _pistol field, so either you didn't call Start before you called Update, or GetComponentInChildren<Pistol>() returned null.
public class AmmoCounter : MonoBehaviour {
public int ammo;
private Pistol _pistol = GetComponentInChildren<Pistol>();
void Update () {
ammo = _pistol.ammoMagazine;
guiText.text = "Pistol: " + ammo + "/7";
}
}
Initialize the _pistol-variable before calling the update-function (Check if the GetComponentInChildren()-function returns null)
Note that it's NullReferenceException.
I think for some reason Update() is being called before Start().
I know this has already been answered..but I think there will be some roadblocks ahead for you if you code everything with private variables like the accepted answer. Usually to get a reference in Unity you need to have an object in the scene with the desired attached script. So first you would re-write the script:
public class AmmoCounter : MonoBehaviour {
public int ammo;
public GameObject _pistol;
void Start( ){
ammo = _pistol.GetComponent< Pistol >( );
}
void Update( ) {
guiText.text = "Pistol: " + ammo + "/7";
}
}
In the scene you want an object with the Pistol script attached, then in the editor ( inspector ) you should drag that object into the _Pistol slot. There are several reasons for doing this but two important ones are 1) You're shortening the code and referencing the script once ( the current answer is duplicating the reference ) and 2) Using GetComponentInChildren is inefficient; Unity searches through every game object underneath a parent to get your component. Thus, a direct object reference is better. Also using this type of object reference will save you several headaches in the future, most notably easy reference and making it easier to follow your own code in the future ( you can see the reference in the editor rather than looking through code -- trust me magical references can be a nightmare ).
If you don't want to reference through objects read up on the static type in C#, this allows you to reference items without using GameObjects. However, static types are finicky creatures, so be careful and know what you're doing before using them
edit: grammar