For Ludum Dare 46, I created a very simple tamagotchi game called “Potted Plant Simulator” in Unity (you can find the code here). We kicked around this idea for “Keep it Alive” at the Knox Game Design online meetup, but it was more or less a joke.
However, brainstorming brought me back to the idea because I started wondering about how to procedurally generate a plant in Unity. I speculated that Unity’s hierarchy system (which localizes position, scale, and rotation to the parent node) would let you chain together branches, and I had to test that hypothesis. (Spoiler: hierarchy does not seem to be the best way to handle this.)
My system revolves around a configurable “plant” object that serves as the root of a tree structure of “branches.” Each branch would maintain multiple leaves and branches; branches could originate either from the end of the branch of from a joint within the branch.
You could build many different types of plants by adjusting graphics and minimum/maximums for random generation. (Early on, I thought about multiple plants–and the code should allow that with minimal changes.)
Initially, I imagined this would generate a basil-like plant: mostly upright with multiple branches and clusters of leaves. Instead, it ended up generating sprawling vines. That felt a bit more whimsical, which arguably worked better with a name like “Potted Plant Simulator.”
Note that this is just one way of procedurally growing a plant (or any other object with a tree-like structure). Tweaking the process could give you much different results or allow for different types of games.
Each branch is represented by a vertical sprite with the pivot set at the bottom. Under that branch game object, we can place other objects representing leaves and joints. In most cases these have a relative X position of 0 (because they’re on the branch) and a relative Y position representing the location along the length. (In my case, 0.64 was the endpoint where new branches could grow.)
When generating a new branch, we do a few things:
Set the branch’s origin. This is the transform that the branch sprouts from. It can be the pot, the end of another branch, or a joint.
Generate a number of joints where other branches can spawn. We pick the number based on the plant’s maximum and minimum branch joints. For each joint, we randomly decide whether it will spawn branches on the right or left side of the branch.
Each joint is a separate game object, which serves as the origin position for any child branches. (Originally, I’d considered adding a small sprite to each joint, but that proved to be too busy.)
We set the joint’s rotation to (0, 0, -90) or (0, 0, 90), depending on which direction it spawns branches. This way, it points in the direction that branches will sprout from it.
Generate a number of leaves on the branch. We pick the number based on the plant’s maximum and minimum branch leaves settings. The logic is basically the same as joints, although our leaf object contains a bit more functionality.
Pick a maximum number of child branches that the branch can support. Arguably this could be lumped in with joint generation, but I decided to keep it separate so that a joint could support multiple branches. We pick the number based on the plant’s minimum and maximum branch children settings.
Pick a branch angle (relative to its parent). We pick the number based on:
- the plant’s maximum and minimum branch angle if the branch is parented to the end of another branch (or the plant itself)
- the plant’s maximum and minimum joint angle if the branch is parented to another branch’s joint.
This allows us to have slightly different behavior depending on where a branch sprouts. If a branch sprouts from the end of a branch, its range is from -30 to 30 degrees so it has a full range of motion.. If a branch sprouts from a joint, its range is from 30 to 60 degrees so that it follows the branch a little more closely.
Incidentally, this is where the upright plant became a creeping vine. Because the angle is always relative to the parent branch, a chain of branches curving in the same direction could quickly wrap back on itself.
This could be mitigated by taking the branch’s absolute rotation into account and limiting angles that would point downwards. (Again, I thought the vine worked. Also it was a weekend and I am lazy.)
Pick a branch length. We pick a number based on the plant’s maximum and minimum branch length. This modifies the branch’s X and Y scale to create a little bit of variation. (This is another bug in my original build–it looks like I was always setting this to the minimum.)
Set branch growth to 0. Every branch starts out at 0% growth. We use this to modify the branch’s Y scale between the plant’s minimum and maximum branch growth.
As the branch consumes food, we increment this value, giving the illusion of it budding and growing out of its parent branch. Because the leaves are parented to the branch, they also appear to grow with the branch. (My game rules don’t allow a branch that isn’t fully grown to sprout new branches, so we don’t have to account for how this should affect its children.)
Now we have a fully functioning branch. When we decide to sprout a new branch from it, we’ll randomly pick either the end of the branch or a joint and start the process all over again. The endpoint or joint game object serves as the branch’s origin.
Every frame, we reposition the branch based on its current values:
Move the branch to its origin’s position. Again, since the branch’s pivot is at the bottom, this makes it look like it’s growing from the origin.
Set the branch’s Z rotation Euler angle. Start with the parent object’s global Z rotation angle, and then add or subtract the branch angle to determine its position in world space. (Normally we add, but if we’re growing out of a joint on the left side of the parent, we subtract.) This looks like:
Mathf.Repeat(parentAngle + (angle * (IsReversed ? -1 : 1)), 360)
Set the branch’s scale. We set the scale as follows:
- X scale = branch growth, so that every branch is the same width when fully grown
- Y scale = branch growth * branch length, so that branches grow into their full length, whatever that is
We start at the root branch for the plant, and then walk down through the tree updating each branch along the way. Each branch’s UpdateBranch() method is responsible for calling all of its childrens’ UpdateBranch() methods. This way, we can be sure that each branch is always positioned based on its parent’s most recent position.
Update the leaf state. Each leaf updates its sprite based on its parent branch’s state. There are a total of 16 sprites, representing a combination of:
- growth state (bud to full leaf)
- watered state (full and green to shriveled and brown)
The Big Bug
I mentioned that I tried to use Unity hierarchy and struggled with it. Surprisingly, I didn’t realize this until I revisited the code for this blog post. My original approach was good enough for quick weekend game jam QA, but didn’t stand up to real scrutiny.
The primary problem was that I needed to set local position and rotation, but global scale.
Parented transforms convert between local (relative to the parent) and global (relative to the world) numbers. Scale can be particularly tricky because you can’t edit the global numbers directly, and Unity’s automatic conversions can’t read your mind.
It worked fine for the first few levels of branches. After several branches that included negative scale values (to determine sprouting from the left or right of its parent) a branch might end up flipped. That could create an awkward angle, which our min/max angles were designed to prevent. If a branch sits at an awkward angle, it also affects the flow of branches growing out of it.
Instead, I was able to create each branch as a top-level object in the hierarchy. The branch keeps track of its “origin” so that it can get its angle and position (since those are the easiest to make relative). It can set the scale directly, since that’s the tricky part.
I don’t love the workaround. If I’m honest, that’s partly because it’s not as clever as the tree structure. But it’s also a bit harder to debug, since you can’t see relationships in Unity’s hierarchy view.
So that’s how branch generation and state changes are handled. When they occur (based on watering and feeding) is a topic for another post.