Building a shoot’em up game with the framework

In my fifteenth video of the “Building a Game Development Framework” series, I demonstrate the Framework by creating a Shoot’em-up game. I implement a sophisticated Visual Effects API, introduce Enemy Projectiles, and streamlines the framework’s actor management.

I updated the “Color Effect” system into a more generic Effects API.

  • Stacked Effects: Instead of a single color slot, every animation and motion now maintains a list of effects. This allows a sprite to simultaneously flash red (when damaged) and become transparent (when dying).
  • Translucency Effect: A new effect class is added that controls the “Alpha” of a sprite over time. This is used to make obstacles fade away once they are destroyed.
  • Global Resets: A reset() method is added to actors, motions, and animations to ensure that all active effects are cleared when an actor is pulled back into the Object Pool for reuse.

To reduce repetitive boilerplate code, I created the ForgedActor class and an updated ActorFactory:

  • Automatic Registration: Previously, developers had to manually call factory.actorRemoved(this) in their code. Now, by extending ForgedActor, the actor automatically notifies its factory when it is “removed” from the game play, significantly simplifying the logic in the Laser and Obstacle classes.

The enemy ship is upgraded with offensive capabilities:

  • Bullet Factory: Similar to the player’s lasers, enemy bullets use an ActorFactory with pooling (starting with a queue of 20 bullets).
  • Burst Fire Logic: The enemy ship uses a timer in its updateChild method. Every 2 seconds, it enters a “firing state” where it launches a burst of 10 bullets, spaced 0.2 seconds apart.
  • Weapons Positioning: The code calculates the ship’s width and fires two bullets simultaneously from different positions on the ship’s hull.

New sound effects are integrated to distinguish between player and enemy interactions:

  • Varying Hit Sounds: Distinct .wav files are added for the player’s ship taking damage versus the enemy’s ship being hit.
  • Explosion Timing: The obstacle’s “hit” sound now triggers as soon as the collision is detected, coinciding with its red color flash and fade-out effect.

I demonstrate the new features in action, showing:

  • Projectile Interactions: Logic is added so that enemy bullets don’t collide with the enemy’s own falling obstacles or lasers.
  • Difficulty Adjustments: The enemy spawning time is reduced from 100 seconds to 5 seconds to get the action started sooner.
  • Visual Polish: Obstacles now smoothly fade out into transparency when hit, rather than instantly vanishing.

By the end of this video, the framework supports a full combat loop where both the player and AI exchange fire, receive visual and auditory damage feedback, and utilize an efficient memory-management system for high volumes of objects.

The Gdx2DGameFramework can be found here on github.

Enhancing a Simple Game with the Framework

In my fourteenth video of the “Building a Game Development Framework” series, I continue developing the Shoot ‘Em Up game, introducing more complex enemy behaviors, advanced movement scripting, and visual color effects.

To improve the game feel, I add an immediate audio-visual feedback when an obstacle is destroyed:

  • Explosion Sound: A new sound effect is triggered the moment an obstacle is “hit.”
  • State Management: The hit() method sets the obstacle’s state to “destroyed,” plays the sound, and swaps its visual representation to a red box while it executes its scripted “easing out” death animation, where it slows down and disappears.

I create a new organizational class, the Enemy Manager, to oversee the spawning of both obstacles and the enemy ship.

  • Dynamic Spawning: The manager waits 10 seconds before spawning the first enemy ship from the top of the screen.
  • Randomized Patrols: To make the enemy ship more unpredictable, I build a Random Destination Action. This action uses “Suppliers” (a Java functional interface) to generate new random (X, Y) targets every time the ship reaches its previous goal.
  • Infinite Loops: By chaining the random movement with a GoToAction from the ScriptMovement, the enemy ship permanently patrols the top of the screen, weaving between random left and right positions.

I introduce a sophisticated Color Effect System to visually indicate damage without needing new sprite art:

  • Color Cycling: The ColorCycleEffect gradually transitions an actor’s color from its base color (e.g., Blue) to a “hit” color (Red) and back again over a set duration.
  • Linear Interpolation (Lerp): The system calculates the percentage of time elapsed and adjusts the RGB values incrementally. This creates a smooth “flash” effect rather than a jarring color swap.
  • Framework Integration: The BaseAnimation and Motion classes were updated to support these effects, ensuring that any actor—whether a single-frame sprite or a multi-directional animation—can utilize the Effects. The Effects can be translucency to color changes.

