Hey! What's good, everyone
Not a whole lot to show this week. The main things that got crossed off the todo list were:
Find a bunch of old megadrive sound effects to use as placeholders and pop those in game (this is still ongoing). I really like the old FM sound effects on the Megadrive/Genesis.
Refactor the way that items work. Previously I'd only pseudo coded items, so you could receive them from chests but they wouldn't actually do anything. I was hoping to avoid actually implementing items just yet as it's a bunch more work and maybe not necessary for this vertical slice. However, after doing a bunch of play testing it felt like the demo would be better for it, it's hard to get through without needing more data/mana. I did have some "vending machines" set up that instantly refill your health and data, but they felt really clunky, plus you can't use them in battle.
So now we have a new menu option to use items outside of battle, and a new option in battle to use items (although this battle code needs a big refactor, it's piggybacking o the magic system and is just really gross and hacky).
Here's another super exciting UI screenshot:
The next big task I took on this week was a refactor of the rendering system. Having refactored the UI to render nicely on tiny screens and look good when scaled up, it seemed like a good thing to do. You wouldn't be able to tell the game is actually rendered at native size and scaled using the cameras orthographic size, unless you look closely (not too closely) at the lighting.
If you look at this screenshot, you can see that the lighting is super smooth, and is being rendered at much higher resolution than the artwork.
I've seen quite a few tutorials on how to get pixel perfect gameplay but I went for my own weird approach as none of them really fit how Jack Move is set up. Here's a brief overview of how I've got it set up.
I have a bootstrap scene that sets up a bunch of stuff. The main object is the World Controller that handles input, loading new maps, showing the gui etc. The World Controller is a singleton and sticks around between scene loads. Nested under the World Controller I have a new "Render Stuff" object, this has new camera and ui canvas underneath it.
The canvas has a RawImage object on it. This is what we'll be rendering to using a simple material.
The important thing here is the PixelRenderer component. This sets up rendering on every scene load or when the resolution is changed. Here's the code:
https://gist.github.com/empika/61bf4939635783c7e133c587465bd429The main part of the code is the UpdateEverything function (a lot of things could all do with better names
). Here we do a bunch of stuff...
...when I say source resolution, i mean the tiny version. Eg, the source resolution for 1280x720 might be 427x240.
* Get the camera we'll be rendering to (Just using GameObject.Find. It doesn't happen very often so we should be ok to do that here)
* Work out the source resolution (small res) we'll be rendering at (see below), and the scale we'll then be rendering that at.
* Create a new rendertexture and set it up
* Most importantly here is setting the resolution and setting the filter mode to FilterMode.Point.
* Set the orthographic size of the camera we are rendering to (this should be 1/2 your source resolution.)
* Setting the main texture of the RawImage's material to the rendertexture we just created.
* Set the size of our RawImage to our source resolution
* Set the scale of our RawImage to our calculated scale
Most of the other stuff in this class is related to turning off the ui when a battle starts and other jack move specific bits. I guess the only other important thing is caching the screen width and height at the end of UpdateEverything. We can check against these cached values in update, if either one has changed then we update everything again to accommodate the new resolution.
Here's the result, note the chunky lighting as it now matches the resolution of the assets.
So, about calculating resolution and scale... I have a simple formula. Given a screens height and a target height we can work out the scale, and then the source resolution (our small one that we'll then scale up).
To work with the example I gave above, 1280x720... I have designed the UI around 320x240. To get our scale we take the height of the screen, 720 and divide it by 240, which neatly divides by 3, otherwise we would round down. Another example, 1920x1080, 1080 / 240 = 4.5, which we round down to 4.
That gives us our scale, which we can then use to workout the source resolution. 1280x720 @ 3x scale = 427x240. 1280x1024 @ 4x scale = 480x270.
Once we have our scales and target resolutions, we can set the rendering camera to use that orthographic height, recreate our rendertexture with the new size, set our UI image to the new size and then scale it up.
I think the main advantage of this approach is that there are never any cropping, we're always rendering at the same aspect ratio as our screen. And using the UI we don't have to mess around with planes and matching their scale to camera size and vice-versa. As long as we set our UI image to the same size as our render texture we can scale it up and down to fit the screen.
That's probably not the greatest explanation of all that, so let me know if you have any questions!
Oh, and this morning I already moved some of the in world UI to the gui layer. This means that it wont get smooshed into any fullscreen effects such as bloom or the battle transition. I think the only thing affected by this is the portals that link maps. I just had to recreate the direction arrow sprite as a UI element and then update it's screen position to track the in world position. They get placed as world objects so I can easily position them within the collider. You can see how the arrow is placed in world on the left. At runtime the renderer component gets switched off so only the UI version is displayed:
This week will mostly involve finishing up adding placeholder sound, a whole bunch of playtesting/qa and fixing all the stuff that comes out of that.
Wow, big update this week! Ok, think that's it!
Cheers