So I want to start off by talking about my tooling so far, and how I've set up the project inside of Unity.
I've not seen how others do this or what a mature in-house shmup engine looks like, so I'm very curious to see the similarities and differences among the solutions I came up with and those of others. Please send suggestions and questions! I looked at projects like Shmup Creator: Build & Blast as I went. There's a caveat here that I'm going to change and expand these further as I go so these aren't their final forms.
I wanted to use as many features native to Unity as I could. So what do we need?
The most fundamental part the view is the Arena. From the outset I needed to have a play area that could be moved and rotated while the player's view remained fixed, and crucially without affecting player motion at all. The motion of all bullets, enemies and the player all happens within the arena's local space. The camera is fixed to the arena as well. My project uses Cinemachine, and the arena camera is a cinemachine virtual camera. Also, the game uses a secondary camera that renders only bullets on top of all other parts of the scene so they can't be obscured.
The white box is the Arena's bounds. There are also some placeholder enemies moving there.
There is also scenery there, the ocean (the first stage happens over the ocean). I use a scenery manager to scroll scenery objects. The transform space of scenery is seperate to the arena, the arena can rotate away from that forward scrolling direction if it wants to.
Scenery in my game is instantiated at runtime. I did this so the player can advance through the stage at a variable pace, and the scenery will be tied to their progress through the stage even when scrolling quickly. So the scenery is made from chunks, which are prefabs, that get loaded in order until they fill up some distance ahead and behind.
The 'chunked distance' is dynamic during the game. I usually keep it short so that response from the system is fast, but it can be made long for instances where the camera faces towards the front and back horizon. And then brought back to short again with removing those chunks. If you look at
, they sometimes use a very short fog point to hide loading objects but I'm hoping that I can display a believable horizon (and sunset) with this system.
Chunks are stored in a Scenery Zone.
It's a basic asset. When the scenery changes to a Zone it will create the Start chunks, then loop the Main chunks. On changing, it add the End Chunks.
Zones usually get changed when new encounters happen, more on that in a second. The chunks themselves are just Unity prefabs with an appropriate component. These have colliders so that when enemy ships are killed they can turn into a physics object in world space and collide with the scenery below.
Stage Manager has the most responsibility in a single level. I intend to have one stage manager per scene and for all intents and purposes it
is the level.
The level is defined by a series of events that are executed in order. Some of these can take time and others are instantaneous. I wrote my own custom inspector for this component.
You can rename and reorder components and change their properties. In this image is the Capture Players component, this will either spawn the player objects if they don't exist or take ownership of them if they got loaded in a previous scene. The stage manager also (for now) contains the information on the player's team. This includes the current leader ship. Everyone's meter and stats are stored individually still.
Encounter is the most important event here and the level will be mostly Encounters.
When an encounter starts it can command a change of zone and it can choose how the level waits, with a time limit or destruction of all enemies. Enemies are stored in Squads, which are just prefabs. There's an optional delay on the instantiating of those squads.
So squads are pretty important because the contain the enemies.
So it's a group of enemies collected inside one prefab that are spawned all at once at a certain location, usually outside of the player's view and then they move into view. The red box in the gizmo indicates the relative position of the player's view.
Enemies are spawned in the transform space of their parent squad. This is useful if they are grounded enemies, but if they are flying around then the Release Mode allows them to be moved into Arena space either immediately or when they come into view. The squad tracks it's own enemies and destroys itself when they have all been defeated.
Enemies are not considered to be 'in play' until they've entered the arena. Until this time, they cannot damage or be damaged. They can despawn by leaving the arena only once they've entered it first.
Enemies are important!
Enemies run on a behaviour driver that's individual for each enemy. They move based on their Move Behaviours, and shoot with Weapon Behaviours. There can be multiple behaviours in a row and they'll run in order. Some are instantaneous and some take time, with a dynamic ending condition (this makes enemy motion difficult to preview without a real enemy doing it in the game). These behaviours are things like 'move to location', 'move to player' or 'follow this path' and have some properties.
The squad stores behaviours, too. The squad will send orders to all it's enemies, so that you don't have to set it up on an individual basis. These orders are added ahead of any individual orders and that's what 'initial behaviours' means. You can also set the 'mirrored' flag on an enemy and all its motion orders will be flipped. I'm currently working on a behaviour that lets the enemies coordinate in different formations, like a circle.
For cutscenes, I'm making use of Unity's Playable Timeline. I'm recording motion for cameras and the arena. The player can be at any position when a cutscene starts, so I animate placeholder transforms and I blend the position of the player between the desired cutscene position and their player position.
As mentioned, I use cinemachine to blend between gameplay and cutscene cameras. I also use Yarn to handle dialogue.
This is all super rough. I've written stuff as and when I've needed it, and I've not had much insight into standard industry practices when it comes to games like these. So I'm curious to see how my approach diverges from others, or is similar to others. I'm definitely open to advice for doing things in a cleaner way or hearing how you did it. I suspect there are some games that build their whole level out in a single scene instead of dynamically loading pieces of it in this manner.
Thank you for reading