A BaseShip abstract class is created to centralize the logic for both the player and the enemy:

  • Hit Protection: A “hit” flag ensures that if a ship is hit multiple times rapidly, the color flash effect doesn’t constantly restart; it must finish its cycle before flashing again.
  • Max Hits (HP): Each ship now has a MaxHits variable. The player is given 10 HP, while the enemy ship has 5 HP. When HP reaches zero, the Actor is removed from the screen.

I refined the collision checks to ensure that game objects only interact with what they are supposed to:

  • Laser Logic: Lasers now check if they’ve hit an EnemyShip. If so, they trigger the ship’s hit() method and remove themselves.
  • Obstacle Logic: Obstacles check if they’ve collided with the player’s ship to deal damage, but they ignore the enemy ship so that the enemies don’t accidentally destroy their own falling obstacles or get destroyed by them.

By the end of this session, the game has a sophisticated AI enemy that patrols the screen, takes damage with a visual flash, and eventually dies after a set number of hits.

The Gdx2DGameFramework can be found here on github.

GameMaps and Layers

In the seventeenth video of the “Building a Game Development Framework” series, I implement a sophisticated Layering System and a new architectural concept called Game Maps. These updates provide 3D-like depth in a 2D game, enabling objects to be rendered on top of one another and moved across different visual planes.

I go from managing sprites directly in the Screen class to using a GameMap.

  • Centralized Logic: SimpleScreen is moved into the BaseScreen, and the logic for storing and updating sprites is delegated to the GameMap.
  • Custom Iterators: To handle complex updates (like removing an object from one layer and adding it to another in the same frame), the creator implements a custom Iterator. This prevents “Concurrent Modification Exceptions” when the game tries to update the list of objects while it is still drawing them.

Entities now possess a layer property (an integer), allowing the framework to sort them from back to front.

  • Rendering Order: The GameMap renders objects starting from layer 0 upward. Objects in higher layers are naturally drawn on top of those in lower layers.
  • Collision by Layer: To optimize performance and enable complex interactions, collision detection is updated to only check objects that are currently in the same layer.

Building on the previous “Mouse Drag” system, I add the ability to physically “lift” an object to a higher layer when it is grabbed.

  • Vertical Transitions: When a user clicks a chip (like in Checkers), the movement class automatically increments its layer. This visually pulls the piece “up” toward the camera.
  • Reversion on Collision: If a user tries to “drop” a piece into a lower layer where another object already exists, the framework detects the collision, reverses the layer change, and prevents the drop.

With multiple layers and overlapping sprites, a major technical hurdle is ensuring that a single click doesn’t trigger multiple objects simultaneously.

  • The Lock System: The creator introduces an InputLocks object. When an actor is grabbed, it “locks” the mouse input to itself. Even if the user drags that actor over another one, the second actor ignores all input because it doesn’t hold the lock.
  • Two-Step Clearing: Unlocking is handled as a two-step process to ensure that a “release” click doesn’t accidentally trigger a different object on the frame immediately following the drop.

I show these features using red and black checker chips on a board:

  • Stacking: Pieces can now be moved over one another without colliding if they are at different heights (layers).
  • Layer Toggling: When a piece is clicked, it visually pops to the top layer. When released, it settles back down into the base layer.
  • Drop Logic: You can successfully drag a piece over another, but you cannot “drop” it unless the square underneath is empty.

By the end of this video, the framework supports complex 2D depth, allowing for board games, top-down RPGs with multi-level environments, or UI systems with overlapping windows.

The Gdx2DGameFramework can be found here on github.

Building a Simple Game with the Framework

I my thirteenth video of the “Building a Game Development Framework” series, I change things up by using the framework to build a functional Shoot ‘Em Up (Shmup) style game. This project shows how to use the framework in a game development scenario. It focuses on using the framework’s features, such as input handling, movement scripting, and collision detection.

