Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 18:49

diondokter

Dum spiro, spero

Topicstarter
Hey tweakers,

ik ben nu in mijn multiplayer unity fps game op het punt aangekomen dat ik met entities bezig moet. Ik heb hier al bepaalde ideeën over:
  • Alle game objecten die maar enigzins met de server gesynchroniseerd moeten worden, zijn Entities.
  • Een entity kan nog aanwezig zijn, maar dan als static gemarkeerd zijn. (Bijv. een muur die al kapot is, aangezien die geen verdere sync meer nodig heeft)
  • Een entity is modulair opgebouwd. Sommigen hebben bijv. een health module, een andere heeft bijv. een selfdestruct mudule... Of beiden.
Over dit laatse punt zit ik nu te twijfelen hoe ik dat voor elkaar kan krijgen.
Ik heb me bedacht dat dit op twee manieren kan.
  1. Ik maak een een Module base class. Van deze class kan ik dan weer bijv. Health deriven.
    Wat ik nu heb:
    C#:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    
    public class Entity
    {
        public int Number;
        public EntityState State;
        public int Owner;
        public Module[] Modules;
    
        public Entity(int Number, EntityState State, int Owner, params Module[] Modules)
        {
            //...
        }
    
        public bool HasModule<T>() where T : Module
        {
            //...
        }
    
        public T GetModule<T>() where T : Module
        {
            //...
        }
    }
    
    public enum EntityState
    {
        Alive, Static
    }
    
    public class Module
    {
        public bool Active = true;
    }
    
    public class Location : Module
    {
        public Vector3 Position;
        public Vector3 Rotation;
    }
    public class Scaler : Module
    {
        public Vector3 Scale;
    }
    public class Health : Module
    {
        public float Current;
        public float Maximum;
    }
    public class DeSpawnTimer : Module
    {
        //...
    }
    public class Parent : Module
    {
        Entity Child;
    }
    public class Child : Module
    {
        Entity Parent;
    }
    
    public class GrenadeEntity : Entity
    {
        public GrenadeEntity() : base(0, EntityState.Alive, 0, Location, DespawnTimer)
    }
  2. In plaats van dat ik modules gebruik, gebruik ik interfaces.
    C#:
    1
    2
    3
    4
    
    public class GrenadeEntity : Entity, ILocation, IDespawnTimer
    {
        //...
    }
Ik neig ernaar om optie 1 te gebruiken, aangezien mijn doelstelling vooral is gericht op het makkelijk en snel uitbereiden van het aantal soorten entities.

Mijn vraag: Is het in mijn geval beter om interfaces te gebruiken, en zo ja waarom? (Zo nee, dan zou ik ook graag weten waarom niet ;)) Of is er misschien nog een betere manier om dit aan te pakken?

Bedankt voor je aandacht zover. Als er vragen zijn, dan beantwoord ik ze graag!

[Edit:]
Ik zie net de misspelling in de titel... |:(

[ Voor 1% gewijzigd door diondokter op 05-08-2014 22:03 . Reden: Foutje in de code ]


Acties:
  • 0 Henk 'm!

  • Xorecs
  • Registratie: Januari 2006
  • Laatst online: 30-05 14:00
Ik zou hier toch interfaces gebruiken. Ik kan me namelijk voorstellen dat je voor alle entities die IHealth (oid) implementeren een functie zou willen hebben 'TakeDamage' of 'Heal'. Met interfaces kan je afdwingen dat alle entities die IHealth implementeren ook deze functies hebben. Dit zou je ook kunnen realiseren met optie 1, maar dan wordt het gegarandeerd veel minder overzichtelijk.

Edit: Nog een linkje van Unity zelf gevonden:
http://unity3d.com/learn/...iate/scripting/interfaces

[ Voor 29% gewijzigd door Xorecs op 05-08-2014 22:22 ]


Acties:
  • 0 Henk 'm!

  • Caelorum
  • Registratie: April 2005
  • Laatst online: 17:28
Ik zou de eerste doen persoonlijk en dan met abstract classes die eenzelfde base class hebben. Op die manier kan je namelijk ook at runtime nog het gedrag van je objecten veranderen. Haal bijv. de destructible component weg en iets is indestructible, voeg een physics component toe en iets wordt beinvloed door dat systeem (bijv. verplaatsen), haal het weer weg en iets is static. Geef iets een selfdestruct component en het kan selfdestructen.
Waar je overigens nu mee bezig bent wordt in veel artikelen uitgelegd waaronder deze http://www.gamedev.net/pa...nent-entity-systems-r3013 en het vervolg http://www.gamedev.net/pa...nent-entity-systems-r3382
en http://gameprogrammingpatterns.com/component.html en http://cowboyprogramming..../05/evolve-your-heirachy/ en http://www.gamedev.net/pa...ty-component-system-r3159

[ Voor 47% gewijzigd door Caelorum op 05-08-2014 22:43 ]


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 18:49

diondokter

Dum spiro, spero

Topicstarter
Wat mij alleen tegen zit bij interfaces is dat je voor elke nieuwe entity weer opnieuw de benodigde methods moet schrijven, ook al zijn die methods overal hetzelfde.
Ookal geeft dat wel weer de vrijheid om verschillende entities verschillende implementaties van Health te geven...

En zoals Caelorum zegt, systeem 1 is erg aanpasbaar. Ook terwijl de game loopt.

Btw: Hoe pas je de topic titel aan? Ik stoor me eraan 8)7

