Zufall/Zeitintervall + andere Fragen



  • ich glaube ich finds nicht,

    oder ist das dieser befehl?

    clrscr();
    

    EDIT: Jetzt wo ich aufn Namen achte, denke ich mal is er das 😃

    danke für die hilfe



  • Respekt. Dein Code hat bisher 7000 LOC? Das zeigt zumindest, daß du wirklich motiviert bist dein Spiel zu schreiben und auch Geduld hast, die man braucht, wenn man ein Spiel in einer Sprache schreibt, die man noch nicht ganz gemeistert hat.

    Dennoch hast du in meinen Augen einige gravierende Fehler gemacht. Nicht unbedingt was das Codieren an sich angeht, denn Coding Geschichten kann man mehr oder weniger sehr schnell reparieren bei kleinen Programmen. Sondern was das Design und den grundsätzlichen Aufbau deines Programms angeht.

    Bei nahezu jedem Spiel fängt man nicht mit Fancy Grafik an, sondern mit den grundlegenden Dingen. Du hast 7000 LOCS aber noch kein funktionierenden Randomizer und Timer?! Fehler! Fang beim nächsten Mal genau mit diesen Dingen an. Deine Spielervariablen sind alle Global. Fehler! Sind nicht mal ein Struct! Fehler! Deine Monstervariablen will ich garnicht sehen. Dein Main-Gameloop würde ich mal gerne sehen. Meine Vermutung ist aber, daß ich ihn lieber nicht sehen möchte! In einem Spiel ballert man sich auch nicht die Main zu mit 20 printf befehlen, die den Titel des Spiels anzeigen. Macht man einfach nicht. Man macht eher folgendes (Pseudocode):

    main()
        // init stuff
        initialize memory system
        initialize file system
        initialize resource manager
        initialize input system
        initialize sound system    
        initialize video system
        initialize frame timer
        initialize all in game stuff
            -> load all item tables
            -> load all monster tables
            -> ...
    
        // gameloop
        gameloop()
            -> update timer
            -> update resourcen
            -> udpate user input
            -> update video
    
        // shutdown
        shutdown all in game stuff
            -> un-load all item tables
            -> un-load all monster tables
            -> ...
        shutdown frame timer
        shutdown video system
        shutdown sound system
        shutdown input system
        shutdown resource manager
        shutdown file system
        shutdown memory system
    

    Und all diese Dinge versucht man so autonom wie möglich zu integrieren. Keine globalen Variablen. Globale Variablen sind ein No-Go bei solch mehr oder weniger komplexen Dingen. Und das meine ich. Wenn dein Spiel funktioniert ist es schön. Ich würde jedoch fast wetten, daß du irgendwann kein Bock mehr drauf hast, weil du den Überblick verlierst und kleine Änderungen des Codes riesige Auswirkungen auf den Rest des Sysems hat. Und alles nur weil du einfach mal losgecodet hast.

    Also wenn du dir selber eine Menge Ärger ersparen willst, dann ändere das. Modularisiere dein Programm und Versuch jedes Medul wirklich nur Zugriff auf die für ihn unbedingt notwendigen Dinge zu haben.

    Ansonsten aber Respekt vor deiner Geduld!



  • bis jetzt ist auch noch alles übersichtlich, ich muss dazu noch sagen, das die story noch gar nicht integriert wurde, genauso wenig das Kampfverhalten etc. bisjetzt nur umgebung und so...

    wie gesagt, ich hab c jetzt gerade mal ein halbes jahr gelernt, und ein struct aus den globalen Variablen zu machen, ist ne sehr gute idee, bis jetzt habe ich aber eh nur 8 - 9 variablen und es werden auch nicht mehr, aber danke für die tipps die werde ich größten teils umsetzen, das, was du Fancy nennst, musste ich schon machen, damit ich das Layout für die restlichen Funktionen habe, aber das wirste sicherlich noch früh genug sehen weil ich meinen code mal hierrein posten werde, sofern das möglich ist ^^

    Nun stelle ich dir mal die Frage, du hast rund 200 Funktionen, und fast jede funktion ist mit 3 - 5 weiteren Funktionen verknüpft, meinst du ich solle da etwa alle Variablen über die Funktionen übergeben? oO
    Ich meine das wäre viel zu aufwendig, und dann kommt man erst recht durcheinander.

    Aber wie gesagt, ich poste mein Spiel + Code einfach später mal herein, und dann könnt ihr nochmal darüber urteilen und mir vllt sogar so genauere Tipps geben.

    PS.: Deinen Pseudocode versteh ich gar nicht xD Könntest du ihn vllt ein bischen eindeutschen, ich bin erstn halbes jahr dabei ( und das nur über schule gelernt) ^^



  • Ich zeig dir ma was ich meine. Du hast ja wies aussieht schon einen Random-Nummer-Generator implementiert über rand(). rand() ist eine Funktion der Standardbibliothek. Du hast jetzt also viele Stellen in deinem Code in dem du rand() aufrufst.

    Besser wäre es diese Funktion zu wrappen. Überall wo du nun eine Zufallszahl brauchst rufst du den Wrapper auf. Alles was jetzt kommt ist Psedocode. Hab gerad kein Compiler in der Nähe. Warum das gut ist, sage ich weiter unten.

    random.c

    PRIVAT:
    begin random_getrandom(min, max)
        return rand() % (max - min + 1) + min
    end
    
    PUBLIC:
    void random_init(void)
        initialisiere den seed
    end
    
    void random_shutdown(void)
        // hier brauchen wir noch nix
    end
    
    int random_get_w6
        return random_getrandom(1, 6)
    end
    
    int random_get_w12
        return random_getrandom(1, 12)
    end
    
    int random_get_w24
        return random_getrandom(1, 24)
    end
    

    random.h

    void random_init(void);
    void random_shutdown(void);
    int random_get_w6(void);
    int random_get_w12(void);
    int random_get_w24(void);
    
    1. Hast du das so designt so wirst du keine Probleme haben, wenn du später einen besseren Generator implementieren möchtest. Du änderst nur random_getrandom und es läuft.
    2. du kannst die Dateeien einfach nehmen und in einer anderen Umgebung testen.
    3. Stellst du später fest, daß dein Kampfsystem nicht so funktioniert, wie du es willst und hast den Verdacht, daß die Zufallswerte imbalanced sind, dann kannste einfach bei random_getrandom debug ausgaben reinhauen und so den Ablauf kontrollieren. Eine debugausgabe gegenüber eventuell mehreren in deinem Kampfmodul.
    4. random_get_w6 ist einfacher zu verstehen als rand() % 5 + 1.
    5. im Notfall ist es einfacher random_get_w6 zu ersetzen als 100 mal "rand() % 5 + 1"

    Das ist natürlich nur ein sehr einfaches Beispiel. Und fast eine selbstverständlichkeit so etwas zu implementieren. Viel wichtiger ist es die anderen Bereiche auf diese Art und Weise zu implementieren. So sollte nirgendwo in deinem Spiel ein einzelnes "malloc" stehen. Gerade bei ressourcenfressenden Spielen sind hunderte von malloc aufrufen und free aufrufen fürn Arsch. Hier gilt es ebenfalls einen Wrapper zu schreiben. Den kann man noch mit zusätzlicher Funktionalität ausstatten. So kann der dann checken, ob eventuell ein free ausgeführt wird, daß nicht ausgeführt werden darf etc.

    Außerdem wrüde ich dir auch eine Debug-print implementation ans Herz legen.
    debug.c

    PRIVAT:
    void print(str)
        switch(mode):
            file: schreibe in file
                  break
            console: schreibe in console
                     break
            stdout: schreibe nach stdout 
                    break
    end
    
    PUBLIC:
    void debug_init(mode)
        switch(mode):
            file: öffne file
                  break
            console: allocconsole()
                     break
            stdout: break
    end
    
    void debug_shutdown:
        switch(mode):
            file: schließe file
                  break
            console: deallocconsole()
                     break
            stdout: break
    end
    
    void debug_print(level, str)
        switch(level):
            case warning: print str
                          break
            case error: print str
                        shutdown everything
                        exit
                        break
            case exception: print str
                            shutdown everything                        
                            exit
                            break
            case information: print str
                              break
    end
    

    debug.h

    void debug_init(mode);
    void debug_shutdown(void);
    void debug_print(level, str);
    

    Das wäre Modulares Design. Wrappen von Standardfunktionen und erweitertes Error-Checking kannst du somit zentral an den Core-DAteien durchführen. Und mußt nicht jedesmal in deinem Code MAnuell checken.

    Ein wichtiger Aspekt ist sicherlich auch das Testing, das viel einfacher ist, wenn du die einzelnen Source Dataien einfach "rausziehen" kannst und sie trotzdem unabhängig von Kontext noch funktionieren.



  • Wat denn nu?
    Quellcode mit

    PRIVAT
    ...
    PUBLIC
    ...
    

    und Endung .c 😕

    MfG f.-th.



  • BraCay1 schrieb:

    Nun stelle ich dir mal die Frage, du hast rund 200 Funktionen, und fast jede funktion ist mit 3 - 5 weiteren Funktionen verknüpft, meinst du ich solle da etwa alle Variablen über die Funktionen übergeben? oO
    Ich meine das wäre viel zu aufwendig, und dann kommt man erst recht durcheinander.

    NDEBUG meint das genau so und er hat recht. Wenn Du wirklich weiterschreibst, wirst Du entweder entnervt aufgeben oder ihm die Füße küssen - Mach das zuerst! Was wirklich global bleiben muß, packst Du in Structs und verwaltest die zentral, dann hast Du auch den Zugriff darauf im Griff. Was lokal bleiben kann, solltest Du lokal halten.

    BraCay1 schrieb:

    PS.: Deinen Pseudocode versteh ich gar nicht xD Könntest du ihn vllt ein bischen eindeutschen, ich bin erstn halbes jahr dabei ( und das nur über schule gelernt) ^^

    Vereinfacht sagt er, daß drei Blöcke in die Main gehören, unterteilt in die Dinge, die man als Module abpacken kann. Jedes Modul hat einen Initialisierungsaufruf und einen Beendigungsaufruf. In der Main werden alle Module initialisiert, dann wird die Hauptspielschleife aufgerufen und danach kriegen die Module ihren geregelten Kopfschuß.
    😉



  • Hier mal mein Vormittag 🙂 ich hab einen einfachen Input-Wrapper geschrieben. Er funktioniert auch schon. Er nutzt leider getch() und so Zeugs. Man könnte ihn auch umschreiben, so daß er DirectInput verwendet, aber wir wollen es ja nicht allzu kompliziert machen. Was macht er? Im Moment kann er mit allen Funktionstasten umgehen. Er kennt Enter, Tab, Space und so. Er ist sehr modular, aber noch zu wenig funtionell. Ich werde ihn, wenn ich wieder Zeit hab, dahingehend modifizieren, daß er über einen Event-Handler arbeitet. D.h. jedes Modul daß, gerne Zugriff auf die Tastatur haben möchte, muß sich per input_add_listener bei dem Input-Wrapper registrieren. Aber es soll ja erstmal ein einfaches How-To sein. Wie gesagt es wird dann etwas komplizierter wenn man das ganze Event-Driven machen möchte.

    Der Code ist mangels Zeit wenig kommentiert. Ich ändere das beim nächsten Mal.

    Hier die definitions.h: hier werden die Standard-Header eingefügt und systemweite Typendefinitionen vorgenommen. Jedes Modul bindet diesen Header ein!
    http://pastie.org/383997

    Hier die input.h: hier werden input-spezifische Definitionen vorgenommen, die nach außen hin sichtbar sein sollen. So sind z.B. alle Tastencodes hier hinterlegt. Wie gesagt sind noch nicht alle Tasten integriert! Außerdem alle Funktionenprototypen von den Funktionen, die nötig sind um mit dem Modul arbeiten zu können.
    http://pastie.org/384005

    Hier die input.cpp: hier die Implementation der Funktionen. Außerdem eine Datenstruktur, die intern von input.cpp verwendet wird und nicht nach außen hin sichtbar ist.
    http://pastie.org/384008

    Hier test.cpp: ein einfaches Testmodul, das die bisherige Funktionalität testet.
    http://pastie.org/384011

    Na dann, ich hoffe du kannst mein Vorgehen nachvollziehen. Diese ganzen Dinge sind notwendig, wenn ich beim nächsten Mal hoffentlich den Event-Driven Ansatz über Listener implementiert habe und die restlichen Tasten alle zugefügt habe.



  • was heißt wrappen?^^

    Vllt wäre das schonmal gut das zu erwähnen, damit ich alles hier halbwegs verstehe ^^

    NDEBUG haste vllt icq oder ähnliches?



  • Nein tut mir leid ICQ habe ich nicht. Wenn es um Codefragen geht, kannste die aber bestimmt auch hier stellen. Wenn du Code posten möchtest, sollteste du vllt wie ich nen pastebin benutzen. Dort kann man einfach Code online stellen und den dann verlinken.

    Ein Wrapper hat verschiedene Bedeutungen glaube ich. Ich meine damit ein Reihe von Funktionen die sich um andere Funktionen, meistens mehr Low-Level Funktionen, legt. Und diese vor unbefugtem Zugriff schützt, Error-Checking vornimmt, den Zugriff auf diese Low-Level Funktionen vereinfacht und die Funktionilatät erweitert.

    So ist mein Input--Wrapper ein Wrapper, da er sich komplett um den (Tastatur)Input kümmert und damit um die Eingabefunktionen legt. Nirgends mehr später wirst du direkt Sachen wie getch() oder getchar() aufrufen. Das läuft alles über den Wrapper.



  • So. Arbeit ist erledigt. Der Input Wrapper besitzt nun einen Dispatcher, d.h. jeder Tastendruck wird als Event an die Listener geschickt. Wer also zuhören will, muß sich vorher mit "input_register_listener(...)" anmelden und wird dann automatisch über KEYDOWN-Events unterrichtet. Ich hab um das zu demonstrieren, deine Menu-Struktur übernommen und über den Event Mechanismus implementiert. Vielleicht verstehst du noch nicht ganz den Vorteil. Der wird aber klar, wenn du mehr Menus hinzufügen möchtest, da du dann nur Copy'n'Pasten mußt. Oder wenn du z.B. auf eine grafische Oberfläche umsteigen möchtest. Generell ist diese Struktur sehr, sehr gut. Ich würde mittlerweile sogar den Event-Dispatcher von dem Input Core trennen und allgemeiner machen, so daß die gesamte Kommunikation innerhalb des Systems über den Dispatcher läuft. Im Moment arbeitet der Dispatcher ja nur mit INPUT_EVENTS. So ist es nicht möglich z.B. ein SYSTEM_SHUTDOWN_EVENT zu dispatchen, was aber nötig wäre, wenn du aus deinen Menüs heraus das System herunterfahren möchtest (auf sichere Art und Weise). Die zweite Alternative wäre den globalen quitlistener (der hört ausschließlich auf ESC und fährt dann das System runter) per Event-Injection von ESC zu verständigen.

    Ich werd dir jetzt nicht mehr weiter Sachen vorcoden, sondern jetzt biste wieder auf dich gestellt. Wenn du Sachen nicht verstehst, kannste gerne fragen, und ich hoffe ich konnte dir modulares Software-Design ein wenig näher bringen. Bei Spielen ist sowas ein Muß. Aber es hat mir auch großen Spaß gemacht, meine Ideen umzusetzen, was du sicherlich an den Kommentaren im Quellcode sehen wirst. Das beste wäre du versuchst es zu verstehen. Dafür solltest du die Funktionen eine nach der anderen nehmen und grünldlich nachvollziehen. Ansonsten wäre vileicht ein Buch zu Spiele-Programmierung nicht verkehrt.

    Hier die Source-Codes:
    input.h
    http://pastie.org/385015
    input.cpp
    http://pastie.org/385019
    menu.h
    http://pastie.org/385022
    menu.cpp
    http://pastie.org/385024
    definitions.h
    http://pastie.org/385026
    test.cpp
    http://pastie.org/385027

    Viel Spaß damit!

    EDIT: das programm kannste jederzeit mit ESC beenden!



  • Bracay1: folgendene Themen wären angebracht allerdings für einen Anfänger nicht gerade bequem..(um NDEBUGs Code zu verstehen)

    Zeiger auf Funktionen
    Verkettete Listen (engl. Linked lists)
    typedefs, enums, und natürlich der ganze Zeiger stuff..

    ein schönes Buch dazu wäre

    http://knking.com/books/c2/index.html

    aber nicht vergessen die second edition!! (C99)

    🙂


Anmelden zum Antworten