KiBo was an experimental project that a friend and I worked on right after we both finished our degrees. It was an extremely rewarding game to work on due to how challenging it ended up being. I was extremely focused on networking challenges and player conveyance throughout the three weeks we had for the project. KiBo was created for the thatgamecompany x COREBLAZER GAME JAM 2025, which released the theme of generosity. After a few brainstorming sessions, the idea of ending your own gameplay experience to allow future players to progress further than you was what we settled on. Thus, the main concept of praying to turn into a statue was born and KiBo was created.
KiBo is the name of the character you play as; an alien creature with a curious disposition. Use your hands to pick up objects and maneuver around the map. Explore the temple and progress further inside to uncover why KiBo wants to make its way through. How do you get past these gates? What do those statues mean? Play to find out!
At the end of the game, you have the option of praying to leave your statue in the world. This lights a torch in the final room and when all the torches are lit, the portal to Nirvana opens!
Creating KiBo meant creating a multiplayer experience in a very short amount of time (approximately three weeks). Now, I am no stranger to multiplayer, having created many small projects in the past. However, creating something this fast was new to me. I also needed to experiment with a new server architecture, as peer-to-peer would not let all the players affect one grand total of statues. My solution was (admittedly rushed due to lack of experience) to run a server off my laptop and allow all players to connect directly to it during the duration of the jam.
I created a DirectConnectionManager which had enough versatility to allow me to toggle if a build of KiBo should be a server or client build. This script was also engineered in mind to allow for the server to shut down and move to a different machine. Player’s statue positions are saved to the server machine and reloaded when the server starts up. I also wanted to have versatility if too many players were playing the game too fast; this led to the creation of a script that initialized the torches (number of players needed) over the network when the server first starts up. This way, I can shut the server down, move the statue save over to a new machine if necessary, and relaunch the server in the exact same state it was in before it shut down!
There were a few challenges during our three-week sprint. Inverse Kinematics was something I had very little experience with, and it took a lot of trial and error to have the hands move in a way that I felt was appropriate for a creature like KiBo. I ended up with a GrabManager script which informed Hand scripts on where to smoothly translate the IK target to.
Just one day before the game was due for submission, we ran a playtest with only two players… and the server crashed in under three minutes. I was distraught. Getting to work immediately, I found the issue to be how large the packets were that each client was sending to the server. I found the issue (which ended up being completely, 100% my fault) and was able to write a new script that compiled the IK target information and send that over the network. This was necessary as before I had attached a NetworkTransform component to every bone that IKs were affecting; over 30 bones per player updating every frame (oops!).
Sending IK Targets
// Create an ikdata array the same length as our targets array.
IKTargetData[] ikData = new IKTargetData[ikTargetsToSend.Length];
// Loop through our ik target data and grab all of their transform data.
for (int i = 0; i %lt; ikData.Length; i++)
{
// The first one should be the head ik.
if (i == 0)
{
ikData[i] = new IKTargetData(ikTargetsToSend[i].position, ikTargetsToSend[i].rotation, headConstraint.weight);
}
else
{
ikData[i] = new IKTargetData(ikTargetsToSend[i].position, ikTargetsToSend[i].rotation, ikConstraints[i - 1].weight);
}
}
UpdateIKTargetsRPC(ikData);
Receiving IK Targets
[Rpc(SendTo.ClientsAndHost)]
private void UpdateIKTargetsRPC(IKTargetData[] ikData)
{
// We should ignore our own updates.
if (IsOwner)
return;
// Loop through our targets and set their positions.
for (int i = 0; i < ikData.Length; i++)
{
ikTargetsToSend[i].position = ikData[i].Position;
ikTargetsToSend[i].rotation = ikData[i].Rotation;
smoothedTargets[i].m_TargetPosition = ikData[i].Position;
smoothedTargets[i].m_TargetRotation = ikData[i].Rotation;
// The first one should be the head ik.
if (i == 0)
{
headConstraint.weight = ikData[i].Weight;
}
else
{
ikConstraints[i - 1].weight = ikData[i].Weight;
}
}
}
KiBo has grown to become my favorite project I have worked on. There were so many different elements that took a lot of trial and error, and I learned more in those three weeks than I did over the course of some entire projects. As demanding as it was, I will work twice as hard for the next game jam I participate in. I hope to carry some of the procedural animation tactics I learned into my future projects, bringing even more life into whatever experience I am creating.
Polariis
Co-Producer and Programmer
May 2025 - June 2025
Skills: Unity Networking, Server-client Architecture,
Inverse Kinematics, C#