The Green Reaper is an incremental corn-harvesting game made in Unity initially for the University of Utah's GameCraft October 2021 game jam. Since the jam, six of the original team members, including myself, continued the project for three months. The following are a couple engineering challenges I encountered while developing the game.
After the game jam, The Green Reaper team wanted to have procedurally-generated levels as they would fit our game's tile layout well, and would increase replayability. We wanted these procedurally-generated levels to have organic patterns of corn and pathways for the player to explore the map. Grayson Spencer and I developed the following procedural generation steps for The Green Reaper.
Note: The following images of steps of the level generation process are not of the same generated level.
The first is to create a grid with the dimensions of our world, and run a game of life simulation (implemented by Grayson Spencer). Each tile in the game is assigned an integer 1 or 0 - white and black respectively.
Next, I run a gaussian blur filter to convert our integer values into floats between 0 and 1. We can think of these float values as the "fertility score" of each tile. Tiles with a value closer to 1 will have corn that takes more swipes of the player's scythe to take down, while tiles with a value closer to 0 will take less swipes. Tiles with an extremely low score will produce no corn.
Next, I create a disc with a random radius at the center of the map (where the player will spawn) and a few orbiting discs with random radii and at random distances and angles from the center disc. I then connect the orbiting discs to the center disc using paths created by Hermite splines. The splines use random positions on the discs near each other and tangents at those positions based on the relative direction of the center of the disc it lies on.
Tiles that lie on these splines and discs are set to have a fertility score of 0. This creates pathways for the player to explore the map while not being encumbered by corn.
Finally, the corn and terrain types are instanced based off of the previous "fertility score" grid using thresholds decided by our designer (Julian Liechty). This process creates nice concentric blobs of red, followed by blue, followed by green corn, while adding clear routes for the player to pick a starting point for harvesting. Random spots for special plants, root monsters, and scarecrows are selected afterwards.
Improving this system would include creating an architecture for generically applying filters onto the "fertility score" grid. This could allow for more interesting patterns of corn, and more cut-out structures than just the paths and orbiting discs.
An issue that was consistently reported with the 48-hour version of The Green Reaper was how when the player would swing their scythe such that the scythe touched multiple plants, the plant that was harvested seemed random. Under-the-hood, what was actually going on was when the scythe would swing, it would harvest the first plant it overlapped with, which at times, may not be the plant the player intended to harvest. And since at the start of the game, the scythe can only harvest one plant per swing, it would ignore all other plants until the swing was complete. This resulted in players frustratingly having to perform multiple swings to hit a single intended plant. It was evident that swinging the scythe did not provide the "clean" feeling the team was looking for; so, I took it upon myself to come up with a solution.
Since looking at just the first plant the scythe touched was not enough, I figured the solution would be to collect all the plants the scythe touched over a small period of time, and then decide which plant would be the best to damage. I implemented this by adding a SortedSet to our scythe script. The entries at the start of the sorted set would be the most eligible for damaging, while the entries at the end of the sorted set would be the least eligible for damaging. But how do we decide which plants are more or less eligible to be damaged?
In testing, there were two factors that determined which plants players preferred to harvest. The first was how close the plant was to the scythe's blade. When players wanted to harvest a particular plant, they would aim their scythe so that the preferred plant would be closer to the center of the scythe's hitbox. So, we could use distance to the center of the scythe's hitbox as a metric.
The second factor that determined which plants players preferred to harvest was the plant type. Players preferred hitting plants with special powers for their immediate benefits. For example, if the scythe hit both an exploding pumpkin and a normal piece of corn, the player would rather harvest the pumpkin first, as the pumpkin's explosion would automatically harvest the nearby piece of corn. Thus, we could assign plant types priorities and use that as an additional metric.
The following code demonstrates how I implemented these priorities into an IComparable class. I used an IComparable class so that the SortedSet would handle the prioritization order.
private class PlantHitEntry : IComparable
{
private PlantHealth plant;
private PlantType plantType;
private float distanceFromHitBoxCenter;
...
public int CompareTo(object obj)
{
if (!(obj is PlantHitEntry))
throw new ArgumentException("Can only compare ...
PlantHitEntry other = obj as PlantHitEntry;
int typeDifference = other.Priority - Priority;
if (typeDifference != 0)
return typeDifference;
float distanceComparison = distanceFromHitBoxCenter
- other.distanceFromHitBoxCenter;
if (distanceComparison < 0)
return -1;
else if (distanceComparison > 0)
return 1;
return 0;
}
...
}
This new priority system drastically improved the feeling of the scythe in The Green Reaper; and it's an interesting case of how gameplay programming is not just about creating behaviours that designers want, but also creating behaviours that are intuitive to players - even if those behaviours are more complicated to implement engineering-wise.
Itch | Simmer | Project Website | Github