[ Voor 7% gewijzigd door diondokter op 05-08-2014 22:53 ]


Acties:
  • 0 Henk 'm!

  • BikkelZ
  • Registratie: Januari 2000
  • Laatst online: 21-02 08:50

BikkelZ

CMD+Z

diondokter schreef op dinsdag 05 augustus 2014 @ 22:51:
Wat mij alleen tegen zit bij interfaces is dat je voor elke nieuwe entity weer opnieuw de benodigde methods moet schrijven, ook al zijn die methods overal hetzelfde.
Ookal geeft dat wel weer de vrijheid om verschillende entities verschillende implementaties van Health te geven...
Je kunt ook objecten met de methods meegeven in de constructor, Inversion of Control heet het volgens mij. Dus je zou de interface IHealth die een IHealthHandler interface die TakeDamage of Heal moet implementeren als property heeft mee kunnen geven door de HealthHandler via de constructor mee te geven.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public interface IHealth
{
    public IHealthHandler { get; set; }
}

public interface IHealthHandler
{
    public int HitPoints { get; private set; }

    public bool Heal(int hitPoints);
    public bool TakeDamage(int hitPoints);
}

public HealthHandler
{
    // implementeer alles
}

public Player: IHealth
{
    public Player(IHealthHandler healthHandler)
    {
         this.HealthHandler = healthHandler;
         // misschien moet healthHandler ook naar this referren of een event krijgen als de hitpoints op 0 uit komen
    }
}


