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

1

08.11.2006, 14:26

Frage zu eigenem Message System

Hi

ich schreibe zurzeit ja an einem spiel, aber die frage hat eigentlich nichts gamespezifisches.

ich habe eine hauptklasse, die als member z.B. DDraw, Physic etc hat.
damit ich nicht für jeden befehl den mein proggy z.b. an DDraw gibt, eine methode in Main schreiben muss, kam mir die idee, in Main eine Mehtode einzubanen "Command"
das erste parameter soll den befehlsnamen enthalten. z.B. "BlitBitmap"

das zweite und dritte parameter sollen verschiedene zusätze enthalten. z.B. die Surface des Bitmaps und die rect struct. aber^^
wie mache ich das, das das 2te und 3te parameter jeden wertetyp annimmt? gibts da irgendne kluge möglichkeit?

Mfg#
Eldarion72

Phili

unregistriert

2

08.11.2006, 14:47

nen void* Pointer müsste da funktionieren...

Lemming

Alter Hase

Beiträge: 550

Beruf: Schüler

  • Private Nachricht senden

3

08.11.2006, 14:56

du kannst das zB genau so aufbauen, wie die MessageProc eines Windows-Fensters... Du Übergiebst deinen Befehl als einen enum oder sowas und dann zwei einfache parameter zB einen unsigned long und einen void*. Dein Befehl sagt der funktion was sie tun soll, der unsigned long könnte angeben was genau gemacht werden soll, oder was sich hinter dem void* verbirgt und der void* übergibt das betreffende objekt. in etwas code gesprochen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Command(CommandType cmd, unsigned long param1, void* param2)
{
    // Tue etwas ...

}

const unsigned long Type_GraphicObject = 42;

int main()
{
    GraphicObject* test = new Monster();
    
    Command(Render, Type_GraphicObject, reinterpret_cast<void*>(test));

    return 0;
}

Hat natürlich den nachteil, dass ein void* für alles verwendet werden kann und das eine massive Fehlerquelle darstellt, aber wenn du damit leben kannst wäre das eine möglichkeit...


/EDIT: mist, zu langsam ;)
Es gibt Probleme, die kann man nicht lösen.
Für alles andere gibt es C++...

Chase

Alter Hase

Beiträge: 753

Wohnort: Nagaoka / Darmstadt / Düsseldorf

Beruf: fauler Studi

  • Private Nachricht senden

4

08.11.2006, 15:10

Hhm, aber wenn man nicht noch eine Nachrichten-Queue oder sowas einbaut seh ich bei der Message-Methode null Vorteile.
In der Command-Funktion steht entweder eine riesige switch-Anweisung oder es werden doch wieder spezielle Funktionen aufgerufen.
Hinzu kommt, das man sich je nach Befehl eine andere Bedeutung der Parameter merken muss, da waer doch ein
RenderGraphicObject(&test);
um einiges anscheulicher als
Command(Render, Type_GraphicObject, reinterpret_cast<void*>(&test));

In meinen Augen ist das ein ueberholtes Konzept aus prä-OOP-Zeiten, aber ich lasse mich gerne vom Gegenteil uebezeugen..
"Have you tried turning it off and on again?"

Black-Panther

Alter Hase

Beiträge: 1 443

Wohnort: Innsbruck

  • Private Nachricht senden

5

08.11.2006, 16:36

Wenn dus OOP haben willst, dann machst du es einfach mit Abstrakten Klassen. Eine als Vorlage für jedes Modul zB CBaseModule mit einer puren-virtuellen Methode ExecuteCommand: zB

C-/C++-Quelltext

1
2
3
4
5
6
7
class CBaseModule
{
public:
    virtual int ExecuteCommand(CommandType Type, void* pParam1, void* pParam2) = 0;

    //... was es halt sonst noch geben soll...

};


Dann leitest du von dieser Klasse alle deine Module ab, und implementierst die Command!: zB

C-/C++-Quelltext

1
2
3
4
5
6
//DDraw-Modul

class CDDraw : public CBaseModule
{
public:
    int ExecuteCommand(CommandType Type, void* pParam1, void* pParam2);
};


Wenn du jetzt zB haben möchtest, dass alles Typendynamischer ist, nimmst du einfach Templates... zB könnte es sein, dass du die Enumeration CommandType für jedes Modul separat definieren möchtest, dann machst du im prinzip nur eine Ableitung mit Templates (CBaseModule als TemplateClass!)
stillalive studios
Drone Swarm (32.000 Dronen gleichzeitig steuern!)
facebook, twitter

Osram

Alter Hase

Beiträge: 889

Wohnort: Weissenthurm

Beruf: SW Entwickler

  • Private Nachricht senden

6

08.11.2006, 17:12

An Deiner Stelle würde ich mal das Internet absuchen.
Ich stand vor ca 10 Jahren vor der selben Frage und habe dann ein gutes OO System gefunden was ich dann sehr weitgehend übenehmen konnte und nur noch um zusätzliche Funktionalität erweitert habe. Es heisst mfccmd ist aber scheinbar inzwischen nicht mehr auf dem internet downloadbar und mich würde es auch nicht wundern wenn es inzwischen etwas besseres gibt.

Bei mir ist es in einem 3D Editor und erfüllt folgende Aufgaben:

1. Scriptbarkeit. Man kann von aussen die Kommandos generieren und aufrufen. Bei mir reicht dabei eine Übergabe im ASCII Format da es nicht sehr zeitkritisch ist. mit so einem "Command§ System hat man einen Flaschenhals and dem man etliches anhängen kann wie mitprotokollieren aller Commandos womit man Scripts manuell erstellen und speichern kann. Debugen wird auch unterstützt. Dies alles von z.B. Python anzusteuern ist sehr leicht. Es ist einfach, mehrsprachige Scripts zuzulassen, z.B. deutsch und englisch.

2. Undo

3. Verwaltung von "isModified" Flags für a) Abfrage ob der Nutzer speichern will wenn er das programm verlässt und Daten verändert hat (und nicht nur z.B. die Ansichten) b) Zeichne ich Sachen nur neu, wenn sie sich verändert haben.

4. Ich weiss nicht mehr ob's auch mit dem Garbadge Collector zusammenhängt.

5. Hilfe beim debuggen des C++ Codes.

Für Dich wäre wohl nur 1. und 5 interessant.

Ein kleiner Nachteil ist dass man bei jedem neuen Commando etliches ändern muss, Eintragen in Liste aller, "beispiel" Object erzeugen, Macro Docu, deutscher und englischer Name, resource.h und natürlich der eigentliche Name. Aber der "overhead" sind wirklich nur ein paar (<5) Minuten.

Ich habe eine BasisKlasse

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CCommand: public CObject
{ protected:
    BOOL canUndo;
 public:
  CCommand(BOOL canUndoIt = FALSE);
  ~CCommand(void);

    virtual int nGetBaseClassId(void) = 0;  
    // Override this to perform a command

    virtual BOOL Do(void * pP) = 0;
    // Override this to undo a command

    virtual BOOL Undo(void * pP) = 0;   

    virtual void PrintToString(char *s) = 0; 
    virtual MYBOOL SetParametersFromString(char *s) = 0; 
    virtual class CCommand *clone(void) = 0; 
    virtual CString *GetCommandName(void) = 0; 
    virtual CString *GetOtherCommandName(void) = 0; 
[...]
};


Von dieser sind alle "Commandos" abgeleitet. Jedes kann andere Parameter haben.

Commandos sind z.B. Drehen der selektierten Eckpunkte um einen Winkel, Translahieren, alles deselektieren, Ansicht um 40% zoomen etc. Insgesamt ca 200.
"Games are algorithmic entertainment."

rklaffehn

Treue Seele

Beiträge: 267

Wohnort: Braunschweig

  • Private Nachricht senden

7

08.11.2006, 17:36

Du kannst natürlich auch deine DDraw, Physics, etc. Objekte aus der Game Klasse nach aussen zur Verfügung stellen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
class Game
{
  private:

    DDraw _ddraw;
    Physics _physics;

  public:

    DDraw& getDirectDrawInstance () { return _ddraw; }
    Physics& getPhysicsInstance () { return _physics; }
};


Dann kann sich jeder die entsprechende Instanz holen und die richtigen Methoden direkt aufrufen.

Ein "forwarding" von Methoden braucht man eigentlich nur, wenn man Teile der Schnittstelle vor dem Programmierer verstecken will, oder wenn man eine fremde Schnittstelle (externe Lib) auf eine eigenen Schnittstelle abbilden will (Pattern: Adapter).

Eine verallgemeinterte Methode, so wie du sie beschrieben hast, ist gewöhnlich niemals eine gute Idee, weil sich sowas schlecht warten lässt und im Zweifel auch viel Zeit bei der Ausführung braucht.

Aber, wenn es dich interessiert, kannst du dich mal mit dem Mechanismus von Ellipsen und variablen Parameterlisten vertraut machen, wie es zum Beispiel printf benutzt.

Gruss,
Rainer
God is real... unless declared integer.
http://www.boincstats.com/signature/user_967277_banner.gif

8

08.11.2006, 17:59

Hi

ersma danke für die ganzen antworten :)

hmm, ich verstehe kaum was ihr gegen die switch habt^^. mir kam als performancesteigernnd noch die idee, ein gruppenparameter einzuführen, so wie z.B. Input, weil da doch einige abfragen zusammenkommen würden. :lol:

hey rklaffehn, das sieht gut aus. wusste net das ich das so nach aussen bekannt machen kontne^^. das löst meine probbys.
super vielen großen danke :lol: :D

9

08.11.2006, 21:38

Ich weiß nicht, für welchen Zweck du das gebrauchst, aber man kann das vielleicht, ein bisschen Typensicherer machen und mit functoren arbeiten. Schau einfach mal in die großen C++ Libs, es gibt sehr viele Implementierungen, ich kann dir evtl. auch was zukommen lassen.

Wenn du das wirklich generisch haben willst, dann schau doch mal in die Nebula Engine, die haben etwas in der Richtung implementiert und nutzen das auch zum Scripting, aber wenn du nicht UT 2010 schreiben willst und sowieso viel Zeit hast, würde ich das eigentlich nicht übertreiben, denn das verbessert nicht gerade die Übersicht und macht Debugging ein bisschen komplizierter.

PS: total ungewohnt wie friedlich alles gerade ist :)

10

09.11.2006, 14:48

ich schreib grad UT3000 :D

nene, ich schreib nen einfachen side-scroller.^^
und hab halt das prob, das ich eigentl. nur die hauptklasse global machen wollte^^.
die andenr klassen sind ja member von der main klasse.
das funzt jetz schon recht gut mit dem was ich von rklaffehn habe^^.
das benutz ich jetz auch schon.

Mfg
Eldarion72

Werbeanzeige