How can I access the component of an instantiated object in Unity? - c#

Ok so here's what's happening
I have this "Bullet" script with an "ApplyForce" method, attached to a Bullet prefab
public void ApplyForce (Vector2 direction)
{
const float magnitude = 5f;
GetComponent<Rigidbody2D>().AddForce(magnitude * direction, ForceMode2D.Impulse);
}
And a "Ship" script that will instantiate a Bullet and try to access its "Bullet" component to then access the "ApplyForce" method to apply a force in the same direction the ship is facing
// check for shooting input (left ctrl key) and fire
if (Input.GetKeyDown(KeyCode.LeftControl))
{
GameObject bullet = Instantiate(bulletPrefab, transform.position, transform.rotation);
bullet.GetComponent<Bullet>().ApplyForce(thrustDirection);
}
Problem is, it doesn't work. It sends this error:
NullReferenceException: Object reference not set to an instance of an object
Ship.Update () (at Assets/scripts/Ship.cs:56)
I get that my problem is how to properly access the newly instantiated GameObject, but I couldn't find a proper way to do it. So here I am asking for your assistance.
I apologize for any newbie mistake that may anger you as I am fairly new to coding/programming and have next to 0 experience with this

Ok guys I found the problem: I am an absolute idiot! :D
I forgot to attach the Bullet script to the Bullet prefab

which line is line 56? one common thing you can try is:
bullet.GetComponentInChildren<Bullet>().ApplyForce(thrustDirection);
if that still doesn't work and that is line 56, make sure that the 'thrustDirection' variable is not null.

Related

Cannot convert vector3 to collider?

I'm trying to make a 3D racing game on Unity and I am currently looking to program checkpoints. Right now I have hit a roadblock in which I can't convert vector3 to collider. Any advice?
private void OnTriggerEnter3D(Collider Checkpoint)
{
if (Checkpoint.tag == "Checkpoint")
{
Checkpoint = transform.position;
}
}
Checkpoint is a Collider data type. Try it with Checkpoint.transform.position
A Collider is not at all a Vector3. I invite you to read the documentation for both types to understand better what each is used for.
As mentioned in another comment, if you want to access a Collider position, you should use Collider.transform.position (so
Checkpoint.transform.position
in your case). A Collider component should be attached to the object your rigidbody is supposed to collide to.
If you want the Collider to respond phisically to impacts, as mentioned in the documentation, you should attach a Rigidbody to it.

Unity Physics.Raycast with LayerMask does not detect object on layer. Used bitshifting, tried inverting layer, still nothing works

