#Referencing another script

1 messages Β· Page 1 of 1 (latest)

primal oasis
#

Hey

rough grove
#

hello

primal oasis
rough grove
primal oasis
#

Alright, I believe I see the issue.

#

in the script Enemy

spawner = GetComponent<EnemySpawner>();
#

GetComponent used this way, targets the game object of the Enemy (game object of the script)
But you need to get the EnemySpawner of another object

#

To fix this, I just need to ask one thing - is SpawnEnemies() working as intended?

#

I do not often see do-while loops

rough grove
rough grove
primal oasis
#

basically, do-while always runs once

#

while doesn't do that, it checks the condition first

#

Anyway, since you have a spawner, you can actually get the component of the Enemy, and add the reference to the spawner.

rough grove
# primal oasis basically, do-while always runs once

should i do this instead?

    IEnumerator SpawnEnemies()
    {
        while (currentEnemyCount < 10)
        {
            yield return new WaitForSeconds(3);
            Vector2 randomPosition = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
            Instantiate(enemyPrefab, randomPosition, Quaternion.identity);
            currentEnemyCount++;
            enemiesSpawnedCount++;
        }
    }
}
primal oasis
#

That looks good. Make sure it behaves as intended.

rough grove
#

when i try to start the game my unity freezes completely now, i have to turn it off with task manager πŸ€”

primal oasis
#

um, okay, regret the changes, and see if that solves it.

rough grove
#

it runs now yes

primal oasis
#

Not sure how that change messed things up really.

#
yield return new WaitForSeconds(3);
Vector2 randomPosition = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
Enemy enemy = Instantiate(enemyPrefab, randomPosition, Quaternion.identity);
enemy.spawner = this;
currentEnemyCount++;
enemiesSpawnedCount++;
rough grove
primal oasis
#

What

#

Maybe I'm confusing things, but which Unity version are you running?

#

Ah right

rough grove
#

uhh my editor version is 2021.3.11f1

primal oasis
#
yield return new WaitForSeconds(3);
Vector2 randomPosition = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
Enemy enemy = (Enemy)Instantiate(enemyPrefab, randomPosition, Quaternion.identity);
enemy.spawner = this;
currentEnemyCount++;
enemiesSpawnedCount++;
#

try that

rough grove
primal oasis
#

but enemy.spawner is private, so in this case make a public method to set it instead

#

I'm going to make an example myself, brb

rough grove
#

set it to public

primal oasis
#

I'm perplexed. Must be something I'm missing.

rough grove
#

hmm

primal oasis
#

I'm having a discussion with some friends πŸ˜…
Seems the summer wiped some stuff.

But another solution is to make your SpawnManager have a static instance, which the other scripts can freely access.
It is called singleton.
@rough grove

primal oasis
#

PS: You can use the solution I set up here, if you

// Change this
public GameObject enemyPrefab;
// To that
public Enemy enemyPrefab;
rough grove
#

no errors now, but it still freezes

primal oasis
rough grove
#
using UnityEngine;
using System.Collections;

public class EnemySpawner : MonoBehaviour
{
    public Enemy enemyPrefab;
    public int enemiesSpawnedCount = 0;
    public int currentEnemyCount = 0;
    public int enemiesKilled = 0;

    // Get coordinates

    public float minX;
    public float maxX;

    public float minY;
    public float maxY;

    void Start()
    {
        StartCoroutine(SpawnEnemies());
    }

    void Update()
    {
        if (enemiesSpawnedCount == 10)
        {
            StopCoroutine(SpawnEnemies());
        }
    }


    IEnumerator SpawnEnemies()
    {

        while (currentEnemyCount < 10);
        {
            yield return new WaitForSeconds(3);
            Vector2 randomPosition = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
            Instantiate(enemyPrefab, randomPosition, Quaternion.identity);
        }
    }
}


primal oasis
#

This happened because you changed the do-while to a while again?

rough grove
#

wait so do i use a do while instead of a while/

primal oasis
rough grove
# primal oasis That's what your original code did. I suggested the while loop instead, but onc...

ah okay my bad i thought the new code you sent will maybe fix the issue, anyways, with the updated code i get these 2 errors now, the first one is when an enemy spawns and the 2nd one is when i kill it, also only one enemy spawns now

using UnityEngine;
using System.Collections;

public class EnemySpawner : MonoBehaviour
{
    public Enemy enemyPrefab;
    public int enemiesSpawnedCount = 0;
    public int currentEnemyCount = 0;
    public int enemiesKilled = 0;

    // Get coordinates

    public float minX;
    public float maxX;

    public float minY;
    public float maxY;

