December 2016 Game Dev: 2D PCG Shooter

lol sorry @roketfiq – Severok has been trailblazing (mashaAllah tabarakallah) and I’m struggling to keep up TBH.

I understood that he said: we’re using squares now, just edit a text file and you can swap in an image instead of a square. (Not sure yet because I didn’t see his code yet.)

If you want to do more than art, that’s cool too bro.

My squares are just images themselves, I literally just made a 10x10 image in gimp and painted it different colours.
It pretty much reflects my artistic abilities.

I am adding additional variables to the objects to indicate their sprites scale.

I am back coding again, the object data is now stored and imported from a json file.

I will try to push my branch as it exists now before I press on.
Edit: Success. Looks like a bit of a mess still, has some old files that need to be cleaned up but it runs.

Edit: I’m about done for tonight. I made a few modifications to how I called game/ai updates to make it a little cleaner.

I added a pair of movement variables to the object class (mx, my) and removed the AI processing call for bullets. AI calls now decide what direction to move in while the general update call actually moves the object and updates the sprite, so bullets no longer need AI to update their position.

I am spawning 2 different enemy types. Each is assigned a different AI behavior.

  • 1 moves up
  • 1 moves right then dies when it moves past 300 pixels.
  • Spawning and Despawning Work
  • Assigning AI behavior to objects in JSON file Works.
  • Selecting AI behavior as assigned Works.
  • Updating sprite during movement Works.

Final Edit:
Just pushed where I am upto now.

I encountered a strange bug where the AI would seem to be randomly be applied to the enemy movement.
It appears that when the JSON file is parsed, there is no garentee to what order the elements are loaded?

Until now I had been assuming that the default enemy will be loaded first as Enemy[0], the ranged enemy will be loaded as Enemy[1] and the Tank enemy will be loaded as Enemy[2] as that is the order they are written in the file, but it appears that is not certain so I will need to re-write how that is referenced.

Currently the regular enemy should move right and die @ x=300 pixels,
The ranged enemy should move up
The tank enemy should be twice the size of the others, but have no movement commands.

I am requesting types 1 and 2 are spawned, but the types I am reciving seem to be randomly swapped on loading.

Guys, just a note. I am with you on this @wulf @roketfiq, I want to build a giant, awesome game too full of good engineering stuff.

But @Severok is right. We’re a new, unproven team. We don’t know how to work together.

Join us. Prove that you can contribute consistently for three sequential months – despite what life throws at you. If we can do that, I know we can work together and make great things.

Join usssssssssssssssssssssssssssssss!!

Resistance is difficult.

I’m struggling to understand your code. The readability and variable names are fantastic – at least 10x-100x better than I remember.

My brain is fried. There seems to be a bunch of “hey we might need this” kinda stuff built in.

I think the problem is that I pretty much exclusively write object-oriented code. Yours is very procedural/functional-like. I’ll see if I can fix your bug first – that will necessitate a strong understanding of how it works.

The fastest I ever coded was CraftyJS, where entities are objects, and basically one file per entity + one file per screen. Sorry, I’ve worked with this kind of style for literally years.

Edit 1: You’re correct that the Python parser does not guarantee the order of elements is consistent with what’s in the file. Observe this JSON file:

{
  "A": { "id": 1 },
  "B": { "id": 2 }
}

With this code:

import json
objfile = open('data.json', 'r')
obj = json.load(objfile)
print(obj)

In four invocations, I get {'A': {'id': 1}, 'B': {'id': 2}} three times and {'B': {'id': 2}, 'A': {'id': 1}} once.

Is this a bug? It doesn’t matter. We shouldn’t be relying on the order of JSON; we should be looking up things by their name, not their index.

I’ll update your code appropriately. Honestly, I find it very hard to figure out what’s going on because the code is scattered in so many places. I don’t see the consistency of what goes where, although I can see the design forest, sort of through the trees. (At my work, we focus on very readable, maintainable code at the cost of almost everything else.) I have become inflexible :frowning: I will fix this! (There’s a Maud’dib quote in there somewhere …)

