08 August 2026

Building an A Level Platform Game Project — Part 6: Adding Enemies, Moving Hazards and Advanced Interaction

 


Building an A Level Platform Game Project — Part 6: Adding Enemies, Moving Hazards and Advanced Interactions

In Part 1, we planned the platform game and set realistic success criteria.

In Part 2, we created the game window and added basic player movement.

In Part 3, we added gravity and jumping.

In Part 4, we added platforms and collision detection.

In Part 5, we turned platforms into proper levels with routes, start points, finish points, collectables, hazards, score and lives.

Now the game is ready for the next step.

We need to make the level feel alive.

A static platform game can still be useful, but once we add enemies, moving hazards and more advanced interactions, the project becomes much more interesting. The player is no longer simply jumping from platform to platform. They now have to react, time their movements, avoid danger and make decisions.

For an A Level Computer Science project, this is an excellent stage. It introduces movement patterns, simple artificial intelligence, state changes, timing, object lists, collision detection, testing and balancing difficulty.

It is also the stage where students need to be careful.

Adding enemies and moving hazards can make the game better, but it can also make the project much harder to finish. The aim is not to add dozens of complicated features. The aim is to add a small number of well-planned interactions that can be tested and explained clearly.

Why Moving Objects Change the Game

So far, most of the game world has been static.

Platforms stay in one place.
Collectables wait to be collected.
Hazards sit in fixed positions.
The finish point waits for the player.

That is fine for an early level, but it can become predictable.

Moving hazards and enemies create challenge because the player must respond to change.

A moving enemy can patrol a platform.
A moving spike block can travel back and forth.
A falling object can appear at intervals.
A moving platform can carry the player across a gap.
A timed laser can switch on and off.

These features make the game feel more dynamic.

More importantly, they introduce useful programming problems.

The Aim for Part 6

The aim of this stage is:

Add enemies, moving hazards and more advanced interactions to make the level more challenging and engaging.

By the end of this stage, the game could include:

  • a simple patrolling enemy

  • a moving hazard

  • collision detection between the player and enemies

  • lives lost when touching danger

  • moving objects stored in lists

  • simple movement boundaries

  • testing evidence for enemy behaviour

  • improved level design using timing and risk

Not every student needs to add all of these features. It is better to add one or two well-tested features than six unfinished ones.

Start With One Enemy

The simplest enemy does not need complex artificial intelligence.

It can simply move left and right between two points.

For example, an enemy might patrol along a platform. When it reaches the left boundary, it turns right. When it reaches the right boundary, it turns left.

This is easy to understand, but still gives the player a real challenge.

A simple enemy could be represented as a rectangle:

enemy_rect = pygame.Rect(300, 540, 40, 40)
enemy_speed = 2
enemy_left_limit = 250
enemy_right_limit = 500

The enemy moves each frame:

enemy_rect.x += enemy_speed

Then the direction changes when the enemy reaches a boundary:

if enemy_rect.left <= enemy_left_limit:
    enemy_speed = 2

if enemy_rect.right >= enemy_right_limit:
    enemy_speed = -2

This creates a patrol movement.

It is not complicated, but it is enough to make the level more interesting.

Turning the Enemy Into Data

As the project grows, one enemy is not enough. The game may eventually need several enemies.

Instead of creating separate variables for every enemy, we can store enemies as data.

For example:

enemies = [
    {
        "rect": pygame.Rect(300, 540, 40, 40),
        "speed": 2,
        "left_limit": 250,
        "right_limit": 500
    },
    {
        "rect": pygame.Rect(500, 360, 40, 40),
        "speed": 1,
        "left_limit": 450,
        "right_limit": 650
    }
]

Then we can update all enemies using a loop:

for enemy in enemies:
    enemy["rect"].x += enemy["speed"]

    if enemy["rect"].left <= enemy["left_limit"]:
        enemy["speed"] = abs(enemy["speed"])

    if enemy["rect"].right >= enemy["right_limit"]:
        enemy["speed"] = -abs(enemy["speed"])

This is a very useful A Level project idea.

The student can explain that enemies are stored in a list of dictionaries so that multiple enemies can be processed using the same code.

This is much better than writing separate code for enemy one, enemy two and enemy three.

Drawing Enemies

Drawing enemies is straightforward once they are stored in a list.

For example:

for enemy in enemies:
    pygame.draw.rect(screen, (150, 0, 150), enemy["rect"])

At first, enemies can be simple purple rectangles.

Later, they could become sprites, robots, monsters, bugs, spikes or animated characters.

The important thing is not the artwork. The important thing is the behaviour.

A plain rectangle that moves correctly is better than a beautiful enemy that does not work.

