Tower Defense Project – Fixing UI Text

September 2, 2019

Tower Defense Tutorial Project

UI Text

GOAL:

Fix game over UI text and look into round text references to see why they update strangely.

NOTES:

For the GameOver screen producing weird text results, my first thought was to the check the inspector references. I know tying the stats into text UI elements uses inspector references, so making sure those were correct was the first check. Sure enough, it looked like the wrong text was dragged into the GameOver UI inspector field. I must have grabbed the wrong one initially, as the generic text was dragged in there as opposed to the text that should be holding the round number.

As for the issue with the round text timing being off, I figured that had to do with the way the coroutine to animate the text was setup. It should count up from 0 to the final round number the player ended on (whether they won or lost), but it was starting on that final round number, THEN going to 0 and counting back up again. This just looks really bad, so it needed fixed. From checking the RoundsSurvived script (which is the same for both the Rounds object in GameOver and LevelCompleted), it looked like the animation coroutine was being set to start, and the next line was setting the roundsText to the playerStats.rounds. This is most likely the issue, as the coroutine starts, the text is immediately set to the rounds value as dictated by the very next line, then the coroutine continues running updating the value until it hits the rounds value again. This fits inline with the incorrect behavior we are getting.

PROBLEM 1

The incorrect text being displayed on the GameOver UI.

TEST 1

Move the Rounds (Text) into the RoundsSurvived inspector field as opposed to the Text (text) for the GameOver screen.

SOLUTION: This was all it was, updating this fixed the issue and the text was all in the proper place on the GameOver UI.

PROBLEM 2

Round counting animation starts at the max value, then immediately goes to 0, THEN counts up to max value again.

TEST 1

I removed the line right after the one initializing the AnimateText coroutine that was setting the rounds text to the rounds value.

SOLUTION: This appeared to solve the issue with some testing. Both the GameOver and CompletedLevel UI would start the round counter at 0, then count up to the correct number of rounds survived.

NEXT STEP:

Make 5 uniquely distinct levels to test the overall system. This includes testing generic gameplay as well as the general level progression and level selection system. As I am using a basic built in Unity way to handle saving progression right now, I have had all the levels unlocked for recent testing. I need to reset that and test that that is tracked properly, especially over several levels to make sure the system holds that many as well as giving more data points for any possible issues.

HFFWS – Working with Conveyor Belt

August 29, 2019

Human Fall Flat Workshop

Conveyor Parameters

Conveyor Parameters

Name: DarkGreyMetalMechMediumDebrisMovingConveyorBeltBody

  • Parent
    • Mesh
      • the mesh of the overall body of the conveyor belt; the central element
      • Default: DarkGreyMetalMechMediumDebrisMovingConveyorBeltBody
    • Conveyor (script)
    • Item Prefab: objects that the belt is made up of
    • Segment Count: creates the designated number of segments; spread out accordingly
      • Initialized
    • Length: Changes the overall length of the conveyor
      • Changes during Runtime, but only properly works in setup
    • Radius: this is the radius of the curved ends; controls overall thickness of belt system
      • Initialized
    • Speed: Direction and how fast the belt moves around
      • Runtime
    • Node Graph (script)
    • Signal Math Comparte (script)
  • BeltM_RollEnd
    • Just seems to be model for cylinder at a belt end
  • BeltM_RollStart
    • Just seems to be model for cylinder at a belt end
  • ConveyorSegment.001
    • This just seems to be the default prefab reference for the object that makes up an individual conveyor segment
    • This does not need to be a child of the overall object, so it can be removed
    • Just the reference to this object is needed in Conveyor script’s Item Prefab
    • Make sure to have a reference to this if removed to use still
  • LoopingSFXStart
    • Controls starting the audio
  • LoopingSFXStop
    • Controls stopping the audio
  • ConveyorLoop
    • Another audio source for the general audio produced by the conveyor
  • top
    • Instantiated at runtime, holds segments making up the top part of the conveyor
  • bottom
    • Instantiated at runtime, holds segments making up the bottom part of the conveyor
  • start
    • Instantiated at runtime, holds segments making up the curving side of the conveyor
  • end
    • Instantiated at runtime, holds segments making up the other curving side of the conveyor

NEXT STEP

I have worked out a lot of what the parameters do, now I just need to test setting them and instantiating conveyors at run time to make sure that works as I expect.

Tower Defense Project – Testing Losing States

August 28, 2019

Tower Defense Tutorial Project

Testing Losing States

