Hiya
Performance! Optimization! Synergy! Words! Do you think Tech Bro types know what these words actually mean? or do they say them to sound smart? In their defense I don’t know what synergy means, I have looked it up several times but it slips from my mind like every other word trying to cling for life in the tech bros brain.
I’m at that point where performance is starting to move to the forefront of me thoughts. Splitting every memory intensive task into sections so I can control when and where each is used. Levels are split into sections, each section is it’s own Unity Scene, my thinking here was that I could add what I wanted in each scene, not having to worry too much about performance if I just wiped each small section when the player moves on. But the wiping and creating of scenes is expensive enough, hence those annoying sections we all know from games, long door opening, weirdly placed narrow corridors, or just static loading screens.
Five Arcade is designed to be sped run (speed ran? Is ran a word? As a wheelchair type person all movement based words have got away from me) and I can think of nothing worse for a speed runner than to be slowed down (slow downed?) by performance drops.
A slight headache I’ve had lately is trying to accept my limitations. I doubt I can get this game up to a polish level of a Mario or Sonic but there is as charm to the solo developed game which I hope to lean into. That sounds to be a pre-emptive excuse for the games limitations, which maybe it is, but finding the line between pushing boundaries and accepting limitations is a line I find hard to see.
Loading of the next section will take place in little rooms at the end of the current section. Just before the player enters these rooms there will be a finish line (like Mario’s flag or Sonics spin-y sign) and the timer will end when you cross the line. This will avoid doing any loading when the player is trying to zoom through the section, mindful of time. The doors in these little rooms will remain locked until everything is sorted, framerates can drop and no one will even know! Sneaky. I’ve often thought that coding games and stage magic were linked, misdirection is the key.
Some terminology to clear up, everyone loves that.
“Scenes are where you work with content in Unity. They are assets that contain all or part of a game or application. For example, you might build a simple game in a single scene, while for a more complex game, you might use one scene per level, each with its own environments, characters, obstacles, decorations, and UI. You can create any number of scenes in a project.” {Unity Docs}
This angers me. For perfectly normal reasons. I’m not unreasonable, all I ask is that my opinion should be the only one and everything should change based on that assumption. Reasonable. A scene to me is a setting, some stuff, and some time. The first scene of MGS4 for instance is Snake banging on about how “War has changed” while in the back of a truck. But a Scene in Unity’s context could be the UI overlapping Snake as he tries to escape the PS3 architecture. As multiple Unity Scenes can be active and displayed at once, I’ve named the collection of these Scenes: Stages. Jenious, Genyus, Genius…one of those is the correct spelling, probably.
So a Stage could be comprised of:
I’ll need to pass around what scenes need loaded and unloaded for each Stage. I’ll store these scenes in a scriptable object, which is basically just data that can be passed around.
public class Stage : ScriptableObject
{
[field: SerializeField] public List<string> RemoveSceneIds { get; private set; } = new List<string>();
[field: SerializeField] public List<string> LoadSceneIds { get; private set; } = new List<string>();
}Future Note: replace the string list with an int list with the numbered scene IDs. Animations have a hash conversion from string to ints, I wonder if I can do the same with scenes?
So I create a Stage Scriptable Object and fill it with the Scenes that need loaded and unloaded. I keep all my shared resources in a scene called Game, so Game is a good place to store a StageManager object. From here I can pass the ScriptableObject
private IEnumerator LoadAllScenesInStage()
{
var asyncOperations = new List<AsyncOperation>();
foreach (var sceneName in stage.LoadSceneIds)
{
var asyncLoad = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
asyncOperations.Add(asyncLoad);
}
bool allScenesLoaded = false;
while (!allScenesLoaded)
{
allScenesLoaded = true;
foreach (var asyncOp in asyncOperations)
{
if (!asyncOp.isDone)
{
allScenesLoaded = false;
yield return null;
break;
}
}
}
IsLoaded = true;
}I used to panic when I saw Async on anything, it’s such a technical sounding word that my brain would trick me into thinking it was an advanced method that only super-intelligent hyper-genius types could grasp the concept.
Asynchronously means not in a fixed time or sequence. So loading the scene will take place in the background and not cause the current scene to have a little panic.
This code creates a list of Async Operations, fills the list with LoadSceneAsync calls, then runs each one until it is done (which is checked with the asyncOp.isDone call). It will continue this until all scenes are loaded.
I have a yield return null line there which is a bit lazy, it should return some sort of progress check, but I have an idea to use this later…if that’s not a hook to keep you reading in the future then I don’t know what is.
The counterpoint to this is:
private IEnumerator UnloadAllScenesInStage()
{
var asyncOperations = new List<AsyncOperation>();
foreach (var sceneName in stage.RemoveSceneIds)
{
var asyncLoad = SceneManager.UnloadSceneAsync(sceneName);
asyncOperations.Add(asyncLoad);
}
bool allScenesUnloaded = false;
while (!allScenesUnloaded)
{
allScenesUnloaded = true;
foreach (var asyncOp in asyncOperations)
{
if (!asyncOp.isDone)
{
allScenesUnloaded = false;
yield return null;
break;
}
}
}
IsUnloaded = true;
}This is basically the exact opposite of the Load from a few seconds ago.
Using these two methods means we can add and remove scenes in game. There are Scenes that are constantly present, namely: Game, UI, Character. The levels are broken into sections, which follow the naming scheme: LxPy where X is the Level Number and y is the Part Number, Level 1 Part 2…very expressive.
No. I have named each level so it’s clear this game was designed by a human. Level 1 is called Breakout because you play Breakout and have to break out of an arcade machine, it’s has so many layers! Well, only two. Two is a perfectly respectable and complex number so sshhh.
My plan is to keep 3 Level parts active at all times: The current Part, the previous Part, the nextPart. So when the player gets to the End Zone of Part 3 for instance, Part 2 will be Unloaded and Part 5 will be loaded. (Part 4 will already be there). I could design the parts in such a way where when the player enters Part 4, Part 3 could be Unloaded without the player noticing, but for now at least, I’ll stick to what I’m doing.
Instead of a progress bar on the UI I’m going to see if I can add a little in-game progress bar in the waiting room while Ve waits. This may end up being completely unnecessary or pointless so I’ve added the task to a “Future Polish” list to be looked at when everything else is done. The idea here is to avoid a kind of feature creep thingy.
Now for the really interesting part! Object Pooling! Object Pooling was my golden hammer for a while, every problem of memory allocation was solved by object pooling, such was my love of the technique. It sat comfortably with the Command and State Pattern in my toolbox. As awesome as the technique is it has flaws (who doesn’t). The basic principle is that instead of creating and destroying objects that you use frequently, you put them in a pool and grab them when you need them. Asteroid fields in my game use them. When an asteroid gets shot by the player, or hits a wall, instead of Destroying the GameObject, I’ll put it back in an Object Pool, reset it and reuse it if needed.
My dilemma involves how to effectively use object pooling for Ve’s bullets. I want quick access to each gun and the ability to swap out any gun at any time quickly and efficiently. The Inventory system in Five Arcade is an Array of GunOrders. So each gun isn’t stored in memory, the description of them is, so when called upon, a GunFactory creates a gun based on the description given. When the gun is created it is parented to one of Ve’s arms. So my options are:
This has the advantage of only having one active bullet pool at a time. HOWEVER!!!! there will be memory issues when creating the pool regularly which may nullify the entire point, diving headfirst into an empty pool.
This sounds better to me but will require some coding wizardry. Luckily, I’m a level 6 coding mage with a sore leg (I don’t know why that is relevant). So when a gun is created it is simply linked to an existing bullet pool. There will still be a set up cost here but it will only be paid when the player retrieves a new gun rather than when they equip any gun. HOWEVER!!!!! there will be a potential of 8 active bullet pools at once which sounds like a lot of water to slosh around in.
Let’s just code the projectile bullet pool…
private ObjectPool<ProjectileBullet> CreatePool()
{
var pool = new ObjectPool<ProjectileBullet>(OnCreateBullet, OnTakeBulletFromPool, OnReleaseBulletFromPool);
return pool;
} OnCreateBullet creates a new instance of the object, which will then be reused. Unity’s website states that it will create this Instance “when the pool is empty.” which confused me greatly because what happens if the pool isn’t empty but you need to create a new instance anyway. So the pool is supposed to hold between 20 and 80 bullets say, but it currently only has 18, the pool ain’t empty but I’m still calling a create function. I reckon the misunderstanding here is in my head but I thought it worth mentioning. If I’m being a bit silly, just ignore me. Projectile bullets in me game are the simplest of the bullet types, so when one is Instantiated all I need to do is parent it to something…
private ProjectileBullet OnCreateBullet()
{
var bullet = Instantiate(bulletPrefab);
bullet.transform.SetParent(transform);
return bullet;
}OnTakeBulletFromPool gets called when you ask for an instance of the GameObject and OnReleaseBulletFromPool is called when the GameObject is no longer needed and is returned back to the pool…
private void OnTakeBulletFromPool(Bullet bullet)
{
bullet.gameObject.SetActive(true);
}
private void OnReleaseBulletFromPool(Bullet bullet)
{
bullet.gameObject.SetActive(false);
}When I get a bullet from the pool here’s what’s happening:
var bullet = bulletPool.Get();
bullet.Assemble(gun);
return bullet;Now, the Assemble method is a bit of a mess, I wasn’t expecting visitors so please don’t judge me:
public void Assemble(Gun gun)
{
active = true;
BulletFactory = partsFactory.BulletFactory;
rb2d.linearVelocity = partsFactory.CharacterAim.Direction * gun.BulletSpeed;
transform.position = gun.ExitPoint.position;
transform.rotation = Quaternion.Euler(0, 0, partsFactory.CharacterAim.AngleInDegrees);
collisionListener = partsFactory.CollisionListener;
AttachedGun = partsFactory.AttachedGun;
}This is doing the usual bullet type thingys, setting the position to the tip of the gun, aligning the rotation with Ve’s aiming direction and adding the initial velocity.
Currently the bullet pool is created when the related gun is Instantiated so my job now is to profile this and compare it with the new idea and see which wins. There can be only one winner. This would be a much better TV show than Celebrity Get me to Feck Out of Here ya Prick It’s Cold Season 654-2.3, or whatever it’s called. We pit Design Patterns against each other to see which is the one Design Pattern to rule them all. Uh oh, I’ve lost myself again.
My deadline for a public demo is for March the 6th which is scarily close so I am off to do a bit of optimization and profiling before I have to polish every pixel inch of the 8 sections I have designed for the demo. Oh, Happy New Year.
Bye for now.