reference from various scripts causing problems [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
For some reason when I'm in the normal view in-game I am able to link the scripts that I need to call in order to make an animation like so
however, whenever I start the game it automatically removes them from the slots, and it doesn't work
I am completely clueless as to why this may be happening and unity keep giving me errors that say that I'm not setting an instance to my script I really have no clue why this may be happening.
I have 3 scripts that I'm working with that is giving me the problem
one is the main script for the enemy vision (I am referencing the other scripts in this one)
the second is the enemy animation script which makes him go from idle to attack and the third is an animation for the gun model since I had to make it follow the hands of the enemy
scripts attached bellow
1st script(enemyAI):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAi : MonoBehaviour
{
public bool detected;
GameObject target;
public Transform enemy;
public GameObject Bullet;
public Transform shootPoint;
public float shootSpeed = 10f;
public float timeToShoot = 1f;
public EnemyAni Animation;
public GunAni GunAnimation;
void Start()
{
Animation = GetComponent<EnemyAni>();
GunAnimation = GetComponent<GunAni>();
}
public void Update()
{
//makes the enemy rotate on one axis
Vector3 lookDir = target.transform.position - transform.position;
lookDir.y = 0f;
//makes enemy look at the players position
if (detected)
{
enemy.LookAt(target.transform.position, Vector3.up);
enemy.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
}
if (detected == true)
{
Animation.LookPlayer = true;
GunAnimation.ShootPlayer = true;
}
if (detected == false)
{
Animation.LookPlayer = false;
GunAnimation.ShootPlayer = false;
}
}
//detects the player
void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
detected = true;
target = other.gameObject;
}
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
detected = false;
}
}
}
2nd Script (EnemyAnimation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAni : MonoBehaviour
{
public Animator animator;
public bool LookPlayer;
public void Start()
{
animator = GetComponent<Animator>();
}
public void Update()
{
if (LookPlayer == true)
{
animator.SetBool("IsShootingStill", true);
}
else
{
animator.SetBool("IsShootingStill", false);
}
}
}
3rd script (GunAnimation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunAni : MonoBehaviour
{
public Animator animator;
public bool ShootPlayer;
public void Start()
{
animator = GetComponent<Animator>();
}
public void Update()
{
if (ShootPlayer == true)
{
animator.SetBool("IsShooting", true);
}
else
{
animator.SetBool("IsShooting", false);
}
}
}

Your code:
void Start()
{
Animation = GetComponent<EnemyAni>();
GunAnimation = GetComponent<GunAni>();
}
Searches the GameObject that holds the EnemyAI script for EnemyAni and GunAni. If the GameObject doesn't have those it will return null.
Sounds like you want to remove that code.
Note that if the scripts exist on a child of that GameObject you will need to use GetComponentInChildren

There are some things to review, good practices:
GameObject target is private for C#, but it is better to put private before.
variable names like Bullet Animation GunAnimation should always begin with lowercase, because they might be confused with a Class Name (search in internet for CamelCase and PascalCase).
Name should be clear. EnemyAni Animation is not clear enough, because Animation maybe any animation (player, enemy, cube, car, etc.).
It is better enemyAnimation to be clear, as you did with GunAnimation (only just with lower case at beginning)
As slaw said, the Animation must be in the GO attached.
about last item
Let's say you have an empty GO (GameObject) called GameObject1 and you attach EnemyAi script to it.
In Runtime (when game mode begins), it will try to find Animation = GetComponent<EnemyAni>();, but it won't find it
Why?
Because GetComponent<> searches for the Component(Class) that is in the <> (in this case EnemyAni) in its GO (in this case, GameObject1), but the unique script attached to GameObject1 is EnemyAI.
So, you have 3 options:
Attach EnemyAni to GameObject1
Create another GO (for example, GameObjectEnemyAni), attach the script EnemyAni and drag and drop GameObjectEnemyAni to GameObject1 and delete Animation = GetComponent<EnemyAni>(); in Start
Keep in mind: if you leave that line of code, instead of getting the script EnemyAni from GameObjectEnemyAni, it will run the code Animation = GetComponent<EnemyAni>(); in Start, and obviously, it won't find it
Create events. It's a really good practice for avoiding code tight coupling, but that is more advanced stuff.

Related

Pause Menu won't activate when I hit escape

I've just started teaching myself coding, and I've been watching Brackey's tutorial series. I'm trying to create a Pause menu, and I've followed along with the video, but for some reason I can't get the menu to activate when I hit escape. I've double checked the code and it should be working, when compared to what's on screen. Is there something I'm missing?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PauseMenu : MonoBehaviour
{
public static bool isGamePaused = false;
public GameObject pauseMenu;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (isGamePaused)
{
ResumeGame();
}
else
{
PauseGame();
}
}
}
void ResumeGame()
{
pauseMenu.SetActive(false);
Time.timeScale = 1f;
isGamePaused = false;
}
void PauseGame()
{
pauseMenu.SetActive(true);
Time.timeScale = 0f;
isGamePaused = true;
}
}
Now that I can see the relevant code and hierarchy I believe I see the issue. In your script, you are checking for input in the Update() which is only called when the GameObject the script component is on is active in the hierarchy.
When you are toggling your pause, you are activating and deactivating the GameObject that you assign in the inspector. As the object you assigned in the GameObject that has the script, the Update() will never be called after it is disabled.
You have a few options, but the easiest one would be to drag in the reference of the panel pauseMenu as the reference object to toggle in your script instead of the Canvas object you currently have. That way the Canvas can act as a manager for the pauseMenu, toggling it when the input changes.