Collision With Enemies

Once enemies exist, the player needs to interact with them.

The simplest rule is:

If the player touches an enemy, the player loses a life and returns to the start position.

Example code:

for enemy in enemies:
    if player_rect.colliderect(enemy["rect"]):
        lives -= 1
        player_rect.x, player_rect.y = current_level["player_start"]
        player_y_velocity = 0

If lives reach zero:

if lives <= 0:
    game_over = True

This creates a clear lose condition.

It also creates excellent testing opportunities:

  • Does the enemy move?

  • Does the enemy turn around at the boundary?

  • Does the player lose a life after touching the enemy?

  • Does the player return to the start?

  • Does the game end when lives reach zero?

These are specific and testable.

Should the Player Defeat Enemies?

Some platform games allow the player to defeat enemies by jumping on them.

This is a more advanced interaction.

The game has to decide whether the player touched the enemy from the top or from the side.

If the player lands on top of the enemy, the enemy is removed.

If the player touches the enemy from the side, the player loses a life.

A simple version might check whether the player is falling and whether the bottom of the player is near the top of the enemy:

if player_rect.colliderect(enemy["rect"]):
    if player_y_velocity > 0 and player_rect.bottom <= enemy["rect"].top + 15:
        enemies.remove(enemy)
        player_y_velocity = -8
        score += 20
    else:
        lives -= 1
        player_rect.x, player_rect.y = current_level["player_start"]

This is more complex and should be treated as an extension.

It is a good example of advanced collision response.

It also creates a useful design decision:

Should the game reward attacking enemies, or should the player simply avoid them?

Either answer is acceptable if the student can justify it.

Adding Moving Hazards

A moving hazard is similar to an enemy, but it does not need to look intelligent.

It might be:

  • a moving spike block

  • a swinging danger zone

  • a rising lava area

  • a moving electric barrier

  • a falling rock

  • a timed laser

A simple moving hazard could move vertically instead of horizontally.

Example:

moving_hazards = [
    {
        "rect": pygame.Rect(600, 400, 40, 40),
        "speed": 2,
        "top_limit": 300,
        "bottom_limit": 520
    }
]

Updating the hazard:

for hazard in moving_hazards:
    hazard["rect"].y += hazard["speed"]

    if hazard["rect"].top <= hazard["top_limit"]:
        hazard["speed"] = abs(hazard["speed"])

    if hazard["rect"].bottom >= hazard["bottom_limit"]:
        hazard["speed"] = -abs(hazard["speed"])

This creates a moving danger area that the player must avoid.

The player now has to time the jump, not just make the jump.

That makes the level feel more alive.

Timed Hazards

Another useful interaction is a timed hazard.

For example, a laser could switch on and off every few seconds.

This introduces the idea of time-based state.

A simple timer variable might be:

hazard_timer += 1

Then:

if hazard_timer < 120:
    laser_active = True
else:
    laser_active = False

if hazard_timer > 240:
    hazard_timer = 0

At 60 frames per second, this means:

  • laser active for about 2 seconds

  • laser inactive for about 2 seconds

  • cycle repeats

If the laser is active, it is drawn and collision is checked:

if laser_active:
    pygame.draw.rect(screen, (255, 0, 0), laser_rect)

    if player_rect.colliderect(laser_rect):
        lives -= 1
        player_rect.x, player_rect.y = current_level["player_start"]

This is a good extension feature because it introduces timing and changing states.

It also needs careful testing.

Moving Platforms

Moving platforms are especially interesting because they are both helpful and difficult.

A moving platform might carry the player across a gap.

That sounds simple, but it creates a tricky problem:

If the platform moves, should the player move with it?

If the player is standing on a platform moving right, the player should probably move right as well.

A simple moving platform could be stored like this:

moving_platforms = [
    {
        "rect": pygame.Rect(250, 420, 120, 20),
        "speed": 2,
        "left_limit": 200,
        "right_limit": 500
    }
]

Updating it:

for platform in moving_platforms:
    platform["rect"].x += platform["speed"]

    if platform["rect"].left <= platform["left_limit"]:
        platform["speed"] = abs(platform["speed"])

    if platform["rect"].right >= platform["right_limit"]:
        platform["speed"] = -abs(platform["speed"])

If the player is standing on the moving platform, we may need to move the player with it:

if player_rect.colliderect(platform["rect"]) and player_y_velocity >= 0:
    player_rect.bottom = platform["rect"].top
    player_y_velocity = 0
    on_ground = True
    player_rect.x += platform["speed"]

This is more advanced than a moving enemy because it affects the player’s position.

