that last slide where you stopped short of the dot for some reason the car looks a lot more nimble in this gif, a lot more controlled too. Did you change the specs? Also, are you thinking of adding multiple car types/unlocks or anything?
No changes to physics in a long time, but I guess on this level there are no collisions so you can go full throttle. Maybe I changed the tolerance for showing skid marks slightly, so there are less here? I’d have to double check.
I’ve already done multiple car types with different handling and sprites. Still more to do though.
I don’t show everything, got to leave some surprises, right?
Wanted to reply here because it pertains directly to this project–
In the modding thread you mentioned your workflow for 3D to sprite sheet @matt, I was wondering if you’d be able to go into more detail here about that. I’m interested in the car results you got for my own projects as pixel art is mysterious to my tiny brain while 3D modelling just makes sense. I would love to be able to take some 3D models I have made / animated and export them relatively easily for PD sprite scripts. You mentioned it briefly above this but if you’re willing, I’d love to hear a more detailed explanation of your workflow/tools.
Sure thing! I finished a new car last night and simplified my files and process a little more. I’ll put together a post today.
Caveat: part of my workflow uses Mac-only app Retrobatch https://flyingmeat.com/retrobatch/
Apologies for the delay, I took the plunge and upgraded to a Mac mini and 4K display so had to migrate my setup and then figure out why my workflow was broken (hint: retina!) compared to my old rMBP with non-retina display.
So, my workflow uses the following apps:
This is so I can re-run a workflow at any point (maybe in a make file) which I often do during development. These become executable assets, of sorts, in my project.
Here's the model definition to try out: car.scad · GitHub
I love building 3D this way, it's kind of like LEGO. I use basic geometric building blocks (cube, sphere, cylinder, polygon, etc) and some boolean operations (difference, intersection, union). There are some other cool things (hull, minkowski). I have the commands in Dash.app alongside the Playdate SDK docs.
This is what the model looks like with all the blocks I have used to make it visible at once.
My particular approach is subtractive — kind of like sculpting — I start with large blocks and cut away at them using other shapes and the difference function. When finding the exact placement for a block I use the # precedent which makes the blocks show up as semi-opaque red blocks.
I colour each block in a one or two shades of grey, black and white. This is to help with the conversion to 1-bit later on. It's not so obvious here as the lighting makes the colours look many different shades - for example the wheels are black with white centre but look grey here.
Using some simple programming constructs and variables I can add booleans to trigger different states, I use this for angled front wheels and tilted car body.
And also to set the distance and rotation of the camera relative to the model when in animation mode. In this mode I enter a speed (doesn't matter but higher the better) number of frames = 32, and the tick the box to dump the images. The tick disappears when the images are all done.
I also rendered the skid marks, car shadow, and some other elements.
There is a lot that is annoying about this app
...but I still use it! I am not aware of anything else quite like it.
Edit: since writing this post I've been able to condense this process into 1 single workflow that executes much faster. See further down this thread.
For each batch all I need to do (now the they are set up!) is set source and destination folders and press the go button. As time goes on I refine these, so at some point I'll make it so that it automatically gets the filename of my current model. Not a priority right now though.
The grey shades that I applied to my model in OpenSCAD give an element of control during this conversion process. Greys can be pushed either way, towards black or white, depending on my need with the specific model I am working on. In this instance I desaturated the greys which blows them out to nearer white. And then I chose to threshold to reduce to b/w. I also have the wheels as a separate finished image so I don't have to worry if their detail is lost during this phase, I can just paste over the accurate/finished wheels.
Final result, unedited:
I would later touch up the sprite by hand to reinforce any details I think have been lost. I use Piskel for edited sprites because it has really nice sprite sheet support, drag and drop loading, and quick and versatile exporting.
holy batman! What a great write up, thank you so much for sharing!!
I find it fascinating that you use OpenSCAD to program your models instead of something like blender, but I can also see how small, more simple objects might be easier to program than model by hand. I’ll have to dig into OpenSCAD more than I have in the past (for 3D printing stuff).
Looking at your explanation for Retrobatch, I wonder how much could be accomplished using vanilla Automator actions. Certainly a cool app, I never knew it exited!
Thank you again for the explanation, you’re doing great things with this project and I continue to look forward to your progress updates
I shouldn’t be this surprised but that is an impressive number of sprites for one car and explains why your animations are so smooth. How are you handling your animation tree so smoothly @ 50fps??
Keep up the awesome work!
Confession time: I have never used Blender. The barrier to having to learn it is always too great when I can just carry on with my existing tools. You know?
No idea about Automator, but I think it would be more limited and way more difficult than Retrobatch. I mean, you could do all this on the command line if you wanted to. But why would you when there’s a great app like Retrobatch?
32 is a number that is a leftover from a different prototype and it’s stuck. I guess it should really be 36? or 24? or 18?. But it’s too late now! Actually it would be relatively easy to change but I have bigger fish to fry.
Handling the animation changes - there is no tree! I don’t do anything special other than set the frame number based on the angle when that changes, but only actually change the sprite image if it needs changing. It’s so very important to do all you can to not update or move sprites unless you actually need to. Minimise drawing!
The animations alone could run at 99fps — it’s anything that causes more drawing which slow things down. Collisions, not because they are computationally heavy, but because they cause a lot of sprite updates - which means drawing moving things - to happen. I’m working hard to maintain 60fps on device (50fps in simulator for… reasons) and am excited I’ve managed to get here.
I’ll have a think about posts for other aspects of my game.
I am curious @ast-rsk how would you go about building a car like this in Blender?
It’s late but I took a crack at the F1 model. Here’s about 5m sped up to about 1m, just the modeling part and no texturing though it would be super easy to texture since you can use a 1-bit texture to uv map in seconds. Of course I have your reference image so it’s faster to accomplish and I made a bunch of mistakes because I didn’t plan this out too well.
oops, gif cutoff. heres the mp4 of the timelapse
My thinking here is that if I were to adapt your process on windows, I’d use blender (I guess it would work on mac too) to setup a template file that has a camera animation where stock camera positions are setup as keyframes, and in sync with those keyframes would be the necessary part movements like wheel positions.
Using the template project you could model up and texture a car pretty fast and then just hit render to render out each frame. From there we would need some way to stitch the images into a sprite sheet but theoretically blender can have its render settings configured to output the right size per-frame, right color depth, even add in shaders that could filter for you, and the only step would be to stitch into a sprite sheet. I’m over simplifying the process because again, it’s late. But that’s just off the top of my head with your great detailed process explanation!
Thanks for that! Very interesting.
All of my positions and distances are based on units that I know map well to a 32px sprite, so eyeballing it like you did gives me shivers. But I guess there’s a way of entering specific coordinates into Blender? But that would perhaps slow down the modelling process?
Something for the weekend?
Here's another demo build of Daily Driver. It is a couple weeks old (dated 30 July) so whilst not representative of where I am right now with the game it does show my progress since the last build I shared at the start of June. Would love to know your thoughts!
Debug Keys for game (simulator only):
Controls for editor (simulator only):
Debug Keys for editor (simulator only):
For 0.11 and newer I added some quick menus:
Feel free to share some levels on here if you dare.
Sorry if I missed something obvious but is there anything I have to do before running it? I copied over the pdx and it crashes because
How strange! I believe others have been playing it fine, and I played it to take the GIF.
@veubeke Is there a chance it’s unzipped wrongly? The error indicates it can’t find the contents of the track subfolder inside the pdx.
Thinking about it a little more… you say “copied over the pdx” could you try opening it on the Simulator? assuming it works there you could choose “upload to device”. This is how I install games on my device.
Thanks, I didn’t know you could open pdx files in the simulator. I had to delete folder in
Data on the device, then upload from simulator, and it worked.
I wonder what the difference is between copying it over and uploading via simulator. I tried a few other demos and they worked fine after copying.
Glad you’ve got it working.
Hi Matt! How do you catch custom (debug) keys in the simulator?
Check the docs at:
Simulator debug callbacks
These callbacks are only invoked when your game is running in the Simulator.
Thanks a lot!
It’s amazing how I missed this in the docs.