Ludus ante Portas

About the game

Ludus ante Portas was a game that could be played on the famous landmark of the city of Trier, the ‚Porta Nigra‘. The game was developed as part of the ‚Design und Kulturtage Trier‘. A maximum of 10 players were able to play the game at the same time and the players were divided equally into two teams.

The players in the team of the Romans tried to protect the Porta Nigra by holding up their shields and repairing broken pillars. The players in the Germanic team tried to destroy the Porta Nigra with their catapults. If they managed to destroy a certain number of pillars, they won. The team of the Romans won through surviving the night of the attack.

My tasks

This project was the first time I got to work with the Unity3D engine. Thanks to many online tutorials I was able to learn the basics quickly. At the beginning of the project I had the tasks to implement effects and animations. Some of the effects I implemented were for example the rustling of bushes or the flickering of a candle flame.

I implemented the rustling effect of the bushes by using a particle system with leaves as particles. The particle system was then activated by hitting the bush with a projectile. Fortunately the engine provides a function that is automatically triggered by a collision event with a trigger collider. Within this function I tested whether a projectile has hit the collider or not. If a projectile hit the collider, the particle system was activated. After a certain amount of time, using a coroutine, the particle system was then turned back off again.

Show code
void OnTriggerEnter(Collider other){
   if (!hit && other.tag != "Projectile"){
      hit = true;
      ...
      else if (other.tag == "bush"){
         other.GetComponent().hasBeenHit();
         aSource.volume = 0.30f;
         aSource.PlayOneShot(hitPillar);
      }
      ...
}



The flickering of the candle flames was implemented in the same way. In addition, the flame was scaled up and down permanently to create a burning animation.

Due to the indisposition of many group members, I took on a variety of other tasks. These included organization of the group, bug fixing, implementation of the player controls and creating the animation tree for the main characters.

The organization of the 38 people group turned out to be a huge challenge. First I decided to use the organsization tool called ‚Trello‚. I hoped that this tool would help to provide a structured working flow throughout the project. Just using Trello as tool for organization turned out to be insufficient. As a solution, I arranged weekly meetings and prototype tests. Having some kind of weekly schedule helped the team a lot the accomplish milestones faster.

In retrospect

Looking back I would do the following things differently:

  • Team size
    With 38 people, the group was too big for a project of this size. Due to the high number of people many people had no tasks or worked on the same parts. A group size of 3 programmers and 4-5 designers would be far more adequate to realise this project.
  • Team Leader
    My assignment as team leader was very late in the project. In order to realise the project smoothly, a team leader should be deployed faster.
  • Animations
    As already mentioned, I implemented an animation via script. Unity provides an animation system which is suited better for this task.


    Diese Diashow benötigt JavaScript.

Werbeanzeigen

Stürmen und Türmen

About the game

‚Stürmen und Türmen‘ was the first commissioned work I was allowed to realise. Under the patronage of Prof. Dr. Linda Breitlauch our team developed a concept for the famous fortress ‚Ehrenbreitstein‘ in Koblenz. After some modifications the concept got approved by our client the ‚Generaldirektion Kulturelles Erbe Rheinland-Pfalz‘. After some minor tweaks on the concept our team was charged to implement the game. In a addition the client decided that the game was to be exhibited permanently on the fortress.

The core idea of the game was a mini-game collection that was suitable for young and old as well as easy to use. The main goal of the game was to collect points. The player with the highest score throughout all mini-games wins at the end.

The mini-games

  • Shooting at the walls
    The aim of this game was to destroy the wall of the fortress. To achieve this, the player fired a cannon at the wall. The closer he got to the marked area, the more points he got. After a shot, the player had to reload his cannon by repeatedly pressing the ‚B‘-button on his controller.
  • Storming the wall
    After the wall of the fortress was destroyed, the next target was storming it. To do this, the players had to run across the battlefield and dodge the puddles of mud. Bonus points could be earned by collecting coins. Another difficulty for the player was the balancing of a ladder, each soldier was carrying. With too much imbalance of the ladder, the player was slowed down.
  • Climbing the wall
    After the ladders were placed, the next task of the players was to climp up the wall. While climbing, they had to dodge falling objects. Occasinonally a coin fell down that could be collected in order to earn bonus points. In addition, a star could be collected. The star caused a short boos of speed.
  • Wind up the flag
    Finally, the players had to hoist a flag on the wall. To do this, they had to alternately press the back shoulder buttons. The player who first hoisted his flag got the most points.

My tasks

At the beginning of the work my main task was the realization of the final scene. The winner of the whole game was shown in this scene as well as the credits. I implemented the scene really fast because I only had to sawp textures depending on the players score.

Show code
private void Awake(){ 
   int[] tmp_Array = HighScoreManager.instance.GetRanking(); 
   for (int counter = 0; counter < 4; counter++){ 
      m_Playerbags[counter].material = m_PlayerColors[tmp_Array[counter] - 1]; 
   } 
   m_HorseRenderer.material = m_HorseColors[tmp_Array[0] - 1]; 
} 



In the course of the project I took over the role of the team leader. Under my supervision weekly meetings were arranged and solutions for existing problems were found quickly. Another part of leading the team was the communication with our client. I arranged meetings on the fortress and bought hardware which was needed for the implementation of the game.

Since I have mainly taken over organizational work in this project, I got a lot of this experience in this area. Because of this experience, I see communication within a team as one of the most important and at the same time most difficult tasks in realising a game.

The project was successfully handed over to the client in January 2018. Since then, the game has been exhibited at the fortress and can be played live on-site.

I’m very proud of the work my team has done!

Diese Diashow benötigt JavaScript.

 

Wave’O’Dome

About the game

In January 2017 the annual Global Game Jam took place. The theme of that year was waves. My first idea of ​​a first-person-arena-brawler was changed by our 5-man group into an arena game with an isometric view. The main gameplay for each of the 4 players consisted of gathering power-ups, avoiding dangerous waves which were emitted from the center of the battlefield and defeating the enemy players with the help of the collected power-ups.

Power ups and waves

A wave and each power-up had a different effect depending on its color.

  • Red
    Red waves killed a player instantly. Red power-ups generated a wave which killed other players on hit.
  • Yellow
    Yellow waves stunned a player. Yellow power-ups generated a wave which stunned other players.
  • Blue
    Blue waves pushed back a player. Blue power-ups generated a wave which pushed back other players.

My tasks

I took on the tasks of implementing the control and the main menu.

Implementing the player control was a challenging task since the game should be controlled with 4 different controllers. I solved this problem by naming the various input axes in a way that they contained a player number. For example, player 1 had a horizontal movement input axis which was called „Player1_H“.

Inputmanager

Wave’O’Dome – Inputmanager

To receive a player’s input, a player number was declared in each player script instance.

Show code
public int m_IPlayerNumber = -1;



This integer served as an unique ID and identified the script to other systems. The input of the player could be queried by putting together a string of the player ID and the name of an axis.

Show code
if (Input.GetButtonDown("Player" + m_IPlayerNumber + "_Start") && m_CanMove)
   ...



I decided to use a script in order to provide proper control over the main menu via a controller. A pointer was used to trigger animations and show or hide various pages in the main menu.

I would have liked to develop the project further, because I realized my preference for working with user interfaces in this project and thought that the idea was fit for a public release.

Diese Diashow benötigt JavaScript.

Connect me!

About the game

At the beginning of 2018, I participated in the Global Game Jam once again. The theme of this years jam was transmission. Our 6-person team decided to try implementing a multiplayer game. The basic idea of our game was inspired by phone companies of the sixties. The main mechanic was that a player would be called by a random character. After hearing his voiceline the player had to draw his face which would then be send to the other players. The other players had to choose a character which was best fit to the drawn face. The whole game should take place in a local network and should be implemented in the Unity3D engine.

My tasks

During the first one and a half days of the project, I tried to implement network code with a teammate. Unfortunately we did not succeed in creating a working prototype. Since we only had little time left, we decided to change the gameplay into a singleplayer version. In this singleplayer the player had to match a random voiceline to a fitting character.

After changing the concept to a single player I had to implement the game logic.
First I wrote a class that represented a character. Within this ‚Caller‘ class a reference for a sound clip and a string was saved. The string served as a unique identification number for a character.

Show code
public class Caller {
   AudioClip m_SoundClip;
   string m_ImageID;
public Caller(AudioClip p_Clip, string p_ImageID) {
   m_SoundClip = p_Clip;
   m_ImageID = p_ImageID;
}

public AudioClip GetSound() {
   return m_SoundClip;
}

public string GetImageID() {
   return m_ImageID;
}

public override string ToString() {
   return "[Clip: " + m_SoundClip.name + " ImageID: " + m_ImageID +  "]";
}
}



The next step was the organisation of the characters as couples. I solved this issue by a creating a class called ‚Callerpair‘. Within this class 4 caller references were stored. The references ‚m_activeCaller‘ and ‚m_passiveCaller‘ were randomly set to one of the two characters on creation.

Show code
public class CallerPair {
   Caller m_Caller1;
   Caller m_Caller2;
   Caller m_ActiveCaller;
   Caller m_PassiveCaller;

public CallerPair(Caller p_Caller1, Caller p_Caller2) {
   m_Caller1 = p_Caller1;
   m_Caller2 = p_Caller2;
   ShuffleActivePassive();
}

public void ShuffleActivePassive() {
   if (Random.Range(0, 100) > 50) {
      m_ActiveCaller = m_Caller1;
      m_PassiveCaller = m_Caller2;
   } else {
      m_ActiveCaller = m_Caller2;
      m_PassiveCaller = m_Caller1;
   }
}

public Caller GetCaller1() {
   return m_Caller1;
}

public Caller GetCaller2() {
   return m_Caller2;
}

public Caller GetActiveCaller() {
   return m_ActiveCaller;
}

public Caller GetPassiveCaller() {
   return m_PassiveCaller;
}
}



After the game was started all resources were loaded and converted into callerpairs.

Show code
private void PrepareData() {
   m_CallerAudio = Resources.LoadAll("Sounds/");
   m_CallerSprites = Resources.LoadAll("Images/Characters/");
for (int count = 0; count < m_CallerAudio.Length; count += 2) {
   m_CallerPairs.Add(
      new CallerPair(
         new Caller(m_CallerAudio[count], m_CallerSprites[ 0 + count * 12].name.Substring(0,2)),
         new Caller(m_CallerAudio[count + 1], m_CallerSprites[ 12 + count * 12].name.Substring(0, 2))));
}
}



After that, prefabs were loaded and positioned. Each prefab was a button for a character. These buttons could be pressed to score points. If a wrong button was pressed the player lost time for his round.

Show code
private GameObject FindPrefabFromPrefix(string p_Prefix) {
   foreach(GameObject obj in m_ButtonPrefabs) {
      if (Equals(obj.name.Substring(0, 2), p_Prefix))
         return obj;
      }
   Debug.Log("No prefab found");
   return null;
}



Working out the logic of the game in one night was very challenging but fun. In retrospect, I would implement the callers and callerpairs as scriptable objects. That should make their organization easier.

Diese Diashow benötigt JavaScript.

Ork Commander

About the game

‚Ork Commander‘ was a recreational project where the player had to build a base and equip his units with the appropriate equipment. Whether the army of the player was effective against the attackers or not depended on the provided equipment. The goal of the game was to destroy the enemy base. The project was realised in the Unity3D engine.

My tasks

My tasks were the implementation of a rasterized construction system associated with an user interface as well as a direction-controlled road system. We decided that the construction area of the player should be predetermined to a limited area. That’s why I decided to initialize every point in the grid of the construction area as a gameobject. In addition to this object a sprite was created which was then used to visualize collisions with other objects.

Show code
for (int x = 0; x < m_FieldLength; x++)
   for(int z = 0; z < m_FieldWidth; z++){
      m_TileGrid[x, z] = Instantiate(m_1x1Tile, new Vector3(0.5f + x, 0.0f, 0.5f + z), m_TilesAndSpriteRotation) as GameObject;
      m_TileGrid[x, z].layer = 14;
      m_TileGrid[x, z].tag = "ConstructionTile";
      m_TileGrid[x, z].name = "Tile ID: " + x + ";" + z;
      m_TileGrid[x, z].transform.SetParent(tmp_THolder.transform);
      ...
      m_SpriteGrid[x, z] = Instantiate(m_SpriteObject, new Vector3(0.5f + x, m_Höhe, 0.5f + z), m_TilesAndSpriteRotation) as GameObject;
      m_SpriteGrid[x, z].transform.SetParent(tmp_SHolder.transform);
      m_SpriteGrid[x, z].name = "CollisionSprite ID: " + x + ";" + z;
      ...
   }



In case of a collision between a point in the grid and an object in the world, the associated sprite was marked as non-buildable and a corresponding entry was placed in the logic of the building area.

Show code
if(Physics.CheckSphere(spherePos, 0.40f)){
   m_PlacementLogic[x, z] = (int)m_TileTypes.building;
   m_SpriteGrid[x, z].GetComponent().sprite = m_CollisionSprite;
}
m_SpriteGrid[x, z].SetActive(false);



Building a structure was controlled via raycasts. The raycast was used to determine at which point of the grid the placement was attempted.

 if Physics.Raycast(m_MouseRay, out m_MouseObject, Mathf.Infinity, m_MouseDetectingLayers))