First I update the framework to the latest versions.

  • LibGDX 1.12.1: I updated the framework to the latest version of LibGDX.
  • LWJGL3 Transition: With newer LibGDX versions, the backend transitions from LWJGL2 to LWJGL3. This requires updating the DesktopLauncher configuration to use Lwjgl3ApplicationConfiguration, which changes how window titles, icons, and dimensions are set.

Implementing the Game

I build the player character using the Actor class:

  • Movement Constraints: First, I override the update method to prevent the ship from moving off-screen. The Movement checks the X-coordinate and “clamps” it so the ship stops just before hitting the window edges.
  • Audio Integration: A laser sound effect (created in ChipTone) is triggered whenever the player presses the Space key.

To handle many objects on screen (like hundreds of lasers or falling obstacles), I create the Actor Factory class with Object Pooling:

  • The Problem: Creating a new “Laser” object every time the player fires is memory-intensive and can cause lag.
  • The Solution (Pooling): The factory maintains a Queue of pre-created actors. When a laser is needed, it’s pulled from the queue and “reset.” When it leaves the screen, it’s added back to the queue for reuse instead of being deleted.

The game needs a way to spawn enemies or obstacles over time. I made these updates to accomplish that:

  • Updatable Interface: A new interface is added for logic that needs to run every frame but doesn’t have a visual sprite (like a “spawner” or “game manager”).
  • Randomized Spawning: The ObstacleFactory implements Updatable. Every 1 second, it picks a random X-coordinate and spawns a falling block at the top of the screen.

The collision logic between lasers and obstacles utilizes the movement scripts built in the previous video:

  • Hit Logic: When a laser hits an obstacle, it doesn’t just disappear. The obstacle’s movement script transitions to an Easing Action, causing it to slow down and changing its color to red (simulating being “destroyed”) before eventually being removed and returned to the pool.
  • Collision Filtering: To prevent “ghost” collisions, the obstacle’s contains method is overridden to return false once it has been destroyed, allowing other lasers to pass through it while it’s in its death animation.

By the end of the video, the user has a playable demo where they can move a ship, fire lasers, and destroy falling obstacles with smooth transitions and efficient memory management.

The Gdx2DGameFramework can be found here on github.

Scripting Movement

In my twelfth video of the “Building a Game Development Framework” series, I introduce a Scripted Movement System. This allows developers to define a sequence of automated actions for Non-Player Characters (NPCs) or cinematic events without writing manual update logic for every frame.

Before diving into scripting, I made a few essential adjustments to the core framework:

  • Window Resizing: Desktop window sizes are now correctly handled via LibGDX configurations and the resize method, ensuring the game’s internal coordinate system matches the actual window dimensions.
  • Safe Sprite Removal: To avoid “Concurrent Modification Exceptions” (crashes when deleting a sprite while the game is still drawing or updating it), the framework now uses a “To Be Added” list and a removed flag. Sprites are only physically added or removed from the main list between render cycles.
  • Clean Interfaces: Redundant methods were removed from the Movement interface, delegating all positional updates to the SpriteUpdate class.

The scripting system is built around two main components:

  • ScriptMovement: The “brain” that holds a list of actions and executes them one by one. Once an action reports it is “done,” the script automatically transitions to the next one in the list.
  • ScriptAction: The base class for specific behaviors (waiting, moving, turning). It includes logic for Gradual Direction Changes, allowing actors to make smooth arcs rather than “snapping” to a new heading.

I detail several specialized actions that can be chained together:

  • Wait Action: Pauses the actor for a set duration. You can also specify a direction for the actor to face while waiting.
  • Time Movement Action: Moves the actor in a specific direction at a set speed for a fixed amount of time.
  • Change Speed Action: Gradually accelerates or decelerates the actor over time.
  • Destination Action: Moves the actor to a specific (X, Y) coordinate. It uses the Pythagorean theorem to calculate the distance and ensures the actor stops exactly on the target.
  • Easing Action: Provides advanced acceleration/deceleration curves (Linear, Quadratic, Cubic, etc.). This allows for “juicy” movement where an actor starts slow and “eases in” to a high speed.
  • Go-To Action: A logic-based action that jumps to a previous step in the script, allowing for infinite loops or repeating patterns.