Okay, so I fixed it to be consistent. The short summary is I rewrote the spawner to use object names/IDs instead of indicies in the array/JSON as the key. You had some redundant code (ai.py is no longer used) that threw me off.

Honestly, I don’t want to be insulting, so please forgive me: that took me a lot longer than it should have. Small classes representing different things (bullet, player, enemy) with perhaps instances of their behaviours, inside, would’ve been wallahu a’lam quicker to create and easier to understand.

Please take a look at the prototyping_redone branch. The last commit is the fix; the previous one is a merge of all your and my code together (well, mostly your code).

I think this comes down to functional style. If you’re okay with writing most of the actual game code, that’s cool, we can continue as-is. I’m not sure how easy it is to extend game_object with specific behaviours (what if we want the player to accumulate XP? What if we want bullets to explode into smaller fragments?) but maybe we can code for what we need now, not what we think we’ll need later.

Alternatively, we could bite the cost to rewrite large chunks of it to be more object-oriented (which is not a best-practice in game dev apparently). We can start by having subclasses of game_object (eg. Player) and instantiating those directly instead of by reading JSON. Maybe. We only have 26-27 days left, though. Every day counts.

You’re the programming lead on this one, so you can pick, I will follow inshaAllah.

Not insulting at all bro, I am fine with critisism.

Just so you know where I am coming form for this project, my coding experience is about 90% uController work with just a little C++ for writing drivers for my hardware and a light mixture of SQL and Python for managing and interpreting data.

My code style sounds like the polar opposite of yours, In my work we put very little emphasis on readability since we need to make the most out of restricted resources (512 bytes of data space, 4KB of code space being fairly typical). So PC coding and Python in particular feels un-natural and wasteful, wasting bytes by the handful even… while I prefer to store 8 different boolean variables in a single byte if I can.

Ultimately I am here to gain more experience and your approach would be much better suited to this enviroment. I will have a read through your code to get a handle on your style and if you have any reference materials I could read, it would be appriciated.

As for my design choices so far:

I had been planning on using a standard object class to be interperted differently in differnent contexts. Eg:

  • Enemy objects list containing the prototypes of different enemy types for copying into the active list for spawning,
  • Player objects list for storing different player objects that could be swapped out if the player picks up a power-up (Altering sprite, health, and what other attributes eventually get added to the object)
  • Misc objects being cosmetic only, with the health attribute decreasing every frame to ultimately timeout and despawn the sprite after the set period of time.
  • Bullet objects being assigned move_x and move_y attributes on spawning with no specified AI so that they continue moving in their specified direction. Just like the Misc objects, their health decreases every step to despawn the bullets after a specified period, resulting in bullet range.

In these cases, the context for processing are selected by the specific list the object is loaded in and the behavior string used to vector to different AI functions.

  • Player list is not passed into the AI_processing function, is passed into collision detection, is passed into game_update function.
  • Enemy list is passed into AI_processing where object behavior attribute vectors the AI function call, is passed into collision detection, is passed into game_update function.
  • Bullet is passed into AI_processing function (MISC behavior to decrease health as a timeout), is passed into collision detection (Is basically what bullets do) and is passed into game_update function.
  • Misc is passed into AI_processing function (using same AI as bullet to timeout and expire the object), is NOT passed into collision detection as intended for cosmetic sprites, is passed into game_update (for despawning on expiration).

I admit it would probbably be understood better to make seperate classes for these objects.

The file ai.py has been depreciated since I grabbed the relevent functions from there and put them in the processing related file proc.py as the AI functions are all called along side the processing functions (Deciding actions vs Actioning them). For what ever reason it wasn’t removed during the push.

Ultimately I was planning to group functions as modules (Eg Object handling, Processing/AI, etc) together in seperate files with an API allowing their various functions to be called by the higher level code in main.py. This way the files holding the underlying code can be pulled out and replaced provided they use common function calls to make things easier for developing with multiple people.

