Instantiated Prefab won't instantiate with a script reference
Good Morning,
TL;DR: My prefabs aren't instantiating with the playerstatemachine script reference and I cannot drop the playerstatemachine script on the prefab when it is in the prefab folder. How to fix?
I have been slamming my head against the wall on this issue for a couple of days now. Here is the issue, in my prototype I have various things being instantiated from bullets to grenades, and even enemies. I have just now discovered that when my grenades and enemies are instantiated they are missing the reference to my PlayerStateMachine script. With out that script in the slot they grenades and enemies don't function properly. I have looked around the internet for solutions and I found that I have to put the playerstatemachine on the prefab so that the script can be called. I tried that and I get this error KeyNotFoundException: The given key was not present in the dictionary. System.Collections.Generic.Dictionary`2[PlayerStateMachine+PlayerStates,System.Action].get_Item (PlayerStates key) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/Dictionary.cs:150) PlayerStateMachine.Update () (at Assets/The Guardian/Scripts/PlayerStateMachine.cs:178)
if I put the grenade prefab or the enemy prefabs in the scene I can fill out the slot and it works as intended BUT when the instantiated items are created it doesn't run any of the functions that require the playerstatemachine reference. Here is my Grenade script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Grenade : MonoBehaviour
{
[SerializeField]
private float throwForce;
//public GameObject explosionEffect;
public float delay = 3.0f;
public float countdown;
public bool hasExploded = false;
public bool inRange;
public float radius = 5f;
public GameObject thePlayer;
[SerializeField]
PlayerStateMachine players;
// Use this for initialization
void Start ()
{
countdown = delay;
GetComponent<Rigidbody2D>().velocity = new Vector2(transform.localScale.x, 1) * throwForce;
}
// Update is called once per frame
void Update ()
{
countdown -= Time.deltaTime;
if(countdown <= 0.0f && hasExploded == false)
{
Debug.Log("My functions should run");
Explode();
hasExploded = true;
Destroy(gameObject);
}
}
public void Explode()
{
//Instantiate(explosionEffect, transform.position, transform.rotation);
inRange = Physics2D.OverlapCircle(transform.position, radius, 1 << LayerMask.NameToLayer("Player"));
if (inRange == true && hasExploded == false)
{
players.GetComponent<PlayerStateMachine>().DamageState();
players.GetComponent<PlayerStateMachine>().playerHealth -= 6;
Debug.Log("I should cause explosion damage");
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, radius);
}
}
If anyone could help me that would be amazing. Have a good day!
Kindly,
Harpoaian
I dont really know if I got it right but all prefabs you need to instantiate with the script need to have that script attached as component.
Pretty sure he just wants a variable that needs to contain a reference to the script on another object. Not having the script on the prefab itself.
Answer by ShadyProductions · Nov 30, 2017 at 01:24 PM
You make a variable:
PlayerStateMachine players;
and then you use it like this:
players.GetComponent<PlayerStateMachine>().DamageState();
players.GetComponent<PlayerStateMachine>().playerHealth -= 6;
But that's NOT how it works..
You need to use GetComponent on the gameobject that has the playerstatemachine (i'm thinking thePlayer gameobject), and then set it equal to your variable.
players = thePlayer.GetComponent<PlayerStateMachine>();
and from there on you can use it like so:
players.DamageState();
players.playerHealth -= 6;
Alright thank you so far so good! I applied the changes and I am getting places! I am now running into this issue NullReferenceException: Object reference not set to an instance of an object PlayerState$$anonymous$$achine.DamageState () (at Assets/The Guardian/Scripts/PlayerState$$anonymous$$achine.cs:837) Grenade.Explode () (at Assets/The Guardian/Scripts/Grenade.cs:69) Grenade.Update () (at Assets/The Guardian/Scripts/Grenade.cs:54) I created a variable for my player data model script and set it just like I did for my Statemachine and I tried this:
public void Explode()
{
//Instantiate(explosionEffect, transform.position, transform.rotation);
inRange = Physics2D.OverlapCircle(transform.position, radius, 1 << Layer$$anonymous$$ask.NameToLayer("Player"));
if (inRange == true && hasExploded == true)
{
data$$anonymous$$odel.ColorTimeIncrease();
players.DamageState();
players.playerHealth -= 7;
Debug.Log("I should cause explosion damage");
}
}
and I am still getting the error that I just showed you. Here is the line of code giving me the issue in my PlayerState$$anonymous$$achine script thePlayer.ColorTimeIncrease(); I would think that making a reference to the ColorTimeIncrease function in my data$$anonymous$$odel would fix this issue. Am I understanding it wrong? I hope I am making sense XD
I don't think you are setting your players variable, as described in the answer (unless you are doing it on Start(), which is what you typically should be doing). Try adding this just before the first place you reference players in the code
players = thePlayer.GetComponent();
Also make sure your 'thePlayer' and 'data$$anonymous$$odel' variables are not null.
What seems to be happening here is the reference you are using (either 'players' or 'data$$anonymous$$odel') is null. You haven't put anything into it, so Unity has no idea what to perform the action on. You can't call a method on a null reference.
I do initialize the variables in the start method here is my full code:
public class Grenade : $$anonymous$$onoBehaviour
{
[SerializeField]
private float throwForce;
//public GameObject explosionEffect;
public float delay = 3.0f;
public float countdown;
public bool hasExploded = false;
public bool inRange;
public float radius = 5f;
public GameObject thePlayer;
//[SerializeField]
PlayerState$$anonymous$$achine players;
Player data$$anonymous$$odel;
// Use this for initialization
void Start ()
{
players = thePlayer.GetComponent<PlayerState$$anonymous$$achine>();
data$$anonymous$$odel = thePlayer.GetComponent<Player>();
countdown = delay;
GetComponent<Rigidbody2D>().velocity = new Vector2(transform.localScale.x, 1) * throwForce;
}
// Update is called once per frame
void Update ()
{
countdown -= Time.deltaTime;
if(countdown <= 0.0f && hasExploded == false)
{
Debug.Log("$$anonymous$$y functions should run");
hasExploded = true;
Explode();
Destroy(gameObject);
}
}
public void Explode()
{
//Instantiate(explosionEffect, transform.position, transform.rotation);
inRange = Physics2D.OverlapCircle(transform.position, radius, 1 << Layer$$anonymous$$ask.NameToLayer("Player"));
if (inRange == true && hasExploded == true)
{
data$$anonymous$$odel.ColorTimeIncrease();
players.DamageState();
players.playerHealth -= 7;
Debug.Log("I should cause explosion damage");
}
}
I am confused @sinjimonkey would you be able to provide more clarification? Thank you by the way for helping me out :)
Seems like something is null at PlayerState$$anonymous$$achine.cs line 837 Please share the code around this line, preferably not just this line. Error messages aren't always accurate.
Here is the function in which the error pops up. thePlayer.ColorTimeIncrease(); is line 837
public void DamageState()
{
thePlayer.ColorTimeIncrease();
if(hasBeenHit == false)
{
playerHealth--;
thePlayer.TakeDamage();
hasBeenHit = true;
gameObject.GetComponent<Renderer>().material.color = Color.red;
}
if(playerHealth <= 0)
{
Destroy(gameObject);
}
else if(thePlayer.ColorTimer () >= thePlayer.ChangeGray())
{
gameObject.GetComponent<Renderer>().material.color = Color.gray;
thePlayer.ResetColorTimer();
hasBeenHit = false;
HandleHorizontalInput();
SetState(PlayerStates.RUN);
}
}
And here is the colortimeincrease function from my player data model
public void ColorTimeIncrease()
{
colorChange += Time.deltaTime;
}