(mijn C# is een beetje roestig, sorry als er kleine foutjes in geslopen zijn)

Voordeel is ook dat IHealthHandler ieder willekeurig object kan zijn wat de interface implementeert wat erg handig is als je unit tests schrijft.

[ Voor 28% gewijzigd door BikkelZ op 06-08-2014 03:28 ]

iOS developer


Acties:
  • 0 Henk 'm!

  • Russel88
  • Registratie: Juli 2009
  • Laatst online: 19:33
diondokter schreef op dinsdag 05 augustus 2014 @ 22:51:
Wat mij alleen tegen zit bij interfaces is dat je voor elke nieuwe entity weer opnieuw de benodigde methods moet schrijven, ook al zijn die methods overal hetzelfde.
Ookal geeft dat wel weer de vrijheid om verschillende entities verschillende implementaties van Health te geven...
Dat kan bij een abstracte class toch ook?
Als een method 9 van de 10 keer hetzelfde is, dan is een abstracte class idd handig, dan hoef je dezelfde method niet telkens opnieuw implementeren.
Bij een Interface kun je je class nog laten overerven van een andere class mocht dat nodig zijn.

Acties:
  • 0 Henk 'm!

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
Uhm klinkt misschien simpel: abstract class maken op basis van interface?

@BikkelZ: Inversion of control draait primair om het feit dat je verantwoordelijkheid van je logica omdraait. Je programma definieert een interface om primaire taken af te handelen, maar de implementatie van die taken ligt geheel bij de implementerende class library. Daarnaast is de primaire applicatie niet verantwoordelijk voor de instantiering van de class. Daarvoor kan je bijv. factory classes, service locators etc voor gebruiken. Maw. het programma heeft een service nodig van het type IFunctionality, maar het boeit de app verder niet waar deze vandaan komt of hoe deze geinstantieert word. Het enige wat telt is dat er afspraken zijn (de interface) over hoe de logica word aangesproken.

Een voor de hand liggend voorbeeld is een plugin systeem voor je applicatie. Er is een generieke interface waarmee een plugin communiceert met je applicatie, de applicatie hoeft echter niet te weten wat je plugin doet en hoe het functioneert. Zodra de plugin geladen is, hoef je alleen maar via een generieke interface de functionaliteit van de plugin aan te roepen. (bijv. via een generieke DoStuff method; 'stuff' in deze verschilt dus per implementerende class, maar dat boeit je app niet)

Game engine voorbeeld: je definieert een IGraphicsProvider voor je graphics subsystem. Of dat vervolgens een Direct3D of OpenGL implementatie is boeit niet; de app roept alleen maar DrawTriangle aan conform de IGraphicsProvider interface. Zodoende kan je verschillende platformen ondersteunen terwijl er in je core game logica niets wijzigt.

[ Voor 16% gewijzigd door Laurens-R op 06-08-2014 09:49 ]


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 18:49

diondokter

Dum spiro, spero

Topicstarter
Dit is precies wat ik bedoel! Ze maken bij de meeste voorbeelden ook gebruik van classes/structs. Oftewel mijn eerste manier.

Heeft het voordelen om een interface te gebruiken in plaats van de (blijkbaar) standaard manier?
Het enige waar ik zo op kan komen is:
C#:
1
2
3
4
foreach(IHealth Ent in Entities)
{
    Ent.Heal(5);
}


Terwijl aan de andere kant het iets meer werk vereist:
C#:
1
2
3
4
5
6
7
foreach(Entity Ent in Entities)
{
    if(Ent.HasModule<Health>())
    {
        Ent.GetModule<Health>().Heal(5);
    }
}

(Dit kun je natuurlijk ook doen met checken voor null bij GetModule... Zou misschien wat sneller zijn)

Acties:
  • 0 Henk 'm!

  • raptorix
  • Registratie: Februari 2000
  • Laatst online: 17-02-2022
Wat je eigenlijk nodig hebt zijn Traits, helaas heb je dat in c# niet ;(

Wikipedia: Trait (computer programming)

Zag het laatst voor het eerst toen iemand een demo Scala gaf.

Acties:
  • 0 Henk 'm!

  • Xorecs
  • Registratie: Januari 2006
  • Laatst online: 30-05 14:00
diondokter schreef op dinsdag 05 augustus 2014 @ 22:51:
Wat mij alleen tegen zit bij interfaces is dat je voor elke nieuwe entity weer opnieuw de benodigde methods moet schrijven, ook al zijn die methods overal hetzelfde.
Ookal geeft dat wel weer de vrijheid om verschillende entities verschillende implementaties van Health te geven...

En zoals Caelorum zegt, systeem 1 is erg aanpasbaar. Ook terwijl de game loopt.

Btw: Hoe pas je de topic titel aan? Ik stoor me eraan 8)7
Volgens mij is het al eerder gezegd, maar dit kan ook met abstract classes. Het voordeel hiervan is dat je je logica in de abstract class kan definieren (voor de TakeDamage bijvoorbeeld). Zolang je deze methoden in de abstract classes virtual maakt kan je hem in je entities ook nog overriden als dat nodig is.

