Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Rushh0ur

Frischling

Beiträge: 67

Beruf: Student Elektrotechnik

  • Private Nachricht senden

11

24.01.2015, 17:13

Was spricht gegen sowas:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
union ObjectFlags {
   unsigned int RawValue;
   struct {
      unsigned int Focusable : 1;
      unsigned int Collideable : 1;
      unsigned int Lighted : 1;
      unsigned int Damageable : 1;
      unsigned int Lootable : 1;
      unsigned int Interactable : 1;
   };
};

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

12

24.01.2015, 17:20

1) es verwendet Bitfields
2) es verwendet Bitfields
3) es ist undefiniertes Verhalten
4) es ist kein Standard C++, denn das kennt keine anonymous structs

;)

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (24.01.2015, 17:27)


Rushh0ur

Frischling

Beiträge: 67

Beruf: Student Elektrotechnik

  • Private Nachricht senden

13

25.01.2015, 14:05

Hmm ok mit undefiniertes Verhalten meinst du wohl das es nicht definiert ist wie genau die Daten auf Bitebene hinterlegt werden, sodass die union nichtig ist.

Sofern man also die Struktur deanonymisiert und nur für interne Strukturen verwendet ist es eine alternative zu den Bitmasken des Threaderstellers.

@LetsGo
Danke für den reichlich informativen Beitrag.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

14

25.01.2015, 14:14

Es wäre möglicherweise auch sinnvoll, als noch simplere Methode, um die Werte im Debugger darzustellen.
Im Programm würde ich auf das C-Bitfield vorsichtshalber so nicht zugreifen.

15

25.01.2015, 14:18

Das undefinierte Verhalten liegt in der union. Sobald auf ein anderes Element lesend zugegriffen wird, als geschrieben wurde, ist das Verhalten undefiniert.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
union
{
    int i;
    float f;
} test;

test.f = 1.0;
float f1 = test.f; // OK

test.i = 1;  // Schreibend, alles gut
int i = test.i; // Auch OK

float f2 = test.f; // Hier undefiniert
"Theory is when you know something, but it doesn’t work. Practice is when something works, but you don’t know why. Programmers combine theory and practice: Nothing works and they don’t know why." - Anon

16

26.01.2015, 09:17

Danke für eure vielen Antworten 8o

Den Ansatz mit dem Union habe ich sogar irgendwo schon einmal gesehen .. allerdings ist Undefined Behavior nicht so das was ich mir vorstelle :D
Der Übersichtlichkeit halber fasse ich nochmal kurz zusammen, welche Möglichkeiten mit welchen Vor- und Nachteilen bisher vorgeschlagen wurden:

Ausgangsvariante: Typ-Eigenschaften als Bitmasken
  • Vorteil: speichereffizient, einfache Operationen
  • Nachteil: gewöhnungsbedürftig beim Debuggen

Variante A: Typ-Eigenschaften als Zeichen im String
  • Vorteil: hübsch zu debuggen
  • Nachteil: nicht speichereffizient, langsame Operationen (besonders das Suchen/Prüfen)

Variante B: Typ-Klasse
  • Vorteil: hübsch zu debuggen, OO-Schnittstelle (je nach persönlichem "Schönheitsideal" ^^ )
  • Nachteil: leichter Overhead (ohne inlining); andere Nachteile je nach Implementierung

Variante C: Typ-Eigenschaften als enum
  • Vorteil: einfache Operationen
  • Nachteil: nicht speichereffizient, Ergebnistyp der Operationen gehört nicht zun enum

Variante D: Typ und -Eigenschaften als union
  • Vorteil: speichereffizient, einfache Operationen, kompakte Deklaration
  • Nachteil: undefined behavior

Hab ich noch was vergessen?

LG

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

17

26.01.2015, 10:54

Dir geht es doch darum die Objekte unterscheiden zu können oder?
Wie wäre es damit:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// besser alles in Klassen kapseln, hier einfach mal so:

std::size_t ids = 1; // 0 sehe ich mal als ungültig an

template <typename T>
std::size_t getId(){
  static std::size_t id = ids++;
  return id;
}

class EineKlasse{ // ließe sich auch als Basisklasse einsetzten die selbst ein template nutzt um die individuelle ID im Konstruktor zu setzen
  public:
    std::size_t getType(){
      return getId<EineKlasse>();
    }
}

// zur Prüfung dann (ließe sich auch noch schöner machen, automatischer):
if(foo.getType() == getId<ErwartetesObjekt>()){
  // ...
}


[Edit]

Okay gerade noch mal Anfangspost gelesen, ich glaube das ist nicht das was du willst :D
Bitmasks sind schwerer lesbar und Speicher sparen ist eigentlich auch Blödsinn... doch lieber einen von den anderen Vorschlägen, oder du nimmst das Ding von mir hier und sagst über "hat Objekt" dass eine Eigenschaft gegeben ist.

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

18

26.01.2015, 11:27

