lol I so grateful Pal sleep well
Classes start tomorrow for me I will get rest to.
Iâm going to take a c# beginners course in UDemy will look for Ben class on the subject I remember he mentioned he had one.
Ok, so that delivers on the first of the three items we initially outlined above;
player crashes (either into scenery or into an enemy)
outcome = Game Over - You Lose- player follows rail to the end but hasnât killed all enemies
- outcome = Game Over - You Lose
- player follows rail to the end and has killed all enemies
- outcome = Game Over - You Win
Bonus content for youâŚ
You mentioned earlier about re-scaling the text on the Splash screen, Iâm sure you spotted the red cross through it, again it was hugely oversized and outside of the canvas.
Iâve re-aligned the text and image and have the screenshots of what Iâve setup below, additionally, Iâve given you a little drop-shadow on the title text so that you can actually read it - I personally find the green on yellow quite hard to see. So Iâll show you the technique, without using Text Mesh Pro, and you can see what you thinkâŚ
If you want to create this, then try the following;
- Open the Splash scene
- Rename your Text UI Text GameObject to Title
- Set the anchor preset to centre/middle
- Set its transform and text properties as follows;
- Duplicate the Title UI Text GameObject and name it TitleShadow
- Set its transform and text properties as follows;
- Order the Canvas hierarchy as follows;
- Set the transform of the Image GameObject as follows;
- Save the scene
- Run the game
You should see a drop shadow on the text which will make it a little easier to read. You can of course move these items around the scene as you wish and change the colours and so on. Be careful when you move them so you donât end up with the same issue as before, e.g. the red-crosses.
Youâre taking it.
Thereâs the Unity 2D course and the Unity 3D course, both teach similar principles from the perspective of C#.
Okay will work on the text in the splash screen btw, did you read the post for the ideas I have to update this game? I will tell you more about another time its going to be great.
I did read it yes. I have to draw a line under how much more time I can invest personally on your project though, my support for yourself far exceeds anyone else Iâve helped on the forum. If I end up doing too much you run the risk of not learning by making mistakes yourself.
It probably would have been good for you to have tried extending some of the other games before this one, especially those in the 2D course, like Block Breaker / Laser Defender etc. You have some good ideas for your game but there are some fundamentals I think you should try to cover before reaching too far. Not least of which are âbackupsâ! Followed by getting your project in order, e.g. weâve deleted at least one script that wasnât being used this evening, we removed one previously when I helped you too, leaving a trail of stuff youâre not using is bad practice and just makes it harder to understand the project. So a good session on âhousekeepingâ would be beneficial here.
You will have noticed we removed at least three bools this evening too, the code still does what it needs to do but doesnât rely on the tanglement of boolean switches. So if you find yourself adding those again in the future, stop and have a think about the design and if there are other/better/cleaner approaches.
Regarding the last two issues, Iâm happy to help you resolve those - BUT - I donât want to have to download this project again and experience the pain of opening it/compiling it/recreating the assets database etc. I can spare a little bit of time tomorrow to try and resolve the last two issues but if you make a lot of changes in between then the copy of the project you have and I have will differ and that could cause problems as the results of what we do at either end may be different.
So, if you can hold fire making any further changes until we resolve these last two items that would be great and Iâll help you get through them.
Rob, you are amazing I commend you for all you do. I will only want guidance after we get these last few update done for researching info. on google for my other projects on this game. I understand if you have limited time . But from time to time if you could guide me to what key words I can use to get my work done I would appreciate it. Sleep well I will leave the game as is until tomorrow when you reach out to me.
Hi Martin,
A fairly long post coming up - please ensure you work through carefully step-by-step, it will be incredibly difficult to unpick any issues half way throughâŚ
Before you start - take a backup of your project as it was when we wrapped up last night, so if any of the following goes wrong you can easily return to that specific point.
Weâll tackle the second issue next;
player crashes (either into scenery or into an enemy)
outcome = Game Over - You Lose- player follows rail to the end but hasnât killed all enemies
- outcome = Game Over - You Lose
- player follows rail to the end and has killed all enemies
- outcome = Game Over - You Win
Before we start we need to look at and resolve an existing problem. As it is, when your game runs you have the player ship following the rail and there are the enemies in waves 1 to 5, these are all being controlled via the Timeline. In total there are 13 enemies in the Timeline and we see those as we fly through your game.
At some point youâve added some code for spawning enemies, this includes a vector to indicate where which you then randomise a little, you have set the number of hazards to be 13, presumably because that was the number of enemies you already had in the Timeline. You then have a coroutine which keeps spawning enemy waves. Within the coroutine method SpawnWaves
you also have a while
loop which never exits, so it runs through and spawns 13 enemies, then runs through again and spawns 13 enemies and so on.
Aside from a few issues with the code itself, the bigger problem here is that you are spawning enemies that the player will never be able to shoot because the player ship has already been moved along the rail passed them. If you want to actually see this in place, remove the while(true)
statement, not the code inside it, just that outer loop, then disable your Terrain GameObject, hide the Default layer via Layers,and, via the Lighting Settings window, remove your Skybox, youâll end up with a blue background and no terrain when you run the game which makes seeing all of the enemies that are getting spawned much easier, but of course you cannot shoot them and ordinarily the player wonât even see them.
The reason this is a problem is because you want to determine the outcome when the player returns to the landing pad based on whether they have killed all the enemies, with things the way they currently are you player will only be able to lose. Iâm not sure if the hazards/spawning code was just something you tried but then its got left over or if you had some other plans for it but for now I plan to strip that all out in order to achieve the items on your list.
So, letâs tear apart your GameController.cs script
- Open GameController.cs within Visual Studio
- Remove the following lines of code;
public GameObject hazard; public Vector3 spawnValues; public int hazardCount; public float spawnWait; public float startWait; public float waveWait;
- Delete the
SpawnWaves
method - Update the
Start
method, removing this line of code;StartCoroutine(SpawnWaves());
- Run the game
There should be no errors and in the Hierarchy you will no longer see enemy waves being spawned, but the original waves 1 to 5 are present and those 13 enemy ships will appear as you progress along the rails.
In order to know whether all of the enemies have been killed or not we need to obtain the number of enemies that are present in the scene. Because you are using Timeline youâll notice that whilst the enemy ships in their waves are enabled in Scene view, as soon as you run the game only the first two ships are enabled, all the others are disabled. As you progress through the time of the Timeline the other enemy ship are then enabled and earlier ones are disabled.
This poses a bit of a problem because during realtime the disabled GameObjects wouldnât be returned in any âFindâ method and as they are disabled they cannot execute code that says, âHey, Iâm hereâ - at least not until they are enabled.
Thankfully we can grab the count of enemies before Timeline disables them. We do that using this line of code in the Start
method;
enemiesLeft = GameObject.FindObjectsOfType<Enemy>().Length;
Whether it is by design or by chance that this executes before Timeline disables the GameObjects is open to interpretation at this point but for now, it works, so weâll stick with it.
Next we need to reduce enemiesLeft
each time an enemy ship is destroyed, this is currently being handled by the GameController.cs script via the DecreaseEnemyCount
method, which itself is called from the KillEnemy
method within Enemy.cs.
So we have the total count, and we are reducing it when enemies are killed. The next step is to determine when to evaluate the value of enemiesLeft
. With both of the remaining two issues you are only interested in the outcome at the âendâ of the game, this is determined by the player ship reaching the landing pad.
We can use the BoxCollider on the landing pad to determine if the ship has landed. At the beginning this would be true, when it takes off it would be false, and then when it gets to the end of the rails it would be true again.
- Select the LandingPad GameObject/prefab;
- Change the BoxCollider component to have its Center properties all set to zero
- Change the BoxCollider component to have a scale of 2.5 on the Y axis
Running the game at this point would trigger a Game Over situation because the CollisionHandler.cs script component attached your the Player Ship detects the collision and ends the game, assuming youâve crashed. Letâs address that so that the BoxCollider on the LandingPad will be ignored from the perspective of âcrashingâ.
- With the LandingPad GameObject/prefab selected;
- Add a new Script component named âLandingPadâ
- Update the code within the LandingPad.cs file as follows;
using UnityEngine; public class LandingPad : MonoBehaviour { }
- Save the script
- Open CollisionHandler.cs
- Update the
OnTriggerEnter
method as follows;void OnTriggerEnter(Collider other) { LandingPad landingPad = other.gameObject.GetComponent<LandingPad>(); if(landingPad == null) { StartDeathSequence(); deathFX.SetActive(true); gameController.GameOver(); } }
When a collision takes place with the Player Ship the above code will try to get the LandingPad component from the GameObject it just collided with, if this is null
the collision wasnât with the landing pad, in which case, we carry on and destroy the player ship.
Running the game now will return us back to where we were, the Player Ship wonât trigger a âGame Overâ condition as soon as it starts but we now have the ability to determine if the player is on the LandingPad or not.
Iâm not a fan of the architecture in the Argon Assault game, but at the same time I canât really go through and re-write your entire project, so Iâll offer the following which is âin-lineâ with the approach used in the course content, specifically around the use of SendMessage
.
Currently we are only carry if the player is ânotâ on the LandingPad, letâs update the OnTriggerEnter
method again to cater for when they are;
- Update the
OnTriggerEnter
method as follows;void OnTriggerEnter(Collider other) { LandingPad landingPad = other.gameObject.GetComponent<LandingPad>(); if(landingPad == null) { StartDeathSequence(); deathFX.SetActive(true); gameController.GameOver(); } else { SendMessage("OnPlayerLanding"); } }
We also want to know when the player was in contact with the LandingPad but no longer is, we can use the OnTriggerExit
method for that;
-
Add the following method to the CollisionHanlder.cs script;
private void OnTriggerExit(Collider other) { LandingPad landingPad = other.gameObject.GetComponent<LandingPad>(); if(landingPad != null) { SendMessage("OnPlayerTakeOff"); } }
At any point when the Player Ship leaves a trigger collider this method will get a reference to the LandingPad component, if it isnât
null
then we know that the player was on the LandingPad and no longer is, therefore they must have taken off.
Using the SendMessage
approach above we are making calls to two new methods on the PlayerController.cs script which we havenât yet created, letâs add those;
- Open the PlayerController.cs script
- Add the following methods;
void OnPlayerLanding() { isControlEnabled = false; if (!landed) { Debug.Log("Landing"); } } void OnPlayerTakeOff() { isControlEnabled = true; landed = false; Debug.Log("Take Off"); }
- Add a member variable as follows;
private bool landed = true;
Running the game now prevents the player from controlling the Player Ship until it has taken off, a âTake Offâ message is displayed to the console once the Player Shipâs BoxCollider leaves the BoxCollider of the LandingPad. When you return to the Landing Pad and descend again the player controls are disabled and the ship lands. A âLandingâ message is displayed to the console.
Now we have determine that the player has landed we can trigger the game over state.
Before we do, letâs just clear up a few more things. Your GameController.cs script has an OnGUI
method, this is called several times per frame and you have again added a âFindâ method in there which is getting a count of all of the enemy ships. We know that this count will be wrong once the game is running because some of the enemy ship may be disabled. If you look at the code you have in there, the reason your âYou Loseâ message appears when thereâs ONE enemy left should be fairly obvious. Regardless of how good the player is, as they kill each enemy, at some point there will ALWAYS be one enemy left, right before killing it, so your code would always display this message as it stands at the moment.
- Open GameController.cs
- Remove the
OnGUI
method - Remove the
endGame
method - Remove this line of code;
private bool killedAllEnemies = false;
- Remove these lines of code from the
Update
method;if (enemiesLeft == 0) { endGame(); }
Once the player ship has landed we can call the GameOver
method from the PlayerController.cs script;
- Open the PlayerController.cs script
- Add the following member variable;
private GameController gameController;
- Add a
Start
method as follows;private void Start() { gameController = FindObjectOfType<GameController>(); }
- Update the
OnPlayerLanding
method as follows;void OnPlayerLanding() { isControlEnabled = false; if (!landed) { gameController.GameOver(); } }
- Remove the following line of code from
OnPlayerTakeOff
;Debug.Log("Take Off");
- Update the
OnPlayerDeath
method as follows;void OnPlayerDeath() { isControlEnabled = false; gameController.GameOver(); }
Now that PlayerController.cs has a reference to GameController.cs and weâve added the call to the GameOver
method within OnPlayerDeath
we can remove all the references to GameController from within CollisionHandler.cs as they are no longer required.
- Open CollisionHandler.cs
- Remove this line of code;
private GameController gameController;
- Remove the
Start
method - Remove this line of code from the
OnTriggerEnter
method;gameController.GameOver();
Whilst we are here, the method ReloadScene
and the member variable levelLoadDelay
are not being used at all by any of your code;
- Remove the method
ReloadScene
- Remove this line of code;
[Tooltip("In seconds")] [SerializeField] float levelLoadDelay = 1f;
We now have all avenues pointing to the GameOver
method within GameController.cs but currently it isnât doing anything to check how many enemies are left and as such is displaying an inaccurate outcome.
- Open GameController.cs
- Update the
GameOver
method as follows;public void GameOver() { gameOver = true; gameOverText.text = "Game Over!"; if (enemiesLeft > 0) { outcome.text = "You Lose"; } else { outcome.text = "You Win"; } restartText.text = "Press 'R' for Restart"; masterTimelinePlayableDirector.Stop(); playerShip.SetActive(false); }
Run the game and you will now find that if you shoot all of the enemies and donât crash you see âGame Overâ and âYou Winâ when you land, but if you miss any of the enemies youâll see âGame Overâ and âYou Loseâ when you land. If you crash at any point during the game youâll see âGame Overâ and âYou Loseâ. In all cases there is an option to restart by press R.
Due to the positions of your enemies and their animations/movements and the angle of the player ship I donât find it easy to shoot them all which makes testing a real pain. To make it easier, if you disable Wave 2, 3, 4, and 5, AND the Enemy Ships within each wave, when you run the game the Timeline will only give you the two ships in Wave 1 to shoot, the enemy count will be 2, youâll stand a good chance of shooting both. You can then just fly to the end and see the win message, or, deliberately miss one or both and fly to the end to see the lose message.
Thatâs it, all three items delivered;
player crashes (either into scenery or into an enemy)
outcome = Game Over - You Loseplayer follows rail to the end but hasnât killed all enemies
outcome = Game Over - You Loseplayer follows rail to the end and has killed all enemies
outcome = Game Over - You Win
I hope I have explained things well enough as Iâve given the instructions above and that the code Iâve added makes sense to you.
There is still a lot of housekeeping that could take place in this code base at the moment, it really could do with a good tidy up. Iâd also move to not specifically set text properties of UI Text GameObjects where you donât need to. If youâve already set âGame Overâ in the Inspector, to change it to an empty string and then re-populate it seems a bit unnecessary, you could just get a refernce to the UI Text GameObject and then enable/disable it from code to show/hide it. The exception would be your Outcome UI Text GameObject which you do want to change to either win/lose.
If youâve followed all of the above and got to this part I would strongly urge you to take a backup of your project right now and keep it somewhere safe so that if you make some changes but then need to return to this point it is easy to do so.
I would then suggest working your way through the code and determine which scripts are not being used and remove them, which variables in scripts are not being used and remove them, which methods in scripts are not being used and remove them. Do the same for any of the assets as well, if there are things there that are not being used, clear them out, add them when you need them. Once youâre on top of all of the housekeeping and have run successful tests, make a fresh backup so you have a âcleanâ version of the project.
Hope this helps.
Rob Thank you, I will work on this now I was up early getting school assignments done your the best Pal
No worries, enjoy
Rob, all done now I will adjust the movement of the Player ship then the enemies movements. I canât thank you enough Pal
I like to give some compensation when I get paid from school.
I plan to update asset for the ships and create a video splash screen to link to the second scene. In the second scene, the player walks around as an fps game killing enemies. Should the player have a choice of payer ships to choose from? considering itâs a short scene I donât think is necessary. what do you think?
Rob, My player ship is frozen I canât move it just flies through the time line I can move with the controls anymore I went back to work on the movement but test one last before working on it - it wont move anymore. There are no errors in the console.
- Pause the game
- Run the game
- Select the Player Ship GameObject in the Hierarchy
- Right-click on the Inspector and select Debug
- Scroll down to the Player Controller script component
- View the
isControlEnabled
field - Un-pause the game
Does it remain set to false
?
hold on
Iâd suggest going back through the instructions one-by-one carefully and see if thereâs a step that got missed.
Yeah thatâs what I going to do. Thank you
The land pad was not sized or aligned correctly that fixed the issue once I aligned it.
Rob, are you busy?
I tried to build the game when it completed the enemies and player ship fx explosion has no sound.
I made some changes to the enemy script below but still no sound after building the game
[SerializeField] GameObject enemyDeathFX;
GameObject fx = Instantiate(enemyDeathFX, transform.position, Quaternion.identity);
fx.transform.parent = parent;