I can't assign script from another object. Is there smth wrong? - c#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeteorRangeController : MonoBehaviour
{
[SerializeField] PlayerController PlayerController; // It has to be script
[SerializeField] GameObject Player; // no problem with assigning this
[SerializeField] Transform playerTransform;
[SerializeField] Vector3 playerPos;
[SerializeField] bool canGo;
[SerializeField] GameObject meteor;
// Start is called before the first frame update
void Start()
{
playerTransform = Player.GetComponent<Transform>();
PlayerController= Player.GetComponent<PlayerController>(); //*The Problem Is Here*
//PlayerController Cannot Be Assigned
}
// Update is called once per frame
void Update()
{
print(PlayerController.IdleTime); //to test it I printed
playerPos = Player.transform.position;
//I tried to take idle time from Player controller here.
if (PlayerController.IdleTime > 2)
{
canGo = true;
}
else
{
canGo = false;
}
if (canGo == true)
{
transform.position = new Vector3(playerPos.x, playerPos.y, playerPos.z);
gameObject.SetActive(true);
Invoke("SetActiveFalse",2f);
}
}
void SetActiveFalse()
{
gameObject.SetActive(false);
meteor.SetActive(true);
Invoke("MeteorSetActiveFalse", 2f);
}
void MeteorSetActiveFalse()
{
meteor.SetActive(false);
}
}
I can't assign it.
I researched it but I couldn't find anything different.
I know it's a very easy question, also I assigned game object player, in it there is a script named PlayerController, and there is a variable named idle time please help me

Okay, that's my first try to answer on topic on Stack Overflow, so hope you will be patient to my mistakes.
Can you say, are you sure, that Player object has a PlayerController script on him?
I think you must use
var isControlSetted = Player.TryGetComponent<PlayerController>(out PlayerController);
instead of:
PlayerController= Player.GetComponent<PlayerController>();
Why? So, that can answer on question №1 and after it you can control the logic of working with it when (use AddComponent for example or throw an exception)
3. Okay, even that can't fix your problem if PlayerController setted on inheritor or ancestor of Player gameobject in gameobject hierarchy
I hope I helped you
Edited:
Okay, I was asked to make answer some clearly, so...
First of all, can you show PlayerController script, pls?
Secondary, why I'm asking to use TryGetComponent instead of GetComponent bcs as I commented below getting null value is not a good practice, but if you want - use it.
Let's look on script more closly
I refactored this pls look
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeteorRangeController
: MonoBehaviour
{
[SerializeField] PlayerController playerController;
[SerializeField] GameObject playerObject;
[SerializeField] Transform playerTransform;
// [SerializeField] Vector3 playerPos; we don't need this
// [SerializeField] bool canGo; and this
[SerializeField] GameObject meteorObject;
/// <summary>
/// Method that called once we use this or Awake for init our fields/props
/// </summary>
void Start()
{
playerTransform = playerObject.GetComponent<Transform>(); // there no need to use TryGetComponent bcs Transform is req. for every GO/MB
playerController= playerObject.GetComponent<PlayerController>(); // there you could use TryGC, but if you use RequireComponentAttribute that's ok
}
/// <summary>
/// Method that's calls every frame
/// </summary>
void Update()
{
if (playerController.IdleTime <= 2)
return;
transform.position = playerTransform.position;// i really can't understand what hepenning after this so i stop
gameObject.SetActive(true);
Invoke(nameof(SetActiveFalse),2f);
}
void SetActiveFalse()
{
gameObject.SetActive(false);
meteorObject.SetActive(true);
Invoke(nameof(MeteorSetActiveFalse), 2f);
}
void MeteorSetActiveFalse()
{
meteorObject.SetActive(false);
}
}
So, afterwards GO.GetComponent can return null only if the requested GO did not have a Monobehaviour script.
You can check similar question there https://forum.unity.com/threads/getcomponent-doesnt-works.516839/

Related

Unity: Even when the "player" game object gets destroyed, the text doesn't appear