I then demonstrate how to combine these actions to create a complex patrol path:

  1. Ease In: The character starts from a standstill and slowly speeds up.
  2. Move to Destination: The character hits a series of four (X, Y) coordinates to walk in a square.
  3. Go-To: After the fourth corner, a GoToAction sends the script back to the second step, creating a permanent walking loop.

This scripting system significantly reduces the complexity of AI behavior, allowing developers to “choreograph” NPCs using a simple list of commands.

The Gdx2DGameFramework can be found here on github.

Handling Collisions in Video Games

In my eleventh video of the “Building a Game Development Framework” series, I implement Collision Detection and update the Screen Management system. This update allows game objects to interact with each other physically and simplifies how developers add entities to their game world.

I make the foundation of the collision system the BoundingBox class.

  • AABB (Axis-Aligned Bounding Box): The framework uses simple rectangle-to-rectangle intersection checks. It verifies if the X or Y edges of one sprite fall within the span of another.
  • Insets and Offsets: A major improvement I made is adding Insets. Often, a sprite’s image (for example, 64×64 pixels) is much larger than the actual character (e.g., a person is thin, but the image is a square). With Insets this allows you to shrink the physical “hitbox” independently of the visual image so collisions will feel more realistic.

I setup the framework to categorizes collisions based on whether an object is an “Actor” or “Decor”. Since Decors do not move, they don’t check if they collide with another sprite. Actors, which move, do check if they collide with another sprite.

  • Impact Flag: Not all objects need to check for collisions. The doesImpact() method allows the system to skip checks for static background objects (Decor) that don’t move, saving processing power.
  • Reversion Logic: When a collision is detected, the framework “reverts” the character’s movement. In the basic implementation, it simply moves the character back to their previous non-colliding position.
  • Sliding Physics: In more advanced movement (like in 4-key or mouse movement), the framework checks horizontal and vertical collisions separately. If you walk diagonally into a wall, the X-movement might be blocked, but the system allows the Y-movement to continue, creating a “sliding” effect against the wall.

I also introduce the SimpleScreen, which automates the rendering loop:

  • The Sprite List: Instead of manually updating every character, the screen maintains a list of all Sprites.
  • Automated Loop: The renderChild method automatically iterates through the list, calls update(), performs detectCollision(), and then calls draw() for every entity.
  • Separation of Concerns: New abstract methods addActor() and addDecor() provide a clean way for developers to populate their game world during the show() phase.

To make the world feel more alive, I introduce a Random Sound class:

  • Footstep Variance: Instead of a single repetitive sound, this class holds a list of similar sounds (e.g., four different footstep recordings). Every time an animation triggers a sound, it randomly picks one from the list to create a more natural, less “robotic” audio experience.

I conclude with a demonstration of an Actor (the player) navigating around our “Soda Machine” Decor objects. I show how the player is physically blocked by the machines and how the “sliding” logic feels smoother than a hard stop.

This set of features transforms the framework from a simple animation player into a functional game engine where objects have physical presence and sound.

The Gdx2DGameFramework can be found here on github.

Sound and Music with LibGDX

In my tenth video of the “Building a Game Development Framework” series, I focus on integrating audio capabilities into the LibGDX framework. I cover the distinction between sound effects and music, how to manage their lifecycles, and how to trigger them within an animation system.

The LibGDX framework distinguishes between two types of audio objects based on how they are handled in memory:

  • Sound: Intended for short clips (typically < 10 seconds), such as explosions or footsteps. These are completely loaded into RAM.
  • Music: Intended for longer tracks, like background music or ambient loops. These are streamed from the storage device to save memory.

I demonstrate the primary methods for controlling both types of audio:

  • Playback: Simple .play(), .pause(), .resume(), and .stop() methods. Note that Music does not have a dedicated “resume” method; calling play() on a paused track automatically resumes it from where it left off.
  • Volume & Pitch: Volume is set as a float from 0.0 to 1.0. Pitch (available only for Sound) speeds up or slows down the audio to change its tone—useful for varied sound effects like different-pitched “hellos”.
  • Panning: Moves audio between the left and right speakers (values from -1.0 to 1.0). This only works on mono audio files.
  • Positioning: Specifically for Music, you can jump to a specific second in a track using setDevicePosition().

