🎈 hansel

Tower defense

Web demo: itch.io

Tree of Life began in October 2021 and finished in eight weeks.

  • I had 3 teammates,
  • Felisha focused on art and game production
  • Yu Ling on our level designs and writing
  • Pei Chih on story craft and a large part of art

We playtested and iterated with a small group of students weekly. All groups were doing either top-down 2 dimension games or side-scrollers, but we wanted to stand out.

So we landed on tower defense, in hindsight, a challenging concept. We also drew on Plants vs Zombies to highlight environmental concerns.

Two challenges to highlight.

Challenge 1: Path finding

Code on Github.

To carve a small scope and distribute some work (such as level designs), it was best to make drag-and-drop and no-code possible for my teammates with no coding background. In tower defense, enemies must somehow find a way to their objective. We could draw a manual path by hand, for many enemies in many levels, or we could write a pathfinding method and simplify level building!

Guess which we chose :)

It wasn't that simple, but I adopted a shortest path method which takes in 2 positions:

  • Start / spawn location
  • Destination / tree of life

The monsters (or rubbish creatures) will have to traverse steps and small chasms by jumping up, or jumping over, or dropping between gaps in platforms to move to their objectives.

Since our game is covered by a grid system, we can traverse grids from the top left (0,0) to the bottom right(x, y) and calculate the 8 different directions in an octagonal path around the object, and consider if the path is a solid block (pavements and steps) or not. It takes one step if unobstructed, or considers jumping or dropping if obstructed.

At the new position, we iterate this process until all paths have been calculated, or we have landed on the destination position.

function scr_fill_the_grid(start_x, start_y, goal_x, goal_y) 
    Initialize path_found to 0
    Initialize point_list and add the start coordinates to it
    Create a copy of the global pathfinding grid

    for each iteration(up to 200):
        if path is found:
            destroy point_list and grid, then exit

        if point_list is empty:
            destroy point_list and grid, return path_not_found

    for each point in point_list:
        get current point's coordinates (ax, ay)

        if current point is the goal:
            set path_found to 1, build path, and exit

        Attempt horizontal move to the right
        If blocked, attempt jump, gap jump, or big jump to the right
        If blocked further, attempt fall cases to the right

        Attempt horizontal move to the left
        If blocked, attempt jump, gap jump, or big jump to the left
        If blocked further, attempt fall cases to the left

    Clear previous points from point_list

End function

Challenge 2: Interactive items

Code on Github.

This was my first time applying wave equations in programming. To create objects that float magically and on button press, be collected, we'll have to implement a sine-wave motion. This happens with game time and ticks, which the GameMaker engine has abstracted but leaves us to manipulate the variables for each tick.

The code looks fairly straightforward, but behind it are days and hours of testing to get it just right.

// Function that runs inherited events and initializes a timer
Run inherited events
Initialize timer and increment by 1 each time

// Calculate sine wave for movement or transparency effect
Set sineWave to a value based on a sine function using current time and amplitude

// Check if there is interaction to process
If interactOrigin is "no one" or interactX is - 1, exit the function

// Calculate distances for movement interpolation
Calculate distanceX as the absolute difference between start position(xstart) and interaction position(interactX)
Calculate distanceLeft as the absolute difference between current x position and interactX
Set time as pickedTime divided by room_speed(used for animations or transitions)
Calculate percent as the fraction of distance covered

// If the item is not picked up
If picked is false
    Set x to floor(x)(align x position to an integer for precision)

  // If the item is picked up
Else
    Decrease image_alpha(transparency) gradually over time
    If image_alpha drops below a threshold or sine function condition met
        Set visible to false(make item invisible)
        Reset position x and y to 0

    // Apply sine wave-based movement pattern
    Move y position based on time along a sine wave

    // Move item left or right based on interaction position
    If x is to the left of interactX
        Move x position to the right by incrementing based on time
    Else
        Move x position to the left by decrementing based on time

Summary

First is a path finding algorithm to enable quick level building. If the enemies could move by themselves instead of a set path, we would be done with just level design and difficulty balancing! Sounds simple, right? Right?

Second, an implementation of wave functions to make delightful animations!

Other challenges from making this

  • Game inputs
  • Camera, movement, fluidity
  • Producing game sprites
  • Organisation of code
  • Saving game state - level restarts
  • Make it pretty

This project brought me immense joy on completion, but there were so many nights burnt and bugs to deal with. It gave me an idea of how the gaming industry moves, under a lot of time pressure to deliver.

Other media

Pathfinding calculations

User interface

Pathfinding

Pathfinding with obstacles

Item picking animation

updated