You are browsing as a guest. Sign up (or log in) to start making projects!

Asteroid Shooter

  • 7 Devlogs
  • 14 Total hours

Simple asteroid shooter game in Pygame(-ce)

Ship #1

I made Asteroid Shooter, a space invaders-like game where you shoot asteroids. I made it with Pygame (community edition), pixel art made manually in Aseprite by me, public domain sounds, and a lot of trial and error. It tracks the player's score, and it gets harder slowly over time. Overall, I learned more about Pygame (sounds, graphics, user input, OOP, etc.), pixel art, and game design.

  • 7 devlogs
  • 14h
  • 7.96x multiplier
  • 114 Stardust
Try project → See source code →
Open comments for this post

5h 6m 15s logged

I have done a lot and decided to just bundle it into one last big devlog, but the work was well worth it!

  • First, I finally redrew all of the sprites! This took the longest out of anything else, but it was very much worth the effort. I learned a lot more about pixel art using Aseprite, and I think the sprites came out very well. First, using references from Google, I remade the rocket. Then, I made 3 different sprites for asteroids and 3 for explosions. For the explosions, I learned about using several different colors/tones to create depth, and they look very good inside the game. Both explosions and asteroids will have their sprites chosen randomly when initalized. Then, I remade the bullet sprite. At first I thought I wanted it to be a missile, but it looked weird, so I just made it a fireball and it turned out very well. I used the same principles from the explosions for it. Finally, I drew a background. I made some preset stars and then pasted them throughout the whole thing.
  • I made some balancing adjustments. First, I made it so that the player can only shoot twice per second, as before it was 4 and quite overpowered. Then, I made it so that the minimum interval at which asteroids could spawn decreased over time as the score increased. I did this by subtracting the score multiplied by 0.005. This makes it so that asteroids spawn 50% faster by the time the player gets to score 100.
  • I then added sounds! I was able to get free public domain (CC0 licensed) sounds from this website called Open Game Art, and then used Audacity to make a loop and edit them to fit the game. I got one for the background music and made it loop infinitely and more-or-less perfectly. Then, I got one for shooting. Alongside the sounds I also added a system for keeping track of the sound’s volumes. This was kind of hard to figure out, but eventually I was able to make a dictionary and then a function that would toggle the volumes between 0 and 1. I had to do this because Pygame doesn’t have a feature to “group” audios or mute globally, and the background music plays constantly but the shooting sound plays ocasionally.
  • I reworked some of the “UIs”. They are still kind of lackluster, but I added a main menu that the player has to press the space key to enter the game, and shows the controls. Then, I made a game over screen and show the player’s final score. I also made the window have a caption and icon. The icon is just the first explosion sprite.
  • I compiled the games into executable files. PyInstaller requires using a special resources function to get paths. This was quite easy to change to, since I have my functions to load assets in one file. I had some troubles with the paths, still, and I ended up moving everything outside of the ./src directory last-minute. The hard part about this was that I switched to Linux a few days before making this project, so I didn’t have another machine to compile the Windows version of the release. At first I found a guide that I could do it through Wine, and then I had to install a Docker image of Ubuntu that had it all set up, but it ended up not working. I ended up just setting up a Windows 11 virtual machine using Gnome Boxes (much easier than having to set up QEMU). It took me a while but it should work. It runs as expected on the VM (Windows Defender notification will pop up but that’s a problem with PyInstaller).

I’m probably missing some stuff, as this was quite a while, but yeah. The game is finished.

0
0
11
Open comments for this post

1h 35m 32s logged

Quite a few improvements, though the codebase gets even more convoluted.

  • I added explosions. This is pretty simple, just adding a Explosion class, and tracking delta-time against a set lifespan. An explosion will last 1/10th of a second, which I think is good enough.
  • I then added a score display. A score tracked by the main game class. When a player shoots down an asteroid, 1 will be added to the score.
  • I added a main menu and pausing. Usually this would be done in a much nicer way, perhaps with an external library, but I decided to make a few simple screens and display them when variables are True. The player can pause with the Esc. key, and it will toggle a variable paused that is False by default. I also have the main menu, which is True by default and is only toggled at the beginning. Finally, I have one for the game over screen, which is False by default and only toggles when the player has no more lives left. When any of these are True, the sprites will not update, and the game will only listen for input.

Overall, the game is basically done and I just need to polish it. I will likely be redrawing the sprites next.

0
0
5
Open comments for this post

2h 35m 17s logged

Lots of improvements!

  • All movement is framerate-independent. I did this by tracking delta-time.I could multiply all the movement deltas by the amount of seconds between frames. This will make the game smoother for everyone despite computer specs.
  • I got rid of the global settings file. All speeds were moved into their respective classes, and the main game now has its own Game class. This makes it generally easier, as now I don’t have to wire so many variables between functions and all.
  • Collisions between the player and asteroid, as well as asteroid and ground are checked. When either of these happens, one of the player’s lives will be removed and the sprite will start blinking. This allows me to have a sort of I-frame where if it is blinking it can not take damage, making it somewhat easier for the player. The game will end when the player has no lives left.
  • Pausing has been implemented, mostly to allow me to record for now, lol.
  • I drew up a heart sprite, which will also be getting redesigned along with everything else.