novice to intermediate Unity developer here. I've been hitting a pretty significant roadblock the past ~2 days concerning the raycast detection of objects with specific layers. I've been researching this issue quite a lot, and all the solutions I've found don't seem to reflect the strange issue I'm facing.
Basically, the problem follows this sequence of events:
My player character has a vision cone shaped trigger mesh called 'InSightBox' which detects all objects with the tag 'Mob' and adds them to a List of colliders called 'MobsInRange'.
public List<Collider> mobsInRange;
public List<Collider> GetColliders()
{
return mobsInRange;
}
// Start is called before the first frame update
void Start()
{
mobsInRange = new List<Collider>();
}
// Update is called once per frame
void Update()
{
}
//add enemy with tag 'mob' to list
private void OnTriggerEnter(Collider other)
{
if(!mobsInRange.Contains(other) && other.tag == "Mob")
{
mobsInRange.Add(other);
}
}
//remove enemy with tag 'mob' to list
private void OnTriggerExit(Collider other)
{
if (mobsInRange.Contains(other) && other.tag == "Mob")
{
mobsInRange.Remove(other);
}
}
This list is then fed up to the root/parent player game object containing everything relating to the player.
public Transform closestMob;
public List<Collider> mobs;
public Transform GetClosestEnemy()
{
Transform tMin = null;
float minDist = Mathf.Infinity;
Vector3 currentPos = transform.position;
foreach(Collider trans in mobs)
{
//find enemy with closest distance and set tMin to it. Method returns tMin
float dist = Vector3.Distance(trans.transform.position, currentPos);
if(dist < minDist)
{
tMin = trans.transform;
minDist = dist;
}
}
//Debug.Log(tMin);
return tMin;
}
The player then uses a 'Look at' method to find the closest of all 'mobs' to the player. The player will set their forward transform to look at the closest mob.
Problem Step ---> 4) When the player raises their gun and attempts to shoot the closest enemy, a ray is cast with a layermask that looks only for objects on the layer 'Enemy', the 8th layer. When the ray detects the enemy, the enemy script should fire its 'TakeDamage' method which decreases the 'curHealth' variable by 8. Only problem is, the cast doesn't seem to detect the enemy object on the 'Enemy' layer.
LayerMask layerMask = 1 << 8;
void Fire()
{
//play the audio of the gunshot
StartCoroutine("SetPlaying");
RaycastHit hit;
//cast a ray from the player forward and check if the hit object is on layer 'Enemy'
if (Physics.Raycast(transform.position, transform.forward, out hit, Mathf.Infinity, layerMask))
{
//if hit object is an enemy, set its 'gotShot' bool to true
print("hit enemy");
closestMob.GetComponent<EnemyBase>().gotShot = true;
}
//play gunshot sound and stop player from turning
source.clip = fireSound;
source.PlayOneShot(fireSound, gunshotVolumeScale);
turnSpeed = 0;
}
I'll also note that all the solutions I've seen to this issue are not working for me. I declared an int variable called 'layerMask' and initialized it in Awake() by bit shifting layer 8 into it (i.e. int layerMask = 1 << 8), but it still isn't detecting it. The enemy contains all that I belive it should need for this to work, including a rigidbody, a capsule collider, the associated scripts, as well as being on the 'Enemy' layer.
This is where it gets weird (at least to my knowledge), when I invert the mask in the cast (~layerMask), it does exactly what I'd expect, and begins firing the code within the raycasts if statement when the player 'shoots' anything that doesn't have the 'Enemy' layer.
Any help would be suuuper appreciated as I'm getting to the point of slamming my face into the desk :/
Side Note: I'm getting to the point where I may just attach a 'fire range' cube trigger to my player and enabled it when the Fire() event is triggered, then have that check for game objects with the tag 'mob' as that kind of detection works most consistent to me.
First, be sure that you didn't confuse layers with tags. They are different.
Second, get rid of any implicit actions and references, i.e. don't use bitshifting, layer indices, or any non-straightforward reference. Instead, create something like this:
[SerializedField] private LayerMask _layerMask;
Use inspector to assign needed layer(s).
This way you will explicitly see, which layer you are using. This is useful not only for you, but for you in future, when you forget layers' indices. Also, for anyone who aren't familiar with the project.
Third is for debug. Be sure that your raycasting works as intended at other aspects:
Try remove layerMask and see if the ray goes where you want it to
Use custom gizmos to check if you cast the ray in a right direction
Try using RaycastAll. Maybe some objects catch (block) your ray earlier than you think
Looks like the issue was that I had my 'Enemy' prefab which contained the necessary components (RigidBody, Collider, scripts, NavmeshAgent) nested inside an empty game object. I thought at first to make something a prefab you needed to have it inside an Empty. I see now that is rather redundant and not necessary (at least in my case).
Physics.RaycastAll solved this issue as it no longer got 'halted' by the parent empty's collider and also hit the child.
I actually got it working just using a regular Physics.Raycast by rebuilding the Enemy prefab as just a single Capsule object (which I'll replace with character meshes later on).
Side Note
Before I got this new method working, I also used a different one that achieves the same goal in a pretty lightweight manner.
I added a long and thin box trigger to the front of my player so that it has enough distance to collide with any enemies within the shooting range.
I then enable the trigger any time the player enters their 'Aiming' state. If the trigger collides with any mesh that has the tag "Mob', it sets a bool on the player that indicates if the enemy is in range.
Then if the enemy is in range && the player enters the 'Firing Gun' state, a method in the Enemy Base script is fired that decrements the enemy health by a publicly decided variable called damagetaken.
Both the Raycast method and the box trigger method work equally well for me, but the box trigger one just took less time to figure out and gave me less headache haha.
Hope this helps anyone else in a bind!!!!!

In Unity2D, GameObject gets instantiated but does not trigger what it is supposed to do when collided with (sometimes)

I have a health GameObject that has a chance to get instantiated when destroying an enemy. Most of the time, it heals the player, plays the sound effect, etc. In other words, it behaves the way it is supposed to. However, sometimes (maybe 1 out of 10 times) when running into the instantiated object, it will play the sound effect but will not heal the player and it will not get destroyed. The error that accompanies this is:
MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Object.Internal_InstantiateSingle (UnityEngine.Object data, Vector3 pos, Quaternion rot)
UnityEngine.Object.Instantiate (UnityEngine.Object original, Vector3 position, Quaternion rotation) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.bindings.cs:211)
UnityEngine.Object.Instantiate[GameObject] (UnityEngine.GameObject original, Vector3 position, Quaternion rotation) (at C:/buildslave/unity/build/Runtime/Export/UnityEngineObject.bindings.cs:285)
Heart.OnTriggerEnter2D (UnityEngine.Collider2D collision) (at Assets/Scripts/Heart.cs:36)
I've been trying to figure out for quite some time what is happening and I can't seem to pinpoint the issue. Under the health up object where the error is pointing to is (error is actually pointing to the line where the tempPickupEffect is getting instantiated):
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.name == "Player_Ship")
{
AudioSource.PlayClipAtPoint(receiveSound, Camera.main.transform.position, 0.5f);
GameObject tempPickupEffect = Instantiate(pickupEffect, transform.position, transform.rotation) as GameObject;
tempPickupEffect.GetComponent<ParticleSystem>().Play();
Destroy(tempPickupEffect, 3f);
heal = true;
Level1.playerHealth += healAmount;
Destroy(gameObject);
}
}
Under my enemy script, I have a method named spawnHealthUp() being called when the enemy's health is at or below zero. This is the method:
private void spawnHealthUp()
{
chance = Random.value;
if (chance <= healthSpawnChance)
{
GameObject copyHealthUp = Instantiate(healthUp, transform.position, Quaternion.identity) as GameObject;
copyHealthUp.GetComponent<Rigidbody2D>().velocity = new Vector2(-healthSpeed, 0f);
}
}
Thank you in advance for any insight into this anyone can offer.
Quite a few things can be happening in your case, but I would say that either your "pickupEffect" variable gets invalidated somehow, or this is happening because of the call to:
tempPickupEffect.GetComponent<ParticleSystem>().Play();
I'll break down both cases in more details below.
First, you can add a check to see if the "pickupEffect" variable is being set no null or some script is destroying that GameObject/prefab (by calling Destroy() on it). Add this check right before you call Instantiate(pickupEffect, ...) inside your OnTriggerEnter2D(...) method. This can be done by comparing pickupEffect == null (the == operator is overriden and will check if the GameObject/prefab has been destroyed, as per sugested in this answer).
If the object is getting destroyed, a simple way to detect where it is being destroyed is by doing the following: create a small script containing the OnDisable() function, and add a Debug.Log() call with any text on it. The Debug.Log() call will log your text, and will also add a stack trace to the log message, which you can use to verify where the call to the Destroy() function has been made (and thus discover where your GameObject is being destroyed).
// This script should be added to the "pickupEffect" gameobject
void OnDisable()
{
Debug.Log("Object destroyed!");
}
When te object gets destroyed, an output similar to the following will be generated:
Object destroyed!
UnityEngine.Debug:Log(Object)
MyObjectBeingDeleted:OnDisable() (at
Assets/MyObjectBeingDeleted.cs:10) UnityEngine.Object:Destroy(Object)
MyDestroyerObject:Update() (at Assets/MyDestroyerObject.cs:11)
The stack trace above tells me that the Destroy() method has been called on line 11 of the MyDestroyerObject.cs script.
On the other hand, the problem can actually be in this part of your code:
tempPickupEffect.GetComponent<ParticleSystem>().Play();
Destroy(tempPickupEffect, 3f);
Here you are commanding the ParticleSystem to play, but right after you emit that command, you are destroying the GameObject which contains that Particle System. This is actually a bad practice (and might eventually cause exceptions that are hard to debug). I recommend you to create a script with an Update() method which checks if the Particle System has ended its execution, and destroys the object after that execution has ended.