Eg Collision detection is written by Person-A. We start with a simple method of drawing rectangles around the sprite locations and scanning for overlaps in their boundries as a quick and dirty method. The file collision.py contains definition for the collision detection function call allowing an object to be passed along with the active-object lists returning an ID of a detected collision.

Later in the project, we have time to improve on this by using masking for collision detection for more accurate detection. Person-B writes an alternate collision.py containing the same function call as the one written by Person-A. Once completed and tested, the file is swapped in the master branch replacing the underlying mechanic without needing a re-write to the higher level code.

Edit:
Rechecking the repo, The following files have been depreciated and should be removed:

  • objlist - original text file holding object information. Has been replaced by data/object.json
  • init.py - original functions for parsing objlist into game object prototypes. Has been replaced by obj.py
  • ai.py - I don’t see it listed anymore but you mentioned it earlier. relevent functions have been merged into proc.py (Processing functions).

I wouldn’t go so far as to say that my approach is better suited. Everything I know about game development suggests that making things simple is best, and performance is critical (and often, functional programming is better than object-oriented) because of how games work. If anything, I should take this as a criticism against myself, that I need to learn to do things more your way.

Your proposed structure about lower-level modules vs. higher-level code in main.py absolutely makes 100% sense. I even tried to do the same thing, except I thought of base classes instead of modules (my bias as an OO web programmer, again).

Let’s agree that there’s no time to spend rewriting stuff. Let’s proceed as best as we can. I think we’re in good shape so far.

I don’t know what timezone you’re in and what working hours you have (I’m in GMT -5 and generally work from 9pm to 1am GMT-5), so at the risk of going slow, I’ll let you work on the input + game processing items.

I’ll do some refactoring (which will break your code, sorry) like renaming game_object to the pythonic GameObject and maybe turning everything into classes (classes are easier to manage than random methods in a file) – at least the main one, not sure if I want to touch the modules.

THEN, I’ll work on the splash screens inshaAllah and the titlescreen. Hopefully, by then, you’ll be far along enough that I can start using your modules to create the core game stuff (collision with walls, bullets, enemy bullets, etc.)

Sounds good? Peachy, even?

Edit: Since you like the state of code so far, please rebase your branch from main (git merge master) and keep working. I’ll recreate my branch too.

Sounds good.

I get to work after my son is asleep. Anywhere between 8 and 9 pm AEST. Though he is sick at the moment so the times are volatile.

I might draw up a quick reference diagram showing the over all structure of how I think the modules will fit together along with some proposed function calls. I am planning to deliver something like an SDK that the higher level coders can call from while actually making the game.

Taking a little time to do some light documentation at this stage could save us some time over all, making sure all contributors are on the same page.

Do you think things like AI should have seperate modules? I don’t want to end up with the code having the code having 20 different import calls, but it seems like something that would modulise fairly well allowing multiple contributors.

That’s cool. We’re 15 hours apart; so you’re coding when I’m sleeping and vice-versa.

Forget the reference diagram and documentation (I can understand your code just fine after fiddling with it for an hour), and maybe even forget the SDK. Let’s go simple and build on GameObject with classes, which is conceptually simpler, and see how it works out. You agreed that this is probably a simpler approach, right? I hope that simpler => faster. It’s already the fifth. I’m already jittery about our end-date.

I don’t know about AI modules. I know that a generally accepted good software engineering principle is SOLID. The S stands for Single Responsibility, which roughly states that each class (I interpret also as module) should do one thing only and do it well. It shouldn’t need to change for more than one reason. (Simple example: Cat class doesn’t have serialization code. If your data format changes, or your business rules change, Cat changes; it has two responsibilities.)

We’re not even at the point of AI, and a good rule of game design is “fake it.” I think we can easily fake AIs, but what you have is also really good. Let’s keep it as-is and modularize if/when we need it…