    void Start()
    {
        StartCoroutine(SpawnEnemies());
    }

    void Update()
    {
        if (enemiesSpawnedCount == 10)
        {
            StopCoroutine(SpawnEnemies());
        }
    }


    IEnumerator SpawnEnemies()
    {
        do
        {
            yield return new WaitForSeconds(3);
            Vector2 randomPosition = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
            Instantiate(enemyPrefab, randomPosition, Quaternion.identity);
        }
        while (currentEnemyCount < 10) ;
    }
}
primal oasis
rough grove
#

line 39 in EnemySpawner.cs

#

Instantiate(enemyPrefab, randomPosition, Quaternion.identity);

primal oasis
#

Are you certain you shared the most up-to-date code?

rough grove
primal oasis
#

sec

#

Try this

Instantiate<Enemy>(enemyPrefab, randomPosition, Quaternion.identity);
rough grove
#

still the same two errors

primal oasis
#

Hm. You know what. I'm going to look into this approach later today. I think it has just been too long since I actually did it, but right now I need to leave the chat and take care of a friend in need.

But there is a solution, and that one will work - Singletons.
You basically make the EnemyManager have a static variable, that contains itself.
Statics are accessible to all scripts in the same namespace, so there's no need to use GetComponent.

What I was trying to achieve with the approach, was for every Enemy to have a reference to the EnemyManager. And the solution with Instantiate seemed fastest, but a singleton is more the right approach.

Sorry for wasting your time with this rust in my mind.
I'll come back in a few hours to help fix it if you haven't already.

rough grove
#

yo dont worry i appreciate that you tried to help me ill try to use singletons and ill let you know if i fix the issue, take care!

primal oasis
rough grove
#

nope, i tried to use the singleton but i couldnt figure out where to use it and stuff, got way too confused, i just launched unity again and ill try fixing it
by the way, this i had to revert this code because it would only spawn one enemy, but i can replace it again in the future if your code is needed ```cs
// Change this
public GameObject enemyPrefab;
// To that
public Enemy enemyPrefab;

primal oasis
#

Alright. I'll see if I can squeeze in a singleton explanation before going afk again.

#

Singleton is just a fancy word for a script with a static instance

#

you could also make a regular static C# class,
but using a singleton MonoBehaviour lets you load/unload that behavior, saving resources.

#

Plus, it ties in better with game logic

#

When the class is static, it means you can type ClassName.Method() or ClassName.Property from any other script.

#

ClassName.instance.Method() etc in the instance of singletons, due to the static instance variable that connects it.

#

Good luck. I'll be back.

rough grove
primal oasis
#

the instance type is the script name

rough grove
#

like this?

primal oasis
#

That produces an error, which makes it obviously wrong.

Singleton is a pattern, not a thing.
It is simply the same script you're working on, but with a static ScriptName instance; field

#

You still need to add the rest from that article, so that only one of that singleton ever exists.

#

Read everything. It's very well explained.

rough grove
#

ive reread it multiple times but i still dont really get it, is this more like it? although i get one error

primal oasis
rough grove
primal oasis
rough grove
#

nah not at all man im struggling really hard

primal oasis
primal oasis
#

If you're using Visual Studio Community, this will fix code indentation mess: CTRL + K + D

rough grove
#

yeah that fixed it, do you want me to resend the code?

primal oasis
#

nah

#

Any errors currently?

rough grove
#

nope

#

actually yes, when i start the game i get this

primal oasis
#

Alright. This looks good. Just a few missteps to go over.

rough grove
primal oasis
#

First off, the line would not work that early in Awake()

#

(Also, it doesn't need to get its own enemiesSpawnedCount)

#

And the int enemiesSpawnedCount inside Awake() is actually a local variabe, and only exists within its parent Method, which is Awake().

#
public int enemiesSpawnedCount;
int enemiesSpawnedCount = EnemySpawner.Instance.currentEnemyCount;
#

The logic here is recursive.
This is the EnemySpawner script.
It has been given a static instance, so that other scripts may access it.

So line 37 shouldn't be there. It doesn't make sense. Agreed?

primal oasis
rough grove
#

ohhhhhhh i see

primal oasis
#

Either way, line 37 doesn't need to exist.
You've already defined the instance variable, and the singleton is referenced from your Enemy script.

#

It kinda looks good to go.

rough grove
#

it works!!

primal oasis
#

Yeah I'm pretty sure you solved this an hour ago, and line 37 has just been holding you back.

primal oasis
rough grove
#

now it does -1 to "currentEnemyCount" when i kill an enemy which is exactly what i wanted

primal oasis
#

Now, I'm curious if we can fix that do-while loop, if you're up for it

#

Is it spawning multiple enemies?

rough grove
#

let me see

#

hmm okay so it spawns 10 enemies, i killed one of them and it didnt spawn another (but it should due to while (currentEnemyCount < 10);)

#

oh wait

primal oasis
#

Indeed, but the do makes the code run once regardless, and then it starts checking the while-condition

rough grove
#
    void Update()
    {
        if (enemiesSpawnedCount == 10)
        {
            StopCoroutine(SpawnEnemies());
        }
    }
``` i have this which is stopping the coroutine right?
primal oasis
#

