Hey guys!
A lot has been done this week. After a couple of weekly design meetings we concluded how the game flow of Spirits of The Shogun works like, and it was up to me to program the game to have the decided flow.
In Spirits of The Shogun, you begin progressing through the endless level by walking to the right. You can’t walk back from where you came from. It’s like the level progression in older Mario games.
When you have reached a certain distance, a wave of hostile enemies will appear from both sides of the screen and the player can’t proceed further. The waves are split up into smaller subwaves, for structures sake. Each subwave has an amount of enemies appearing at the same time and when all of those enemies has been defeated, a short break follows before the next subwave of enemies appear. When all of the subwaves has been cleared, the wave is finished and the player can proceed forward again until a certain distance has been traveled.
Each wave will be designed to be different of course and during gameplay, all the waves will appear in a serial order and then be repeated. There is an introductory wave when starting a new game and it is going to be designed to introduce the enemies and mechanics of Spirits of The Shogun. It is the only wave that won’t be repeated after the series of waves starts over.
I really just added the ellipses of Game Over and Menu Screen so it wouldn’t look too empty…
The gameplay was structured into separate phases. The travel and fighting phases are the only ones in the game. It’s still easier to imagine the gameplay being split up like that, especially for someone who has to program it.
Programming this kind of gameplay is about having different codes being executed depending on the current state of the gameplay.
I’m using a boolean variable to determine which state is the current and I separated the different executable codes into functions.
The code for the travelling phase runs for as long as the player has distance left to cover. Programmatically speaking, if the player pushes the right boundary of the screen (which is about half the screen in the travelling phase) then decrease a certain numeric value. If that value is equal to or under zero, then it shifts into the fighting phase by changing the boolean variable. I won’t go into detail in how the code detects when the player pushes the boundary, because it’s not relevant to the main topic of this post.
While shifting into the fighting phase, the code prepares a wave that will be spawned.
Before talking further of how I programmed the fighting phase, I have to show what the waves are made of, visually.
Ignore the name of the script. It makes sense in a particular way.
If you don’t know what you see here, then I’ll tell let you know that Waves is an array that consists of objects of the type custom class Wave. It is the same with Subwaves and its elements. It holds objects of the custom class Subwave. You can see what kind of properties/variables each array and element has, in the picture.
The only thing you don’t see is a hidden property of the Subwave objects, called m_cleared, which is a boolean value that turns true when all enemies are defeated. I’ll return later to why this is necessary.
Another hidden property is a Wave object called m_currentWave and it holds the wave that approaches the player.
Now when the code shifts into the fighting state, a function is being called.The function uses an incremental value to assign the required wave to m_currentWave, then increases the value so next time the function is called, the next inbound wave in the order will be assigned.
Lastly, if the incremental value exceeds the amount of elements in Waves, then it’ll be reset to 1, skipping the introductory wave which is at index 0 of Waves.
In the fighting state, an if statement checks if the wave assigned to m_currentWave isn’t the current one yet and if it ain’t then it’ll start a coroutine. The reason it has to pass an if statement is to prevent several coroutines kicking off each time the game updates.
In the coroutine, it immediately makes the wave become the current one (it is in fact a hidden value that I didn’t mention about, that turns true) so this doesn’t happen.
Ultimate Ninja Storm
Here comes the real stuff – The enemy instantiation from the waves!
I wrote a nested loop so that it would iterate through both the subwaves and the enemies that contains in each subwave.
Each iteration through the enemies of a subwave instantiates them on the scene. What happens after the loop is something more special. I have the code wait (using yield return) until the m_cleared property of the current subwave has turned true.
Let’s jump out from this coroutine and explain what will exactly happen. As I’d put it, a coroutine contains parallell code that executes alongside the main one. The program doesn’t have to wait until that code of the coroutine has been executed. That’s why I can make the code wait without stopping the whole program!
Now while that coroutine does its thing, the main code checks if the amount of enemies defeated is equal to the amount of enemies in the current subwave. Of course, that current subwave’s m_cleared property turns true and resets the killcount to 0 if it meets the required condition. It only does this when the player is fighting the enemies.
What makes m_cleared go true, though? The value that measures the amount of defeated enemies is static and is being added to whenever an enemy is hit and is dying. That happens in another script, though.
Now, in the coroutine, the code proceeds to another line of code that tells it to wait for a brief time before iterating and instantiating the enemies of the next subwave. This creates the break between subwaves and the length of this period can be adjusted from the Unity Inspector.
When all subwaves has been iterated through, the wave is finished and the coroutine finishes by resetting all the boolean values involved to false. If I didn’t do this, the wave would be completed in an instant next time it arrives. All the subwaves m_cleared would still be true! In fact, no new waves would ever arrive because the game would never return to the travelling phase. It is indeed important to make sure you clean up after you’re done, so to speak.
And lastly, it calls the function that resets the distance left to the next fight. I’ll let you in on how it looks like with code.
For the curious programmers
After this, the game returns to the travelling phase again, and so on it goes.
That’s pretty much the game flow and even though it is simple, it is still a lot to cover. It might appear to be a difficult process to implement all this and know how but everything will really work out if you break it out into smaller and smaller steps. Spend some time writing a pseudo code in Notepad and figure out what needs to be done.
What’s next for me is to improve the current enemy AI and make the gameplay music dynamic (making it sound different when shifting realms). I’m sure to have a blog about either one of them next week!