I've been using Unity to create a simple 2D game but the problem is that even when the game object "player" gets destroyed, the gameobject "isDead" (text) doesn't appear.
This is my script.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class youDied_Text : MonoBehaviour
{
private Transform player;
private Text isDead;
// public static bool isDead;
// Start is called before the first frame update
private void Start() {
isDead = GetComponent<Text>();
}
void checkForDeath()
{
if (player==false)
{
isDead.gameObject.SetActive(true);
}
else
{
isDead.gameObject.SetActive(false);
}
}
// Update is called once per frame
void Update()
{
player = GameObject.FindWithTag("Player").transform;
checkForDeath();
}
}
This script is attached in the text which I need to display in UI element.
As was noted currently you would get a NullReferenceException which is definitely not what you want.
There is absolutely no need / redundancy going through Transform at all actually. Simply store the GameObject reference instead
You are currently setting the object to inactive which has the Text attached ... which is the same object your component is attached to as well!
=> As soon as you end up in the second case once you set it to inactive => from now on Update is never called anymore!
In general as it sounds like this should only happen once anyway I would use a more event driven approach and have a component on your player like e.g.
public class Player : MonoBehaviour
{
public UnityEvent onDied;
private void OnDestroy ()
{
onDied.Invoke();
}
}
And then simply attach a listener/callback to that event once without poll checking states. You can do this either via the Inspector directly (just like in e.g. Button.onClick) or via code like e.g.
public class youDied_Text : MonoBehaviour
{
// Already reference things via the Inspector if possible!
[SerializeField] private GameObject player;
[SerializeField] private Text isDead;
private void Awake()
{
if(!isDead) isDead = GetComponent<Text>();
isDead.gameObject.SetActive(false);
// If you want to rather set it via the Inspector remove all the rest here
//if(!player) player = GameObject.FindWithTag("Player"). GetComponent<Player>();
// or even simpler
if(!player) player = FindObjectOfType<Player>();
player.onDied.AddListener(OnPlayerDied);
}
// If you want to rather set it via the Inspector make this public
private void OnPlayerDied()
{
isDead.gameObject.SetActive(true);
}
}

How do i add the objects that the raycast is hitting in unity and add them to a list

I have this problem that i just can't figure out how to solve.
I am trying to make a game in Unity, and i have stumbled across a problem that goes like this.
I want to put an object in the scene in a list, when i hit it with a raycast.
With what i have tried so far. Either it puts everything that is tagged the same thing on the list when i press mousebutton on one of the objects, or it only puts in the same thing (Cube in this example).
My code is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTagged : MonoBehaviour
{
public float damage = 10f;
public float range = 100f;
public Camera fpsCam;
public List<GameObject> playersTagged;
private void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
}
void Shoot()
{
RaycastHit hit;
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Target target = hit.transform.GetComponent<Target>();
if (target != null)
{
target.takeDamage(damage);
if(hit.collider.tag == "Taggable")
playersTagged.Add(GameObject.FindWithTag("Taggable"));
}
}
}
}
use
playersTagged.Add(hit.collider.gameObject);
Instead of using GameObject.FindWithTag() which returns the first object it'll find source
Use gameobject property playersTagged.Add(target.gameobject). Since all components store the reference to the gameobject they are attached to.
You are adding GameObject.FindWithTag("Taggable") which means you are getting any GameObject that has that tag and adding it to the list. But instead, you want to add the hit object, so you need to add hit.collider.gameObject.

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.

How can I respawn an object in Unity?