Im making a jump script for my game, but it won't jump

The way my code works is it checks if touching the ground, then if its true, it waits for space to get put in as an input, then if both statements are true, it uses the rigid body method to propel itself into the air.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump : MonoBehaviour
{
public string objectName;
public Rigidbody rb;
public int h;
// Start is called before the first frame update
void OnCollisionEnter(Collision col)
{
if(col.gameObject.name == objectName){
Debug.Log("On ground statement is true.");
if(Input.GetKey("space"))
{
rb.AddForce(0, h, 0);
}
}
}
}
The problem is, when i run it, it knows its on ground, but it won't respond to key inputs.
I know it can't be a problem with my computer, as my other controls work.
It's a bad idea to be testing for player input inside a collision event - if the player is pressing space, it will only ever register during the exact same frame that the object collided with something. A better place for that would be the Update() method, which happens every frame.
You should also create a variable that stores whether the player is currently on the ground or not, based on your collision events. Your input code will check this variable when deciding if the player is allowed to jump.
Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Jump : MonoBehaviour
{
public string objectName;
public Rigidbody rb;
public int h;
public bool onGround = false; // new
// get your player input in here instead
void Update()
{
if(onGround && Input.GetKey("space"))
{
rb.AddForce(0, h, 0);
}
}
void OnCollisionEnter(Collision col)
{
if(col.gameObject.name == objectName){
Debug.Log("On ground statement is true.");
onGround = true; // new
}
}
// new
void OnCollisionExit(Collision col)
{
if(col.gameObject.name == objectName){
Debug.Log("No longer on the ground.");
onGround = false;
}
}
}

Is this script resource intensive? If so, how do one improve?

This is a simple script that turns a saw blade in my game. The problem is there is approx 18 active blades on the scene, at a time. I am trying to eliminate any probability of lag. This made me wonder if using such a script in "Update", can cause lag?
public class SawBladesRotate : MonoBehaviour
{
public bool GameOver;
public GameObject Player;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
GameOver = Player.GetComponent<PlayerController>().GameOver;
if(GameOver == false)
{
transform.Rotate(new Vector3(0, 0, -45) * Time.deltaTime);
}
}
}
Put this on top of the Start method as class field
private PlayerController playerController;
and this into Start:
playeController = Player.GetComponent<PlayerController>()
Then re-use the reference:
private void Update()
{
if(playerController.GameOver) return;
//...
}
The rest is fine but ofcourse it always depends completely on your usecase.
Even more efficient it would be to directly reference the Component within unity:
[SerializeField] private PlayerController playerController;
Now you can simply drag&drop the Player GameObject into that field in the Inspector and can get rid of the GetComponent call.

How do i get object position and transform position to it