Edit: I’m going to try what you said. I’ll make Main use all the sub-modules. If I need something new, I’ll stub it out and you can come back and engineer it properly/etc. in a way that makes sense. I think that will work really well and play to both our strengths.

Edit 2: Done for the night (I think it’s useful to keep adding these to our posts like you did). Welcome to the brain-bending world of polymorphism! That is, obj.spawn can now create subclasses; SplashScreen is a subclass of GameObject; and it has an update method that proc.py calls if and only if it exists.

Confused yet? Sorry, I know, I’m bad. I didn’t do it on purpose to confuse you; it was just the simplest solution I could think of at 11:30pm.

I like that it keeps type-specific code isolated, and it lets us leverage your (frankly) awesome code, while making (inshaAllah) both of us happy.

If you like it (check the ashes999 branch), or if you’re okay with it, let me know and I can merge to the master branch. If you prefer to do this The Git Way ™, we can work in our branches and open a PR (Pull Request) for the other person to review/merge as necessary.

Useful (if you do code reviews) but slower. And we don’t have time to be slow yet.

Feedback please?

Alrighty.

I pulled your branch and it looks good, The MG & DG spash screens look legit.
I like the changes you made to proc.py, so go ahead and merge it.

As for my contribution tonight I have:

  • Basic Inputs (Mouse + Keyboard) are working, though I am not satisfied with it yet.
  • Player moves with the WASD keys
    • Player has 8 directional movement, should this also apply to enemy movement?
  • Attacks are spawned with mouse button press
  • Objects have added attribute: Cooldown
  • Player has 2 types of attack.
    • Left mouse - Melee attack (Very short range, very quick cooldown)
    • Right mouse - Range attack (Long range, long cooldown)

Pew pew pew

Currently keypresses are handled in proc.py as intended using the KeyStateHandler which tracks button states between events. This lets me periodically check the keystate when it is time to process the frame update.

For the moment the mouse event and subsequent processing is handled in main.py, but I am looking into shifting that into the same function that processes key-press.

I will submit my next push once I have that sorted out.

Once I am satisfied with the input handling, I will take a look at collision detection now that we have attack spawning.

  • How is the Art team going? Have we considered the themes and art styles we will eventually be using?

  • Any thoughts about gameplay, How about a series of arenas where enemies continiously spawn?

    • After a quota of enemies has been dispatched the arena changes into the next stage.
    • Path finding will need to be considered when we reach AI development
    • If we can get custom sprites, a transition from floor tile to wall and back would be cool. sections of the Arena rising and falling to reshape the stages would look cool.

Sounds good. I’m eagerly waiting for the push with input handling. I’ll probably be up coding tonight before you though.

Pinged @roketfiq (aka The Art Team) about the art direction again.

Some project manager stuff:

  • No thoughts on gameplay yet. If we have a borining “hey guys here’s a blue square shooting infinite red squares in a grey square arena” I’m okay with launching that. So let’s get there, and then we can layer on fun gameplay. (That’s my goal: reach a releasable milestone ASAP)
  • For collision detection, AABB (axis-aligned bounding boxes, i.e. check if rectangles overlap) is Good Enough. Pixel collision is usually overkill (to code and to debug), and polygons are not necessary unless we have really weird shapes, which we don’t.
  • Ditto for path-finding – we can just have dumb enemies, or make them just walk over each other (Starcraft drones, anyone?) for now. Pathfinding is complex and can wait until “maybe never.”

Feel free to disagree. Arena wall transitions sound awesome, but hard to code and probably something we can do at the end if we have time. I hope we do.

Alrighty, I took a brief extra shift this morning when I woke up.
Now I need to rush to get ready for work, but I am happy with the input for now, but Insha Allah you are not held up too much when you are working on the main menu.

I made a quick class that is initiated, accepting the window object and contains variables for the mouse state and a reference to the keyboard handler.

