Aggressively Open Plan
TEAM PROJECT – MANICHUS
DURATION: 1 Month (01/23)
MADE WITH: Unity, Blender
CORE SKILLS:
Rigging
Animation
C# Programming
GAME OVERVIEW
In this game, the player or players will compete for space in a high rise building. By placing tiles adjacent to one another the player will grow the size of their offices til they reach the opposite side to their starting position. When a player reaches the opposite side of the board, the game ends. Players who have tiles placed with multiple tiles adjacent to each other get bonus points. The player with the most points wins.
MY ROLE
Manichus is a group of friends from my university and childhood. In this particular project, there were professional web programmers, a concept artist, a character artist and a musician eager to take part.
I knew I wanted to fill more of a support role to art and tech, focusing more on rigging and animation as my core responsibilities. To that end I rigged and animated characters modelled by another member of the team and helped direct the style and aesthetic of the game, engaging in look-Dev and reference gathering. I was also involved in the early formation of game design, creating paper prototypes to expand the initial idea and mechanics.
There were also opportunities for me to implement features such as random character spawning and posing as well as creating the background environment by generating a city using modeled building components and lighting the level.
INITIAL PITCH
We began by sketching up ‘paper’ prototypes using mural (an online whiteboard), where we were able to test our ideas and the mechanics of the board game. Alongside designing the game, we also explored how we would theme the game or what the story of the game would be. Once we developed a game with a set of features we liked we used a trello board to draw up tasks and began work on the Unity project.
Prototype
In the first prototypes we started with the idea of playing on a grid with players taking turns claiming a square whilst snaking across the board.
We added more complexity to the design to try to make gameplay more interesting. This in turn created new design problems in the process. One issue we did not get around to addressing was the possibility of an unending stale mate between players.
We also experimented with environmental blockers and power ups to give the game more depth and strategy.
Game Story
We also discussed themes. We didn’t want to present the game strictly as a board game, we also wanted to give the game some sort of story. We settled on the colourful open plan office depicted in commerical info-graphics as our main source of influence with an added 1960/70s flare. From the games competitive bent and space seizing mechanics we came up with the title of the game – Aggressively Open Plan.
Once we had the theme of the game nailed down I worked together with another team member to establish the art bar we wanted to try to hit. He gathered together some stylised assets (downloaded from sketchfab, altered and modelled) roughly appropriate for the time period and using these assets I assembled a scene inside of blender. I made up an early concept of what a tile could look like and tried to establish an appropriate tone – I wanted something colourful with an element of whimsy.
SPAWNING CHARACTERS
Each tile prefab has a range of dressing objects that are activated when on tile spawn. The script that managed this was called the Decoration Swapper and was created by another team mate.
I placed empty objects in each tile indicating character positions and rotations with tags for whether the character is sitting or standing.
Using the existing variable ‘r’ I was able to determine which spawn point the characters should spawn at and randomised the number of them to spawn from none to a maximum of 3.
The character variant is instanced with a random index
public class DecorationSwapper : MonoBehaviour {
[SerializeField] GameObject[] characterPrefabs;
[SerializeField] GameObject[] characterSpawnBox;
[SerializeField] int nChar;
[SerializeField] int r;
[SerializeField]
GameObject[] decorationVariants;
GameObject selectedSpawnBox;
private void OnEnable() {
r = Random.Range(0, decorationVariants.Length);
Debug.Log("R" + r);
ChooseVariant();
CharacterCount();
}
private void ChooseVariant() {
if (decorationVariants.Length == 0) return;
for (int i = 0; i < decorationVariants.Length; i++) {
decorationVariants[i].gameObject.SetActive(i == r);
}
// After decoration is loaded we recolour it
Tile t = gameObject.GetComponent<Tile>();
t.SetPlayer(t.Player);
}
//Code Written by me below this point
void CharacterCount()
{
//Debug.Log("How many characters!?");
nChar = Random.Range(0, 4);
//Debug.Log(nChar + " of them!");
for (int i = 0; i < nChar; i++)
{
SpawnRandomCharacter(i);
}
}
void SpawnRandomCharacter(int addChar)
{
Debug.Log("Character number " + addChar + " Added to Tile");
//Grabbing Random Character Prefab
int characterIndex = Random.Range(0, characterPrefabs.Length);
//Setting the Spawn index to block of spawns on the correct tile variant
int spawnIndex = (r * 3);
//Counting up the spawnboxes to the number of characters to spawn (nChar)
if (characterSpawnBox[spawnIndex + addChar] != null)
{
selectedSpawnBox = characterSpawnBox[spawnIndex + addChar];
} else
{
return;
}
Vector3 spawnPos = selectedSpawnBox.transform.position;
GameObject selectedCharacter = Instantiate(characterPrefabs[characterIndex], spawnPos, selectedSpawnBox.transform.rotation);
selectedCharacter.tag = selectedSpawnBox.tag;
selectedCharacter.transform.parent = selectedSpawnBox.transform.parent;
}
public class CharacterAnimator : MonoBehaviour
{
Animator animator;
[SerializeField] GameObject[] prop;
int propIndex;
IEnumerator Start()
{
animator = GetComponent<Animator>();
propIndex = Random.Range(0, 2);
SelectPose();
while (true)
{
yield return new WaitForSeconds(Random.Range(6, 12));
animator.SetInteger("FidgetIndex", Random.Range(0, 2));
animator.SetTrigger("Fidget");
}
}
void SelectPose()
{
if (gameObject.tag == "animsitting")
{
animator.SetBool("isSitting", true);
return;
}
animator.SetInteger("PoseIndex", propIndex);
if (propIndex != 0)
{
prop[propIndex].SetActive(true);
Debug.Log("Prop Index" + propIndex);
}
}
}
I also set up a script to manage the characters fidget animations and prop – A coffee mug that gets activated for the character to sip out of.
Having given the spawning objects tags for either sitting or standing, I used those tags to select the animating pose for the characters.
GENERATED BACKGROUND CITY
I modelled a building for the game to sit on top of and wanted to set it in amongst a city. Creating a procedural system for scattering buildings was a quick way of bedding the game in the world.
This script is attached to a box prefab with empty objects parented to it. These objects act as slots and are referenced in the array ‘buildingSlot’.
Buildings spawned in the slots also have their own slots for towers or roof huts which are also randomised.
The random aspect of the generation also means that the skyline is different in every game that is played.
public class ProceduralBuildings : MonoBehaviour
{
[SerializeField] GameObject[] buildingSlot;
[SerializeField] GameObject[] basePrefabs;
[SerializeField] GameObject[] towerPrefabs;
[SerializeField] GameObject[] roofHuts;
GameObject building;
void Start()
{
BuildingCount();
}
void BuildingCount()
{
for (int i = 0; i < buildingSlot.Length; i++)
{
SpawnBuilding(i);
}
}
void SpawnBuilding(int loopIndex)
{
int buildingSeed = Random.Range(0, basePrefabs.Length);
building = Instantiate(basePrefabs[buildingSeed], buildingSlot[loopIndex].transform.position, Quaternion.identity);
GameObject towerPos = building.GetComponent<Building>().towerSlot;
building.transform.SetParent(transform, true);
int makeTower = Random.Range(0, 2);
if(makeTower != 0 && towerPos != null)
{
int towerIndex = Random.Range(0, towerPrefabs.Length);
GameObject tower = Instantiate(towerPrefabs[towerIndex], towerPos.transform.position, Quaternion.identity);
tower.transform.parent = building.transform;
building = tower;
}
GameObject[] roofHutPos = building.GetComponent<Building>().roofHutSlot;
int roofHutIndex = Random.Range(0, roofHuts.Length);
Debug.Log("roof hut index " + roofHutIndex);
int roofHutPosIndex = Random.Range(0, roofHuts.Length);
Debug.Log("roof hut position " + roofHutPosIndex);
if(roofHutPos[roofHutPosIndex] != null)
{
GameObject hut = Instantiate(roofHuts[roofHutIndex], roofHutPos[roofHutPosIndex].transform.position, roofHutPos[roofHutPosIndex].transform.rotation);
hut.transform.parent = building.transform;
}
else
{
return;
}
}
}
RIGGING AND ANIMATION
Rigging
I thought adding some animated office workers to the game would help bring the spaces to life. In order to do that I needed rigged characters.
With character models provided by the character artist on our team, I learned how to rig in blender with the intention of creating a set of idle animations that could loop and occur randomly as time went on.
I created two skeletons called the Control_rig and Game_rig.
The Control_rig is the skeleton with the all the control bones and shapes I used to pose the skeleton to create the animations.
The animations are then baked to the Game_Rig and the constraints removed so it can be exported without the extra bones and shapes that were used to make the controls for the rig.
Motion Capture Tests
The results after cleanup weren’t bad but unfortunately the time to cleanup and figure out looping the animations was going to take too long, so I decided to stick with hand animating.
But it was incredibly fun and I would be keen to go back to it and try again.
Animating
Consistent with the style we were creating I wanted the animations to be grounded in the office setting with people standing around drinking coffee, tapping away at type writers and firing finger guns as every office worker in the 70s used to do.
CONCLUSION
Creating Aggressively Open Plan was a really fun time and everyone on our team was impressed with what we were able to achieve over the course of a month.
Working with others also opened up the chance to work in other areas other than coding or art. Animation does a lot to bring a game to life and I felt confident that I could divert my time towards doing something other than coding or art and explore rigging and animation and I was really happy with the result.
Learning to rig and animate characters was a big learning experience for
me thinking about a very different pipeline and workflow. It was also
helpful to see how other people wrote code and being able to open up a
script and figure out what was happening and then implementing my own
features was a bit of a confidence boost.
When we finished the project and all of the work I did was present in the game, I felt like my contribution had been really meaningful. Working alone has its own benefits but when working with people who you feel comfortable talking to and pushing back against I feel so much more invested in the project and looking back at it even though it is flawed I feel incredibly proud of it.
TEAM CREDITS
PROGRAMMING
Max Waring
Andrew David Smith
Robert Allison
Nimorum
MUSIC
Rodaidh Mackay
ENVIRONMENT ART
Kevin Ua-Wanapaska
CHARACTER ART
Sam Bang
TECHNICAL ART
Dominic Dalseme
This project uses the 60’s Office Props assets from SeanNicolas