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

21

15.09.2010, 21:16

so, link hab ich repariert...blöde leerzeichen :)

ein beispiel mach ich auch noch, kommt irgendwann bald, vielleicht zusammen mit der doku^^
eigentlich ist das ganze so gedacht, dass den eigentlichen code niemand sieht (außer ihr). zum benutzen brauch man ja nur das interface, die lib und die dll.

22

15.09.2010, 21:24

Jo klappt ;)

@Kommentare
Die würde ich trotzdem einführen und am besten gewöhnst du dir das Kommentieren gleich an,
da nach 6 Monaten der eigene Code ganz wirr vorkommen kann. Oder bei größeren
Projekten du gar die übersicht verlierst und außerdem sieht kommentierter Code schöner aus :D

@Doku
Erspar dir das lieber ;)
Ist nur viel Arbeit ohne Lohn, denn so Leid es mir tut, ich glaube kaum, dass jemand ein anderes Framework außer
SFML, SDL, Ogre etc. benutzen würde. Ist nicht böse gemeint, ich war auch zuerst entäuscht.

@Anderes
Ansonsten mache bitte eine Unterteilung in "Source", "Header", "Libs" und "Binarys" (und entsprechend "Example").
Ist sonst sehr konfus.

Ich hoffe ich konnte dir helfen :)

hanse

Alter Hase

Beiträge: 472

Wohnort: Wien

  • Private Nachricht senden

23

15.09.2010, 22:59


@Kommentare
Die würde ich trotzdem einführen und am besten gewöhnst du dir das Kommentieren gleich an,
da nach 6 Monaten der eigene Code ganz wirr vorkommen kann. Oder bei größeren
Projekten du gar die übersicht verlierst und außerdem sieht kommentierter Code schöner aus :D

Das würd ich nicht so pauschal sagen. Guter Code sollte eigentlich keine Kommentare brauchen. Kommentare haben den Nachteil, dass sie sich nicht an Codeänderungen anpassen und ein veralteter Kommentar kann schlechter sein als gar kein Kommentar. Deshalb gilt hier eigentlich: so wenig Kommentare wie möglich, so viele wie nötig.

24

16.09.2010, 09:59

Hallo Newby,

ich habe deinen Framework jetzt ein bisschen genauer angeschaut und gebe jetzt ein wenig meinen Senf dazu und auch ein paar Tipps.

Habe da schon ein wenig Erfahrung bei größeren Projekten ( unter anderem Sacred 2 ) und werde dich erstmal auf grobe Fehler hinweisen.


a) Ein Wort "declaretions" (declaretions.h) gibts nicht, es heisst Declarations.

b) du includierst zwar alle deine Klassen Header nur in der Simple2D.h und niergendwo sonst. Aber gewöhn dir trotzdem bei jedem Header Include-Guards zu schreiben, sonst hast du später große Probleme.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
#ifndef HEADERNAME_H
#define HEADERNAME_H

#ifdef (_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif 

// dein code hier

#endif


Kurze Erklärung dazu:
Normalerweise würden nur "pragma once" oder Include-Gurads reichen, aber wenn du sicheren und portablen Code schreiben willst, benutz beides. Pragma once läuft beim Kompilieren viel schneller (Link MSDN : "This can reduce build times as the compiler will not open and read the file after the first #include of the module.") Aber läuft nicht mit allen Compilern. Bei dem Codegerüst oben wird also pragma once genommen, wenn man deinen Framework mit Visual Studio kompiliert, wenn nicht, werden langsamere aber mit allen Compilern laufende Include-Guards genommen.

c) Bei deinen Exceptions nutzt du immer 2 Parameter (Errorcode und einen Beschreibun-String). Wenn du mit Errorcodes arbeiten willst, dann übergib als Parametern nur die und keine Strings. Dann sparst du dir bei gleichen Errornummern an verschiedenen Stellen die Tipparbeit und minimierst Vertipp-Gefahr. In der Exceptionklasse kannst du dir einfach eine Methode machen, z.B. std::wstring GetErrorDescription(int Number), die dir diese Beschreibung liefert.

d) Du wirfst überall Exceptions (ist gut), fängst die aber niergendwo. Bei manchen Stellen musst du das machen, z.B. da wo du in deinen Methoden andere deine Methoden aufrufst. Genauso musst du auch diese Exceptions dann verarbeiten, um z.B. deinen Code backrollen / aufräumen oder Exceptions weiterleiten.