Edit: Dit idee: MSDN: override (C# Reference)

[ Voor 6% gewijzigd door Xorecs op 06-08-2014 17:45 ]


Acties:
  • 0 Henk 'm!

  • diondokter
  • Registratie: Augustus 2011
  • Laatst online: 18:49

diondokter

Dum spiro, spero

Topicstarter
Xorecs schreef op woensdag 06 augustus 2014 @ 17:43:
[...]
Volgens mij is het al eerder gezegd, maar dit kan ook met abstract classes. Het voordeel hiervan is dat je je logica in de abstract class kan definieren (voor de TakeDamage bijvoorbeeld). Zolang je deze methoden in de abstract classes virtual maakt kan je hem in je entities ook nog overriden als dat nodig is.
Ik snap het principe van abstract classes, maar ik snap niet hoe ze me kunnen helpen. Een class kan maar van één andere class deriven, maakt niet uit of die abstract is of niet.

Het voordeel van interfaces is namelijk dat een class van meerdere kan deriven, en dat haal je met abstract classes nou juist weer weg.
Als het me echt kan helpen, dan zie ik graag een voorbeeld aangezien ik nu niet snap waar je op doelt.

Acties:
  • 0 Henk 'm!

  • Caelorum
  • Registratie: April 2005
  • Laatst online: 17:28
diondokter schreef op woensdag 06 augustus 2014 @ 20:26:
[...]
Ik snap het principe van abstract classes, maar ik snap niet hoe ze me kunnen helpen. Een class kan maar van één andere class deriven, maakt niet uit of die abstract is of niet.[...]
Ik zie dat ik het ook al verkeerd heb verwoord ^^
Caelorum schreef op dinsdag 05 augustus 2014 @ 22:40:
Ik zou de eerste doen persoonlijk en dan met abstract classes die eenzelfde base class hebben. [...]
Moet uiteraard zijn: ".... met classes die eenzelfde abstract base class hebben..."
diondokter schreef op woensdag 06 augustus 2014 @ 11:50:
[...]
Dit is precies wat ik bedoel! Ze maken bij de meeste voorbeelden ook gebruik van classes/structs. Oftewel mijn eerste manier.

Heeft het voordelen om een interface te gebruiken in plaats van de (blijkbaar) standaard manier?
Het enige waar ik zo op kan komen is:
C#:
1
2
3
4
foreach(IHealth Ent in Entities)
{
    Ent.Heal(5);
}


Terwijl aan de andere kant het iets meer werk vereist:
C#:
1
2
3
4
5
6
7
foreach(Entity Ent in Entities)
{
    if(Ent.HasModule<Health>())
    {
        Ent.GetModule<Health>().Heal(5);
    }
}

(Dit kun je natuurlijk ook doen met checken voor null bij GetModule... Zou misschien wat sneller zijn)
Ik zou eerder een dictionary doen en dan die laatste. Nog beter zou zijn als je het idee van een entity class loslaat en een entity alleen laat bestaan als een set van components die toevallig dezelfde id hebben, maar dat is denk ik iets waar je nu niet meer iets aan kan doen en wellicht in C# ook niet zo relevant, omdat de voordelen voornamelijk op performance vlak liggen. Mocht de site het binnenkort weer gaan doen: http://www.altdev.co/2011...0%93-part-iii-components/ Is dacht ik een goede uitleg van wat ik bedoel als ik het me goed kan herinneren.

Acties:
  • 0 Henk 'm!

  • Xorecs
  • Registratie: Januari 2006
  • Laatst online: 30-05 14:00
diondokter schreef op woensdag 06 augustus 2014 @ 20:26:
[...]


Ik snap het principe van abstract classes, maar ik snap niet hoe ze me kunnen helpen. Een class kan maar van één andere class deriven, maakt niet uit of die abstract is of niet.

Het voordeel van interfaces is namelijk dat een class van meerdere kan deriven, en dat haal je met abstract classes nou juist weer weg.
Als het me echt kan helpen, dan zie ik graag een voorbeeld aangezien ik nu niet snap waar je op doelt.
Oke, het klopt inderdaad dat je niet meerdere base classes kan implementeren, dat was echter ook niet mijn bedoeling. Je kan echter wel het volgende met abstract classes / interfaces regelen (je hoeft code dan niet dubbel te schrijven):
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    
    public interface IHaveHealth
    {
        int Health { get; set;  }
        void TakeDamage(int damageAmount);
    }

    public interface IHaveWhatever
    {
    }

    public abstract class LivingEntity : IHaveHealth, IHaveWhatever
    {
        public int Health { get; set; }

        public virtual void TakeDamage(int damageAmount)
        {
            Health -= damageAmount;
        }
    }

    public class HumanEntity : LivingEntity
    {
        // Eventuele override van TakeDamage
    }

(even in notepad++ geschreven, kunnen fouten in staan)

Je bent hiermee wel minder vrij dan jij hebt aangegeven te willen zijn. Je kan moeilijk 'modules' weghalen als het object gemaakt is. Als je die vrijheid wil zou ik in ieder geval met optie 1 verder gaan. Denk er dan wel aan dat het zou kunnen voor komen dat een object twee dezelfde modules bevat.
Pagina: 1