Because audio objects consume system resources, they must be “disposed” when no longer needed.

  • Automatic Disposal: I have updated the BaseScreen with maps for both Sound and Music objects. When a screen is closed, the framework automatically iterates through these maps and disposes of every audio file to prevent memory leaks.

Then I update my framework to have “Sound-Aware” animations. By adding a Sound field to the BaseAnimation class, animations can now trigger audio automatically:

  • Static/Block Animations: Play a sound once when they first appear on the screen.
  • Loop Animations: Play a sound at the start of every loop cycle (e.g., a footstep sound every time a walk cycle reaches the first frame).
  • Bounce Animations: Trigger sounds at both the beginning and the “apex” (last frame) of the animation, making it ideal for things like bouncing balls or swinging pendulums.
  • Random Animations: Use a counter to ensure a sound plays every time the animation completes a full set of frames, even though the frames themselves are randomized.

I then recommend two browser-based tools for generating 8-bit/16-bit sound effects for game development:

  • SFXR: A classic generator for “retro” sounds.
  • ChipTone: A more modern, user-friendly version of SFXR for creating explosions, jumps, and other game effects.

The Gdx2DGameFramework can be found here on github.

Improved Sprite System For Games

In my ninth video for the “Building a Game Development Framework” series, I focus on establishing a professional-grade Sprite System. I enhance the framework, moving from loose animation and movement logic into a structured architecture by introducing two primary classes: Decor and Actor.

To better manage a game world, the framework now separates objects based on their behavior:

  • Decor: Represents static elements that do not move or have complex logic, such as a rock or a building. These are lightweight and optimized for objects that just need to be drawn.
  • Actor: Represents dynamic, “living” entities that can move, animate, and react to logic (e.g., players, enemies, or NPCs).

The “Sprite” in this framework acts as the parent container. It ties together the separate components built in previous videos into a single, cohesive unit:

  • Movement: Handles the math of where the object is going (Keyboard, Mouse, or AI).
  • Motion: Manages which animation set to use based on the heading (4-way, 8-way, etc.).
  • Animation: Handles the actual frame-by-frame rendering.

In this video, I demonstrate how to build the Actor class to automate the character’s update loop.

  • Automated Updates: Instead of manually calling movement.update() and motion.update() in your screen’s render method, you now simply call actor.update(). The Actor class internally handles data (delta time) delegation between its movement and motion components.
  • Flexibility: Because the Actor uses the Movement and Motion interfaces, you can swap a player’s controls from “Keyboard” to “Mouse” just by changing one line of code during initialization, without changing how the character is drawn or updated.

I then integrate this with the LibGdx library by updating the Screen class logic, making the game screen significantly less cluttered. Instead of managing individual floats for X and Y or multiple animation timers, the screen simply holds a list of Sprite objects and iterates through them to call update() and draw().

By separating Decor from Actors, you optimize performance (static objects don’t need movement logic) and create a system where complex game entities are built like “Legos.” This modularity makes it easy to add features like pathfinding or new animation types later in development without breaking existing characters.

Note: Since this is part of a series, this video serves as the “bridge” that turns the experimental code from earlier videos into a structured framework ready for actual game projects.

Movement using a mouse

In my eighth installment of the “Building a Game Development Framework” series, I expand on the movement system to include mouse-based interaction and introduce visual translucency for sprites.

I have created a new Movement class that allows characters to move toward a target destination clicked by the user.

  • Goal-Oriented Logic: The class tracks a goalX and goalY. When the user clicks, the system calculates the direction from the Sprite’s current position to the click point and moves the character at a set speed.
  • Overshoot Prevention: To prevent the Sprite from going past the target, the code includes logic to check whether the next move would exceed the goal. If so, it simply sets the character’s position to the exact goal coordinates.
  • Continuous Tracking: If the mouse button is held down, the character will continuously follow the cursor.

