On Sale: GamesAssetsToolsTabletopComics
Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

[DevLog] Air Ace

A topic by GlitchThief created Jan 07, 2017 Views: 855 Replies: 20
Viewing posts 1 to 16
Submitted(+3)

Greetings, I'm GlitchThief, real name Mori, and this is my first DevLog for my first game in this FIRST GAME GAMEJAM! I'm playing with the PICO-8, which is an application that bills itself as "fantasy console for making, sharing and playing tiny games". It's incredibly charming in its design and provides some tools to dive in and get dirty making simple lo-fi games. It's really cute and fun to play with and I can't wait to make my first game in it.

I'm arbitrarily titling my game Air Ace. I've played all kinds of games all my life, and SHMUPS are seemingly the simplest that I regularly come back to. I'm aiming for an incredibly simple "technically complete" experience, with any surplus time I'm left with used for polish. We'll see what someone with no art experience can do with an engine that lets you make 8x8 16-color sprites.

Some basic goals:

  • Allow players to move in 8 cardinal directions
  • Allower players to shoot forward. Bullets remove simple enemies.
  • Two enemy types. Simple enemies are removed when hit by bullets. Tough enemies become simple enemies when hit by bullets.
  • Provide 3 lives. Getting hit by a bullet causes a player to lose a life.
  • Provide a bomb, an emergency button to remove all enemies from the screen and make the player temporarily invincible. Bomb gets reset when a life is lost.
I'm so effed.
Host(+1)

this is a solid set of goals for a shmup! i can't wait to see your works in progress!

Submitted(+1)

The goals look really good for a first game, particularlly for PICO-8. I know I'm putting off all the coding of objects that need to act indpendently (such as the bullets or enemies in your game) because I know it's going to make my brain hurt. Good luck!

Submitted(+1)

Day 1 + Day 2

