Placed Prefab is behaving differently to Instantiated Prefab

Hi there! First question on the forums!
Im currently making a game in which waves of enemies are sent to the player.
My most recent enemy has a more complex AI script then all of my other enemies, which work completely fine when instantiated.

my Reaper enemy has 3 behaviors: chase, aim, charge.
When placed in the scene the 3 behaviors act normally, however when I instantiate Reapers, then seem to be stuck on the chase behavior

Any Help would be greatly appreciated! <3

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ReaperMovement : MonoBehaviour
{


    [Header("references")]
    public GameObject target;
    public Rigidbody2D rb;



    private Vector3 CursorPosition;

    [Header("Variables")]

    public float maxSpeed;
    public float accelerationSpeed;
    public float Damage;
    public float ChargeForce;

   

    //Aiming flicker
    private Color DefaultColour;
    public SpriteRenderer sprite;

    private void Start()
    {
        target = GameObject.FindGameObjectWithTag("Player");
        DefaultColour = sprite.GetComponent<SpriteRenderer>().color;
        StartCoroutine(Think());
    }

    private void Update()
    {


    }


    //Deal damage to player
    private void OnCollisionEnter2D(Collision2D collider)
    {


        if (collider.gameObject.CompareTag("Player") && collider.gameObject.name == "Player")
        {
            //Debug.Log(collider.transform.name);

            Health enemy = collider.transform.GetComponent<Health>();
            if (enemy != null)
            {
                enemy.TakeDamage(Damage);
            }

        }
    }

    public enum AIState { Chasing, Aiming, Charging};

    public AIState aIState = AIState.Chasing;

    IEnumerator Think()
    {
        while (true)
        {
            switch (aIState)
            {
                case AIState.Chasing:
                   
                   
                        Vector3 direction = target.transform.position - transform.position;
                        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
                        transform.rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);


                        float distanceToPlayer = Vector3.Distance(target.transform.position, transform.position);



                        if (distanceToPlayer > 0.01f)
                        {
                            rb.AddForce(transform.up * accelerationSpeed, ForceMode2D.Force);
                       
                    }
                        if (distanceToPlayer <= 6f)
                        {
                            aIState = AIState.Aiming;
                        Debug.Log("IN RANGE");
                        }
                   
                  
                    break;
                case AIState.Aiming:


                    int i = 5;
                    while (i > 0)
                    {
                        sprite.color = new Color(1f, 1f, 1f, 1f);
                        yield return new WaitForSeconds(0.1f);
                        sprite.color = DefaultColour;
                        yield return new WaitForSeconds(0.1f);

                        direction = target.transform.position - transform.position;
                        angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
                        transform.rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);

                        i--;
                        yield return new WaitForSeconds(0.25f);
                    }


                   
                    aIState = AIState.Charging;




                    break;
                case AIState.Charging:

                   
                        rb.AddForce(transform.up * ChargeForce, ForceMode2D.Force);
                   
                   
                    yield return new WaitForSeconds(0.75f);


                    aIState = AIState.Chasing;
                   
                    break;



                default:
                    break;
            }



            yield return new WaitForSeconds(0.3f);
        }
    }



}

Staring at a wall of code won’t be useful when you say it works in one case and not in the other.

From that it sounds like some assumption in the code or something else about the scene is making it fail.

Here is how to debug:

You must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

https://discussions.unity.com/t/839300/3

1 Like

Hey! thanks for the help, I did what you said and found that this If statement

if (distanceToPlayer <= 6f)
                        {
                        Debug.Log("Changing State");
                            aIState = AIState.Aiming;
                        Debug.Log("IN RANGE");
                        }

is not running in the instantiated prefab, however it does run in the placed prefab

That’s awesome… but don’t stop now! KEEP PRINTING!!

  • What value does distanceToPlayer have?

  • What values went into calculating that quantity?

  • Which of those input values differ between prefab vs scene?

  • Investigate each of the differences!

This is the kind of chain-of-investigation… never stop when you reach the next link. And the next…

I actually got it working! Thank you for helping me troubleshoot, I have been a bit of a tutorial Andy, so I dont have too much experience with problem solving <3 :slight_smile:

Oh no, yikes, I hope you haven’t wasted too much time in that mode!

Pay special attention to Step #2 below. Without it you might as well just go weave baskets out of river reeds because you’re certainly not going to learn anything without step #2!

Tutorials and example code are great, but keep this in mind to maximize your success and minimize your frustration:

How to do tutorials properly, two (2) simple steps to success:

Tutorials are a GREAT idea. Tutorials should be used this way:

Step 1. Follow the tutorial and do every single step of the tutorial 100% precisely the way it is shown. Even the slightest deviation (even a single character!) generally ends in disaster. That’s how software engineering works. Every step must be taken, every single letter must be spelled, capitalized, punctuated and spaced (or not spaced) properly, literally NOTHING can be omitted or skipped.

Fortunately this is the easiest part to get right: Be a robot. Don’t make any mistakes.
BE PERFECT IN EVERYTHING YOU DO HERE!!

If you get any errors, learn how to read the error code and fix your error. Google is your friend here. Do NOT continue until you fix your error. Your error will probably be somewhere near the parenthesis numbers (line and character position) in the file. It is almost CERTAINLY your typo causing the error, so look again and fix it.

Step 2. Go back and work through every part of the tutorial again, and this time explain it to your doggie. See how I am doing that in my avatar picture? If you have no dog, explain it to your house plant. If you are unable to explain any part of it, STOP. DO NOT PROCEED. Now go learn how that part works. Read the documentation on the functions involved. Go back to the tutorial and try to figure out WHY they did that. This is the part that takes a LOT of time when you are new. It might take days or weeks to work through a single 5-minute tutorial. Stick with it. You will learn.

Step 2 is the part everybody seems to miss. Without Step 2 you are simply a code-typing monkey and outside of the specific tutorial you did, you will be completely lost. If you want to learn, you MUST do Step 2.

Of course, all this presupposes no errors in the tutorial. For certain tutorial makers (like Unity, Brackeys, Imphenzia, Sebastian Lague) this is usually the case. For some other less-well-known content creators, this is less true. Read the comments on the video: did anyone have issues like you did? If there’s an error, you will NEVER be the first guy to find it.

Beyond that, Step 3, 4, 5 and 6 become easy because you already understand!