Reel or be Gilled

Tijdens Project Vrij II hebben we gewerkt aan een local multiplayer game genaamd Reel or be Gilled. De game is een fast paced collection game waarin spelers het tegen elkaar opnemen om zo snel mogelijk zoveel mogelijk vissen te vangen en deze in te leveren om punten te scoren.
In tegenstelling tot traditionele, rustige visgames zijn de vissen in Reel or be Gilled allesbehalve onschuldig. Ze bijten niet alleen aan de haak, maar kunnen ook terugbijten, spelers stunnen of zelfs exploderen. Deze chaotische elementen zorgen voor een competitieve en humoristische gameplay-ervaring.
Spelers kunnen elkaar actief dwarszitten door gebruik te maken van verschillende powerup-vissen, die strategisch ingezet kunnen worden om tegenstanders te vertragen of op te blazen. Het doel is niet alleen om zelf efficiënt te vissen, maar ook om ervoor te zorgen dat je vrienden het minstens zo moeilijk hebben als de vissen die je uit het water trekt.
Project Info:
Teamleden: 10 teamleden zie de itch.io projectpagina
Project tijd: HKU Jaar 2 Periode 4 (14-04-2025 t/m 12-06-2025)
Engine: Unity
Code Languages: C#
Design Patterns: Observer, Singleton & FlyWeight




Hier onder deel ik wat relevanten systemen die ik heb uitgekozen (dit is maar een fractie van het werk wat ik gedaan heb)
Character Selection

Ik heb gewerkt aan het volledige local multiplayer systeem, waaronder het character select systeem. Speler kunnen op elk moment joinen door een specifieke input te geven. Zodra een speler karakter selecteert, wordt deze vergrendeld en is hij niet meer beschikbaar voor andere spelers. Wanneer alle deelnemende spelers een karakter hebben gekozen, start het spel automatisch een countdown van drie seconden, waarna de game begint. Als een speler zijn selectie ongedaan maakt of de lobby verlaat, wordt de countdown direct gestopt.
public CharacterSelection GetNextSelectionPoint(CharacterSelection currentCharacter, int direction, out float yOffset)
{
List<CharacterSelection> selections = characterPoints.Where(i => !i.IsSelected).ToList();
int currentIndex = 0;
if (selections.Contains(currentCharacter)) currentIndex = selections.IndexOf(currentCharacter);
else currentIndex = characterPoints.IndexOf(currentCharacter);
currentIndex += direction;
if (currentIndex < 0) currentIndex = selections.Count - 1;
else if (currentIndex > selections.Count - 1) currentIndex = 0;
yOffset = GetSelectionOffset(selections[currentIndex], null);
return selections[currentIndex];
}
public float GetSelectionOffset(CharacterSelection characterSelection, CharacterSelectPlayer player, float offsetStart = 3.7f)
{
float yOffset = offsetStart;
List<CharacterSelectPlayer> selections = playerSelectors.Where(p => p != null && p.GetCurrentSelection() == characterSelection).ToList();
int offsetIndex = 0;
if (player != null)
{
offsetIndex = selections.IndexOf(player);
if (offsetIndex < 0) offsetIndex = 0;
}
else offsetIndex = selections.Count;
yOffset += offsetIndex * .6f;
return yOffset;
}De functies om de hoogte van de player cursor te bepalen en de vollegende. Met deze manier kunnen we zonder code edits nieuwe karakters toevoegen.
Vissen

Spelers kunnen hun hengel uitwerpen om vissen te vangen. Omdat het een fast paced game is, mocht de vis mechanic niet te lang duren. Daarom is gekozen voor een compacte fishing minigame waarin de speler de stick moet bewegen naar een doelpunt binnen een radius en dit bevestigt met een knop.
Wanneer de speler drie keer mist, faalt de fishing minigame en ontsnapt de vis. Het systeem ondersteunt technisch gezien meerdere inputtoetsen voor deze minigame, zodat het mechanisme flexibel is en eenvoudig aanpasbaar. Deze optie is uiteindelijk niet gebruikt in de game, omdat dit de complexiteit zou verhogen en mogelijk verwarrend kan zijn voor beginnende spelers.
public void UsePressed()
{
//Blocks first input (Preventing an unfair strike).
if (initTime == Time.time || initTime == 0 || playerInput == Vector2.zero) return;
FishingPoint fishPoint = CheckLocations();
if (fishPoint == null)
{
strikeCount++;
if (strikeCount >= maxStrikes) CancelFishing();
return;
}
fishingPoints.Remove(fishPoint);
Destroy(fishPoint.gameObject);
if (fishingPoints.Count == 0)
{
OnCompleteCallback?.Invoke(owner, transform.position - Vector3.one * 2);
OnFishingComplete?.Invoke();
CancelFishing();
}
}
private FishingPoint CheckLocations()
{
for (int i = 0; i < fishingPoints.Count; i++)
{
float pointAngle = fishingPoints[i].FishingAngle;
float wrappedPlayerAngle = (playerAngle + 360f) % 360f;
float wrappedPointAngle = (pointAngle + 360f) % 360f;
float angleDifference = Mathf.Abs(wrappedPlayerAngle - wrappedPointAngle);
angleDifference = Mathf.Min(angleDifference, 360f - angleDifference);
if (angleDifference <= collectionRadiusDistance)
{
fishingPoints[i].ShowHighlight(true);
return fishingPoints[i];
}
}
return null;
}Fishing Point location checking