Add more stars after a game finish

Hello everyone, I need some help with my glitch garden game.
I have a problem with my stars, they are 150 in every level but I want to add 20 for every level passed (there are 9 levels + the enemy mode) because the enemy are stronger and the defenders costs more (the better ones). Now I have a sunflower that produces 20 star in 3 frames ( less than a second) and then it get destroyed and that’s ok for now because there are only 9 levels but when I’ll do 30 levels there should be 29 sunflowers off screen and they are too much (I think that the phone will slow down).
How should I do that script?

Hi Francesco,

Just so that I am understanding the problem and question correctly…

  • you want to give the player an extra 20 stars to spend at the start of each new level
  • you currently place sunflowers in the scene, but out of view, to generate the extra stars

Is that right?

Sorry for my bad English.
I want to give the player 20 stars to the player after the level one so in the level two the player will have initial stars (150)+20 but then in the level 3 the player should have 170+20, in the level four 210, level five 230 and so on.

Now I am using a sunflower (0 in the first level, 1 in the second, 2 in the third and so on) that gives 20 stars and then the sunflower gets destroyed (on porpouse).

1 Like

Hi,

Nothing to apologise for Francesco, I was just making sure I understood the issue so I could frame a suitable response.

Again, just to check, the sunflowers are generating stars over a period of time during each level yes? So the ones that you are adding per level are effectively hidden, and they are just their for this boost for the player each level?

It sounds like the missing component is a persistent object which retains key information about the state of your game.

This certainly wasn’t covered in the original Glitch Garden, although Rick is currently remastering it so he may add something in the new version.

In a nutshell you’ll want to create a GameObject at the start of your game and doesn’t get destroyed as each new scene is loaded, you can achieve this using DontDestroyOnLoad. You’ll most likely want to use a singleton pattern in a script which you then attach to the persistent GameObject. This will ensure that only one can exist at any time, e.g. you don’t spawn another persistent object in on level 2.

Within this script you’ll then store the amount of stars the player has at any given time during the game, this will be slightly different to the approach in the original course which had a script attached to the UI from memory (StarDisplay.cs?).

using UnityEngine;

public class GameSession : MonoBehaviour
{
    private int _stars;

    // singleton pattern code
}

At the start of the game you’ll want to initialise the stars field with the 150. You could consider having a method to help with this;

public void IncrementStars(int value)
{
    _stars += value;
}

You’ll probably want a method to remove stars too, you would call this when the player buys a defender to place in the scene;

public void DecrementStars(int value)
{
    _stars -= value;
}

