Unity can't acess prefab's script's variable - c#

I am working on a 3D game on Unity but recently a problem occurred. I can't access a variable from another prefab's script. I tried it before when the object which has to be accessed wasn't a prefab and it worked correctly.
This is the script which tries to access the "slashtime" variable, but when I run it gives back 0 though in the other script the variable is changing continuously.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collision : MonoBehaviour
{
public GameObject sword;
public float slashtime;
private void Update()
{
slashtime=sword.GetComponent<movement>().slashtime;
}
private void OnTriggerEnter(Collider collider)
{
if (collider.tag == "sword" && slashtime+1f > Time.time)
{
Destroy(gameObject);
}
}
}

You can use this. It is also working.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class collision : MonoBehaviour
{
public GameObject sword;
public float slashtime;
private void OnTriggerEnter(Collider collider)
{
sword = collider.gameObject;
slashtime=sword.GetComponent<movement>().slashtime;
if (collider.tag == "sword" && slashtime+1f > Time.time)
{
Destroy(gameObject);
}
}
}
Reason for your code is not working because when you write some code in update then it calls every frame. so when your object is destroyed, update call that code and it shown error.

Reworking your code a bit, with explanations below:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class collision : MonoBehaviour
{
[SerializeField]
private GameObject _sword;
private float _slashTime;
private void Start()
{
_slashTime = GameObject.Find("Movement").GetComponent<Movement>().GetSlashTime();
}
private void OnTriggerEnter(Collider collider)
{
if (collider.CompareTag("sword") && _slashTime + 1f > Time.time)
{
Destroy(gameObject); // What is gameObject in this context? the _sword? something else?
}
}
}
Use private properties. Why? Because it increases modularity and protects the object(s) you're working with from accidentally having their values overwritten.
In conjunction to this, use public methods to obtain/change the values in other objects. Public methods are your API.
It's convention to begin the names of private properties with an underscore.
Like I said in my comment above, you first need to Find() your desired object before you can attempt to GetComponent<T>().
Like others have said, attempting to GetComponent<T>() in Update() is computationally expensive. It's better to get that component at Start() and then talk to it as necessary (in this case, during collision).
CompareTag() is the more modern/accepted way to check a tag.
EDIT:
[SerializeField] will keep the property private while also making it available to the Unity editor. It's good for debugging and linking objects.

Related

How can i un do a destroy() function in C#

I need to destroy an object, in my case is "Player2" witch is in the same scene as "Player1" and then after Player1 position.x is past "baraj".position.x reapear. Basically how can i make a temporarly destroy method.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class switchCameras2 : MonoBehaviour
{
public GameObject baraj;
public GameObject CameraOne;
public GameObject CameraTwo;
public GameObject Player1;
public GameObject Player2;
public void Start()
{
CameraOne.SetActive(true);
CameraTwo.SetActive(false);
}
void Update()
{
if (GameObject.Find("Player1").transform.position.x > GameObject.Find("baraj").transform.position.x)
{
CameraTwo.SetActive(true);
CameraOne.SetActive(false);
}
if (GameObject.Find("Player1").transform.position.x < GameObject.Find("baraj").transform.position.x)
{
Destroy(Player2);
}
if (GameObject.Find("Player1").transform.position.x > GameObject.Find("baraj").transform.position.x)
{
Destroy(Player1);
}
}
}
If you want to Destroy it then you should make a new one with Instantiate method. If you don't want to Destroy it, one alternative is to deactivate it with SetActive like this Player1.SetActive(false); and then enable it like this Player1.SetActive(true);
Try creating a new GameObject variable called "U". And then right before destroying or in the start do U = player2.gameObject;
Now let's say you destroyed it and want to spawn. To do that we do
player2 = U.gameObject;
Instantiate(player2);
That will spawn player 2.
But as mentioned in the other answer .SetActive(false); would make things easier. Anyway if you still want to use destroy then that is how to do that. Good luck

I dont get why unity gives a compile error here

