Log In  

Cart #hofeyakade-1 | 2024-02-05 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
8

My First Try - This "Game" is a Test!

After some umming and ahhing I decided to give PICO 8 a try. I've played around with the likes of Unity before, but for all intents and purposes I'm a complete noob when it comes to both coding and game dev.

The Intention

My intention here was to get familiar with PICO 8 and Lua. I spent a few weeks looking at tutorials and making up as I went along. The general plan was to create a vertical slice of a top down shooter in a similar vein to the classic VIC-20 game AMOK.

The Concept

The protagonist is able to move and shoot projectiles in 4 directions from a top down perspective.

There are several enemy types:

  • Enemies that move along a set path
  • Enemies that follow the player
  • Enemies that follow a set path and shoot projectiles
  • A "Boss" enemy that appears when other enemies are killed
  • Some enemies including the boss have a health pool, whilst others can be killed in one shot.

The Player has a health pool which is depleted on collision with enemies and/or enemy projectiles.

A simple single-screen map which has "walls" that are collidable and can be used to provide cover from enemies.

The Execution

The code here is terrible....I know :), first attempt at mish-mashing a bunch of tutorials and concepts together.

Movement

My first challenge was to create a movement system for the player that allowed smooth movement in 4 directions without moving diagonally. By "smooth" I mean the ability to switch directions easily and naturally. This entailed creating a system that allowed the user to be pressing multiple directional buttons at any given time, with the last detected input overwriting any other inputs, making the player move in that direction. By way of simple example:

Holding "D" to move right, then pressing "A" whilst still holding "D" will cause the player to move left.
This goes for any combination of held directional keys, regardless of direction.
The reason for experimenting with this was that the normal input methods resulted in diagonal movement which I did not want. Restricting movement to 4 directions, by just using the directional inputs resulted in strange behaviour if more than one directional key was held at any one time.

I eventually settled on a method that increments a value when a directional button is pressed, with the lowest value taking precedence and moving the player in that direction. This results in the player moving in the direction of the last button press.

This worked, but turned out to be horrendously complex in the end and probably not worth it. I wouldn't be surprised if this system ended up using the lion's share of code tokens. Trying to restrict diagonals, do the math for the value increments, understand which direction the player is moving etc. became a never ending string of spaghetti code and IF, ELSEIF, THEN statements. I'm certain there are more efficient and effective ways to do this, but it was nonetheless a great learning experience (and not one I'd want to repeat any time soon!).

Shooting

With the 4 directional player movement, it's obvious that I'd want the player to shoot projectiles in the direction that they are facing. This was made more complex by the movement mechanics described above, as I couldn't simply tie shooting direction to directional button presses. It wasn't too bad in the end as I was able to tie it to a variable that got the direction the player was facing, but coding that in compounded the spaghetti that was the player movement code.

The hardest part was getting my head around spawning in bullets, but a few tutorials later and FOR and DO became statements I was intimately familiar, if not entirely comfortable with.

Enemies

The plan was to start with some simple enemies that followed a set path, simple enough and using IF THEN statements to change their direction of travel within a set of x,y coordinates was simple enough.

Next up was to create an enemy that would follow the player and attempt to catch them. After much trawling of videos and forums and going down AI and pathfinding rabbit holes, I settled on very simple solution that just checks if the player x,y coords are greater than the enemy coords and if they are, adds 1 to the enemy x or y position. This results in some very robotic movement, but it's serviceable for now.

I then wanted to create a "cannon" type enemy that moved up and down the bottom of the map intermittently firing projectiles. Coding the movement for that was straightforward as you may imagine, however getting the projectiles to spawn, fire based on where the enemy was on the screen and do that at an interval, required a fair bit of Googling. I eventually settled on implementing a timer that controlled the firing intervals. Basically, a value that decreases by 1 every frame, with the firing linked to to the timer values. For example, timer starts at 60 and counts down to 0 before resetting back to 60. The enemy fires the projectile when the timer reads 60, 50, 40 etc.

Finally, I wanted to create a "Boss" enemy that appears when all other enemies are killed. I wanted this enemy to also follow the player, but take a more direct path than the other follower enemy. Again, I settled on a simple, pretty dumb solution here. A variable stores the difference between the Player coords and the Boss coords. That difference is used to move the boss toward the player. The value needs to be divided by another value to prevent the Boss teleporting to the player location. The results are janky and because of the division it means that the Boss slows down the nearer it gets to the player. I changed the dividing value based on proximity to account for this, but it's certainly not perfect.

Collision, Health and Death

Collision detection broke my brain....

Basically having to code my own collision engine from scratch was both a blessing and a curse. A blessing in as much as it really forced me understand how collision works and required me to code some fairly complex logic, that I wouldn't have even wanted to attempt otherwise! A curse in as much it was flippin' hard to understand and implement!

I won't go into specifics around how I attempted collision implementation as it would take far too many words. Long and short is that I had to get used to FGET and MGET for map tile collision, had to figure out weird ways to calculate where the edges of sprites are and code Boolean functions that I didn't entirely grasp to understand when objects were colliding! Getting my head around this took the most time in this project by far, and required copious Googling, Reddit threads and Youtube tutorials just to get basic functionality implemented. What's there right now is janky (a familiar theme!) and not entirely accurate. Still, it's done now and was worth it for the knowledge....

Death was another weird one for me....