In an area around this point was then checked whether the placement of the building was possible or not. The size of the area depended on the size of the building.

The directional road system was a much heavier task compared to the construction system. In addition to the requirement of the directional control it was also necessary to move the units correctly on the roads, that the units were routed into buildings and that streets were adapted to neighboring streets.

I implemented the direction control in a way that a road was marked with a special type in each direction. This type was either a wall, an entrance or an exit. The adjacent streets were then adapted to the newly placed street depending on its types.

Show code
switch (p_Direction){
   case 0:
      if (script.GetNorthWay() == m_DoorTypes.wall)
         script.SetNorthWay(m_DoorTypes.exit);
      if (script.GetSouthWay() == m_DoorTypes.wall)
         script.SetSouthWay(m_DoorTypes.entrance);
   break;
   case 1:
      if (script.GetEastWay() == m_DoorTypes.wall)
         script.SetEastWay(m_DoorTypes.exit);
      if(script.GetWestWay() == m_DoorTypes.wall)
         script.SetWestWay(m_DoorTypes.entrance);
   break;
   ...
   default:
      Debug.Log("INVALID DIRECTION");
      break;
}


The correct routing of the units was controlled by the road system. Each unit polled the system for information about the next road. The unit moved forwards after the system returned a positive message.

Show code
public bool IsThereAWayForward(Vector3 p_Position){
   int x = (int)p_Position.x, z = (int)p_Position.z;
   Doorways_Script Script = m_Doorways[x, z];
   if (z + 1 < m_MainConstructor.m_FieldWidth && Script.GetNorthWay() == m_DoorTypes.exit &&
   m_Doorways[x, z + 1].GetOccupationStatus() == false &&
   m_Doorways[x, z + 1].GetSouthWay() == m_DoorTypes.entrance)
      return true;
   ...
   return false;
}