I believe all the moving parts for my scene management system are finally starting to come together and work properly for this project. The losing conditions are the last state I really need to check with. I just need to make sure it properly ends the game when the player loses, does not allow them to progress, displays the proper UI elements, and moves between scenes from the losing state correctly.

GENERAL TESTING

Test #1:

The first test was going to Level 1 from the level select menu and immediately losing.

This seemed to work fine. The game stops when the player loses, and the game over UI comes up. The UI text does not seem to be working properly, so I will have to adjust that. It says the player survived 25 rounds, which is just the default value. I need to tie this into the script controlling the round count.

I think tried Retry and Menu from this losing state and both seemed to work properly. Retry started the level again (and actually reset all the values and waves as well) and Menu took me back to the level select menu.

Test #2:

Open level 2 from the level select menu and immediately lose.

This ended up having the player immediately lose and show the gameover screen as soon as the level loaded. I believe this has to do with how I arrived at the level. I was at the level select menu from the Menu button on the game over (losing screen) of the previous level (Level 1). I think going from losing, directly the the level menu, directly into another level, is missing a reset somewhere.

I did try Retry as soon as I lost level 2 and this reset the level just fine. I also tried selecting Menu and going back to the level, and this only worked properly sometimes. I believe it may have had to do with the timing of the wave.

The issue is that lives in PlayerStats gets set to <=0 to meet the losing condition, but does not get set to a proper value on level start in time. The Base scene loads in, then the Level scene. The Base scene has a check on player lives to see if they have <= 0 for a game over, and the Level scene has LevelInformation which is responsible for setting values like lives. So lives is being checked before being set. This was worked around initially by starting lives at 1 instead of 0, and then anytime the player won they had more than 0 lives so checking a value greater than 0 gave no issue.

POSSIBLE SOLUTIONS: Test #2

My idea to fix this timing issue of the gameIsOver check among other values (like the number of lives) was to move the gameIsOver bool to the GameManager. The GameManager is in the Logic scene, which always persists. This is also where PlayerStats resides, which holds the player lives. My thought with this was that I could have both the gameIsOver bool and the lives be set from the same place at the same time, and with them being in the same exact script, checking against them should be more consistent. Basically, since the game has to be started (gameIsOver == false) as well as having your lives <= 0, if any way of ending the game sets gameIsOver == true, we can make sure gameIsOver is not false until we also set lives first.

SOLUTION: This approach worked (for the most part). I reworked the LevelInitializer script in the Level scene to call StartGame in the GameManager, which just sets the gameIsOver bool to false. LevelInformation is also in the level scene, and this is responsible for setting the lives. This ensures the same scene is setting both the lives and the gameIsOver to false (starting the game). Now the LevelManager checks with the GameManager to see if the gameIsOver is true/false, as well as PlayerStats to see how many lives there are. This basically passes all the value setting and value checking to objects in the Logic scene, which makes sense since it is the omnipresent scene for the project.

This did lead to one issue where losing and retrying the level was not turning the CameraController back on.

PROBLEM: CameraController Bug

The CameraController was not coming back on when losing a level and attempting to retry it.

It turns out that going into a new level was fixing it since the CameraController works by default when loading a new scene, but when the game enters an end state (when we turn off the controller) and we keep the same scene (it’s in the Base scene) like when you call Retry, then it does not come back on.

Test #1:

SOLUTION: Since activating the camera controller was another thing that should happen on level start, I wanted to tie it into the LevelInitializer script. This already had a reference to the LevelManager from my previous attempts at creating Start/End game methods, and the LevelManager had a reference to the camera controller since it was initially solely responsible for controlling the game camera. Using this existing setup, I had the LevelInitializer script go through the LevelManager to enable the CameraController script on the camera object. The LevelManager simply acted as a holder for the camera object and the method for enabling it for this purpose (disabling the camera controller was still handled by the LevelManager when it determined the game had ended).

This ended up working well and gave no errors as far as I could tell.

FINAL NOTES:

With fixing up this last issue, all states and scene transitions should basically be accounted for. Continue, Retry, and Menu all work during gameplay (paused); Continue, and Menu work after beating a level; Retry, and Menu work after losing a level. This should finally be the end of the majority of the scene management work.

NEXT STEP:

The game over UI text is still weird, so I need to fix those references. Also along those lines, it’s only a minor issue, but the game winning text starts at the final round number and THEN counts up again from 0 to the final round number. It isn’t game breaking, but it looks silly.