For some students, moving platforms may be an excellent extension. For others, they may be too much for the main project.

Again, the key is scope.

The Danger of Adding Too Much

This is the stage where many student projects can become messy.

Once enemies work, students want more.

They want:

  • different enemies

  • bosses

  • power-ups

  • moving platforms

  • falling platforms

  • switches

  • keys

  • locked doors

  • weapons

  • animations

  • sound effects

  • particle effects

  • multiple routes

Some of these ideas are excellent, but too many can destroy the project.

A strong A Level project is not judged by how many features were imagined. It is judged by how well the chosen features were planned, implemented, tested and evaluated.

A sensible feature plan might be:

Essential for Part 6

  • one moving enemy

  • collision with enemy

  • losing a life

  • testing enemy behaviour

Desirable

  • two enemies with different speeds

  • one moving hazard

  • improved level layout

Extension

  • jumping on enemies to defeat them

  • moving platforms

  • timed hazards

  • different enemy types

  • simple enemy animation

This keeps the project controlled.

Making Difficulty Fair

Enemies and moving hazards should make the game more interesting, not unfair.

A fair challenge gives the player time to understand what is happening.

Good design questions include:

  • Can the player see the enemy before reaching it?

  • Is there enough time to react?

  • Is the safe route clear?

  • Does the enemy movement pattern make sense?

  • Can the player learn from a mistake?

  • Is the first enemy easier than later enemies?

A moving enemy placed immediately at the start of the level may feel unfair.

A moving enemy introduced after a safe first jump may feel much better.

Good games teach the player gradually.

Good projects should show that thought process.

Testing Enemies and Moving Hazards

This stage needs careful testing because moving objects create more possible outcomes.

Example test table:

Test NumberTestExpected ResultActual ResultPass/Fail
1Start the levelEnemy appears in correct positionEnemy appears correctlyPass
2Observe enemy movementEnemy moves between two limitsEnemy patrols correctlyPass
3Enemy reaches left limitEnemy changes directionEnemy turns correctlyPass
4Enemy reaches right limitEnemy changes directionEnemy turns correctlyPass
5Player touches enemyPlayer loses one lifeLife decreases by onePass
6Player touches enemy with one life leftGame over occursGame over displaysPass
7Player avoids enemy and reaches finishLevel can still be completedLevel completePass
8Moving hazard reaches top limitHazard changes directionHazard turns correctlyPass
9Player touches moving hazardPlayer loses life and restartsPlayer restarts correctlyPass
10New user plays levelUser understands enemy dangerUser understood after first attemptPass

Testing should include observation over time.

A moving enemy might work for the first few seconds but fail later if the boundary checks are wrong.

User Feedback

At this stage, user feedback becomes especially useful.

The student could ask a tester:

  • Was the enemy easy to understand?

  • Did the movement pattern feel fair?

  • Was the level too easy or too hard?

  • Did you know what to avoid?

  • Did the hazard look dangerous?

  • Did the game give enough feedback when you lost a life?

  • What would improve the level?

Useful feedback might say:

The enemy was difficult to see because it was too similar in colour to the platform.

That could lead to a design change.

Or:

The moving hazard was too fast, and the player had no time to react.

That could lead to reducing the hazard speed.

This is exactly the type of evidence students should collect.

Recording Bugs and Fixes

Enemies and moving hazards are likely to produce bugs.

Examples include:

  • enemy moves off the platform

  • enemy gets stuck at the boundary

  • enemy speed becomes zero

  • player loses several lives instantly from one collision

  • moving hazard does not reverse direction

  • player respawns directly onto a hazard

  • moving platform pushes the player through a wall

  • enemies keep moving after game over

These are not disasters. They are opportunities.

A good development log should record:

  • what the bug was

  • how it was found

  • what caused it

  • how it was fixed

  • how the fix was tested

For example:

Problem: When the player touched an enemy, several lives were lost at once because the collision was detected repeatedly over several frames.
Solution: I reset the player position immediately after the collision and added a short invulnerability period after losing a life.
Test: I deliberately collided with the enemy several times and checked that only one life was lost each time.

This is strong evidence of debugging.

Adding a Short Invulnerability Period

One common issue is that the player may lose multiple lives very quickly if they remain in contact with an enemy for several frames.

A simple solution is to add a temporary invulnerability period after damage.

Example:

invulnerable_timer = 0

Each frame:

if invulnerable_timer > 0:
    invulnerable_timer -= 1

When the player touches an enemy:

if invulnerable_timer == 0:
    lives -= 1
    invulnerable_timer = 60
    player_rect.x, player_rect.y = current_level["player_start"]

At 60 frames per second, this gives about one second before the player can be damaged again.

