(Receive email notifications)
(warning: contains some flashing lights)
(Fake08 support added)
Survive 50 micro bullet-hell levels set over more than a dozen environments to face the final boss.
Player 1: Arrow keys to move, hold Z,X,C,V,N, or M to slow your ship down for maximum control
Player 2: ESDF to move and Q,TAB,SHIFT, or A to slow.
Pressing Q/TAB/SHIFT/A at any time will enable player 2 to join at the start of the next level.
Standard: Survive each of the 50 ten-second levels. If anything hits your cockpit you'll need to restart.
Invincible: Experience the game without having to restart. See how few times you can get hit.
Pressing Right on the main menu will take you to a level select screen. Every level is unlocked from the start to make it easier to resume a game, or skip past any difficult levels.
Pressing Down on the level select screen will change game modes.
Pressing Left on any menu screen will turn off background music.
If you complete the game playing all the levels in order then your total number of restarts will be recorded on the cartridge as a high score (single/two player and each game mode count as separate high scores). High scores can be reset in the cart menu.
Aliens have invaded the solar system. A giant ship has been discovered beyond Mars, that is utilizing a mini black hole to convert matter into energy to fuel their attack. You must stop them by any means necessary. You have the fastest ship in the fleet equipped with a revolutionary boost drive. You've got no offensive or defensive capabilities, but a path has been determined that will avoid the most enemies. It will take you through Earth's atmosphere, past the Moon, and deep into caves on Mars.
When you're boosting, you'll be invincible, but your drive only operates for a short time and will need 10 seconds to recharge. Just stay alive while you're recharging. I don't know what you can do when you get to your target... you'll need to improvise. But don't fail, you're our only hope.
Huge thanks to Krystman from Lazy Devs Academy for his excellent tutorial series that were instrumental in getting me through this game.
Also big thanks to: SlainteES, Heracleum, SmellyFishstiks, FReDs72, Thelxinoe5, and everyone on the Pico-8 Help Discord.
And thanks to my son Matthew for his invaluable playtesting and encouragement.
I really enjoy the dodging part of bullet-hell games, but have actually no interest in the shooting part. Stripping shooting out just gets rid of a lot of clutter and distractions on what you should be doing. I also dont like having to restart large sections of gameplay, so kept each level to a 10 seconds limit. With quick restarts I felt this got rid of all the frustration points a game like this could have.
None of the enemy behaviour is randomly generated. Every shot either happens exactly the same each time, or is aimed at the player. That way levels can be 'learned' and mastered.
Having two player support was important, actually I was tempted to make it 8 players, but would have found testing difficult. In invincible mode the game doesn't track which player is hit more often because I wanted to emphasis cooperation over competitiveness.
As a 3D artist trying to come up with pixel art was really difficult. It was fun creating 8x8 sprites, but after a while I experimented with using 3D models to generate the sprites. The title screen, asteroids, and craters were all 3D models that I rendered out at the tiny resolution, then used photoshop to set it to the pico8's indexed colours, then saved as png files and dragged into pico8's sprite sheet. I found converting them in photoshop before dragging them in looked a tiny bit better.
I found this method to be a nice simple approach for me, but the results aren't anywhere near what a good pixel artist can do.
Environments and Music
I tend to lose interest in things quite quickly if they become too repetitive, but with limited gameplay variations and enemy types in this game I wanted to keep things interesting by packing in as many environments and different music ques as possible. The environment features are randomly generated to save Map space. Music is never the same for two levels in a row, though there are themes tied to the different environments.
Invincible mode was added as an accessibility feature. The standard game is quite hard in places and it felt a shame to restrict the game only to people with the physical ability to be able to deal with it. Similarly I wanted to have the full level select from the start, so as not to limit how people could play the game. I feel more games should have accessibility options.
Half the game was written with a token count of 7000-8100, I hadn't anticipated how much of development would be refactoring and learning token optimisation tricks (if I couldn't write tables as
split"1,2,3" this would never have been finished :) )
The _ENV feature was essential for keeping the token count down in the game. It seems confusing at first, but can be implemented very easily and saved having to write
self. for every object variable.
I hadn't anticipated how tricky it could be to smoothly integrate music into a project. I spent a while unsure which part of the code should have ownership of the music controls. In the end I created one master function that controls what music is playing and when and put it straight in '_update' running beside my state engine.
(Receive email notifications)
I'm been trying to play around with raycasting and I hit lots of problems with the maths so tried copying some code from cast.p8. it kinda works but I'm clearly making some silly mistake when I try to render the image to screen. I'm taking the length of each ray (from left to right, one for each x-axis column), and calculating the height of the wall it hits by just dividing an arbitrary number by the ray length.
Then for each column I'm drawing a line which has the y-value being half the height of the screen + and - half the height of the wall.
In my head this makes sense, but when I render it out the walls look fine, but I'm getting tons of noise above and below the walls.
Does anyone have any idea what I'm doing wrong?
wallH = (1500) / tdist --set the wall height by distance, smaller the distance the bigger the wall
if(wallH > 127) then wallH = 127 end --cap the wall height
line(sx, 64-wallH/2, sx, 64+wallH/2) --sx is the x-coordinate