HFFWS Finding Objects to Test

August 27, 2019

Human Fall Flat Workshop

Instantiating More HFFWS Prefabs

I ended up running into an issue testing the HFFWS. When in Play mode in the Unity editor, my player model was missing.

PROBLEM: Missing Player Model in HFFWS Unity Editor

SOLUTION: I decided to open HFF to see if there was a direct access to the workshop levels from the main menu, and found out there was not. While I was in the main menu, I decided to have some fun and customize my player model. After doing so, I found out that my player model in the HFFWS editor was missing. It turns out that the editor tries to use your in game model, and if you are not using the “Default” as the option for your character’s model, nothing will show up. model

The Prefab scenes seem to work pretty well from interacting with them for a bit. I will look to go through these and grab some interesting objects to run some spawning tests on to make sure I find a nice way to approach this. Setting the parameters for node and non-node objects will be crucial for setting up levels.

NEXT STEP:

A decent looking prefab I will look to work on next time was the GraphHolderConveyorBelt from the PowerPlant prefab scene. It has a good bit of factors for running a large conveyor belt, which could also just be a useful tool for me to work with in the future.

Tower Defense Tutorial – Fixing “Continue” Scene Errors

August 26, 2019

Tower Defense Tutorial Project

Fixing Scene Transition Between Levels When Continuing

I was able to clean up the scene management enough last time that going through the basic starting game scenes all worked well as far as I could tell. I could get from the main intro scene, to the level select screen, to level 1 without any issues. Progressing to level 2 was not working because of some reference with the enemy spawner object, so I will be looking into that.

PROBLEMS

PROBLEM #1

Level 2 does not load after playing Level 1 because of some reference issue for setting the spawn object.

The error was actually that spawnPoint in WaveInformation was not assigned, so I looked into that and saw that there was a public field for drag/dropping the spawn point object into that I just hadn’t done in Level 2.

SOLUTION: Just setting this public field with the spawn point allowed the level to work properly.

General Testing

I edited Level 2 a bit more just to make sure the data was all being passed over to the WaveSpawner. I made sure to move the starting spawn point just in case that wasn’t being updated and it just worked out visually since they were spawning from the same place as before. I also changed up the map layout and moved the waypoints around to fit this new layout. Running this updated level layout worked as expected, everything was properly updated when opening Level 2.

I had not tested using the Continue button from Level 1 since Level 2 was not working in any capacity until now, so I tried that now. This seemed to open Level 2 fine and ran the level, but it did not end when the last enemies were destroyed. I then tried using the pause menu to retry the level and this gave an error saying the SceneManager tried to SetActive an invalid scene.

Going to Level 2 from the level select menu seems to give no errors. The game ends with the player winning as expected, and the Retry and Menu options from the pause menu both work as intended. Something is being messed up when transferring directly from Level 1 to Level 2 with the Continue button function.

PROBLEM #2

Using the Next level option on the level complete screen is giving some errors. Most notably, the level complete screen does not come up on the next level when the player completes it, and using Retry on the level returns a scene error.

The Retry level error makes sense. The SceneManagerLite holds a reference to an int called currentLevelBuildIndex that is supposed to represent the scene index of the currently loaded level scene. This however is not being properly set when using the NextLevel method, so the RetryLevel method in SceneManagerLite refernces the currentLevelBuildIndex to determine which scene to load, and is actually seing the last level’s index here. So what is happening is that it is loading the last level scene, then unloading it, and then trying to set it active. Since it is now unloaded, there is nothing to set active resulting in the error.

I believe the game not ending is as simple as the GameIsOver bool in the LevelManager not being reset. This is initialized on false, and set to true when the level does end. However, going straight from one level to the next never unloads the Base scene which contains the object holding the LevelManager, so that is never reset to false.

Test #1:

Two fold test: Properly set the currentLevelBuildIndex within the NextLevel method (in a way similar to the SelectLevel method); also reset the GameIsOver bool in LevelManager when using the NextLevel method.

Editing the NextLevel method in the SceneManagerLite was simple enough to just add setting the currentLevelBuildIndex to the proper value, but figuring out exactly where to reset the GameIsOver bool was a bit tricky. I wanted to place it somewhere where the reference to the LevelManager fit in cleanly, but it also needed to be somewhere that could set the value after calling a method like RetryLevel or NextLevel from the SceneManagerLite. (RetryLevel may be important for when the game ends from the player losing the level, or if we want to add a retry option even on completeing the current level). Setting it too early might cause issues where the game tried to end again while loading/unloading scenes.