Figuring out how to delete bullets and projectiles if they went offscreen or hit a wall was as easy as using DEL to remove them from a table once they were in a collision state or beyond a certain set of coordinates.

For enemies I couldn't figure it out. Because I wasn't using a spawning function or tables to spawn enemies in (they are basically called at INIT and exist in the game at initialisation), I couldn't figure out how to delete them! I settled on moving them to a set of coordinates offscreen if they were killed. Out of sight, out of mind! A terrible, terrible solution, but by this time I just wanted to get the vertical slice working and was running short on time. Plus the Googling around collisions had left me somewhat exhausted!

Health was a fairly easy implementation using a number system that decreased by one on collision and resulted in a death state when reaching 0. My implementation of a health bar was (again!) pretty jank. I used a set of sprites for the health bar that decreased by one pixel every time health is reduced by 1/8th (the sprites are 8x8 pixels), using a basic function to control this. It seems basic enough but if feels like I've made it more complicated than it needs to be and it's highly likely that it's using way more code than it needs...(another common theme here)!

Game States

Another thing that broke my brain was the concept of coding in different game states....

Again, another topic that required a ton of research to get my head around. I basically ended up coding different game "states" for the start screen, gameplay, dying and winning the game and the logic between moving between them. Having to write different states for the update and draw functions and call them when appropriate took some time to grasp, but was pretty straightforward once I got some understanding of it. I'm sure there are some really elegant solutions for making this nice and streamlined, which I need to do more research on.

Sprites, Animation and Sound

I absolutely love the inbuilt sprite editor in PICO 8. Ditto for the sound editor.

Drawing and rendering sprites is easy and intuitive and the limitations actually make it a ton of fun! However.....

My previous implementation of player movement made animating the player sprite a damn chore. Because the sprite movement and flipping is not directly tied to button directionality and rather the value based system described above, I ended up having to implement a whole set of additional logic to animate the player. What should have been a simple as rendering a sequence of sprites depending on which direction button is held, ended up with me having to write another set of functions to control this. I eventually got there, and it's functional - but I sure do like making more work for myself!

The sounds in my "game" are terrible....

But the creative process is again, simple, intuitive and a ton of fun. Having heard some of the stuff other people have created is pretty mind blowing, so I plan to put more time into sound and music for my next project. The only real hurdle here was implementing the logic for sounds to only play once, based on certain conditions. My expectation was that there would be a built in function to play sounds once, or loop them, but this is PICO 8 and PICO 8 expects you to code that stuff in yourself! It wasn't too hard to control the sounds via existing or new Boolean functions once I got the hang of it though.

Lessons Learned and Reflections

In the world of game engines, PICO 8 doesn't really seem to get much airtime. I happened to stumble across it whilst researching "simple" game engines. In this case "simple" most certainly doesn't mean "easy" though! I was drawn to it's all-in-one functionality and single interface and the fact that it's limitations would help me focus in on the basics of a project without getting side tracked by all kinds of flash.

What I found was closer to a pure-code based engine than I could have imagined. I thought the simple interface would make things easy....I was fooled. You have to code-in every aspect of a game's functionality. Collision detection - you'll need to code that. Physics? You'll need to code that too. Player movement? Code that bad boy yourself! Scenes? You best get used to coding game-states!

I think I learned more about game development coding a basic, crumby, single level top down shooter (an 8-bit one no less!) than I did from mucking around with the plethora of tutorials for Unity. That's not to say I won't start using more complex game engines as I progress in my game dev journey, but there was pure joy I experienced problem solving in PICO 8. And that's what my first try at this has been - an exercise in problem solving. And I don't think I could have picked a better game engine to do that in.

PICO 8 is pure. And difficult. Frustrating at times. But I loved attempting to make a game with it!

Shout Outs

I'd like to give massive thanks to the dedicated PICO 8 dev community and the excellent resources they provide. I couldn't have dipping my toes into this if it wasn't for the excellent folks below:

Lazy Devs
Nerdy Teachers
Space Cat

If you've read this far, thanks for listening to my ramblings! If you'd like to, please let me know how I can improve, or what I can attempt for my next project!

P#140916 2024-01-31 14:49 ( Edited 2024-02-05 08:35)

2

i'd recommend adding a wait time in between killing the final enemy and the restart screen. i'm holding X and i just skip right over it. great first game tho

P#140956 2024-02-01 00:15
1

Thanks, will give that a go! Really appreciate the feedback :)

P#140970 2024-02-01 06:46
2

good

P#141014 2024-02-02 07:42
1

Good job making this game I just started and I think your janky coding is good because your learning so much because your not going to be a master soon as you start. Hope to see more games from you. You inspire me so much thank you.

P#141123 2024-02-04 22:23 ( Edited 2024-02-04 22:25)
1

Thanks so much, I'm definitely learning! Good luck in your journey :)

P#141131 2024-02-05 05:44

I've updated the game to wait a second or 2 after either killing the boss or dying before going to the restart screen as per @Ummmm_ok suggestion :)

P#141136 2024-02-05 08:38

you dont need to kill the guy at the bottom to spawn the boss. is that on purpose?

P#141519 2024-02-15 13:29

@conofive yes, I originally made the shooty thing at the bottom unkillable, but added in the ability to kill it afterwards. I thought having it shoot at you whilst the boss spawned would add a bit of extra challenge :)

P#141766 2024-02-22 06:45

[Please log in to post a comment]