How you approach the increment per level will depend on how clever you want to be :slight_smile: For example, you could just call the IncrementStars method before loading the next scene (I suspect you have some form of LevelManager.cs which is handling that, you’d push in the value of 20 and job done. This isn’t the most tidy of solutions.

Another option would be to use the sceneLoaded event from the SceneManager and assign a delegate, you could do something like this;

private void OnEnable()
{
    SceneManager.sceneLoaded += OnLevelChange;
}

private void OnDisable()
{
    SceneManager.sceneLoaded -= OnLevelChange;
}

…and then for the delegate itself, something like;

private void OnLevelChange()
{
    _stars += _levelChangeStarsIncrement;
}

The above would require adding an addition using directive at the top of this script;

using UnityEngine.SceneManagement;

It would also require you to create the _levelChangeStarsIncrement variable, at the top of the script;

[SerializeField]
private int _levelChangeStarsIncrement = 20;

Now, whenever a scene loads the stars will automatically be incremented by 20. I’ve added a [SerializeField] attribute here so you could change that value via the Inspector. You could consider the same for the number of stars the player begins with which would aid your tweaking as you play test.

You may find later that adding some code to determine the number of stars to automatically add may be more beneficial, for example, take the level number, multiply by a value, round where necessary, or perhaps only increment at certain level increments, perhaps every 5 levels or 10 levels for example.

Note, I mentioned that each time a scene loads the stars will be incremented, you’ll need to add a way to manage the scenario of a game over situation, if you have a scene that you take the player to (the course version did from memory), you don’t want the stars to be incremented at that point, in fact, you don’t necessarily still need the persist GameObject at this point. You could either consider a reset option, add a method to clear all the values down, or, you could destroy the persistent GameObject on a game over scenario, knowing that it would be recreated when the game restarts - possibly easier.

The above script snippets would perhaps look a bit like this when put together;

using UnityEngine;
using UnityEngine.SceneManagement;

public class GameSession : MonoBehaviour
{
    [SerializeField]
    private int _levelChangeStarsIncrement = 20;

    private int _stars = 0;

    // singleton pattern code - allows only one instance of this object to exist

    public void IncrementStars(int value)
    {
        _stars += value;
    }

    public void DecrementStars(int value)
    {
        _stars -= value;
    }

    private void OnDisable()
    {
        SceneManager.sceneLoaded -= OnLevelChange;
    }

    private void OnEnable()
    {
        SceneManager.sceneLoaded += OnLevelChange;
    }

    private void OnLevelChange()
    {
        _stars += _levelChangeStarsIncrement;
    }
}

Hope the above helps, any problems, let me know :slight_smile:

If you want some code for the singleton pattern also, again, let me know.

Thank you very much! you wrote all that code for me :smiley: .
So I made a singleton code (I found a guide on how to write singleton) and it’s like this : private static GameSession Instance; <-initialize the instance and then

public static GameSession Instance
    {
        get { return instance ?? (instance = new GameObject("Singleton").AddComponent<GameSession>()); }
    }

Then I’ve copied your script with the sceneLoaded but Unity won’t accept it and it gives me an error, one in the OnEnable() and one in the OnDisable()

A method or delegate GameSession.OnLevelChange()' parameters do not match delegateUnityEngine.Events.UnityAction<UnityEngine.SceneManagement.Scene,UnityEngine.SceneManagement.LoadSceneMode>(UnityEngine.SceneManagement.Scene, UnityEngine.SceneManagement.LoadSceneMode)’ parameters

I’ve searched on google CS0123 (the error number) but I didn’t found my scenario :frowning:

Well done regarding the singleton class, using a static instance variable is definitely the best way to go, your code should then create one automatically if it doesn’t exist and the static variable ensures only one can ever exist.

My apologies regarding the error, that is entirely my fault. The delegate method needs to have a corresponding signature as per the event.

Change it to this;

private void OnLevelChange(Scene scene, LoadSceneMode loadSceneMode) 
{
    //... 
} 

Hope this helps and my apologies, I was writing it off the cuff :slight_smile:

Thank you, now it’s everything fine. Now I should change this part of the script in DefenderSpawner if (starsDisplay.UseStars(defenderCost) == StarsDisplay.Status.SUCCESS) {//spawn defender if you have enough stars with our new script

gameSession.DecrementStars(defenderCost)==GameSession.Status.SUCCES
{//spawn

and change the decrement stars method with this

 public Status DecrementStars(int value)
    {
        if (_stars >= value) {
            _stars -= value;
            return Status.SUCCESS;
        }
        else
        {
            return Status.FAILURE;
        }
    }

and then add the star displayer to the GameSession script. Now it’s gonna be an easy job I think :slight_smile: Thank you

Definitely heading along the right lines, although I would personally keep the methods for incrementing and decrenting separate and put your logic for purchases in other.

Ideally, you could break the stars out of this class altogether, and give them their own class and own behaviour, for now I wpuld suggest keeping it simple and progress your changes in smaller steps.

I’m glad the example was of use to you :slight_smile:

I’ve just finished to update my script linking every component and script needed for the GameSession.CS and it works but it’s changing the wrong Text, even if I made the Text public.


it changes the text in the game mode

but it works, in level 2 I have 190 start (in the wrong place).
Tomorrow I’ll find the solution.
Thanks for your help :slight_smile:

Surely that is just how you have the UI Text components referenced in the Inspector? In your first screenshot it has “Cost” in the field, in the second one it has “Star” in the field. Do they not just need swapping around? Sorry, its a bit challenging to be helpful without having the project to hand to look though.

That’s the problem. After entering the game mode the text swap from star (the right one) to Cost (it’s a child of the button’s sunflower). I don’t know if you want to have my project but if you do I can send it to you (tomorrow because now it’s late here). If you don’t want my project I can write here our script. Thank you for your time :grin:

Sure, you can share the project if you want to and I’ll take a look, happy to download it tonight or tomorrow but realistically won’t be able to look until the morning due to a few other commitments. I will respond though with anything I can spot. :slight_smile:

I broke the game this evening but now it should be Ok.
The game is in Google Drive in a Zip folder, this one:
https://drive.google.com/open?id=1djwFbSI1fUva0iaY-AfQ-85lrilfLYLe
If you don’t want to extract the file, you can enter in my unity organization so you can download the game from unity and everything should be synchronized. For now there are only 9 levels and 5 enemy mode’s level (kill all the defenders to win).
Different things from the course:
-I’m using an audio Mixer instead of the script attached to the sliders (the only problem is that the sliders come back to the default value after you leave the option scene but the audio mixer works)
-enemy mode (spawn the enemies and kill all the defenders)
-New defenders (from Italian to English Leona =female lion , cinghiale = boar, mucca=cow, RanaOp= over powered frog, gatto felice= happy cat and bomba=bomb)
-stop sign pause the game
-info menu, a menù where you can find info about the defenders (attackers and the cat are coming soon).
-The +20 script that we (you) have written yesterday
-support for bigger screen (more grass and a red panel to the left).
-Health bars
-defender can have 2 types of projectiles (the frog and the cat have a super attack)

that’s all, the other things are equal to the course .
Thank you very very very much! I’m hoping that you’ll like my version :slight_smile:

sorry for the G word

Hi Francesco, it’s ok. We have a series of watched words which the forum then monitors, it will automatically take action if one occurs. The word in question was not offensive, but we tend to check on the context in which certain words are used in also.

Back to your project, I’ve tried downloading the zip file twice and had problems on both occasions;

image

It is about 84mb and has several thousand files according to my anti-virus software, but it will not extract/open.

Any chance you could retry re-zipping it and provide me with another link?

this time I’m using the .rar format
https://drive.google.com/file/d/1iKyy9DowGoLHIFawS-Y_oOj1d5xoflL0/view?usp=sharing

Interestingly, its 110mb this time instead of 84mb, that can’t just be the different compression type surely? I will let you know what happens :slight_smile:


Updated Thu Dec 13 2018 17:18

Seems to be extracting ok this time. Will respond after I’ve taken a look at the project, will be a little later this evening I suspect. :slight_smile:


Updated Thu Dec 13 2018 17:50

Hi,

I’ve not fully tested this yet, but I suspect your issue is going to be this line in your GameSession.cs;

starText = FindObjectOfType<Text>();

I’m not entirely sure why you have it, as you expose the field to the Inspector and have dragged across the UI Text GameObject within the scene. The above line however then overwrites that reference with what ever it finds - and that’s the thing - whatever it finds.

You have to be very vary using any of the Find methods, if you look at the documentation you’ll see it mentions that it returns the “first, active GameObject”, so there is absolutely no guarantee that when you have multiple GameObjects of the same type that you will be given the one you want.

I’ll spend a little more time on this but I suspect this is going to be your issue.


Updated Thu Dec 13 2018 17:54

Yeah, it’s that line. I commented it out and the code worked as it should and the correct UI Text GameObject was updated in the scene.

On a separate note, you would probably be better off creating a separate GameObject for the GameSession.cs script component, still create the necessary references with the other GameObjects, but at the moment the script is a bit hidden away on that Stars UI Text GameObject. I’d create an empty GameObject in the Hierarchy, reset the transform, and then wire up the references - you’ll be able to see it easily in the Hierarchy then and when it comes to debugging it will be easy to find.


Updated Thu Dec 13 2018 18:14

Just realised, you have it as a separate GameObject, but you also have the GameSession.cs script attached to the Stars GameObject also. Shouldn’t be necessary. I have also noted that the stars go down when spent, e.g. the UI Text is updated at that point, but when the trophies generate stars, whilst the value is incrementing in the background, the UI Text isn’t updated at that stage.

To resolve, add your UpdateDisplay method call to the IncrementStars method, as you have in the DecrementStars method.

Also, the UpdateDisplay method can be simplified, from this;

private void UpdateDisplay()
{
    int stars = _stars;
    starText.text = stars.ToString();
}

to this;

private void UpdateDisplay()
{
    starText.text = _stars.ToString();
}

Updated Thu Dec 13 2018 18:22

I can see why you were using the Find method in the Start method now, you were trying to deal with the issue that occurs when the next scene loads and it loses the reference to the Stars GameObject.

You have some choices;

  • set the Stars GameObject to not be destroyed on the change of a scene, just like the GameSession, why does it need to be destroyed and then recreated? It’s going to be there for the whole game etc
  • add a unique script to the Stars GameObject which will then allow you to use the Find method but by specifying a specific type, rather than one that could be on multiple objects
  • switch things around a bit so that the Stars GameObject, via its own class, manages its own data, provides behaviours for adding/subtracting stars which other GameObjects can use. I touched on this previously. You could, potentially, remove the need for this object to be found then. It could also persist through the scenes as mentioned above.

Hope this helps :slight_smile:


See also;

Ok so I should make a new game object for the text script and make it DontDestroyOnLoad()
and paste the GameSession.cs in the new star script (only the writings inside of the script)

It should be very easy :slight_smile:
another bug that I found is when you click on the stop sign the pause panel shows up but the “esci” button (esci= exit) doesn’t work. The level manager is attached to it but on click it doesn’t let me go on the start menu. Maybe it’s the behind the core game.
Another time, Thank you so much

I was thinking that your Stars GameObject would persist, as that is part of your Canvas, you may have to make that persist also.

and paste the GameSession.cs in the new star script (only the writings inside of the script)

I would take the functionality that belongs to Stars and put it in a Stars class, along with the data, as an example;

using UnityEngine;

public class Stars : MonoBehaviour
{
    private int _count;    // holds the number of stars the player has to spend


    // public property to return the count of stars
    public int Count
    {
        get { return _count; }
    }


    public void Increment(int value)
    {
        _count += value;
    }

    public void Decrement(int value)
    {
        _count -= value;
    }
}

The above gives you the core behaviour and data storage. Potentially, there should only be one of these classes in the game at any point too.

Something else you could consider, if you are up to the challenge, would be using events to enable other things to interact with it. As an example, you could have an event along the lines of OnCountChanged, other classes could subscribe to it, so whenever the count of stars changes, other methods would be called automatically. The Stars class wouldn’t really care what was subscribed, it would just say, “Hey, my value has been changed, it is now X”, whatever subscribed to it would go, "Great, thanks for letting me know, I can use the value of X to… " etc.

Note, implementing the above may mean you need to take a couple of steps backwards in order to have it working, but then your code should be a bit more streamlined going forward.

An example of the above with the event may look like this;

using UnityEngine;

public class Stars : MonoBehaviour
{
    private int _count;    // holds the number of stars the player has to spend

    public delegate void CountChanged(int count);
    public static event CountChanged OnCountChanged;


    // public property to return the count of stars
    public int Count
    {
        get { return _count; }
    }


    public void Increment(int value)
    {
        _count += value;

        if (OnCountChanged != null)
        {
            OnCountChanged(_count);    // notify subscribers that the count has changed
        }
    }

    public void Decrement(int value)
    {
        _count -= value;

        if (OnCountChanged != null)
        {
            OnCountChanged(_count);    // notify subscribers that the count has changed
        }
    }
}

Other classes which want to be notified when this event occurs, would do something like this;

using UnityEngine;

public class Example : MonoBehaviour
{
    private void OnEnable()
    {
        Stars.OnCountChanged += MethodThatCares;    // subscribe
    }

    private void OnDisable()
    {
        Stars.OnCountChanged -= MethodThatCares;    // unsubscribe
    }

    // the following method will be called whenever the value of `_count` within the _Stars_ class 
    // is changed (using either of the `Increment` or `Decrement` methods)
    private void MethodThatCares(int count)
    {
        Debug.Log("Stars : " + count);
    }
}

Note, we do have the Count property, which, of course, you could access to get the count at any point, but this often means polling the class to get the value, rather than the class announcing when something occurred which can be very useful and often more performant.

You could now have a StarDisplay script on your UI Text GameObject, and all it has to do is subscribe to the OnCountChanged event and have a method which updates the text property of the UI Text GameObject. It’s sole purpose is to update the number of stars, that’s all. It doesn’t have to find anything to do so.

using UnityEngine;
using UnityEngine.UI;

public class StarDisplay : MonoBehaviour
{
    private Text _stars;


    private void OnEnable()
    {
        Stars.OnCountChanged += Update;    // subscribe
    }

    private void OnDisable()
    {
        Stars.OnCountChanged -= Update;    // unsubscribe
    }

    private void Start()
    {
        _stars = GetComponent<Text>();
    }

    private void Update(int count)
    {
        _stars.text = count.ToString();
    }
}

Hope the above makes sense, any questions let me know, also, I’ve just written this out here, it’s not tested, any formatting of your C drive is purely coincidental :wink:

there are no error in your code, that’s amazing!!
should I put the eStats SUCCES and FAILURE in this method public void Decrement(int value) { _count -= value;
like this

 if (_count >= value) {
            _count -= value;
            return Status.SUCCESS;
        }
        else
        {
            return Status.FAILURE;
        }

so in the defender spawner script I can do like

if (stars.Decrement(defenderCost) == Stars.Status.SUCCESS)
or should i change the spawner script too?

Privacy & Terms