SOLUTION 1: Setting the currentLevelBuildIndex in the NextLevel method of SceneManagerLite fixed problems loading the scene using NextLevel, as well as fixing the menu errors in game. Now using Retry and Menu after loading into a level with NextLevel work properly.

On the other end, I tried to fix the GameIsOver bool by creating a new script called LevelInitializer and placing it on to the LevelInformation object found in level scenes. This script simply attempts to get a reference to the LevelManager (which now has a static method and reference to be gotten) and ensure the LevelManager is properly reset when a level starts. This is done in the new LevelInitializer script’s Start method, the thought here being that this should be called anytime a level scene is loaded, which is exactly when we want to ensure the LevelManager is reset. However, the game still was not ending after continuing from level 1 to 2.

Test #2:

I only needed to fix the second part of the problem now, and it seemed to be a rather easy fix. It turns out, since the LevelInformation object I added the LevelInitializer script to wasn’t a prefab (it is now thanks to this issue), I only added LevelInitializer to Level 1. So Level 2 did not have this script at all, so it had no way to run the new reset method I created.

SOLUTION 2: I just needed to actually add the LevelInitializer script onto the LevelInformation object in every level scene. Doing this reset the LevelManager as intended so the player can now win when they use the Continue menu option on the level complete screen.

NEXT STEP

The scene management is almost completely finished and looking to work pretty well. The main process to test next will be losing conditions, since I have not checked that in a while. This may have a few kinks that I need to fix out since it has not been through the same testing as winning and progressins, but it should hopefully be some quick easy fixes if any.

Unit Testing in Unity

August 25, 2019

Unit Testing

How to Use It

Youtube – Unit Testing in Unity – Livestream

By: Unity3d College

Unit testing is used to check that small chunks of your code can pass designed tests. These tests can be made in a way to show why a certain method or snippet of code is designed the way it is, and help inform other programmers on the project of why that is and also if they break some intended uses of the designed code. They just generally ensure that your code is doing what you expect it to, and remains doing what you expect.

The tests generally follow a “triple A” format: Arrange, Act, Assert.

  • Arrange: Set up the conditions for the test
  • Act: Perform actions that you want to test
  • Assert: This is the check used to test

In this lecture, they have setup a very nice system for holding all of their testing needs in Unity. Their is a specific Testing namespace with all the testing classes that hold public test methods. These are accessible through a specific window in Unity and can be run in the Editor, as opposed to constantly needing to run the tests in the play mode of Unity. This uses a setup with assembly definitions and other editor interactions I am unfamiliar with, so I will need to look into that more to setup a system like this.

The test system they have setup does not work particularly well with MonoBehaviours. This is because a lot of tests involve creating new objects to arrange the test, and Unity does not allow MonoBehaviours to be created with the new keyword. You must create a separate gameobject and attached the MonoBehaviours with an addComponent type approach.

This can be worked around with something called NSubstitute. You can grab this online, it is a .dll file that you place in your Pluging > Editor folder to help use with all your testing. It allows for use of the NSubstitute namespace to use this Substitue.For method which somehow works around the MonoBehaviours issues for testing purposes. This can also be used to test interfaces, which also cannot just be made as new objects.

Later they do some play mode tests, which test that adding force to a rigidbody is moving the body properly. This type of testing could actually lead to something very useful with my thesis research as I may want to be monitoring the movement of physical bodies to see if they are behaving properly.

Others to Checkout for Unity Programming Content