It’s coming along great!

0
0
16
Open comments for this post

2h 13m 9s logged

I made some pretty simple changes to the overall codebase but now it’s much cleaner.

  • Instead of storing all globals like movement speed inside the single settings.py file, I moved them into attributes of their own classes.
  • Sprites are now loaded in the __init__ method of their classes. Previously, I was under the impression that this was not possible because you needed to run pygame.init() before you used the .convert() (and similar) methods after loading the image. However, I was putting it outside of the class, so it would always run before running pygame.init(). This overall makes the code much cleaner, as now I don’t have to load in the sprites in the main file. Also, it was getting quite challenging to deal with figuring out ways to pass sprites into functions like player.shoot() so that it would spawn in a Bullet correctly.
  • I am now using .convert_alpha() instead of .convert(), which allows the sprites to have transparency (as seen in picture 2). I had been saving all of the sprites as .png files, but they had a black background when overlapping with other sprites (as seen in picture 3). With this, it actually allows for transparency and looks much nicer.
  • I added meteors. This was actually quite easy, just another class like the Bullet class but falling downwards. Pygame has this really nice feature where I can check for all collisions between all sprites from any 2 groups like so. pygame.sprite.groupcollide() takes in 2 sprite groups (pygame.sprite.Group), 2 booleans representing whether to remove the sprite right after, and a callback function. This will be useful later for adding explosions and score. They also spawn at a constant interval regardless of FPS. I did that by using delta-time and adding a counter to get the amount of milliseconds since the last frame. A meteor will spawn every 250 milliseconds. I find that this is a playable amount for now.
  • Shooting now has a cooldown. I do this in the same way as the asteroid spawning. The player can shoot 4 times per second.

Next thing to do is to add lives and subtract them when the player gets hit with a meteorite.

0
0
10
Open comments for this post

1h 17m 15s logged

I made some pretty drastic changes, but for the better.

  • First, I added shooting. I drew up a quick bullet sprite (also getting changed in the future). The main thing with this was that I wanted to have the Player class have its own .shoot() method, but that required me to pass the sprite Group object, as well as the actual sprite image into the function. I searched for a “better” solution for a while but ultimately couldn’t find one. The shoot() method handles the placement, the creation of the Bullet object, and adding it into the Group.
  • I have an actual file hierarchy now! I took inspiration from DaFluffyPotato, though I am using an assets folder instead of data, because I won’t be storing anything other than sprites and maybe sounds. Also, I will store the .ase files for the sprites just in case, so that I can fully edit them in Aseprite if there are any layers. The one roadblock I came across here was that the paths to the assets were kind of confusing, but I figured it out quickly.

Overall, I think it’s coming along great!

0
0
4
Open comments for this post

53m 56s logged

Again it takes longer than I would’ve thought, but I implemented movement. To be honest, the movement part was actually the easiest thing and I spent most of the time fighting with ty (the LSP/type checker).

  • Instead of editing the coordinate attributes of the class in the main event loop, I took inspiration from some established Pygame projects and made the Player class have a custom update() method. Inside, I check for the currently pressed keys and then use an if-statement to eddit the coordinates there. This makes the whole thing much smoother, as with the former option it would be choppy and wouldn’t let you keep holding down the key. Also, when I add the other entities (asteroids), I can make it have its own update() method, and that can be called as well. I am using this cool feature called pygame.sprite.Group, which lets me have a set of sprites and update and draw them all simultaneously. The player can be moved with the left and right arrow keys or A and D. I made it so that if they are both pressed the player will not move at all.
    • The main issue with the type checker/LSP was that Pygame sets the type hint of the rect attribute of pygame.sprite.Sprite to pygame.Rect | pygame.FRect | None, which causes ty to throw an error because None has no attributes. First, I tried ignoring all errors of that type by setting them to "ignore" in the ty configuration, but then I figured out I could just set the rect attribute to pygame.Rect | pygame.FRect inside the entity class, and that fixed the issue.

It’s a little complex as of right now because of the sort of unique way Pygame projects are structured, but I think it’s coming along very well.

0
0
32
Open comments for this post

36m 25s logged

This took somewhat longer than I would’ve expected, but first devlog!

  • I first got a very basic window going, nothing much to say.
  • Then, for the player, I decided to make it its own class. This is because, from my research, games made in Pygame are programmed in OOP (Object Oriented Programming), where each thing is its own class. I also want to draw my own sprites and stuff. Luckily, I purchased Aseprite a while ago on Steam, so I quickly drew up a basic rocket (second image). This will change in the future, but for now it’s just a very basic placeholder.
0
0
9

Delete project?

Are you sure you want to permanently delete this project? This action cannot be undone.

All devlogs, followers, and associated data will be removed.

Followers

Loading…