Just pass this object to any function you want to handle input (Eg game processing or main menu).

This allows us to process the input states outside of the active event callbacks. Thus we can more easily process input contextually (Proc.input is called during game, future mainmenu functions can call the object without pilling more code ontop of everything else.)

the keyboard dictionary is updated automatically on keypress, the mouse variables are updated on the mouse event callbacks.

One small bug that remainss here: The callback for mouse_button_release does not appear to clear the button state, causing the command to stick once a mouse-button has been asserted.
I patched this for the moment by clearing the mouse_button variable once it has been processed once. We do not currently have the ability to maintain a mouse_button press, but we do not keep shooting forever.

Your sacrifice will not be in vain, inshaAllah.

Next time, please don’t over-extend yourself like this. We should aim to be sustainable, and cut scope (cut out features), not work harder, when we can’t finish.

I’ll update this post later once I get to work on your code.

I was disappointed that I fell short the night before and that I could tackle it more easily with a clear head.

The main difference was that this morning had breakfast while coding instead of watching YouTube.

I don’t think you fell short at all. I think you have that “engineering brain” that can’t rest while there’s an unsolved problem. That’s cool (I have that too). Just don’t feel obliged to do extra on my behalf. I’m all about sustainable pace.

I’m going to merge and update your branch so it’s at the latest version. For future reference, you can use git merge master to merge master into your branch. The more often you do this (less changes on master), the easier / more automatic it should be.

Edit 1: I think I just merged your branch into main (successfully?) It was complicated because you were 11 commits behind and 1 commit ahead. Please pull the latest master branch, verify everything is fine and works fine, then merge from master into your branch. That way, we don’t have to deal with complicated merging again.

Edit 2: I’m done for the night. I fixed the input bug by storing when a button is clicked/released, and firing shots based on that. So basically, click+hold to rapid fire. I like this better. Sorry to encroach on your creativity though :frowning:

Please pull my branch, test it, and if you like it, merge it into yours and continue working. Then you can merge to main, ya’ne, whenever.

Reporting in.
A fairly late start tonight, my son was not cooperative at all tonight but Insha Allah I can make some progress and atleast contribute… Something tonight.

Following last night I have simple attack spawning and input handling. Next on my list is simple collision detection.

I got my coffee, youtube and an hour to code so lets do this thing.

Edit 1: I pulled your branch. Now skipping the splash and getting straight into the game for testing. The code seems to crash on the mouse-button event:

AttributeError: ‘InputHandler’ object has no attribute ‘mouse_moved’

Patched and pushed. Sorry, sorry, sorry! And, coffee at 11pm, srsly…?!

One side-effect bug of my changes is that there are two players spawned. It’s … eerie. I’ll look at this on my turn inshaAllah (~15 hours from now)

Alrighty, our branches have been merged and it looks like I am good to code again.

Mash’Allah that bug is creepy. I love it!

pew pew pew intensifies.

The best part is that the clone only occasionally responds to keypresses and tends to get stuck in movement. It gives the impression that it is moving independantly with a mind of its own.

Can we somehow turn this bug into a feature?

Edit: Okay, I have tamed the clone. It’s movement is based on the state of the keyboard when the mouse button is pressed.
Eg, if you are moving left when you shoot, the clone will start moving left. if you are standing still when you shoot, the clone will stop. Gotta be connected to the mouse-button event.

Edit: Correction, the clone moves based on the keypresses while spawning an attack. The clone is far more responsive when using the melee attack with its’ rapid cooldown.

Edit: It is appears the start_game function is being called twice. There are 2 players, 2 enemies of each type. We had not noticed the additional enemies as they are moving over top of eachother.

I put in a conditional at the start of the start_game function to basically check if a player has already been spawned to stop the function processing multiple times if re-called. Goodbye clone, you will be missed.

Still, see if you can find why this is being called twice. Always better to fix a bug at its’ source than patch the results.

Edit: Now implementing sprite scaling from the json data.

1 Like