There were a few final notes on some people to find on Youtube for more on programming in general and with Unity:

  • UncleBob Bob Martin (Design Patterns, C# focused, not Unity specific)
  • Mob Mentality Show – Good for groups of programmers

Object Pooling in Unity

August 24, 2019

Object Pooling

Unity

Youtube – Unity3D Object Pooling – How to use them & why you should

By: Unity3d College

This tutorial just seemed like a really useful concept so I wanted to make sure to note it down. Destroying lots of objects in Unity can lead to a lot of garbage collection, so you can use this concept of object pooling to help minimize this or remove it significantly.

The example used has a lot of objects that are spawning and then being destroyed. This can lead to lots of garbage collection because the newly unused memory needs to be accounted for in some way at some point. Object pooling helps get around this by not destroying the objects. You simply deactivate them, and then add them into a pool of deactivated objects. When you need to spawn another one, you just grab one of these deactivated objects and reactivate them instead.

It’s a very useful concept that is pretty easy to implement. It will have the most impact when you are creating and destroying a decently high amount of objects, so keep an eye out for that as a time to use this methodology.

Tower Defense Tutorial Fixing More Scene Timing Issues

August 24, 2019

Tower Defense Tutorial Project

Fixing Problems with the Scene Manager

A major issue I may have to deal with is updating the version of this Unity project. To clean out space on my computer, I wanted to condense the versions of Unity I was using on several projects so I removed one of the older 2018 versions I was using originally for this tutorial. I wanted to upgrade it to a 2019 version, so I just used the most recent one I had. I don’t believe this should have very major impacts on this project, but it is worth noting for any extra strange behavior or errors.

First things first, I need to do a bit of play testing to get a feel for the current errors and go over the main scripts (mostly those dealing with scene management) to see where to start. The opening scenes seem to work well (the initial main scene into the level select menu). Selecting level one works ok. Any other level leads to weird errors (not properly closing other scenes so lights add up and UI elements stack on top of each other), which is expected since my work focused on getting level 1 to work first. Retrying while a wave is spawning leads to some interesting behavior (it spawns the second wave instead of the first when the level restarts).

To make it easier to keep track of all the different scripts, I started organizing the scripts folder hierarchy. This began with simply creating any hierarchy at all, as initially I just had all of the scripts in the scripts base folder. While it may be a bit risky since I do move things around from time to time at this stage, I decided it would make sense to make a folder for each scene and organize the scripts by scene basically at the first level. This would help me see which scripts are running in which scenes simply by checking the folders. And when I am in a certain scene, I can just open its corresponding scripts folder to see what all is available.

This was actually a very straight forward way of placing a lot of the scripts. Many of them pretty directly only exist in one scene that makes sense. Some of the more instantiated object related scripts (like for the turrets and the enemies) pretty clearly made sense to just put in the Level scene folder since that’s where they should be instantiated so they were easy to place as well.

PROBLEMS

PROBLEM #1

Restarting a level while the first wave is still spawning will have the second wave spawn first on restart.

This is most likely an error that happens because restarting while the coroutine is running for spawning a wave has the value reset that happens when restarting overridden by the coroutine then ending. Checking the WaveSpawner script affirms my assumption since it has a method called ResetWaveSpawner, which sets the waveIndex to 0 (the first wave) but the coroutine spawning the wave, called SpawnWave, increases the waveIndex by 1 once it is finished running. The coroutine is most likely “finishing” after the reset is happening, so the index gets pushed to 1 immediately (insted of being at 0 as it should).

Test #1:

Add a check to ResetWaveSpawner to see if the SpawnWave coroutine is running and stop it before resetting.

This is a perfect opportunity to try the new way I learned to control the flow of coroutines. Instead of using an outside bool variable that is set to true when the coroutine starts, false when it ends, and checking later if that bool is true/false to stop the coroutine, you can control an IEnumerator reference. You first create an IEnumerator reference variable. You then set this equal to the coroutine you want to start right before starting it, then Start this reference variable as your coroutine. When you want to check if it’s running or not later to determine if it needs stopped, you can just do a check to see if this reference variable is not null and then stop the coroutine.

SOLUTION: This worked perfectly. Restarting at anytime (even immediately when the wave starts) has the proper wave spawn. This also stopped the case of enemies continuing to spawn (which was the same issue of the coroutine running into the next scene start, it was just a longer coroutine in that error).

PROBLEM #2

The level does not end when the player defeats the final wave.

The LevelManager script that ultimately starts the “winning process” has a lot of conditions to start this process. The if statement checking in Update to see if the player has won has 4 separate conditions, so there is a high chance one of these is not acting as it should. This is a good place to start looking for why this error occurs. The conditions are as follows:
if (waveSpawnerReference.isActiveAndEnabled && waveSpawnerReference.WavesFinished() && waveSpawnerReference.enemiesAlive == 0 && GameIsOver == false)
{
WinLevel();
}

waveSpawnerReference.WavesFinished() is a method that does a bool check in the WaveSpawner script, so my first thought was to check there and make sure it was being set properly. I simply tested this condition with GameIsOver == false to prompt winning to see what this did. This resulted in the player winning immediately on the level starting. I then remembered, this is a scene loading timing issue reflected in the WavesFinished method check. This method is:
public bool WavesFinished()
{
if(waveIndex == waves.Length)
{
wavesFinished = true;
}
return wavesFinished;
}
The reason this checks as true immediately is because the method gets called before any of the values are set, so the waveIndex is 0 by default (which is accurate) but the waves.Length, which should be the count of how many waves are in the level, is also defaulted to 0 before being set. This results in this being true at frame one.

Looking at the WaveSpawner, I see why these conditions are not being met simultaneously. The WaveSpawner has an Update check to see if the waveIndex == waves.Length to indicate that the last wave has been spawned, and if this check is true, if disables itself. So the condition including a check for hitting the end of the waves AND seeing if the WaveSpawner is enabled will never be true simultaneously.

Test #1:

Create a DeactivateWaveSpawner method which will allow me to deactivate the spawner with the LevelManager AFTER performing the check to see if it’s active for winning (and just after calling the losing method for continuity).

I also had to tweak the WaveSpawner script a bit since it was in charge of deactivating itself as soon as it spawned the final wave. I wanted it to stay active until the player won or lost, and then have those methods deactivate it. This method however gave me very interesting errors. It still was having the player win immediately the first time I selected level 1, but every subsequent time I selected level 1 from the level select menu, it worked perfectly fine. It started properly and allowed the player to win correctly.

Once again, this is an annoying timing issue. The WaveSpawner actually starts enabled in when starting the game from scratch in the Logic scene. The WaveInformation script in the tested Level scene contains the following:
private void Start()
{
waveSpawnerReference = WaveSpawner.GetInstance();

// Reset the wave spawner each time a level loads
waveSpawnerReference.resetWaveSpawner();

PassWaveInformation();
waveSpawnerReference.enabled = true;
}
What’s happening is that the necessary values are being passed to the WaveSpawner through the PassWaveInformation method and the game is immediately ending with a win and then deactivating the WaveSpawner. Now on returing to level 1, the values needed are already set, and these statements before the final statement which enables the WaveSpawner are fed into nothing (since the WaveSpawner is disabled at that time). Now however, anything that checks the values of WaveSpawner will be checking the correct values as intended since they were set way back when the level was previously started. This is removing any frame perfect timing check errors since everything is set before the WaveSpawner is even turned on, but only when starting the level after the first run.

Test #2:

Edit the conditions in the WavesFinished method in the WaveSpawner to be more accurate.

SOLUTION: The general idea is that this is basically returning a false positive check by immediately checking the default value to meet its only condition, so I am just going to add another check to make sure a default case that doesn’t make sense to ever happen anyway isn’t what one of the current values is set to. So basically I changed the condition from:
if(waveIndex == waves.Length)
to
if(waves.Length != 0 && waves.Length)
No level should ever have 0 waves in it anyway, so this check is safe for my game environment and removes the false positive check with the default values where everything is initially at 0.

Using this method worked perfectly, and may also serve as a useful secondary check regardless to reduce the chances of future wave spawning bugs.

PROBLEM #3

The spawn coroutine is still giving some errors in some scene change circumstances.

The main issue with the spawn coroutine was happening when retrying a level, so I specifically fixed a method which was called at the start of loading a level scene so that it would stop the spawn coroutine. However, Unity still doesn’t like that this coroutine continues in other scene swap scenarios (like going back to the menu). It isn’t a game breaking error, but it still shows up as an error in the logs as the game wants to complete the coroutine but cannot because certain objects are not gone (or destroyed according to Unity). This is not a big issue, but I thought it was an interesting enough problem to record as I basically want a consistent way to stop this coroutine when the scenes change.

My first thought was to have a StopCoroutine call in an OnDisable method since that’s a pretty general practice, but unfortunately that does not work here. It would be in the WaveSpawner script which always exists since it’s part of the logic scene. On second thought, this should work because the WaveSpawner should be disabled anytime we leave the Level and Base scene anyway. Fixing that may lead to a nice solution for fixing this error.

Test #1:

Use an OnDisable method in WaveSpawner to stop the SpawnWave coroutine, and follow this by making sure anytime we leave the Level/Base scene we disable the WaveSpawner.

SOLUTION: I looked to the LevelManager and OverlayCanvasManager in the Base scene as these together deal with all of the UI interactions that lead to leaving the current Level and Base scene combination. The main issue was that using Retry and Menu were not deactivating the WaveSpawner, so I looked to add this deactivation into the OverlayCanvasManager where these button functionalities reside.

Since LevelManager already has a reference to WaveSpawner and was deactivating it, I actually just made the WaveSpawner deactivation into a public method, DeactivateWaveSpawner, which called the WaveSpawner’s DeactivateWaveSpawner method, and gave the OverlayCanvasManager a reference to the LevelManager so it could just use this method. I’m not sure if this is better, but it seemed silly to have two seperate references to the same object for the same reason when these two scripts would always be on the same core object anyway.

I then noticed that all my small methods for switching scenes (Retry, Menu, NextLevel) all called the ResetOverlayUI method, which just sets all the UI elements in the level to inactive before switching scenes just in case they would stick around for any reason. Since they all called this method, I just added the DeactivateWaveSpawner reference here, to the ResetOverlayUI method. Now they all deactivate the WaveSpawner before shifting scenes.

This appeared to work, as the error stopped coming up. I even tested with a very large amount of enemies in one wave just to ensure it should still be running after switching scenes had it not been actively stopped. This also meant I could remove stopping the coroutine from the waveSpawnReset method in WaveSpawner without any issue. This was the method referenced by WaveInformation I initially used to solve the weird double spawning issue when restarting a level. This problem is now more cleanly solved as well.

NEXT STEP

Everything seems to work cleanly with Level 1, so the next step is just making sure any level works.

Unity Prefabs (Unity 2018) – Overall Coverage

August 24, 2019

Unity Prefabs

Youtube – Unity Prefabs – The Complete Animated Guide | Game Dev Classroom

By: Lost Relic Games

While this tutorial on Unity prefabs is slightly behind (the version they’re using in the tutorial looks to be 2018.3.5) it still covers a lot of the fundamentals of using prefabs in Unity, as well as some of the pros and cons of using them and how to use them properly. It will still be important to follow this up with more recent Unity prefab information since some concepts (like prefab variants) may already outdate some of the information in this tutorial once I understand them better.

The basic starting point is that a prefab is a container for a gameobject and all its children that you can package together into one nice, tidy, and reuseable object. You can then make several instances of this object from this one prefab.

Editing Prefabs

You can approach editing prefabs and their instances from multiple directions. You can edit the master prefab and apply those changes to every other instance of the prefab. You can edit a prefab instance, and then apply those changes back to the master prefab, and then apply those changes back to all other instances. You can also edit individual instances to have them vary from the original prefab.

You can edit the master prefab in its own prefab window now. When doing this, these changes default to being applied to all other instances as soon as they are committed to.

When editing an instance, you have options on whether to push those changes to the master or keep them separate. This can be observed with the overrides tab at the top of your prefab instance in the inspector. This lists all of the differences between your master prefab and this current instance, so you can see and decide which, if any, changes to push to the master prefab.

Nested Prefabs

You can child prefabs into other prefabs. The child prefab is still its own prefab that can be edited, and the change can be reflected in the parent nested prefab. The parent prefab now just contains that child prefab as part of its overall prefab package.

Using Prefabs

While they are generally thought to be used for objects that you will make many instances of (rightfully so), there are also benefits to having core objects of your game be prefabs as well. Objects such as your scene managers or player controllers can be prefabs to serve as sort of a backup object. If you are working on these types of objects in an isolated instance in a single scene, and somehow you lose that object or that entire scene, it will be gone forever. However, if you made these objects into prefabs, they will still exist as their own object in your assets.

Unpacking Prefabs

There are two versions of unpacking prefabs: unpack and completely unpack. Normal unpacking will just make the current prefab instance you have selected not be bound as a prefab anymore. All of its individual parts will be freed from the prefab connection. If there were any nested prefabs within this prefab, they will still be their own prefabs. It is just the overall selected prefab that is no longer a prefab. Completely unpacking however will unpack the selected prefab as well as any child nested prefabs. This ensures every single object in this selected object’s hierarchy is no longer connected to a prefab in any way.

It is important to note that unpacking a prefab does not destroy your master prefab object. It simply makes the selected prefab instance not a prefab anymore, so it is no longer connected to your master prefab. The master prefab will still exist to be used in the future/will not remove your other prefab instances. This made it a useful way to make similar prefabs, by unpacking your similar prefab and editing it and making that into a new prefab (this may be somewhat outdated with Unity’s new prefab variants).

Instantiating HFFWS Platforms with Parameters Determined in Script

August 23, 2019

Human Fall Flat Workshop

Instantiating Moving Platforms with Varied Parameters

Goal: Instantiating various HFF prefabs with varied parameters determined in script
– Should be able to work with multiple prefabs or provide a base foundation that can make further scripts which can work with
other prefabs
– Should be able to work with the signal and node system of prefabs
– Should also work with more standard values (basic Unity components)

CreatePlatforms Test

– This script focuses on working with the MovingPlatformVertical prefab
– It seems that using initialValue works to set values in the node system for this object when doing it upon instantiation
– Need further testing to determine the difference between value and initialValue and when to use both
– Interesting components and values to check:

The components for MovingPlatformVertical that we need access to are:
– Parent
– Mesh Renderer
– Mesh Filter
– Mesh Collider
– Signal Math Mul (Script): In 2
– Children (Axis)
– Transform (Rotation: X and Y)
– Linear Joint
– Max Value
– Max Speed
– Max Acceleration

– Tests:
– 1) Try assigning various parameters all throughout the prefab to see which work and which don’t
– I used the following code to set a bunch of various values of the instantiated prefab through script:

