This blog will be way to track ideas and progress related to improving MOTU’s background composition system and PicoDraw.
While developing Masters Of The Universe I was considering the idea of having some for of scrolling backgrounds but there was no time nor token space to experiment. Right now, I am re-using the same system on Klax to draw the biggest part of the play area (the conveyor belt) but it’s still nothing more than a static background.
The final goal of this experiment is applying it in scrolling games, generating backgrounds with a combination of limited tiles and “generated” graphics brushes using pico-8’s gfx primitives and fit a whole game with at least 3 big scrolling levels in a single cart.
A simple beat’em up could re-use a lot of MOTU’s code for sprites, input and states. I had my sights set on Double dragon because e it’s kind of short and simple, plus I played a lot on the Amiga version which had a limited move set and graphics compared to the arcade but was still fun enough.
Last week I saw @Pigmeat ‘s post about his Streets of Rage pico-8 demake and that gave me enough motivation to experiment a bit while finishing the most boring technicalities on Klax.
First thing, I did some research on Double Dragon to see what I have to deal with and found these 2 treasure troves of information:
http://www.electriccafe.xyz/double-dragon
https://doubledragon.kontek.net/
The arcade game has a resolution of 256x240 which is a good fit to be reduced by 50% to 128x120, 1 arcade screen can fit in 1 pico-8 screen with 1 row of 8 pixel left at the bottom. Characters can fit in a 2x4 composite sprite, like MOTU so I can reuse the same composition system.
The map can be divided in 5 stages, 3 of which are plain horizontal scrolling, 2 are mixed vertical and horizontal. The firs stage is around 5 screens wide and as a first test I’ll try to reproduced with the hybrid system.
As a first step, I design a mockup of the first screen, using standard tiles/map to get an idea of the aesthetic at this resolution and proportions of various elements.
This looks fine to me but It’s using xx tiles for this single screen!
Studying the tile set and the object represented we can see a couple interesting things: there are some huge elements (windows, door, car, some large walls) that needs at least 4/6 tiles to be represented and, even if in this screen are repeated 2 or 3 times, will never appear in the game again., and a couple which are used once in other levels. These are good candidates to become object brushes.
I will define a table of these object with an id and anchor position in level coordinates, and then draw them on the fly after creating them in picoDraw.
brushes = {{2,-8,8,24,24},{1,16,8,24,24}, ...} --id, x pos, y pos, W, H |
The drawing routine expects object coordinates to be within -48 to 173 but we can reposition them while scrolling using pico-8’s camera() function.
Let’s test it on a wall and a window after drawing them in picoDraw
in code this object is a string like this:
"+&NNem\0&ONdl□&QQbj⁴!PPck⬆️!NNemく#VdXh¹#VeWh²#UeVh\n!PUcb$F" |
Reusing most of the code from MOTU, we will draw this and the tile version side by side and see how it looks
I’d say not bad but while scrolling, there’s shimmering in the gradients because fill patterns don’t respect local coordinates. This is a problem that I couldn’t solve while working on MOTU and I was banging my head on until recently when I found this life -saver post on pico’s forum.
I applied @sparr ‘s code and finally, this long-lasting problem is solved! The window is next
There will be a minor reduction of 1 px details with these objects, but overall, I think they can look the part and the tile set is drastically reduced (I did the door too and the missing ground tiles are because they were just palette swap and are now generated in code).
Now drawing a lot of these will use a bit of CPU so we can’t abuse it but, we can already skip drawing the one that are outside screen view for a minor optimization and, in the future, I’ll try to implement a form of caching to improve performances.
for i=1,#brushes do b=brushes[i] if b[2]+b[4]>-(cx+8) then camera(-cx-b[2],-cy-b[3]) do_draw_cmds(brush[b[1]]) --@todo: test caching to user ram/sprites end end |
It scrolls! (at 60fps in pico-8, gif is a bit jerky)
Next on the list is drawign the car and revamping of picoDraw to add 2 functionalities I will need for more complex objects, sprites and text management.
Looking great so far. It'll be really interesting to see how much you can fit in using these techniques.
Looking good! If I may make a suggestion... It would be useful if the code was structured so that someone using a map that is procedurally generated each frame could drop in their map generation function in a single place.
Not sure if I understand it correctly, but as long as the generated map is stored in map memory with mset, picodraw drawing system already supports it automatically with the exception of the layer parameter. If it's something else, could you give me an example?
While planning how to add the missing functionality to picodraw, I kept thinking that it would be useful to have the ability to mirror these kinds of objects either while creating them or while drawing them, so the car and the door could be just 2 mirrored halves like I was doing with sprites.
I tried experimenting and obviously went for the hardest and less useful approach which was trying to copy the drawn object into user ram and then mirror it when memcopy-ing it again to screen, similar to the motu background but repeated for every object….
At first, I was just memcopying from and to screen in reverse order, but to preserve transparency this would have required a lot of bit math manipulation.
Then, I tried coping a chunk of user ram to sprite ram in a single 16x1 sprite strip and drawing it with spr() preserving color transparency and leveraging the existing flipping functions.
As an initial experiment I was looking for a game with vertical symmetry to just copy entire rows of background tiles and I remembered level 1 of r-type had pretty symmetrical parts.
The bad news is that both these methods were slow no matter what, specially at 60fps, and I wasted an afternoon chasing the wrong solution
The good news is that they were totally unnecessary, because I already had all the data I needed to just mirror while drawing the primitive on the first pass, as we know the boundaries of the objects and all objects are created starting at coordinate 0,0 so we just subtract the coordinates form the max W and H depending on how we want to mirror it and it’s done at no extra cost.
The 2 big “arms” are the same, mirrored brush object
The better news is that now I got this
Since this game logic seems a bit easier than double dragon, I decided to work on a demo for the first 2/3 levels of an r-type pico-8 port and then re-use the underlying system for double dragon
I'm looking forward to the updated picodraw. I ran across it recently and wondered if there was going to be a vertical mirroring, too.
Your solution seems far more flexible than what I was attempting with 'meta tiles' and limiting tiles to 4 colors so I can stack two different 4-color graphics into a single 8x8 block and then coloring them on a fly with a predefined palette when they're put into larger 16x16 tiles consisting of normal sprite palette and flip operations (obviously done prior in a custom editor like pico draw). However, this would be extremely difficult to pull off something like those rad robo arms, if not impossible using the idea I was dorking around on.
Anyway, great job on this, and if you ever get the inclination to do so, I'm sure I don't just speak for me when I say "Can't wait for R-Type, Double Dragon and PicoDraw 2 (or whatever it may be called if you end up sharing with us other dorks)".
Peace!
Thanks, i have a new picodraw release ready but there are a kot of changes and I need to find the time to write some documentation. Maybe now that the new r-type demi is out i can take a break and finally do it.
[Please log in to post a comment]