Signaller
Tijdens keuzedeel moesten we een game maken voor een museum. Wij kozen voor het Spoorwegmuseum. Signaller is een game waarbij de speler de rol van een trein verkeersleider speelt. De speler moet treinen van punt A naar punt B brengen door seinen op groen te zetten en de baan vrij te maken voor de trein. De speler heeft ook controle over de wissels. Zorg dat treinen optijd rijden en niet tegen elkaar op rijden. De game heeft 2 levels, hierbij is geen rekening gehouden met de moeilijkheidsgraad. Het 1e level bestaat uit 2 stations en het 2e level uit 3. Het doel van de game is om een zo hoog mogelijke score te halen. Deze score komt op een scoreboard met Dagelijks, Wekelijks en lifetime scores. De art style van de game is simple, omdat een trein verkeersleider een heel simpel scherm heeft. Wij wouden dit namaken, maar nog wel wat meer detail geven dan normaal.
Signaller is origineel ontworpen voor een museum en ook een groot scherm met touchscreen. De game is speelbaar op een desktop, maar de ervaring is het best op een groot touchscreen scherm (Input support meerdere vingers). Dingen zoals inactiviteits timers zijn dus aanwezig in de reguliere versie.
Project Info:
Teamleden: David van Rijn & Robin van den Dungen
Project tijd: Jaar 4 Periode 3 (2023)
Engine: Unity
Code Languages: C#
Design Patterns: Singleton & FlyWeight
Tools
Tijdens het maken van Signaller heb ik voornamelijk gewerkt aan tools voor het bouwen van de levels. Deze tools zorgde ervoor dat levels maken een heel stuk sneller ging. Het waren simpele buttons, maar op de achtergrond stelde ze veel in voor het rail network. Je kon RailNodes voor en na andere Railnodes maken en deze verplaatsen. Later heb ik ook nog de “Enter Rail Placemode” toegevoegd deze functionaliteit plaatste een railnode aan het einde waar je klikte met je muis. Door deze tool ging het bouwen van een level sneller dan zonder. Het snel ittereren op level design was ook zeer makkelijk met 1 knop een signal toevoegen. De tool kon ook een mesh maken van het gemaakte level, hierdoor was het level exact hetzelfde als de debug liet zien. De game gebruikt ook vertex painting om de switch state aan te duiden.

private Transform CreateRailNode(bool isParent, Transform node)
{
Transform nodeTransform = node;
if (!isParent) nodeTransform = node.parent;
RailBlock railBlock = nodeTransform.GetComponent();
RailNode lastRailNode = railBlock.railNodes[railBlock.railNodes.Count - 1];
railBlock.railNodes.Add(CreateNewRailNode(nodeTransform, lastRailNode.transform.localPosition, lastRailNode.transform.localRotation));
GameObject railNode = railBlock.railNodes[railBlock.railNodes.Count - 1].gameObject;
Selection.activeGameObject = railNode;
EditorUtility.SetDirty(railNode);
return railNode.transform;
}
private T CreateOtherNode(string name) where T : RailNode
{
GameObject currentNode = Selection.activeGameObject;
RailBlock railBlock = currentNode.transform.parent.GetComponent();
RailNode currentRailNode = currentNode.GetComponent();
int railNodeIndex = railBlock.railNodes.IndexOf(currentRailNode);
railBlock.railNodes.Remove(currentRailNode);
DestroyImmediate(currentRailNode);
T stationNode = currentNode.AddComponent();
railBlock.railNodes.Insert(railNodeIndex, stationNode);
currentNode.name = $"{name} {currentNode.name.Split(' ')[1].Split("-")[0]}-{railBlock.name.Split(' ')[1]}";
Selection.activeGameObject = stationNode.gameObject;
EditorUtility.SetDirty(stationNode);
return stationNode;
}
Met CreateOtherNode kunnen we elke RailNode aanmaken die gebruik maakt van RailNode, hierdoor hoeven we niet voor elke node een script aan te maken.
RailNodes
RailNodes zijn objecten in de scene. Deze kunnen uit de volgende nodes bestaan: RailNode, DirectionNode, DespawnNode, ExitNode, SignalNode, SpawningNode, StationNode en SwitchNode. Alle nodes zitten in een RailBlock vanaf hier wordt ook de mesh beheert van de lijn. Treinen weten op welk RailBlock ze rijden en op welke Node. Alle RailBlocks zitten in de RailRoot het hoofd object van de RailObjecten. Treinen kunnen instructies negeren als de directie niet hetzelfde is als de rij richting (DirectionNode).
De Signalnode en Switchnode waren het meest complex om te implemeteren. Signals kunnen ook op oranje/geel staan. Dit is afhankelijk van de sein die er na komt, maar een switch kan ervoor zorgen dat een eerst volgende sein anders is dan de sein die “rechtdoor” is. Een sein checkt dus het sein voor hem, maar update ook het sein achter hem. SwitchNodes sturen ook updates naar signals, zodat deze altijd correct gedisplayed worden.
private void UpdateSignalsInFront(RailBlock block)
{
int signalIndex = block.railNodes.IndexOf(this);
for (int i = signalIndex + 1; i < block.railNodes.Count; i++)
{
if (block.railNodes[i].GetComponent()) return;
if(block.railNodes[i].TryGetComponent(out ExitNode exitNode))
{
UpdateSignalsInFront(exitNode.transform.parent.GetComponent());
return;
}
if (block.railNodes[i].TryGetComponent(out SignalNode signal))
{
if (signal.nodeDirection == nodeDirection)
{
signal.SwitchUpdate();
return;
}
}
}
}
Gaat door de RailNodes heen en update het eerst volgende sein. Bij exit node switcht hij naar het RailBlock van de exit node.