Actually, I believe you can only stop a coroutine by string

#

maybe I'm remembering old doctrine

#

confirm if it works

rough grove
#

hmm wait one sec

primal oasis
#

If it doesn't work, it should be fixed by rewriting the start and stop

StartCoroutine(nameof(SpawnEnemies));
StopCoroutine(nameof(SpawnEnemies));
#

The video I'm recalling is actually from last month, so I'm pretty sure your original solution will Not stop the coroutine.

rough grove
#

yeah it doesnt stop it

primal oasis
#

Alright, then the above rewrite should fix it

#

nameof(SpawnEnemies) == "SpawnEnemies"

rough grove
primal oasis
#

Nowhere. I just told you in programming-speak what it does

rough grove
#

ahh ok

primal oasis
#

== is equal to

rough grove
#

yep now it stops the coroutine

primal oasis
#

Awesome

#

so, the do-while

rough grove
#

can i use SetActive on the couroutine to pause it when 10 enemies spawn, and resume it when the player kills 10 enemies?

primal oasis
#

You don't need to stop the coroutine to achieve that effect.
The while condition will ensure that it doesn't spawn while the condition is false.

rough grove
#

ahh ok

primal oasis
#

For optimization, it is a good idea, but not critical right now.

rough grove
#

alright

#

so now i should replace the do-while with a while only?

primal oasis
#

That's the idea, but the last time you did this, you said Unity froze.
So BEFORE you do it

#

Save the Project

#

It's really important to save before making experimental loops πŸ˜…
One infinite loop and all unsaved changes are lost.

rough grove
primal oasis
#

Looks good. Try it out.

rough grove
#

yeah the game works fine, it stops spawning the enemies after 10 of them spawn

#

now i need to do the 10 kills to resume the loop thing?

primal oasis
#

ah, you're actually using enemiesSpawnedCount for that one

#

ah wait

#

I misread

#

so you want to kill all enemies, and then they respawn one by one?

rough grove
#

yes, exactly that

#

also theres this for this code

primal oasis
#

Then it makes sense to stop it.

#

ah yes, remove the ;

#

it isn't there when it's a normal while-loop

rough grove
#

oh yeah mb dunno why i wrote it

primal oasis
#

I think it is required in a do-while

#

aka do-loop

#

sec

#
void Update()
{
    if (enemiesSpawnedCount == 10)
    {
        StopCoroutine(nameof(SpawnEnemies));
    }

    if (enemiesKilled == 10)
    {
        StartCoroutine(nameof(SpawnEnemies));
    }
}
#

Two things:

#

You probably want to track the total kills, but we also need to count the kills between each stop/start.
Create private int enemiesKilledTotal;

rough grove
primal oasis
#

We'll get to that :))

rough grove
#

i dont really understand public/private fully yet

primal oasis
#

yes, I see no reason for it to be public, especially with what I have in mind

#

public means it is accessible from other scripts

rough grove
#

ok got everything written

rough grove
primal oasis
#

also

#

in the inspector

#

[SerializeField] private and public both "serialize" the field so that Unity can work with it (drag and drop in the inspector)

#

but you can also have

#

[HideInInspector] public

#

So anyway

#

you make the KillsTotal variable

#

and now we're going to create one or two public methods, so that we can automate some of the logic, and not worry about it outside the script itself

#
public void EnemyKilled()
{
    currentEnemyCount--;
    enemiesKilled++
    enemiesKilledTotal++;
}
#

place that inside EnemySpawner

#

and in the Enemy script, line 23:

EnemySpawner.Instance.EnemyKilled();
rough grove
#

okay i see what we're doing

primal oasis
#

Last thing:

rough grove
#

so i will have to use EnemySpawner.Instance.whatever to access anything right?

primal oasis
#

Yes

rough grove
#

got it

primal oasis
#

on the line above StopCoroutine

enemieskilled -= 10;
rough grove
#

wait is this code even doing anything actually? i can delete it and i get no errors

primal oasis
# rough grove wait is this code even doing anything actually? i can delete it and i get no err...