Proper forwarding to buildings was controlled by triggers. If a unit moves into the trigger of a building the building checks for available capacities. If capacities were available the unit was directed into the building.

Show code
void OnTriggerEnter(Collider other){
   if (other.tag == "Unit" && !other.GetComponent().m_AlreadyEquipped){
      if (selectedBody!=null || selectedLeftHand!=null ||
      selectedRightHand!=null)
         StartCoroutine(startEquip(other.gameObject));
   }
}



Working on this project was one of the most difficult and nerve-wracking experiences I had in my studies. Nevertheless, the result motivated me a lot. In retrospect, I would research for more information about systems and not develop them „from the gut“.

Diese Diashow benötigt JavaScript.

Corridor of Horror

About the game

‚Corridor of Horror‘ was another project I got to work on. In this game the player takes on the role of a young woman who’s inside of a hypnosis. Storywise she tries to cope with traumatic experiences from her past. In order to do so she has to solve puzzles and avoid the monsters in the level. We decided to use motion capturing for our animations since we had access to a motion capturing equipment through college. The project was realised in the Unity3D engine.

My tasks

In addition to the level design, I took over the implementation of a puzzle in this project. It was my first encounter with level design. The level should take place in a hospital and therefore I created a layout of a hospital after researching in the internet.

Layout