I am working on a 3d basketball game in unity.
It has a score and a timer. After a certain time my scene loads and starts from the beginning. Every time I throw the ball, I have to spawn it.
It has a spawn button and a shoot button. But I don't want to use the spawn button. So I want to spawn the ball automatically.
How should I do it? I am giving my spawn button code and throw button code bellow.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnButton: MonoBehaviour
{
public GameObject ball;
public GameObject BallPosition;
public void Spawn()
{
ball.transform.position = BallPosition.transform.position;
var ballPosition = ball.transform.position;
ball.GetComponent<Rigidbody>().useGravity = false;
ball.GetComponent<Rigidbody>().velocity = Vector3.zero;
ball.transform.position = ballPosition;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ThrowButton: MonoBehaviour
{
static Animator anim;
public GameObject ball;
public float ballThrowingForce = 5f;
internal bool holdingBall;
void Start()
{
anim = GetComponent<Animator>();
ball.GetComponent<Rigidbody>().useGravity = false;
}
public void Throw()
{
anim.SetTrigger("isThrowing");
StartCoroutine(Test());
}
IEnumerator Test()
{
yield return new WaitForSeconds(1.5f);
ball.GetComponent<Rigidbody>().useGravity = true;
//ball.GetComponent<Rigidbody>().AddForce(transform.up * ballThrowingForce);
ball.GetComponent<Rigidbody>().AddForce(0, 380.0f, ballThrowingForce);
}
}
To spawn a ball you should create a prefab and instantiate it. Example:
private class Spawner : MonoBehaviour
{
public GameObject prefab;
public GameObject Spawn() => Instantiate(prefab);
}
So that your throw code should spawn a ball and if you want destroy an older one.
The above answer by Iv Misticos mentioned the use of Instantiate which is a great way to spawn a new ball.
before you spawn a new ball, you need to specify when it must spawn.
Either you can
use a Invoke("SpawnNewBallMethod",3) //after this piece of code is executed, it waits for 3 seconds and executes the method you mention in it which can have the code to instantiate.
Or After the first ball's work is done, you can destroy it(Destroy(ball)) or set active to false - Then check the state (if it exists(null check?) or gameObject.active==true) and accordingly instantiate a new ball (if it is setactive(false) don't forget to destroy it later).
Although gameObject.active is obsolete, it will serve the purpose without complicating things.
In my opinion, Invoke is better than a IEnumerator here as Invoke will not stop the execution flow and continue with the other things while the timer is running.

OnTriggerEnter() is called right on the start

I'm making some sort of a Evolution simualtor game. I have a script that is supposed to destroy the GameObject it's attached to when a creature's CapsuleCollider Triggers the OnTriggerEnter().
I have a problem that even tho the Creature's collider isn't even close to the Food, it still destroys the GameObject.
My script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FoodEat : MonoBehaviour
{
public GameObject FoodGO;
public Rigidbody FoodRB;
private void OnTriggerEnter(Collider Creature)
{
Destroy(FoodGO);
}
void Start()
{
FoodRB = GetComponent<Rigidbody>();
FoodGO = FoodRB.gameObject;
}
void Update()
{
Rigidbody[] allRigidBodies = (Rigidbody[])FindObjectsOfType(typeof(Rigidbody));
foreach (Rigidbody body in allRigidBodies)
{
if (body.gameObject.layer == 10)
{
OnTriggerEnter(body.gameObject.GetComponent<CapsuleCollider>());
}
}
}
}
OnTriggerEnter is a monobehaviour lifecycle method. You should not call it from your own code; it will automatically be called when it detects collisions.
Furthermore, the logic in your code right now seems to be incorrect, it is...
"Every frame, loop through all rigidbodies in the scene and if 1 is found on layer 10, destroy the FoodGO"
Simply remove your entire Update method and put an if in your Collision method, and it should work:
[RequireComponent(typeof(Rigidbody), typeof(Collider))]
public class FoodEat : MonoBehaviour
{
private void OnTriggerEnter(collider other)
{
Debug.Log(other.gameObject.name + " on layer " + other.gameObject.layer);
if (other.gameObject.layer == 10)
Destroy(this.gameObject);
}
}
A few noteworthy edits of your code:
I removed FoodGO, since it's the GameObject this script is attached to, you can access it by just writing gameObject or this.gameObject.
I remove the Rigidbody reference since it is not used anymore, and thus the entire Start() method.
Since this code requires a Rigidbody and a Collider to work, I added a [RequireComponent] attribute in the top, which will make Unity tell you if you forgot to add those components on the object you attach this script to.
I added a Debug.Log that prints the name & the layer on the creature that collides with the food, so you can debug and make sure it is working as expected

Categories

Resources