A static is globally accessible, hence there can be only one (of each static class).
Singletons have a static instance, but fundementally derive from MonoBehaviour.
So if you were to spawn in yet another clone of the EnemySpawner, things would get messy fast, but that code keeps it clean, and makes sure instance is only set if it is null beforehand, and if it exists already then the object realizes it is a doppelganger and destroys itself.

#

Let's say you start the game and load the first scene.
It has EnemySpawner.
Then you kill 10 enemies 3 times, and load a new scene.
The new scene also has EnemySpawner.
If that new spawner sets the EnemySpawner.instance to be itself,
then it will start off with blank values, and all the data from the first scene is separated, because the new spawner overrode itself as the static.

rough grove
#

hmm okay

primal oasis
#

The old data is still there, but you'd have to make the first spawner set itself to instance again

#

Normally, GameObjects are destroyed between scenes, but

#

on singletons there is usually also DontDestroyOnLoad()

#

which you have not added here

rough grove
#

also now im not tracking the "currentEnemyCount", can i just add it like this?

    public void TakeDamage(int damageAmount)
    {
        currentHealth -= 15;
        print(currentHealth);
        if (currentHealth <= 0)
        {
            Destroy(gameObject);
            EnemySpawner.Instance.EnemyKilled();
            currentEnemyCount++; <--
        }
    }
primal oasis
rough grove
primal oasis
#

the line above it triggers EnemyKilled()

#

which does currentEnemyCount--

#

currentEnemyCount++ should be in the while loop

rough grove
#

oh yes i meant to add it in the while loop shit

#

just like i had it before

#

so to keep track of currentEnemyCount and enemiesSpawnedCount i can just do ++; on them both below?

yield return new WaitForSeconds(1);
            Vector2 randomPosition = new Vector2(Random.Range(minX, maxX), Random.Range(minY, maxY));
            Instantiate(enemyPrefab, randomPosition, Quaternion.identity);
primal oasis
#

Yes

#

And inside Update() in the first if-statement

// Instead of
if (enemiesSpawnedCount == 10)
// Do this
if (currentEnemyCount == 10)
#

I think that should take care of the technical logic.

#

If you're up for more, I have two or even three specific goals in mind.

  1. Improve script structure
  2. Events instead of polling
  3. Display stats in UI
rough grove
#

the enemiesKilled gets reset but I think thats fine for now, I was maybe thinking of keeping track of it if i were to make upgrades in the future you know, because it resets to 0 when i get 10 kills

primal oasis
#

Alright. BTW I just realized you don't need an event, because it is already running on the OnCollision event and Coroutine.
I want to suggest a final change

#

Create private void EnemySpawned()
Move the number++ logic into it (from the coroutine SpawnEnemies)
And then finally, move the two if-statements from Update()
into the bottom of EnemyKilled and EnemySpawned respectively

#

Then you're not polling every frame in Update

primal oasis
#

that's why we made enemiesKilledTotal, to keep track of the total sum

#

Anyway, you've done great.

#

Let me know if you have any questions about my last suggestion.

rough grove
rough grove
primal oasis
#

kk

#

remember to add EnemySpawned() in the Coroutine

rough grove
#

like this?

primal oasis
#

Yeah, just one thing we both missed

#

== 10 but in the coroutine you do < 15

#

make an int variable, and use it instead of hardcoding the numbers

#

maxEnemies has a nice ring to it

rough grove
primal oasis
#

yep, exactly

#

and the if-statements

rough grove
#

yep i was gonna ask if i should do that but i wasnt sure if that was the case

#

so if (enemiesKilled == maxEnemies) and if (currentEnemyCount == maxEnemies)? what about the enemiesKilled -= 10;

primal oasis
#

That one, too.

#

and

#

now you don't need the ints to be public

#

And you can remove Update() entirely

rough grove
rough grove
primal oasis
#

Double Yep

rough grove
#

now the script stops the couroutine when the "currentEnemyCount" hits 10, but if i kill the enemies while theyre spawning, I can technically never stop the couroutine, maybe it would be smarter to somehow make it so it ends the couroutine every 10 enemiesKilledTotal?

#

or is that a stupid idea

primal oasis
#

no that's possible, I'm just not sure it's practical for statistics

#

Say you wanted to increase by 5 enemies per cycle

#

Then that formula breaks

#

Sec

#

post your script again

rough grove
primal oasis
#

add private int enemiesToSpawn;

rough grove
#

yes done

primal oasis
#

it's over 2am, just give me a few mins πŸ˜…

rough grove
#

all good dont worry its 3 am here im almost dying myself lmao

primal oasis
#