I've been learning a lot, but I don't feel like I have a lot to show for it, except for an appreciation for better planning. Things went swift at first, making some odd sprites I knew I'd need and working on preparing the stage. This involves drawing a big blue rectangle over the screen (THE OCEAN!?) then drawing Ace, our protagonist, on top. A basic pong-like tutorial featured in Pico-8 Zine #1 had already prepared me to set the controls, and I'm smart enough to set rules to keep Ace on the screen (don't let his X or Y positions increment up or down at their respective borders). That's where I left off last night.

Today, things got more interesting. I drew some really rough functions for firing bullets and tracking those bullets across the scren. Then, I drew an enemy sprite. He's uh... an alien ship. A demon head?


okay, he's programmer art

I realized I'd need to control when and where these jerks would spawn, which took some thinking on my part until I came up with a solution I'm not really satisfied with. I'm keeping a count of frames that increments on each frame. Then, when the count reaches a certain number, in this case 30 (aka 1 second in 30fps), the enemy appears on a position I designate. In theory I can time and release enemies at specific positions this way. Seems kind of cumbersome, but I'm not really sure how else I'll do it.

The next horrible realization I had is that I would need to be able to track the positions and states of multiple enemies throughout the game. In truth, I had realized I'd need to do the same thing for bullets beforehand, but I had cheated by just setting the life of each bullet to roughly the amount of time it would take the bullet to cross the speed and then preventing the user from firing another one until that time had passed. Kinda gross to be honest.

I really really really didn't want to have to write code to declare, track, and draw every single enemy in the game, so I took a break with PAT Shooter before diving into its code. As a side note, other PICO-8 games are definitely my main excuse for procrastination during this jam. I couldn't stop playing more lives of PAT Shooter for "inspiration."

Anyway, it took me quite a while to figure out what I was looking at, but when I got it, it was like a lightbulb going off in my head. Actors with consistent behavior in the game are all declared in tables. As far as I can tell, Lua tables are basically just arrays. The way this handles is pretty smooth. At any point I choose, I can (for example) declare enemy E, sent E.X and E.Y to the appropriate X and Y coordinates I want to set it at, then add E to a table called Enemies. At any other time I want, including while iterating through that table, I can change these properties of the enemy, or even delete the enemy outright. From there, it was child's play to write code to iterate through the enemies to alter their positions (movement) and draw them.

I opted to refactor my bullet code to take advantage of tables and it has already payed off. I ended my day by setting up a bullet and enemy collision check where I iterate through each bullet in my Bullets table and, in each loop, iterate through each enemy in my Enemies table. If their positions overlap (or more accurately, exist within ranges that appear like an overlap on the screen), I delete the given bullet and the given enemy from the table. They stopped being tracked or drawn and PICO-8 doesn't choke on the mid-iteration adjustment to the tables at all! Beautiful!

When I started on all this bullet and enemy stuff this morning, I thought I had hit the hard part. Oh boy was I wrong. Now I have to consider enemy movement and firing behavior. I guess I could just have each enemy move at a static speed and fire a bullet that moves at a faster static speed? Kind of boring, but I think I'll stick with that plan for now. If I end up with more time later, I may try to fit in more complex enemy behavior, but at this point I'm already choking on the idea of just trying to declare and position enemies over the course of the game.

I gotta be careful going forward. I made my first bullet system in a rush and ended up having to refactor the whole thing when taking shortcuts failed me. That was already painful and it was a relatively small chunk of code. I could definitely see this blowing up in my face.

Submitted(+3)

Here's the bullet collision, by the way. It's a good thing I'm deleting all bullets that leave the screen or I bet this process would start to slow down real quick.

Submitted(+1)

Nice! If you have a chance, it may be worthwhile to make a modular set of functions to determine intersecting objects that you can then use inside enemybulltecollide() and other collision checks. I'm currently using a couple functions for different purposes:


function in_range(value, imin, imax)

return value >= min(imin,imax) and value <= max(imin,imax) --min/max calls are unnessary if imin and imax are correctly ordered

end


function range_intersect(min1, max1, min2, max2)

return in_range(min1,min2,max2) or in_range(max1,min2,max2) or in_range(min2,min1,max1)

end


function rect_intersect(r1, r2)

return range_intersect(r1.x + r1.off_x, r1.x + r1.off_x + r1.width - 1,r2.x + r2.off_x, r2.x + r2.off_x + r2.width - 1) and range_intersect(r1.y + r1.off_y, r1.y + r1.off_y + r1.height - 1,r2.y + r2.off_y, r2.y + r2.off_y + r2.height - 1)

end


Where each object has an x/y position (.x,.y), an x/y collision box offset if I don't want it to start at x/y (.off_x/.off_y), and a width/height for the collision box (.width/.height).

Submitted

Wow this is way more elegant and maintainable! Thank you for sharing!

Submitted(+1)

No problem! Can't promise it's the most efficient, but I thought it may be useful.

Submitted(+1)

uhh is it really Day 6? I was sick and now I'm working extra hours to make up the lost time at my real job so I don't know. Today I tried to implement logic to handle two types of enemies, and spent the better part of an hour trying to figure out why the system was reporting unclosed functions. It seems, DESPITE some examples in the wiki, the correct syntax is elseif, not else if. I would have known this if I had just looked at the manual instead of relying on some random 3rd party BULLSHIT that I googled up.

Would-be PICO-8 users, avoid the wikia page. It fucking blows.

Submitted(+1)

Bummer, I usually just look to see if the command turns pink, but i guess both elseif and else if would lol. Just heads up that the foreach(table,function) function is one word and does NOT turn pink.

Submitted

hahaha extreme thank you for the heads up.

Host(+1)

wow great work on the tables so far! it looks like you've been really considering different solutions for your mechanic and i'm looking forward to seeing how you expand and improve your current one!

Submitted

Day 8 i think

At this point I'm just blatantly putting off working on the hard stuff. I added my "tough" enemy type that requires two hits to destroy. It is simply a recolored version of the original sprite. When it gets hit, it changes "class" to the simple enemy type, and is drawn and tracked thusly.

That was okay, but what I really enjoyed tonight was working on the "feel." I hated the way enemies just dissapeared with no fanfare. I'm trying to keep it simple here, but it was driving me nuts, and like I said before I didn't really feel like getting into the hard mechanics of the game.

I'm no good at the sound stuff, straight up. I'm pretty much guessing. But in the process of "guessing" for the sound of destroying a ship, I ended up coming up with a pleasing sound for firing a bullet. So I added that, as well as a "thud" sound for hitting tough enemies and an "explosion" sound for hitting simple enemies.

I was gonna call it there, but it was driving me nuts, again, how the enemies just dissapeared. It was even worse because there was this long explosion noise but no real visual signifier of what that noise is. This led to fun with drawing "explosions." I created a new table to track explosions and, when an enemy is destroyed, I find the center position of the sprite and save it along with a "lifespan." The lifespan signifies how long the explosion should remain on screen. For each frame that passes, the explosion's lifespan increments. If the lifespan is less than my arbitrarily set count of frames, it remains in the table and is drawn as normal. But when it hits that point (currently 15 frames), it is deleted from the table and removed.

If I end up with some polish time (unlikely at this rate), I'll try to make it a little flashier. For now though, I'm glad that it actually looks kind of like something is happening. The gif only tells half the tale, the sound takes it another level.

v

Submitted (1 edit)

Day 9

Starting to get some steam here. I added enemy firing, which necessitated adding lives, which necessitated ending the game. Included a sound for being hit by enemy missiles and (my favorite) a series of hearts that change color as you get hit.

Tomorrow I add the bomb to the game and begin working on enemy appearance patterns in earnest!

EDIT: I was feeling restless so I did some continued work. I added the bomb and a few more enemy appearances. At this point, all that's left to fit my "technically complete" requirement for completion would be to add "enough" enemies to make a stage and then mark the stage's completion somehow. I'm gonna go nuts doing that, so for the rest of the week I'm going to alternate between that and adding various pieces of polish to make this game slightly less crappy. Still, I wanted to post a quick update to mark this milestone in my project.


Submitted(+1)

Day 10

Fixed a bug where colliding with enemies wasn't causing Ace to take damage. However, after the fix I ended up making Ace take TOO much damage. If I'm gonna check for collision every frame, I need to give Ace some clemency as an 8x8 object colliding with other 8x8 objects. First, I refactored the code so all the effects of ace's collisions are tracked in a single function, and then I made that code conditional on an invincibility counter not being in progress. When the invincibility counter is incrementing every frame, ace is invincible. When those increments hit a certain point (arbitrarily 5 frames at the moment), they cease counting and reset until the next time Ace takes damage.

Added some more enemy placement.

Submitted(+1)

Sounds like you made a ton of progress this weekend. Keep it up!

Submitted(+2)

Day 11

Even though I had the full intent of completing this jam when I started, I'm still sort of amazed I made it this far. I keep on thinking I'll hit a point where working on this project will cease being rewarding, but each step further fleshes the game out and is all the more enjoyable to me.

I decided I couldn't stand to have this game go without a beginning or end any longer, so I added a launch screen, a game over screen, and a victory screen. The launch screen looks like butt, but I'm still weirdly proud of it. I also added sounds to the new screens, sort of musical one-offs. Making music is extremely challenging, even harder than making sprite art. I don't really enjoy that part, but it still gives me joy to hear my nightmare sounds play like I've just approached some old arcade machine with a super retro soundchip.


Submitted(+1)

19XX! :)