private void GOInstantiatePlatform()
    {
        GameObject newPlatform = (GameObject)Instantiate(movingPlatformPrefab);

        newPlatform.GetComponent<SignalMathMul>().in2.initialValue = input2Param;

        newPlatform.GetComponentInChildren<LinearJoint>().maxValue = maxValueParam;
        newPlatform.GetComponentInChildren<LinearJoint>().maxSpeed = maxSpeedParam;

        axisChild = newPlatform.transform.GetChild(0).gameObject;
        axisChild.transform.localEulerAngles = new Vector3(12.2f, 35.5f, 12.5f);
    }

– Many of the components I wish to alter are not using the node based system and were easy enough to set as pretty standard
Unity component values
– Setting these did change the values on the instantiated prefab, but they did not seem to impact the actual platforms
– For example, even though the axis was rotated at a different angle, it continued to just move up and down
– It appears the platform’s behavior is using its base prefab values before having the values set by the script
– UPDATE: Editing the LinearJoint params in script seems to work for instantiation as well
– It is specifically the Axis rotation that is not being properly followed

– 2) Try assigning values to an existing object through script
– See if this allows an object to operate based on scripted values
– This did not work either
– At least the axis alteration clearly does not work
– EDIT: This did work on the LinearJoint parameters
– it takes on the scripted values then uses those to drive the platform
– doesn’t use the Unity editor set values