Deep sleep after this lol

rough grove
#

same lol

#

would this script that we're doing be viable if my game were to have a wave system? wave 1 = x enemies, wave 2 = x enemies and so on, probably just set values, no need to multiply them by anything as if you die you lose everything (roguelike game)

primal oasis
#

Yes, but you could also multiply them or make a simple formula.

rough grove
#

ill finish this enemiesToSpawn feature and ill go to sleep probably, i can barely keep my eyes open lol

primal oasis
#

if that doesn't work, we'll fix it tomorrow

#

I renamed the int btw

#

inverse logic

rough grove
#

works perfectly!

primal oasis
#

Awesome!

#

Hope that holds up when you're actually awake πŸ˜†

rough grove
#

it should 🀣

primal oasis
#

Now you've got some cool scripts that don't even use Update

#

good day's work

rough grove
#

i genuinely want to thank you for your help today youve spent literally hours helping me and we actually got so far, i wont say i understood everything we did completely but i definitely learnt something!!

#

i get this "error" once in a while not sure why but it doesnt seem to harm my project

primal oasis
rough grove
primal oasis
#

Restarting Unity can help.

rough grove
#

yeah i think it stopped after restarting

#

okay ill go to bed wont hold you for any longer once again i appreciate the help a lot, for tomorrow ill try to do some ui stuff, track the kills and stuff, in my mind it doesnt seem hard but we'll see how it goes :D ill probably close this thread and message you or @ you if I have any questions tomorrow?

primal oasis
rough grove
#

alright ill keep it open then, goodnight!

primal oasis
#
using TMPro;
//...
[SerializeField] private TMP_Text ui;
//...
// in Update or run on any change
ui.text = $"text {variable} text {anotherVariable}";
rough grove
#

hey goodmorning, should i make a different script for the text ui stuff?

primal oasis
#

@rough grove Oh, and use a TMPro text object.
I don't recall if the Unity default UI is still in 2021, but it's beyond bad, so always use TMPro

#

TextMeshPro*

rough grove
#

i only see the text in my "Scene", not in my "Game" scene, is this something to do with the layers?

primal oasis
#

are you sure it's not the other way around?

rough grove
primal oasis
#

Where was the Canvas when you first created the object?

#

UI objects need to have a parent Canvas.
Cameras render canvases.
Canvases contain objects.

rough grove
#

its setup like this right now

primal oasis
#

Where in Scene View could you see the Canvas when it was first spawned?

#

is it like 100 times larger than the camera view itself?

rough grove
#

oh yeah itis

#

red is the canvas blue is the camera view

primal oasis
#

Screenshot the component settings of the Canvas

rough grove
primal oasis
#

Hm. Weird.

rough grove
#

ok i fixed it

#

yo, i have to go for like 3-4 hours (ill be able to respond but not code cause ill be out of home for now), ill message you whenever im back if you dont mind!

primal oasis
#

Last thing I can offer to help you create, is an FPS counter.

rough grove
#

im available now

primal oasis
#

Well, it's Saturday and you've done good work, so here's the simple FPS Counter I helped a guy make some time ago.

#
using UnityEngine;
using TMPro;

public class FPSCounter : MonoBehaviour
{
    private TMP_Text uiText;
    private int frameCount;
    private float timeCount;
    private float framesPerSecond;
    private float pollingRate = 1f; // Update interval in seconds

    void Awake()
    {
        uiText = GetComponent<TMP_Text>();
    }

    void Update()
    {
        frameCount++;
        timeCount += Time.deltaTime;

        if (timeCount >= pollingRate)
        {
            framesPerSecond = frameCount / timeCount;
            uiText.text = framesPerSecond.ToString("F2");

            frameCount = 0;
            timeCount = 0;
        }
    }
}
#

In the simplest form, FPS can be calculated by 1/Time.deltaTime
But doing that every frame would be overly exhausting to watch.
That is why one usually adds pollingRate, to only calculate FPS at specific intervals

#

In this solution, the FPS counter keeps statistics for all frames and time, between each time of polling, to get a true average.

primal oasis
#

The F2 in ToString will limit the output to 2 decimals.
F0 is no decimals.

primal oasis
#

Since it has a private TMP_Text variable, without serialization,
and it uses GetComponent in Awake, that means the script needs to be attached to that UI game object.

#

if you are ever getting a reference from a different game object than the script is attached to,
then you have to get that component (whichever way) in Start() which runs after all other objects in the scene have awoken.

primal oasis
primal oasis
#

@rough grove Did you try it out?

rough grove
#

i did but i couldnt get it to show up in my game scene, i think i just didnt set it up properly