The riddle I implemented consisted of a simple puzzle game. It was controlled via raycasts and a crosshair. The crosshair was displayed after the activation of the puzzle. In order to solve this riddle the player had to move each piece of the puzzle to its correct position.

Light puzzle

Corridor of Horror – Light puzzle

Once the player solved the puzzle hidden documents were made visible throughout the level. These documents contained individual characters that could be joined together to form a codeword. After collecting all fragments of the codeword the player was able to open the final door and escape from the level.

Corridor of Horror - Pin pad

Corridor of Horror – Pin pad

Working on level design made me realise that I don’t like working at this area. However capturing and using motion capturing animations was very interesting. Looking back I would assign the task of level design to another person in the group.

Diese Diashow benötigt JavaScript.

Chaos God

About the game

The game ‚Chaos God‘ was born during my first Global Game Jam. The goal of the game was to bring as much harm as possible to the world. The player got to the god of chaos. This god could perform various rituals in order to spread chaos in the world. Our team choosed a 2.5D look which was strongly inspired by the survival game ‚Don’t Starve‘. The project was realised in the Unity3D engine.

My tasks

Beside game design my tasks included to implement the logical representation and execution of the available rituals as well as their presentation in an user interface. I did not have any experiences in the area of user interfaces and therefore selected these tasks on purpose.

