One of the key unique elements that makes A Purrtato Tail a unique title is Character Connecting. But... what the heck IS that? Is it... like... slamming cats together so they stick then using those stuck cats to create a ledge to reach somewhere you can't get to on your own?
That's exactly what it is.
The origins of Character Connecting stemmed from the first prototype of A Purrtato Tail, which was a stage-based puzzler where the player connected potato cats together to create physics-based chains that could collect objects. If you think that sounds like "World of Goo but with cats" - then you'd be right. That's pretty much exactly what it was! As the game evolved to have player character and side-scrolling action, the connectivity gameplay was maintained and became part of the platforming itself.
Gameplay mockup from the original 2016 Purrtato Tail prototype.
Everything looked just a bit different back then.
Originally, characters could be connected to 'literally anything'. So if you wanted to build a tower straight up from the terrain - okay, do that. If you wanted to arch from the top of a castle to over a wall - cool, go for it. And... it was a nightmare. As cool as it was to be able to build anywhere - it made the world extremely challenging to design and maintain, as players could simply go anywhere they wanted. In addition the player originally received multiples of the same character type. So you'd have 5 Spud Soliders, 15 Tater guards, etc. And this was neat, but again - it was just too much. There was no focus! It was pure cat-chaos.
What we settled on was a system that followed a handful of basic rules:
- There will be only one of each collectible character, and that character will be a truly unique 'character' in both a gameplay and a fiction sense.
- Characters can be rotated and placed in any orientation the player desires.
- Characters cannot be placed in a way that they would intersect with world or physics objects.
- Collectible characters can be connected to other collectible characters.
- The world will have specific mount points called Fur Static Points, which are the only places a character can be connected to the game world.
- If a link in the 'chain' is removed, all characters now disconnected will be returned to the player's character inventory (their 'party'), rather than dropping to the ground.
- Each collectible character will be a different size / shape, making them have unique uses.
These rules made the game far more practical to design. As for placing the characters themselves - those needed some rules as well. Thankfully, these had already been well established in our original prototype, and those same rules worked well in our new game design. Placing characters in the game world and creating chains of them feels seamless and smooth thanks to the one simple guideline that any placed character must be connected to an object fused to the world (a 'Fur Static' point).
While it feels seamless - describing how the chains work can take a little doing. Let's see if we can describe it in a clear manner with the following two examples...
Link Chain Example A
The green area represents terrain or objects. All character links must begin with a character connected to a valid, connectable character or fur static. Nothing can be placed inside of terrain or an object. The character labeled A in this diagram is properly connected to the green terrain.
The character marked B is a Key Link character. A Key Link, if removed, will destroy part of the chain, but not all of it. For example, if the B character is removed, C will also be removed, however A and D will not be removed. This is because B is the only thing connecting C to the Link Chain, however D is also connected to A. Now, if A were to be removed, everything would be removed from this link chain example.
Link Chain Example B
This example is a bit more complex but is very similar to the Standard Link Chain example. The difference here is that the C chain is connected to the ground by both the A and D characters. With this example, any one link can be removed without the others being removed. For example- if D is removed, C is connected to B which is connected to A- therefore they are all connected. However, if B is removed, C is connected to D- which is a valid connection- and A is itself a valid connection, therefore they all stay connected, however become two separate chains. Separate chains can be turned into one chain by linking the two chains with a character.
As we proceeded with our actual development for that initial prototype, the character connecting went rather smoothly! There were a lot of the standard game-engine physics hurdles such as checking your connection joints, adjusting values, and tweaking things for days. We actually had connectivity working pretty quick. We even built out a system where when placing a character, it would utilize the vertices of the sprite's mesh to raycast outwards to find anything nearby it could connect to.
But then we got to the part where objects should not intersect other objects, and that... that Unity did not like. What we thought would be a simple detection of overlapping colliders was met with Unity looking us square in the face and spouting, "No sir, I don't like it."
First we found that Unity has no clean way to detect if two collision bodies are overlapping. So we had to come up with a solution for that. But then we found if there was a large collision body - like a terrain - then the system was unable to detect if a character was trying to be placed inside of that. So we had to adjust our thinking. What we came up with was basically a custom system mean to detect sprite collisions overlapping. It's surprisingly fast - however I would not doubt for a second that if loads of objects were trying to be placed at once, it would scream to a halt. And that's okay, because we're only worried about trying to place one object at a time.
The system we came up with again uses the existing vertices on the mesh, along with the vertices on the nearby objects. There is a custom collider object type - simply named GamePieceCollider - which is applied to anything which placeable characters can interact with, including the characters themselves. When the player is in the process of deciding where to place a character, three things are happening with each engine cycle:
- Check in a radius around the character being placed for nearby GamePieceColliders. Store a list of these.
- For all GamePieceColliders found in step 1, check if any vertex in the GamePieceCollider of the character being placed is close enough to an edge of any other GamePieceCollider around the character. Store these points so we can show a visual 'able to connect here' line.
- For all GamePieceColliders found in step 1, do a GamePieceCollider overlap. If the character being placed intersects with anything, nullify any action from Step 2 and mark the character as 'is currently intersecting'.
The GamePieceCollider overlapping is where things get interesting. We actually create an internal 'net' of points for both the character being placed, and the GamePieceCollider being compared against. Think of it like Ghostbusters - if you cross the streams, all life as you know it will stop instantaneously and every molecule in your body will explode at the speed of light. Only in this case, it's the character and whatever object they just collided with. So - crossing the streams is a bad thing, and something we want to avoid.
Okay what did I literally JUST SAY.
For both objects, we create a series of streams from every vertex to every other vertex in the mesh of the GamePieceCollider. This gives us a rather safe set of collision lines. We then check to see if any of the streams from the GamePieceCollider on the placing character crosses the stream on any GamePieceCollider in range of the character. If so - they are intersecting. With debug options all turned on, here is what one of those collision webs looks like:
What you may catch in there if you look close, is that area on the right - a place that visually looks like a character COULD be place - however the collision beam web prevents it. This is the one 'gotcha' of this setup - but a rather minor one at that. It just means if we want a very large object with concave areas to place a character, it has to be made up of multiple objects. Which is fine - as our level editor is based around the concept of stamping pieces into the world from a vast library of small components.
All of that complexity comes together to do one thing - allow the player a unique method of expanding their world! There are over 15 characters currently planned for the game - including hidden characters - which together can create chains, bridges, ramps, ledges, and more. All of this is also accompanied by character's unique Abilities, which further what can be done with character connecting.
Abilities.... now that is a topic for a future DevBlog.
Thanks for sticking around for another Purrtato DevBlog. Until next time!