I'm trying to change the text of a TMPro text box, but unity reports at 20,79 I need a }. I put in the bracket and 11 errors pop up. debug pls? I don't know much c# myself so I can't do it myself. pls, help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class tetxmeshpro : MonoBehaviour
{ int previoushealth = 100;
private TextMeshProUGUI textMesH;
// Start is called before the first frame update
void Start()
{
var Player; GameObject;
textMesH = GetComponent<TextMeshProGUI> ();
}
// Update is called once per frame
void Update()
{
if ((Player.GetComponent(playerScript).Health)) not == previoushealth;)
{
previoushealth = Player.GetComponent(playerScript).Health;
textMesH = previoushealth;
}
}
}
What exactly is
var Player; GameObject;
supposed to do? ;)
It seems you rather mean
GameObject Player;
Either way the Player is not a field so later in Update you don't have access to it either. You probably rather wanted a
[SerializeField] private GameObject Player;
on class level so you can reference your object in it. Or even better give it the correct type right away:
[SerializeField] private playerscript player;
This way you don't need the GetComponent later.
What is this
if ((Player.GetComponent(playerScript).Health)) not == previoushealth;)
This is c# not some Shell/Dos script -> use != and not not ==. Also if conditions don't end with a ; and there is a ) too much. And also GetComponent either is generic, then it is GetComponent<T>() or you pass in a type but then you have to cast it like ((T)GetComponent(typeof(T))).
If something it should rather simply be
if (Player.GetComponent<playerScript>().Health != previoushealth)
Your class should rather look like
public class tetxmeshpro : MonoBehaviour
{
int previoushealth = 100;
// Link these via the Inspector using drag&drop
[SerializeField] private TextMeshProUGUI _textMesH;
[SerializeField] private playerscript _player;
// Start is called before the first frame update
private void Start()
{
if(!_textMesH) _textMesH = GetComponent<TextMeshProGUI>();
}
private void Update()
{
if (_player.Health != previoushealth)
{
previoushealth = _player.Health;
textMesH = previoushealth;
}
}
}
Though, note that naming your class textmeshpro a) might lead to confusions with the existing TextMeshPro and also doesn't at all reflect the purpose of that class. I would suggest something like e.g. HealthDisplay or anything similar meaningful.

Unity GameOver Screen

I am just trying to activate game over screen when the Player's activate is 'false'. There is no animation, just the
There are 3 objects that need to be active and i added the script to those 3 objects but the screen does not appear.
How can i fix?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameOverManager : MonoBehaviour
{
public GameObject _player;
void Start()
{
_player = GameObject.FindGameObjectWithTag("Player");
}
void Update()
{
if (_player.activeInHierarchy == false)
{
gameObject.SetActive(true);
}
else
{
gameObject.SetActive(false);
}
}
}
My suspicion is the following. Whenever a Gameobject is not enabled, its code does not run. Test this by adding a Debug.Log("test") message.
If no message appears you can be certain that this check is never evaluated. To work around this simply add a script that is bound to an active gameObject. Creat a new empty gameobject in the scene. And add something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameOverManager : MonoBehaviour
{
public GameObject _player;
public GameObject _endscreen;
void Update()
{
if (_player.activeInHierarchy == false)
{
_endscreen.SetActive(true);
}
else
{
_endscreen.SetActive(false);
}
}
}
Assign the Variables in the Inspector by dragging the object to the empty fields. Never use GameObject.Find Methods.
If you need any further help tell me :)
What you're currently doing is to find a single GameObject and checking whether that is active or not.
It would make more sense, and be more optimized, if each player object adds himself to a list of all players when he spawns. You can then loop over that list instead to check all players.
An even better way would be if you only call that "GameOver" check after a player has died. So when you call whichever method kills him.
Based on Franz Answer, I did some modification in the code. You won't need any if else statement if you use a shortcut.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameOverManager : MonoBehaviour
{
public GameObject _player;
public GameObject _endscreen;
void Update()
{
_endscreen.SetActive(!_player.activeInHierarchy);
}
}