How do i localize other GameObject position
and Move to it (like teleport to that object) how can i do it?
BotController.Player
it's object what i want to get position of it and move to it (teleport)
i work on game hack
In case you already have reference to the target object you can just use this.position = BotController.Player.position anywhere in the object you wanna teleport.
public class EasyTeleporter : MonoBehavior
{
...
public void SomeFunction()
{
position = BotController.Player.position
}
}
If you are creating first person game and want to achieve something like teleporter to any object you should use raycasting for that.
For example you can take Unity default asset FirstPersonCharacter (available on asset store or you can add it when starting a new project) and add the following script to the FirstPersonCharacter gameobject (wich is child of FPSController prefab):
using UnityEngine;
using System.Collections;
public class PlayerTeleporter : MonoBehaviour
{
bool shooting = false;
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
shooting = true;
}
}
void FixedUpdate()
{
if (shooting)
{
shooting = false;
RaycastHit hit;
// you are casting a ray in front of your camera wich hits the first collider in its path
if (Physics.Raycast(transform.position, transform.forward, out hit, 100f))
{
// normally you shouldn't teleport directly into the trget object
transform.position = hit.transform.position;
}
}
}
}
Generally you should clarify you question. What game you are creating, what are the target and object of teleportation and how do you wanna trigger that.

boolean from another script in Unity3D

So I've got two scripts, one is called EndCollider.cs it's somewhere on the map,
It have one OnTriggerEnter function which set a boolean to true.
using UnityEngine;
using System.Collections;
public class EndCollider : MonoBehaviour {
public bool isShow = false;
void OnTriggerEnter ()
{
isShow = true;
}
}
the other script is SlowDownRun.cs, it's on a monster object, in this script I'm trying to detect if the boolean from another script is true or not, if it is then move the monster.
using UnityEngine;
using System.Collections;
public class SlowDownRun : MonoBehaviour {
GameObject TrollScript;
EndCollider MonsterShow;
// Use this for initialization
void Start ()
{
TrollScript = GameObject.Find("Troll");
MonsterShow = TrollScript.GetComponent<EndCollider>();
}
void Update()
{
if (MonsterShow.isShow == true)
{
float movementSpeed = 10f;
transform.position += transform.forward * Time.deltaTime * movementSpeed;
}
}
}
Now the code doesn't work, the code where move the monster works if I take them out the IF statement. I also keep getting this error NullReferenceException: Object reference not set to an instance of an object on the line if (MonsterShow.isShow == true)
Please help I'm a beginner to Unity and this problem stuck on me for so many hours now, I've done a lot Google searches and modified my code again and again but just can't solve this problem, I feel anxious and just don't know what to do.
Your find troll call in the Start() method is not finding the troll or not finding the EndCollider component on it, resulting in it being null. And since you only search once at startup it will always be null.
This is probably because your troll is not yet created at the start of the scene.
If your intent is actually to make the monster appear when the player hit the collider, you should look at Prefab Instantiation.
http://docs.unity3d.com/Manual/InstantiatingPrefabs.html
So in your OnTriggerEnter, you can simply instantiate a brand new monster:
public class EndCollider : MonoBehaviour {
public GameObject MonsterPrefab;
void OnTriggerEnter ()
{
Instantiate(MonsterPrefab);
}
}
One better way of communicating data between two scenes might be using PlayerPrefs:
//EndCollider.cs
public bool isShown
{
get { return PlayerPrefs.GetInt("IS_SHOWN")==1? true:false; }
set { PlayerPrefs.SetInt("IS_SHOWN", Convert.ToInt32(value));}
}
//SlowDownRun.cs
if(PlayerPrefs.GetInt("IS_SHOWN")==1) // isShown = true
{
...
}
Make masterShow endcollider public
clean your start method.
Just drag and drop the troll object from hierarchy to your script.
public class SlowDownRun : MonoBehaviour {
public EndCollider MonsterShow;
// Use this for initialization
void Start ()
{
}
void Update()
{
if (MonsterShow.isShow == true)
{
float movementSpeed = 10f;
transform.position += transform.forward * Time.deltaTime * movementSpeed;
}
}
}

Categories

Resources