The rituals could be performed by combining three different items out of the players inventory. I decided to use a XML document to represent the rituals. Using a XML document should make it easier to add rituals to the game while providing a good overview over their structure.

Ritual XML Document
Chaos God – Ritual XML Document

After the game was started all rituals were loaded from the XML document, saved and processed in order for the player to use them while playing.

Show code
private void LoadRituals(){
   XmlDocument xmlDoc = new XmlDocument();
   xmlDoc.LoadXml(GameAsset.text);
   XmlNodeList ritualsList = xmlDoc.GetElementsByTagName("Ritual");    foreach (XmlNode ritualInfo in ritualsList){
      XmlNodeList ritualcontent = ritualInfo.ChildNodes;
      obj = new Dictionary();
      foreach (XmlNode ritualItems in ritualcontent){
         if (ritualItems.Name == "name"){
            obj.Add("name", ritualItems.InnerText);
         }
         if (ritualItems.Name == "object"){
            switch (ritualItems.Attributes["name"].Value){
               case "Item1": obj.Add("Item1", ritualItems.InnerText); 
               break;
               ...
            }
         }
      }
      rituals.Add(obj);
   }
}



Another task of mine was to make sure that objects reacted to casted rituals. I created scripts which could be applied easily to an object. Within these scripts the needed behaviour had to be implemented via code. Our team decided to use sprite swapping in most cases.

The next task of mine was to create a user interface where the player could easily access and cast the different rituals. I decided to display the individual objects in a grid, as this was a typical representation of inventories in rollplaying games.

Inventory

Chaos God – Inventory

When selecting an item, its image and description were displayed in an additional information window. Using an item made it appear in the ritual circle on front of the player character.

Casting a Ritual

Chaos God – Casting a Ritual

In retrospect

In retrospect, I would implement my duties differently. The usage of an XML document and the associated effort to read this document could easily be circumvented by implementing it as a class or struct. My argument was that rituals could be added and be displayed in a simple way. In such a small game an approach like that was not necessary and a waste of time.

In addition, I would implement the user interface in different way. I would no longer manually create and place each item in the inventory but create them at runtime and then position them using vertical and horizontal layout groups. And finally, I would no longer work with string comparisons. These are costly and can have a negative impact on the performance of the game when used heavily.

Ultimately, I can say that this weekend was a lot of fun. Everyone shared my enthusiasm for games and their development. We helped each other and talked shop together. I hope to later work in an company with the same kind of atmosphere.

Diese Diashow benötigt JavaScript.