e) Was davor bespochen wurde mit Create und Destroy und wie du das jetzt umgebaut hast, halte ich für falsch. Ich würde die Methoden beibehalten und das nicht im Konstruktor/Destruktor direkt machen.
Sondern würde dann, da wo es sein muss mehrere default Constructors machen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class RenderDevice
{
    RenderDevice();
    RenderDevice(int width, int height);

    Create(int width, int height);
    Destroy();
};

RenderDevice::RenderDevice()
{
}

RenderDevice::RenderDevice(int width, int height);
{
    Create(width, height);
}

RenderDevice::~RenderDevice()
{
    Destroy();
}


Somit gibst du dem Benutzer freie Wahl, ob der direkt beim Erstellen der Instanz die Initialisierung vornehmen will oder danach. Genau das gleiche für Destruktor, somit kann der Benutzer die Aufräumarbeiten vor der Zerstörung der Instanz vornehmen (ist auch nötig, weil z.B. explizites Aufrufen vom Destruktor nicht geht, z.B. bei einer Exception).

f) Bei deinen Klassen hast du einige private Members, was ok ist, aber die musst du alle im Konstruktor erstmal initialisieren. Alle Zeiger auf 0, alle Strukturen mit memset leeren, alle stl Container am besten mit clear() reseten.
Wenn du manche Zeiger uninitialisiert benutzst, dann zeigen die auf 0xcdcdcd (Nirvana), wird es recht heftig crashen. Manchmal auch nur in Release Version, so dass du es gar nicht bei der Entwicklung merkst und die Fehlersuche stark erschwert wird.

g) Benutze keine chars* oder wchars*, benutze std::string und std::wstring.

h) Benutze keine rohe Zeiger, benutze std::tr1::shared_ptr / std::tr1::scope_ptr (oder alternativen aus Boost).


Ein wenig zur Optimierung und Verbesserung:

i) Pack deinen Framework in ein Namespace, so vermeidest du Kollisionen und Namenskonflikte mit anderen Bibliotheken.

j) Benutze da wo es möglich ist nicht i++, sondern ++i, z.B. in deinen for Schleifen. Die Compiler optimieren da den Code besser.

k) Declariere deine eigene Datentypen, z.B. typedef unsigned int u32, damit kannst du deinen Code später portieren und kapseln.


Ok, bevor ich hier alle Buchstaben aufbrauche, kannst du vielleicht diese Sachen verbessern (oder Gegenteil argumentieren).

Viele Grüße,
Sergius

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

25

16.09.2010, 15:13

Erzgänzend zu Sergius noch ein paar Punkte die mir aufgefallen sind:

  • Keine konsequente Durchführung der "const correctness"
  • WndProc global, wieso?
  • Inline in Klassendefinition macht wenig Sinn.
  • Nutzung von explicit (z.B. CVector)
  • Kopierverhalten im Allgemeinen (z.B. CWindow, CTexture, ...)
  • Exceptions aus Destruktoren (Meyers)
  • ZU exzessiver Einsatz von Exceptions (z.B. mSprite->Begin, ...)
  • Konstanten für Fehlercodes (allg. keine Magic Numbers [z.B. pRotationAngle*0.0174532f in CSprite.cpp])
  • I.A. eher prozedurale Designansätze
  • Zuständigkeiten sind oft nicht klar (z.B. CServer - Server, Winsock Management, ...)

Ausserdem ist das Framework nicht (immer) intuitiv verwendbar. Ich hab relativ lang gebraucht um ein Fenster zu erzeugen, und musst dir Internas anschauen weil mir nicht klar war wie das konkrete Vorgehen sein muss. Schlussendlich kam ich zu folgendem:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
    try
    {
        CWindow window("Hallo Welt", 800, 600, GetModuleHandle(NULL));
        while (!window.Closed())
        {
            window.DoMessageLoop();

            // render stuff
        }
    }
    catch (const SException &ex)
    {
        printf("FATAL ERROR: %s\n", ex.ErrorDescription.c_str());
    }
}


Und schon da gabs ein haufen Fehler. Zum Beispiel beendet sich das Programm immer mit einer Exception ("Window could not be destroyed"). Der Versuch ein zweites Fenster zu erzeugen schlug sofort fehl ("Window class could not be created"). Dank falschem Kopierverhalten wird das Orginalfenster schnell zu früh zerstört, mit negativen Auswirkungen (das ist bei vielen anderen Klassen übrigens auch der Fall), usw...

Da muss nochmal einiges generalüberholt werden!
@D13_Dreinig

26

20.09.2010, 06:44