To make the movement classes more flexible, the framework now uses a SpriteUpdate interface. This acts as a bridge (using a visitor pattern) that allows the movement logic to “get” and “set” the X and Y coordinates of any object, whether it’s a player character or an NPC, without the movement class needing to know the specific details of that object.

A significant challenge in 2D games is clicking on the screen when the camera has moved or is moving.

  • The Problem: Standard input gives “screen coordinates,” but if your camera has scrolled to the right (or any direction from center), a click at screen position (100, 100) is actually a different coordinate on the game’s screen (game world).
  • The Solution: The ScreenInput class is updated to take the camera’s position into account when returning the location of the mouse click on the world map. It offsets the raw mouse input by the camera’s current X and Y coordinates, ensuring that clicking on a world object always works regardless of where the viewport is positioned.

Additionally, I added a “translucency” property (often called Alpha, or sometimes transparency) to the animation and motion interfaces.

  • Value Range: A float from 0.0 (fully invisible) to 1.0 (fully opaque).
  • Inheritance: Setting the translucency on a Motion object automatically applies it to all underlying animations (4-way, 8-way, etc.).
  • Visual Trick for “Behind Walls”: I demonstrate a clever rendering trick for characters walking behind objects (or through objects). Instead of making the character translucent, you render the character in front of an opaque image and behind another layer, then the front layer is rendered semi-transparent. This creates a professional-looking “X-ray” effect where the character appears to be obscured but still visible.

I also point out a subtle LibGDX “gotcha”: the built-in color constants (like Color.WHITE) are actually mutable objects. If you change the alpha of Color.WHITE for one sprite, it would change it for every other sprite using that constant. To fix this, the framework now creates a new color object every time a color or translucency is set, ensuring visual isolation between sprites

Movement in Games

In this video for “Building a Game Development Framework” series, I focus on integrating LibGDX input handling into my custom 2D framework. The goal is to move away from complex, repeated logic and instead encapsulate movement and input into reusable classes.

First, I handle the coordinate system. A common issue in game development is that mouse input coordinates often start from the top-left, while OpenGL (used by LibGDX) renders from the bottom-left. I created a ScreenInput helper class to:

  • Flip the Y-axis: Automatically subtracts the input Y-position from the screen height so the “game world” Y matches the “input” Y.
  • Delta Movement: Provides methods to get the change in mouse position (DeltaX, DeltaY), which is essential for mechanics like first-person camera rotation or smooth scrolling.

Then I demonstrate the difference between the primary LibGDX input polling methods:

  • isButtonPressed vs. isButtonJustPressed: The former returns true as long as a button is held down (useful for continuous fire), while the latter fires only once per click (useful for menus).
  • Key Input: Similar logic is applied to keyboard keys using Gdx.input.isKeyPressed.
  • Multi-touch: Brief mention of how to handle specific finger indexes for mobile development.
  • Text Input: A quick look at a built-in dialog for simple user text entry.

Then I explain my Movement Interface and its implementations, which decouple “how a character moves” from “how the animation is drawn.”

  • Base Movement & Magnitudes: A base class calculates movement “magnitudes” using sine and cosine. This ensures that when a character moves diagonally, they don’t move faster than they do horizontally or vertically (maintaining a consistent speed).
  • Two-Key Movement: Encapsulates logic for moving along a single axis (e.g., Left/Right or Up/Down) using two specified keys.
  • Four-Key Movement: Handles full 2D movement. It includes a “Multi-key” mode that allows for diagonal movement (8 directions) by checking if two keys (like Up and Right) are pressed simultaneously.

I sum this up by showing how the framework simplifies the code. Instead of writing complex if statements in every character class, a developer only needs about five lines of code to:

  1. Initialize the movement type (e.g., FourKeyMovement).
  2. Update the movement with delta time.
  3. Update the animation’s direction based on the movement’s calculated heading.
  4. Update the character’s X and Y coordinates using the movement’s calculated updates.

This modularity allows for easy swapping of controls—for example, switching a character from keyboard controls to a different input method—without touching the animation logic.

The Movement package can be found here: Movement in Gdx2DFramework.