Method in Script is never called

I have a Projectile Script attached to a GameObject. Now i want to access the LinearProjectile() method in Projectile Script from Script A attached to GameObject A. Here i made a public field of type Projectile in Script A and passed in one GameObject where Projectile Script is attached.
To access LinearProjectile() method i just did.
projectile.LinearProjectile(transform.position, transform.rotation, this.direction, this.shotPower);
//Here is my method defination..
public void LinearProjectile(Vector3 position, Quaternion rotation, Vector2 direction, float force)
{
Debug.Log("called");
this.direction = direction;
this.force = force;
this.projectileType = ProjectileType.Linear;
}
All the field value assigned in this method are in default state since this method is never called. How can I access this method from other GameObject Script.
I tried GetComponent().method() but even though i can't acess what is the error here? i spent alot of time to find out but no success.. Help me out
Provided a scenario like this:
We have a GameObject in scene, let's call it Item
We have ProjectileScript attached to Item
We have a A script attached to Item
All we need to do is to call inside A script:
gameObject.GetComponent<ProjectileScript>().LinearProjectile();
If it's on another GameObject, I would personally create field in script A to be used in Inspector, for example: public GameObject ProjectileScriptHolder and then just drag GameObject from scene that holds ProjectileScript into that variable in Inspector and the access it that way:
ProjectileScriptHolder.GetComponent<ProjectileScript>().LinearProjectile();
I would also check every GetComponent<T> before calling method as it might return null, ie:
ProjectileScript script = ProjectileScriptHolder.GetComponent<ProjectileScript>();
if (script != null)
script.LinearProjectile();
If you can't attach item through Inspector, you can use FindWithTag() and find GameObject in scene provided it has proper Tag attached. FindWithTag() takes string as parameter and will look for GameObject with such tag in scene.
I understand that you want to access a script from another script on another gameobject!
Way 1
There is a simple way to do that, caching the script. (Assuming the name of the script is: "ProjectileScript")
On ScriptA you create a
public ProjectileScript pS;
and you need to initialise it, there is two ways:
1- on the inspector of GameObjectA, drag the GameObject with the projectileScript.
or
2.1- if the script is in the same game object:
pS = GetComponent<ProjectileScript> ();
2.2- if it is on another gameobject you might need to find this object somehow. Consider using a tag
pS = GameObject.FindGameObjectWithTag("projectile").GetComponent<"ProjectileScript">();
And cast it inside Script A:
pS.LinearProjectile();
Way 2
Or you can make a generic tag on the GameObject with the ProjectileScript and find it that way:
GameObject.FindGameObjectWithTag("projectile").GetComponent<"ProjectileScript">.LinearProjectile();
This make sense for you? Sorry my english.

Teleporting player object on collide not working

I'm trying to teleport my player to 0,1,0 on the coordinate system (right above the origin) when it hits any object with the "Death" tag.
Resetting the level works fine with this, but I want it to just teleport, so I tried this. I've searched many tutorials, but I just couldn't get this to work.
Any tips on what could be wrong?
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Death"))
{
gameObject.transform.position = new Vector3(0.0f,1.0f,0.0f);
}
}
(Note that the player just falls through the object right now)
Are you using gravity on Rigidbody component on your player object? turn it off and check the behaviour.
Found the problem, apparently there was some weird randomly assigned script that didn't exist on my death ground object.

Categories

Resources