danke nochmal für die ganzen tips :)
ich wollte gestern anfangen das framework mit Hilfe eurer Vorschläge zu überarbeiten, aber irgendwie ist das ein riesiger Aufwand, alleine z.B. alles char in std::string zu ändern...
deswegen habe ich mir überlegt, nochmal quasi von vorne anzufangen. Also ruhig noch viele Tips posten, damit ich die direkt einbauen kann und nicht alles wieder ändern muss später ;)
zu eurer vorschlägen:

@Sergius
a) korrigiert
b) include guards baue ich dann auch ein
c) wie speichere ich deiner Meinung nach die Beschreibungen der Description Codes? Als Konstanten? In einer Datei? Irgendwie anders?
d) wäre es sinnvoll, einfach in alle Methoden am Anfang einen try block zu setzen und am ende alle exceptions weiterzuleiten?
e) das prinzip finde ich am besten, werde ich so einbauen
f) das sollte auch kein problem sein..
g) werde ich versuchen, aber manchmal ist das schon recht schwer glaube ich (z.B. bei vsprintf)
h) muss ich mir erstmal angucken wie die smart pointer funktionieren
i) könnte ich das einfach um alle includes machen? (wie bei extern "C++")
j) okay ;)
k) naja, portieren will ich eigentlich nicht und plattformunabhängig muss das eigentlich auch nicht werden

@david
const werde ich mal korrekt verwerden ;)
...keine zeit mehr...

Benedikt

27

20.09.2010, 09:25

Zur Dokumentation kann man prima Doxygen benutzen, der erzeugt aus Quellcodekommentaren eine schöne Doku. So ist die Doku einerseits immer recht aktuell und andererseits immer direkt zur Hand, wenn man den Quellcode liest.

d): Nein, irgendwas einfach so überall zu machen ist eigentlich nie sinnvoll. Exceptions werden automatisch weitergeleitet, wenn du also nix mit ihr anstellen willst und sie nur weiterschmeißen willst, macht ein try keinen Sinn.

g) es gibt Dinge wie std::stringstreams oder boost::lexical_cast (der intern stringstreams benutzt) oder auch boost::formate, also eine Menge guter Alternativen.

h) Ja, auf der boost Seite sind die aber gut erklärt. Die helfen vor allen, Exceptionsicheren Code zu schreiben (sonst kann delete schnell mal übersprungen werden).

Ach übrigens: Wenn du Exceptions magst, kannst du dir mal boost::exception angucken, damit kann man ganz witzige Sachen machen.
(Und falls du es noch nicht gemerkt hast: boost hat eine ganze Menge netter Sachen, ein Besuch auf der Seite lohnt sich immer)
Lieber dumm fragen, als dumm bleiben!

CBenni::O

1x Contest-Sieger

Beiträge: 1 145

Wohnort: Stuttgart

  • Private Nachricht senden

28

20.09.2010, 13:58

Ach ja, und zu k)

Stell dir mal vor, du entwickelst ein Spiel so wie jetzt. Iregndwann wird es immer besser und dann wächst der Wunsch, dass es portiert wird. Daran hattest du nicht gedacht und plötzlich stehst du vor einem riesen Problem. Glaube mir, ein komplettes Projekt nach einem paradigma o.ä. umzuschreiben ist eine höllische Arbeit. Fange so früh wie möglcih damit an.

mfg CBenni::O
Ein Mitglied der VEGeiCoUndGraSonMaWiGeS Bewegung.
42!
Aufräumen kann jeder, nur das Genie überblickt das Chaos!
Metal will never die!
1. Sppro Gamecontest - mein Beitrag

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

29

20.09.2010, 14:56

Wenn im Vorfeld klar ist für welche Platform er entwickeln will dann sollte er sich darauf konzentrieren. Platformunabhängig zu entwickeln ist zwar nett, aber erfordert viel mehr Aufwand (meistens will man später ohnehin nicht portieren). Wenn man immer Platformunabhängigkeit anstrebt, dürfte man ja z.B. gar keine Spiele mehr mit C# entwickeln... Oder müsste auf tolle (Platformabhängige) Lösungen verzichten...
@D13_Dreinig

CBenni::O

1x Contest-Sieger

Beiträge: 1 145

Wohnort: Stuttgart

  • Private Nachricht senden

30

20.09.2010, 15:03

Gibt es in C# keine möglichkeiten, Implementierugen per #ifdef o.ä. umzuschalten? Habe zu lange nicht mehr damit gearbeitet...

mfg CBenni::O
Ein Mitglied der VEGeiCoUndGraSonMaWiGeS Bewegung.
42!
Aufräumen kann jeder, nur das Genie überblickt das Chaos!
Metal will never die!
1. Sppro Gamecontest - mein Beitrag

Werbeanzeige