This is an excellent feature for stronger students because it introduces timed state management.

Making the Level Feel Alive Without Overcomplicating It

A level does not need dozens of features to feel alive.

One patrolling enemy can change how the player approaches a jump.

One moving hazard can make timing important.

One moving platform can create a new route.

The best approach is to add one feature, test it, improve it and then decide whether another feature is needed.

A controlled level might include:

  • one easy enemy near the start

  • one moving hazard in the middle

  • one harder enemy near the finish

  • collectables placed near danger for optional challenge

This creates variety without overwhelming the project.

Linking Back to Success Criteria

This stage can support success criteria such as:

  • The game includes at least one moving enemy.

  • The enemy changes direction at set boundaries.

  • The player loses a life after touching an enemy.

  • The game includes at least one moving hazard.

  • The player can avoid enemies and complete the level.

  • The game gives clear feedback when the player loses a life.

  • User testing is used to adjust difficulty.

The student should update the success criteria if new features are added.

This is important because projects often evolve.

The final evaluation should compare the completed game with the final agreed success criteria, not just a vague original idea.

Practical Task for Students

Part 6 Student Task

Add one enemy or moving hazard to your platform game.

Your program should include:

  1. One moving enemy or hazard.

  2. Movement between two limits.

  3. Collision detection with the player.

  4. A consequence when the player touches it.

  5. A way for the player to avoid it.

  6. Testing of movement and collision.

  7. At least one adjustment after testing.

  8. Screenshots or video evidence.

  9. A development log entry explaining the feature.

  10. A clear link back to the success criteria.

Extension Task

Add one advanced interaction, such as:

  • a second enemy with a different speed

  • jumping on enemies to defeat them

  • moving platforms

  • timed hazards

  • temporary invulnerability after damage

  • enemies stored in external level data

  • different enemy types

  • sound effects for damage or enemy defeat

Students should only attempt extensions once the first enemy or moving hazard works reliably.

Development Log Example

A good development log entry might look like this:

Development Stage

Adding enemies and moving hazards.

Aim

To make the level more challenging by adding a moving enemy that patrols between two points and causes the player to lose a life on contact.

What Was Added

  • enemy rectangle

  • enemy speed

  • left and right movement limits

  • enemy movement loop

  • collision detection with the player

  • life loss after collision

  • player reset after damage

Problems Found

  • The enemy initially moved off the platform.

  • The player sometimes lost more than one life from a single collision.

  • The first enemy position made the level too difficult for a new user.

Changes Made

  • Added left and right movement boundaries.

  • Reset the player to the start after collision.

  • Moved the enemy further into the level.

  • Reduced the enemy speed after user testing.

Evidence Collected

  • screenshot of the enemy position

  • short video of enemy movement

  • code showing enemy data

  • test table for enemy collision

  • user feedback about difficulty

  • notes explaining changes made after testing

This gives the project a clear development trail.

Personal Reflection: This Is Where Students See the Difference Between Animation and Interaction

Students often enjoy this stage because it looks more exciting. Enemies move. Hazards travel. The player has to react.

But the most important lesson is not animation.

It is interaction.

A moving enemy is not just a picture sliding across the screen. It has rules. It has limits. It changes direction. It affects the player. It changes the difficulty of the level.

This is a powerful idea in Computer Science.

The game becomes a system of interacting objects.

The player, platforms, enemies, hazards, collectables and finish point all have relationships with each other.

That is why a simple 2D game can become such a useful programming project.

It gives students a practical way to explore logic, data, testing and design.

Final Thoughts: A Level That Feels Alive

By adding enemies, moving hazards and advanced interactions, the platform game begins to feel much more alive.

The player is no longer just travelling through a static route.

They must watch, time, avoid, decide and react.

That makes the game more engaging.

It also makes the project stronger because the student can show more advanced programming ideas: lists of enemies, movement limits, collision consequences, timers, state variables, user testing and difficulty balancing.

The key is control.

One well-designed enemy is better than five unfinished ones.
One moving hazard that works reliably is better than a whole level full of broken ideas.
One carefully tested interaction is better than a long list of features that cannot be explained.

A good A Level project is not about adding everything.

It is about choosing the right features, building them properly, testing them carefully and explaining the decisions clearly.

In the next article, we can look at menus, lives, scoring, high scores and the wider game structure that turns a playable level into a more complete game.

No comments:

Post a Comment

Building an A Level Platform Game Project — Part 6: Adding Enemies, Moving Hazards and Advanced Interaction

  Building an A Level Platform Game Project — Part 6: Adding Enemies, Moving Hazards and Advanced Interactions In Part 1, we planned the pla...