Finger weg von Strings. Stringly Typing ist ein furchtbar hässliches und furchtbar unsicheres Anti-Pattern. Gründe lassen sich mit "Stringly Typing" recherchieren. Ansonsten würde ich einfach zwei Ansätze empfehlen:
  • Bitmaske wurde bereits genannt. Kannst du hexadezimale Zahlen gut lesen, ist auch das Debugging kein Drama.
  • Komponenten-basierte Bots. Kann ein Bot ausgeraubt werden? Geb ihm eine Loot-Komponente. Kann ein Bot dir in den Hintern treten? Geb ihm eine Battler-Komponente.
Eine weitere Möglichkeit wäre es, dass z.B. ausraubbare Bots von einer Klasse "Lootable" erben und du dann mit dynamic_cast arbeitest. Der Nachteil davon ist Mehrfach-Vererbung.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

19

26.01.2015, 11:53

Okay gerade noch mal Anfangspost gelesen, ich glaube das ist nicht das was du willst

Richtig :D Die Eigenschaften sind nicht disjunkt sondern (quasi) beliebig kombinierbar.

Kann ein Bot ausgeraubt werden? Geb ihm eine Loot-Komponente. Kann ein Bot dir in den Hintern treten? Geb ihm eine Battler-Komponente.

Eine weitere Möglichkeit wäre es, dass z.B. ausraubbare Bots von einer Klasse "Lootable" erben und du dann mit dynamic_cast arbeitest. Der Nachteil davon ist Mehrfach-Vererbung.

Beide Möglichkeiten sind mir etwas "zu objektorientiert". Ich hatte mein Spiel Code-technisch schonmal weiter entwickelt gehabt mit starkem OO-Fokus. Dabei hatte ich jedem Objekt bestimmte Komponenten im OO-Sinne gegeben. Inzwischen fahre ich (da ich damit einige Performanceprobleme hatte, die auf virtual methods zurückzuführen waren) ein eher Data-oriented Design und verwende (gerade in den Physics- und Graphics-Systemen) einfache, homogene POD.

Daher möchte ich (z.B. was verschiedenes Physik- oder Render-Verhalten angeht) auf zu viel Vererbung verzichten. Bleibt also die von dir angesprochene andere Variante (Loot-Komponente, Battler-Komponente): Bis zu einem gewissen Grad ist das durchaus ein guter Ansatz, wenn die Objekte für verschiedene Eigenschaften verschiedene Daten verwenden. Der POD-Komponente dann z.B. unique_ptr<MoveData> zu geben wenn sie Movable ist (und ansonsten nullptr) klappt gut solange die Daten auch vollständig disjunkt zu anderen "Eigenschaften" sind.

Der andere Fall ist aber interessanter.. nämlich der, wenn die Eigenschaft gar keine extra Daten benötigt wie z.B. Focusable. Die Eigenschaft sagt nur etwas darüber aus, ob das Objekt von einem Spieler fokussiert werden kann (Truhe: ja, mir entgegenfliegender Feuerball: eher nein). Sicherlich könnte man sagen, man interpretiert den Anzeigenamen als eben diese Daten, die zu Focusable gehören, so dass wieder da unique_ptr<FocusData>-Ansatz realisierbar wäre.

Im Moment bin ich bei der Implementierung des Physics-Systems: Jedes Objekt (was fokussieren kann ^^ ) ändert beim Drehen (und anderen Sachen) seinen Fokus. Dazu möchte ich auf der Suche nach dem neuen Fokus natürlich prüfen, ob das Objekt fokussiert werden kann... sonst speichere ich mir die ID des Feuerballs statt des Gegners (der in der Sichtlinie weiter hinten steht und eigentlich fokussiert werden sollte).
Und da ich das ganze isn Physics-System einordne (wer wen fokussiert ist, da sich der Fokus nur durch Drehung/Bewegung ändern lassen soll) im Grunde Aufgabe meiner (einfachen) Physik. Ich könnte mir vorstellen, dass das der ein oder andere gänzlich anders sieht :P

Je weiter ich dann später voranschreite, desto mehr solcher Eigenschaften werden dazu kommen .. und ich vermute dass es wahrscheinlich ist, dass irgendwann mal eine Eigenschaft ohne eigene Daten auftritt.... daher meine Vorsicht :) Aber der Vorschlag gefällt mir ansonsten wirklich sehr gut! :thumbup:

/EDIT: Der Pointer-Ansatz passt auch gut zu dem im Artikel (verlinkt in deinem Post) beschriebenen "Hot/cold splitting" :)

Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von »Glocke« (26.01.2015, 12:01)


Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

20

26.01.2015, 12:05

Zitat

die auf virtual methods zurückzuführen waren
Da hast du dann Mist gemessen. Virtuelle Funktionen verursachen von sich aus keine nennenswert messbaren Verluste. Verluste erreichst du durch grottenschlechte Aufteilung der Objekte im Hauptspeicher. Sie werden immer und überall genutzt, von Compilern und Betriebssystemen, über OpenAL Soft, bis rauf zu fast jedem Videospiel und in fast jeder Programmiersprache. Im zuvor verlinkten Buch werden auch Strategien genannt, Cache-Misses stark zu reduzieren.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Werbeanzeige