Planet Defense devlog

Latest build here: PlanetDefense.pdx.zip (489.5 KB)

I was pretty inspired by the Starlight thread from Dustin, I felt why not share my experience with this first prototype.

I started this game idea in Unity twice, but with much bigger scope and much failure, originally from the LD46 game jam. I didn't finish even the basics because I got caught up in all sorts of deep rabbit holes and wasting time on things that didn't matter to the core of the idea.

As this is my first experience with LUA (and of course the playdate SDK!) I broke down the idea into a core nugget of an experience heavily associated with old arcade and simple games. The goal is to destroy enemies as they spawn and fly towards the planet while the character moves around the planet to shoot. This is a fitting concept for the crank and I hope I can make something interesting.

I first got my workspace setup on an old (and freshly revived) mac mini using the Nova beta. It was super easy to get setup compared to my attempts with windows and it's really great to have the simulator as I patiently await the dev kit delivery.

Hello World:
I followed a wonderful intro from the panic twitch page and managed to get some basic input and drawing working. My experience with p5js really helped here and so did the graphics API.
workingSample

Core Mechanics:
I never had to dig into the logic of how to setup this player movement as Unity APIs helped do a lot of the work for me. So I looked up some information and wound up figuring out how to get my player to move around the planet.
circleMove

Along with this simple movement, I set out to begin experimenting with different things to draw as the player.
lineSample

triangleSample

New Features:
Alternatively, getting sidetracked by a nugget of an idea. I wanted to play around with the idea of things existing outside the screen. So I spent a considerable time figuring out how to "peek" a little further as a mechanic for the player who will be dividing their time shooting and moving around the planet.
peekSample

Hopefully this is interesting, I'll try to update this thread with considerable milestones. For now, I need to figure out spawning enemies and having enemies move towards the planet.

6 Likes

Alright, in a last minute burst of inspiration, I managed to get "enemies" spawning and moving towards the planet. I had to learn about lerping and used what I learned about the character movement to accomplish off-screen spawning around the planet and I think it turned out great. Because I'm lerping the movement varies as they get closer to the target (planet). And I think I have it setup in a way where I can actually expand what details I apply to the enemies such as health, move speed, and whatever else. Very interesting learning about LUA and its tables.
enemySpawnSample

I'm still waiting on hardware so I'm going to upload the current build to this thread if anyone wants to try it. On my mac it runs flawlessly (as you can see in the FPS text) but I'm curious how the real hardware will handle hundreds of entries in the enemies table.

For the latest build, check the top post. Fair warning, I played around with the metadata and some placeholder images, but the simulator doesn't really deal with that right now so I can't guarantee it will even launch. If you do get it to launch, I'd love to hear how the performance is :playdate_finder:

1.6MB is huge for a pdx.

Check your folder structure to make sure you've not got any non-source files/folders in your working directory.

Will try on my device in the morning!

edit: your .pdx includes your Builds folder with a handful of previous builds inside. PDC bundling everything in your project directory also caught me out early on.

1 Like

Thanks, I'll fix that now!

Edit: Cleaned it up, hope that's better. Appreciate it!

1 Like

Here's your .pdx on my Playdate. As you can see, you're nowhere near dropping frames yet :playdate_proud:

(The video is unlisted on YouTube, so it's not publicly visible without knowing the URL.)

1 Like

Thank you so much for sharing that! I see I have a lot more room than I thought, visually. Playing in the simulator on a 1080p screen upscaled to 1440p really doesn't convey the space used compared to that real screen.

I'm curious how you feel about the 1:1 crank movement. I hadn't realized just how small the system is in hands where holding it using the D-Pad like that might make it more difficult to crank and peek. I guess that's more for me to figure out mechanically when I get mine. But again, thanks for sharing I appreciate it!

1:1 crank seems good to me. We have a longstanding game with a similar radial crank control scheme, and this feels just like it.

1 Like

New version! This was a bit of a challenge trying to work around my OOP-oriented training. But hopefully it means I'm reducing the amount of memory and graphics.
enemySpawnUpdateSample

Changes:

  • Refactored and reduced overall size.
  • Paused state based on crank dock status. (drops to 1fps when paused)
  • More sensible enemy spawning rate.
  • Ramping difficulty of enemy spawns.
1 Like

One thing I noticed is that with the crank flush along the body of the device, what I would call up/down, the cursor in your game points right/left. So it's 90' away from where I expected it to be. With my crank code, it took some trial and error to get things lined up in that regard when there is direct visual feedback of crank alignment.

Your 1fps pause mode is a good idea, but it causes the menu button on the device takes a while to open. Though I would say that is perhaps an issue with the system software.

Oh interesting. I noticed when it unpauses, it takes a second for all the gameplay to get going, but that was an unplanned feature, I found the delay to be satisfying, giving a moment to the player before the action continues. But I hadn't considered the impact outside of the game. I wonder if that is SDK or firmware related and if I can mitigate it somehow.

I'll play around with increasing the paused refresh rate a little to see if it feels better while still having that brief pause for the user. At the same time, it would be interesting to see what any devs have to say. I'll also consider adding a silent count-in so regardless of refresh rate the user gets the respite. That might be the best course of action right now.

Appreciate the feedback, keep it comin' :playdate_happy:

Edit: Oh and to the point of the crank, I must have accidentally reverted something because I did have it lined up. I've noted that and will fix tonight!

Alright, new day new build! This one's bigger.

I managed to make a decision about state management and just go with simple string states:

local currentState = nil
local lastState = nil
local states = { 
            playing = "Playing", 
            mainMenu = "MainMenu", 
            dead = "Dead", 
            paused = "Paused" 
         }

switchModesSample

pauseSample
It works fine and still comes with the benefits of being able to expand it later. My Update method is a bit of a mess, but for the most part it's a simple if else tree where the logic is kept in matching state checks.

   if currentState == states.mainMenu then
      -- Do stuff
   elseif currentState == states.playing then 
      -- Do different stuff

I found I've been spending a lot of time cleaning up code and the non-OOP aspect of this winds up with a fair amount of spaghetti, but I'm not good enough to really avoid that yet. So I'm just living with it for now.

The next thing I added was UI during gameplay. The nineslice features are extremely useful and really help quick prototyping. I made a small tile in aseprite and loaded it in using the examples in the documents.

topBar.backgroundImage = gfx.nineSlice.new('Assets/Sprites/topBar', 10, 10, 44, 44)

uiSample

I've run into a bit of an issue with the cells drawing in the gridview that I'm using as the UI topBar. This will be a problem to solve another day though. I feel like I need to keep the momentum up and address these issues later.
tableDrawErrorSample

I'm not doing a great job organizing my thoughts here today so if there's any questions, please feel free to ask! I just enjoy sharing my progress. I'll leave you with a "stress" test I did where I just left the spawning going (enemies spawn more often as time goes on) which I felt was pretty funny.
600enemiesSample

As always, the top post contains the latest build. Curious how this one runs now that I cleaned up some stuff (though I imagine it's pretty much the same).

3 Likes

Great approach! It's so easy to get caught on little things and get zapped.

I try to get to playable prototype as qickly as possible, just to prove it's fun!

@ast-rsk 1.6 megs isn't necessarily big for a .pdx. (When you add in sound, they will get much bigger.) I wouldn't worry about optimizing for size at this point.

@greg that's good to know. I think we're all just trying to get a handle on it at this point. Appreciate the feedback!

Very true Greg. I should have been more specific in that it seemed big for such an early prototype with few assets.

Progress continues. Today was big because I finally stopped messing with structure and really bit into the mechanics of the game.

Major changes include:

  • Added shooting! Finally!
  • Added collisions for player and bullets.
  • Added i-frames for player if they're hit in flying mode.
  • Added ability to "sweep" enemies in ground-mode.
  • Added death screen with final score and mockup for high scoring.
  • Added enemy and bullet recycling so the table doesn't grow if we can safely reuse the existing table entry.
  • Tweaked a ton of variables and reordered lots of the checking logic so it makes more sense frame to frame.

This has been a fun day because I really got to see a lot of progress, visually. A lot of the ground work in past updates setup where I am now, and it felt really good to be able to rapidly implement these long standing TODO features.

There's not a lot left right now on my TODO list, in general more cleanup is needed as well as effectively splitting main into multiple library files and splitting out that top level variables into tables that can effectively communicate between those files. Thanks to help from the discord I have a plan of attack but that will be the cleanup phase. Other than that, the only major work left is audio- I'm excited to break into the synth system and start generating those w a v e s :playdate_cry_laugh:

I also need to crack open file saving and figure out how to write the current high score to the system in order to allow persistent scoring. It shouldn't be that difficult and I already have some of that framework in place to make it quick to implement. For example, a lot my logic is using centralized score counting so it's only ever effectively updated in one location.

Another thing I need to tweak is how the bullets fire. Or rather, how they move. Specifically, it's possible for them to jump from frame to frame and skip over an enemy's hit radius. I'm don't want to go into line intersect math tonight so good luck to anyone who tries it :playdate_tear:

To that end, the final thing on the TODO list is to have a way to reset gameplay so game over can be reset and a new game can start. This is a little scary because of all the variables in main but I might hold off on this until after the planned refactoring in which this might be more simple to accomplish.

Not a ton to show off today, so I'll leave just a couple new gifs below. As always, latest build in the top post :playdate_smirk:

gameplayShootSample

gameplayDeathSample

Edit: Not really sure why the last gif is so slow, it came right from the simulator. This might be a bug. I'll file something if I can repro.

4 Likes

I'm at a point in this project where updates will probably be smaller and less "hey look new feature!" focused. But I still wanted to share some progress I made today that I'm really happy with.

My TODO keeps growing and shrinking but I think that's healthy. Between last night's review and tonight's, the list grew about double but I also knocked off about half. For me, that's a big win-- particularly because it keeps me motivated as I see things getting accomplished.

In this particular update, I focused on finishing the total gameplay loop from first launch to death, as well as an admittedly clunky reset to let players get a high score and come back around for another go. A lot of this is still dependent on adding flair such as title graphics and state transitions/animations.

On a more mechanical note, I fixed a long standing issue which was that I was lerping bullets instead of moving them at a constant rate, so there should be significantly better shoot-to-kill feel as it's far less likely to miss an enemy by bullets skipping frames. I also fixed the order in which collision was being detected to give it a more realistic sync between visuals and calculation.

Other small improvements include the ability to earn back the shield after you lose it, the framework for storing high scores persistently (locally for now) and fixing the crank animation at the title screen. :playdate_cry_laugh:

I'm probably going to step away from this for a few days because I've technically reached my goal with this prototype. So I need to let my brain recharge a little and digest the good and the bad so I can come up with a final scope of wants and needs for this to reach a "1.0" state.

I just wanted to give a big thanks to everyone who's helped me out when I got stucktemporarily fixed at one point and anyone who has followed along so far! This is already a great community and I'm very glad to be a part of it!

1 Like

This will probably be my final update, save for any major issues discovered when I get my hardware delivered or if I post the source.

I managed to add in sound samples using the sampleplayer. Really great feature that's super easy to use. I just had to sprinkle in triggers for playing effects and I feel really good about how easy it was to find where to place these 9 samples. I generated them using sfxia and just loaded them into memory because they're short samples. I'm in no way an audio engineer but playing in the simulator and hearing sounds really made me feel like this project has come to a close.

I'm not sure if the high score write/read is working properly because I'm still getting used to the simulator and how it functions. But honestly, this project isn't going to be some season release. It was just to get used to the SDK and I feel like I've done this. From here on out, it's all about improving my understanding of Lua and learning about/implementing a lot of the great libraries and ideas others have kindly shared.

There's a good chance I'll come back to this project later either as a "what I know now" rewrite, or if I feel exceptionally motivated to make big changes. But for now, it is what it is and I'm happy with it.

Thank you all for your kind words, your help, and in general your awesome sense of community. And thank you to the Panic devs for all your support and hard work! On to new, sprite-ier pastures :playdate_wink:

5 Likes