Submitted

Day 12

It was really sticking out to me today that there isn't really any reason to shoot most of the enemies that appear in the game. So I added scoring. Now players will experience bloodlust in pursuit of ever higher scores!

If I had done this last week, I'd probably have a story about everything I learned and struggled with, but this is getting easier.

Submitted(+2)

Air Ace is has now been published and is available to play. Check it out if you like.

I have so many crazy feelings about this game. On one hand, I'm disapointed that I set my sights so low. I thought as a first project, on a timeline, it would be prudent to keep it simple and not even think about things like fun or polish. Given that I ran out of time due to unforseen consequences, this was probably smart. And on the other hand, I learned a lot. Like, a LOT. I thought I had ideas before, but now I've experienced so much, I have more realistic ideas I can pursue. I don't feel this game represents me particularly well, but I feel equipped to make one that does.

I'd say a few technical things I learned... USE OBJECT ORIENTED PROGRAMMING! My code base is a shambles because I tried to program out all the behavior on the fly. If I had planned things out, thought about what I"d need to re-use, I would have had a much more attractive and maintainable code base. Expanding this game would be a nightmare. I have some shmup ideas I want to pursue in earnest, but I would not build directly off Air Ace.

The other thing is like, holy crap I knew I can't do art or music, but I REALLY can't do art or music. If ever I decide to engage in a professional venture (that's a far off dream), I need serious help with that stuff.

So yeah, I feel mixed up. Working on the game, and in this jam in particular, was very rewarding. And I truly learned a lot, which was my objective. But oh jeez, I don't think I'll be able to rest until I build something better to prove what I can do.

I want to say thanks! Thank you to everyone that encouraged me here! Thank you to everyone reading this. And most of all, thank you to anyone who plays my game.

This jam is rad and you all are rad. Making my first game is a big step on a dream I've been stalling a long time. I'm filled with confidence like I've never experienced before. I don't know what else to say. Just thank you. Thank you.

Submitted(+1)

Congrats! Just getting something complete and out there is a huge first step. I can't tell you how many of my projects have wound up in limbo over the years because I just caught up in making things perfect. Now you have a much better idea of the many facets of game making and can use that knowledge when making your next game :D