Unity 3D(C#) - How to connect more than two box colliders used as triggers

The current error : NullReferenceException: Object reference not set to an instance of an object BackboardTrigger.OnTriggerEnter (UnityEngine.Collider altCollider) (at Assets/Scripts/BackboardTrigger.cs:10)
I believe my question is different from, What is a NullReferenceException, and how do I fix it?, because it requires a narrow answer. The other post provides a broad scope of what a NullReferenceException is while I need to know how to connect more than two triggers.
I'm a novice in C# and I'm attempting to recreate a simple basketball game. As of now I'm trying to trigger a score for when the ball hits the backboard first and sequentially enters the hoop. The tutorial currently teaches how to trigger a score for when the ball begins to enter the hoop and reaches near the bottom of it.
Right now when the ball enters the hoop through the first trigger(PrimaryTrigger) and sequentially through the second(SecondaryTrigger) a score is triggered.
I'm trying to cause a different scoring action for when the ball hits the backboard first.
My PrimaryTrigger Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PrimaryTrigger : MonoBehaviour {
void OnTriggerEnter(Collider collider)
{
SecondaryTrigger trigger = GetComponentInChildren<SecondaryTrigger>();
trigger.ExpectCollider(collider);
}
}
SecondaryTrigger Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SecondaryTrigger : MonoBehaviour {
Collider expectedCollider;
Collider possibleCollider;
public void ExpectCollider(Collider collider)
{
expectedCollider = collider;
}
public void PossibleCollider(Collider altCollider)
{
possibleCollider = altCollider;
}
void OnTriggerEnter(Collider otherCollider)
{
if(otherCollider == expectedCollider && otherCollider == possibleCollider)
{
ScoreKeeper scoreKeeper = FindObjectOfType<ScoreKeeper>();
scoreKeeper.IncrementScore(1);
}
else if(otherCollider == expectedCollider)
{
ScoreKeeper scoreKeeper = FindObjectOfType<ScoreKeeper>();
scoreKeeper.IncrementScore(2);
}
}
}
BackboardTrigger Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BackboardTrigger : MonoBehaviour {
void OnTriggerEnter(Collider altCollider)
{
SecondaryTrigger newTrigger = GetComponent<SecondaryTrigger>();
newTrigger.PossibleCollider(altCollider);
}
}
Any form of help would be greatly appreciated. My first game in Unity.

Editing variables with GetComponent C#

I am trying to get a conveyor belt to reverse its direction when the player presses a button.
Here's the code for the conveyor belt
using UnityEngine;
using System.Collections;
public class Conveyor : MonoBehaviour {
public float speed = 1.0f;
void OnTriggerStay(Collider col)
{
col.transform.position += transform.forward * speed * Time.deltaTime;
}
}
And the code for the button
public class PushButton : MonoBehaviour
{
public GameObject Button;
private Conveyor conveyor;
void Awake ()
{
conveyor = GetComponent<Conveyor>();
}
void OnTriggerStay(Collider entity)
{
if (entity.tag == "Player")
{
if (Input.GetKeyUp(KeyCode.E))
{
conveyor.speed = conveyor.speed * -1;
}
}
}
}
I'm getting an error saying "Object Reference not set to the instance of an object PushButton.OnTriggerStay (Unity Engine.Collider entity) (at Assests/PushButton.cs21)
I'm still not very familiar with using getComponent so I'm not sure how to fix this. Any help would be appreciated.
GetComponent will need a reference to an initialized object and a component or class that object has assigned. For what you are wanting, you will want to find a game object in the scene. Because you are already specifying that your GameObject has a Conveyor class assigned to it, find your GameObject and then specify the Conveyor component.
void Awake ()
{
conveyor = GameObject.FindWithTag("Conveyor").GetComponent<Conveyor>();
}
This should do the trick pending you have tagged your Conveyor game object with a 'Conveyor' tag.
However, there is an even easier way to quickly "grab" something like this. Be careful though!
void Awake()
{
conveyor = Object.FindObjectOfType<Conveyor>();
// ONLY DO THAT IF THERE IS ONLY >>ONE<< OF THE THING!
}
Here's a little essay on that. http://answers.unity3d.com/answers/46285/view.html
You often do that for example to find "boss" objects .. scene managers and the like.
Don't forget though, you can always just use a public variable, and drag inteh Inspector - that's always a sound idea for beginners working with Unity.

Categories

Resources