Prototype for the character selection screen

Learning Game Dev #3: Fixing bugs, development mode, UI, Lists vs Arrays, and renaming GameObject instances

At the beginning of the week I ended fixing multiple bugs just by changing one little thing, when I check if the menus/UI is enabled/disabled I set the game Time.timeScale to 0 , this was causing a wide variety of issues: The time counter was starting before expected, for example:

Multiple bugs coming from the same place without knowing it
  • The time left to complete a level was running through the GameOver screen even if the UI was not updated, so when you started a new game, some time went by already.
  • You could keep playing after dying without major consequences just by pressing a movement key, this is because the input trigger for the player was enabled, messing the GameOver() logic.
  • The time left counter kept running after dying, so if you died because of an enemy or hitting a wall, you could die twice while you were in the UI.

Debug checks:

I’ve been adding debug checks here and there in order to make it easier to work in the game while trying stuff, this basically runs before certain pieces of code when in development, but won’t be active in production. For example in order to increase the change of something happening by 100%, disable enemies, etc, … without having to touch the actual code, just a true/false switch.

UI, general art, and consistency

I started to work in the final art of the game, and for that I tried to follow certain consistency in colours, palettes, sizes, etc …, with all assets, so the end result will be more cohesive. For example the interface:

- Background: eeeac5
- Soft green: b4d2b4
- Strong green: 5a855a
- Non-interactable green: 98A382
- Soft red: e69594
- Strong red: de696a
- Font size: 80px for bigger headings, 30px for the rest
- Buttons: 280px x 60px
- Icons: 16px for background, 24px for characters with 2px black outline, 32px for items with 2px black outline.

Other stuff I learned the hard way this last week when working in the game art:

  • If a parent object doesn’t have a rect transform, the child objects rect transforms won’t show up. This may happen easily if you create the object directly without using an UI element. For fixing it you only need to add a RectTransform component to the parent object, but may not be super clear at first glance.
  • The Canvas object > Canvas scaler component > UI Scale Mode: Scale with screen size && Reference Resolution should fit your target device, in this case mine was x:900 & y:1600 . After changing this there was some oddness but mostly nothing wrong.
  • Via Anchor Presets (shift key to set the pivot + option key to set the position + click in one of the 9 squares) will snap the text to a certain part of the screen. Once this is done, you can move it around, as will snap anyway to a relative distance in all devices. More details on how to do multi-resolution:

“UI elements are by default anchored to the center of the parent rectangle. This means that they keep a constant offset from the center, If the resolution is changed to a landscape aspect ratio with this setup, the buttons may not even be inside the rectangle of the screen anymore.”


https://docs.unity3d.com/Manual/HOWTO-UIMultiResolution.html

Regarding image sizes, I though .jpg was better for web/optimization, but all my experiments with Aseprite and creating assets for the game have thrown that saving as .png was a better choice.

For example when working on a 1280 x 2272 px background saving it as as jpg was ~1mb, however saving it as .png ended in ~283kb . Even better, when I moved into 640 x 1136 px resolution to fit the 5s testing device this went all the way down to 18kb and 5kb respectively. This most certainly will need more tweaking when testing in other devices, but for now this seems the way to go.

ArgumentOutOfRangeException and understanding Lists vs Arrays

You want to be sure that the elements of a List are there when trying to access them, otherwise you’ll face this error.

A List is basically a dynamic array which its size can be modified at run time, this means that you can assign memory to it if needs to hold more variables. With a List you can add or delete memory on runtime. On the contrary arrays are not modifiable later as the memory they need is reserved when the array is declared, this memory then can be used or not but you cannot have more or less memory, that memory is taken up.

You will use one of the other entirely based on your specific case, for example you’ll use a List in an RPG where you need to add items to your unlimited inventory, however if these spaces have a cap then you know its limitation and will be more efficient to use an array.

The Factory Pattern

I’ve added an EventGenerator() to the game which creates exits, items, and enemies based on certain randomized parameters, later on I discovered that this is a very simple approach to a known pattern known as The Factory pattern . I haven’t had the chance to learn about this deeper yet, but definitely looks interesting for this type of process.

Real playtesting

As was not possible to submit the app to TestFlight (I initially though that was going to be simpler process to just submit an Alpha and let cherrypicked testers to play it, but the app must be already in a good state as well as have pretty playable and not have major bugs, gaps, or performance issues) so for the moment the app is still not in TestFlight but I had the chance to make some people around me play with it, which game me really good feedback about things to fix this week for the next iteration, things that you (as a developer) don’t really see and fresh eyes always help.

Definitely let people test your game as soon as possible, even when is just a gameplay prototype.

Level 1
Level 2

Removing (clone) from GameObjects

When instantiating GameObjects I saw that they appear with the (clone) string attached to its name, this was not a major deal until it become a technical problem. How? See the following snippet:

if (gameObject.name == "enemySpider"){
enemyDirection = transform.position.y - 1.0f;
transform.localPosition = new Vector3(transform.position.x, enemyDirection, transform.position.z);
}

What happens when the GameObject name is not enemySpider anymore, but enemySpider(clone)(clone)(clone)(clone) ? Yep, nothing happens, the code won’t trigger because the if statement is not met.

With just an extra line of code we can remove the extra bit from instantiated GameObjects (we don’t remove it technically, just rename it as the original variable), that would be: _yourInstantiatedVariable.name = _yourOriginalGameObject.name; here’s an example:

GameObject enemySpiderGameObject = Resources.Load<GameObject>("Prefabs/enemySpider"); 
GameObject enemySpider = Instantiate(enemySpiderGameObject, _availableVectorsToScreenUp[mRandomPosition], Quaternion.identity); 
enemySpider.name = enemySpiderGameObject.name; // Removes "(clone)" strings from the GameObject name

Join 241 other subscribers

Comments

Leave a Reply

%d bloggers like this: