Skip to content

Expert review sprint 3

Criteria die in dit bestand aangetoont worden

K1: OOP en database
K3: Serverconnectie
K5: Documentatie (dit hele bestand)

K1

Je hebt object georiënteerde software gemaakt die samenwerkt met een database.

Basics van Object-Oriented Programming

De basis van object georienteerd programmeren ligt in het plaatsen van code in objecten, zodat deze zo compact mogelijk geschreven wordt. De code wordt, door gebruik van OOP, overzichterlijker, omdat code georganiseerd staat in verschillende objecten. OOP wordt gekenmerkt door 4 basisprincipes: abstraction, encapsulation, inheritance en polymorphism.

Abstraction

Abstraction houdt in dat herhaalde code geinitialiseerd wordt in een functie. Hierdoor blijft de code beter leesbaar, compacter en efficient, terwijl het ook makkelijker is om specifieke regels code op te roepen als gebruiker.

//checks whether the mouse is inside the collision box (image) of a card
public bool CardMouseCollision(Vector2 Mouse)
{
    return card.BoundingBox.Contains(Mouse);
}

Hier wordt abstraction gebruikt om de cardMouseCollision heel makkelijk opnieuw aan te roepen op een ander deel van de code. Deze staat in de card class, waardoor deze ook voor elke card apart kan worden aangeroepen.

Encapsulation

Encapsulation houdt in dat variablen en functies, die specifiek aan een object zijn, gebundeld worden in een class. Hierdoor wordt initialisatie van een object, die meerdere keren wordt gecreeerd, een stuk simpeler.

public abstract class Card : GameObjectList
{
    //rest van code
    protected void InitializeCard(string CardTypeName)
    {
        card = new SpriteGameObject($"Images/Cards/{CardTypeName}", 0,
        $"{CardTypeName}");
    }
}

Deze functie bevindt zich in de Card class. Als deze functie in de losstaande card classes wordt aangeroepen (zoals een buffkaart), dan hoeft er in de constructor alleen maar een parameter van het kaarttype meegegeven te worden.

Inheritance

Inheritance houdt in dat een class functies overerft van een andere class. Ook kan de nieuwe class de functies veranderen, en nieuwe functies krijgen die de oude class niet heeft.

public class Card : GameObjectList{
    public Card(CardType type, int x, int y) : base()
}

De Card class inherit van de gameObjectList zodat de card class belangrijke functies overerft, zoals de HandleInput.

public override void HandleInput(InputHelper inputHelper)
{
    base.HandleInput(inputHelper);
    for (int i = children.Count - 1; i >= 0; i--)
    {
        children[i].HandleInput(inputHelper);
    }
}

Hierdoor is het gemakkelijk om input te ontvangen op de kaarten. Door inheritance is het mij gelukt om de kaarten te kunnen bewegen, door de HandleInput functie.

public abstract class Card : GameObjectList
{
    //rest van code
    public Card(int x, int y, GameState gameState) : base()
    {
        //creates a card with an x & y offset, position and image
        StartOffsetX = x;
        StartOffsetY = y + 50;
        Gamestate = gameState;
        inputHelper = new InputHelper();
    }
}
public NerfCard(int x, int y, GameState gameState) : base(x, y, gameState)
    {
        InitializeCard("Nerf");
        card.Position = new Vector2(x, y);
        Gamestate.Add(card);
    }

In de constructor van de (abstracte) class Card wordt er een standaard StartOffset x & y meegegeven op basis van de startpositie van de kaart. Als er een instance van bijvoorbeeld de NerfCard wordt gemaakt, hoeft deze offset niet nog een keer meegegeven te worden aan de kaart, wat de code makkelijk houdt, en vaker herbruikbaar voor extra kaarttypes die eventueel nog worden toegevoegd.

Polymorphism

Polymorphism de laatste van de 4 pillaren van OOP. Het is de techniek, waarbij een overgeerfde class, een functie kan aanpassen en op zijn eigen manier kan gebruiken.

public abstract class Card : GameObjectList
{
    //rest van code
    public virtual void CardUsed(int TileX, int TileY, string TileID,  
    Piece OccupyingPiece)
    {
        Console.WriteLine("Card used");
        Gamestate.cards.Remove(card);
    }
}
public class ObstacleCard : Card
{
    //rest van code
    public override void CardUsed(int TileX, int TileY, string TileID, 
    Piece OccupyingPiece)
    {
        Console.WriteLine("ObstacleCard used");
        CreateObstacleTiles(TileX, TileY, TileID);
        Gamestate.Remove(card);
        SendCardUsedData();
    }
}

Class diagrams

Ik heb 2 class diagrams gemaakt op basis van mijn toevoeging aan het project. Hierin is alle overerving weergegeven, en de attributes/methods die ik heb toegevoegd aan de classes. De ‘’ duidt aan dat alle attributes/methods die hierin stonden niet zijn aangepast, en de x duidt aan dat een class geen attributes/methods bevat.

Class diagram cards

Class diagram data packet

Database

Voor een databaseconnectie met de game hebben wij allereerst een EER gemaakt. Dit EER focust op het belangrijkste van wat wij met een database wilde doen; een ELO-systeem.

EER van ELO-systeem

Dit deel van het EER is vervolgens in een groter EER gezet. Deze bevat extra tabellen voor bijvoorbeeld de kaarten, voor eventuele uitbereidingen. (Hierin is gamesession bijvoorbeeld vervangen voor Game)

EER van de hele game

Beide van deze EER’s bevatten de kardinaliteit tussen tabellen, voor extra overzicht en informatie over de relaties tussen tabellen. Verder hebben wij een connectie met de HBO-ICT Cloud opgezet.

Connectie met cloud

Als laatste hebben wij nog dummydata in de database gezet aan de hand van SQL-queries, en heb ik nog een query geschreven die in de code wordt aangeroepen. De database wordt alleen niet geinitializeerd, dus er kan geen data vanuit de game naar de database verstuurd.

const response = await this._socketConnectionListener.exectutePreparedQuery("INSERT INTO player (idPlayer, Name, elo, Wins, Losses, Game_idGame) VALUES (?)", ['14','TestPersoonVoorExpert','100','5','3','1']);
console.log(`${response}`);

Dummy data

K3

Je hebt een infrastructuur ontworpen en gebouwd volgens de gegeven specificaties.

Onze game maakt gebruik van een server om local/online multiplayer te kunnen spelen. Een serverconnectie gaat van speler -> server -> speler. We hebben dit idee uitgewerkt in een network diagram.

Network diagram

De server zit meer aan de kant van player 1, omdat dit de speler is die het spel op de server start, om te spelen met speler 2. Om de flow van data visueel te maken, hebben we een sequence diagram gemaakt om aan te geven wat voor data er wordt verstuurt, en waarheen.

Sequence diagram

Als laatste heb ik nog een paar berichtjes die ik verstuur naar de server in de code.

protected abstract void SendCardUsedData();
protected override void SendCardUsedData()
{
    CardData cardData = new();
    cardData.UsedCardType = "ObstacleCard";
    SocketClient.Instance.SendDataPacket(cardData);
}

Deze functie wordt alleen aangeroepen op het moment dat er een kaart gebruikt wordt. Dan stuurt deze functie een berichtje naar de server waar de UsedCardType instaat. De andere speler in de gamesession ziet dan dat de tegenstander een obstacle kaart heeft gebruikt (in dit voorbeeld ten minste).

private void SendObstacleTileData(int ObstacleTileX, int ObstacleTileY)
{
    ObstacleTileData obstacleTileData = new();
    obstacleTileData.X = $"{ObstacleTileX}";
    obstacleTileData.Y = $"{ObstacleTileY}";
    SocketClient.Instance.SendDataPacket(obstacleTileData);
}

Deze functie wordt aangeroepen op het moment dat er een obstacle kaart wordt gebruikt. Er wordt dan een berichtje gestuurd naar de server met de positie van deze obstacle tile, zodat die vervolgens op het andere scherm kan worden neergezet.

Bronnen:
OOP uitleg
OOP video
Sequence diagram tutorial


Last update: April 2, 2024