– 3) Add script directly to Axis object to change rotation at Start
– Code:

private void Start()
{
RotateThis();
}

private void RotateThis()
{
transform.localEulerAngles = rotationValue;
}
– this also did not work
– still properly sets value, but platform uses original values to move

– 4) Try Test 3, but on Awake instead
– Code:

private void Awake()
{
RotateThis();
}

private void RotateThis()
{
transform.localEulerAngles = rotationValue;
}

– This actually worked! The rotation was set and the platform followed the new rotation

– 5) So try running the instantiation method of the platform in Awake
– this did not work
– similar outcome, values were edited but it did not follow the new axis

– 6) Set Axis rotation by using the LinearJoint Axis reference instead
– did not work
– similarly changed value, but did not have platform follow it

Thoughts for Further Tests:

– Might be worth seeing if this Axis is fed into the Signal system of the platform prefab main parent object
– Could be something where once it’s set in the Signal system, it’s set for the platform’s life
– Maybe could prevent this reference from happening until the Axis rotation is properly set

– Could be the LinearJoint reference for Axis that takes in the Axis transform
– this may get set once initially at Start and never be looked at again
– TESTING
– 1) Removing this reference at run time
– didn’t do anything
– platform kept moving as it was
– supports that this is just used once to set initial direction
– 2) Removed reference before playing in editor
– immediately grabbed a reference to the Axis transform anyway
– appears to be scripted to grab its own transform on Start
– POSSIBLE SOLUTION:
– could possibly use this by removing the LinearJoint on the prefab, so then we could set the
Axis transform and then add the LinearJoint so it would take on the Axis transform after being edited
– 3) Removed LinearJoint component from prefab, then in script, edited Axis transform, then added LinearJoint component,
then set the LinearJoint values
– This setup the object correctly (components were in the right place) but the platform did not move
– The LinearJoint did not take on the Axis transform automatically as assumed, and it was also missing the parent object
reference as the body to move
– Next: try setting those references manually in script as well
– 4) Tried adding LinearJoint with script as well as setting more values in script
– added the axis transform and the parent body object manually
– still did not move

– NOTES:
– from testing, found that editing LinearJoint values such as minValue, maxValue, maxSpeed, maxAcceleration at run time even work,
so it makes sense they were working sooner as they appear to be fine to edit at any time