Zufall/Zeitintervall + andere Fragen
-
supertux schrieb:
BraCay1 schrieb:
Guten Tag,
ich bin gerade dabei, mein eigenes kleines Rollenspiel zu proggen. Ich bin eigentlich nochn Anfänger in c, aber es reicht für mein Game xDdann lern erstmal die Sprache und mach dir mit der Programmierung vertraut, bevor du dich an (weit) fortgeschrittene Sachen ranmachst
Wenn du mir nicht helfen kannst, wieso schreibst du dann irgendeinen Müll hierein?
Ich habe gesagt, das ich ein Anfänger in C bin, aber das es reicht für mein Spiel!Wenn du mir nicht helfen willst dann lass es...
Nun zur dir ~fricky
mein Prog hat zurzeit 7000 Zeilen, werden noch einige Tausende dazu kommen, deswegen poste ich nurn kleinen Abschnitt
void neu_mensch_krieger() { srand(time(0)); attack = ((rand() % 5) + 9); defense = ((rand() % 5) + 7); geschick = ((rand() % 5) + 4); lifepoints = ((rand() % 50) + 100); manapoints = ((rand() % 10) + 40); intelligence = ((rand() % 5) + 2); gelifepoints = lifepoints; gemanapoints = manapoints; printf("+----------------------------------------------+\n"); printf("| RASSE: Mensch KLASSE: Krieger |\n"); printf("| +--------------------+------+------+------+ |\n"); printf("| |ATTRIBUTE |R & K |DICE | ALL | |\n"); printf("| +--------------------+------+------+------+ |\n"); printf("| |Lebenspunkte | 100 | %3.d | %3.d | |\n", lifepoints - 100, lifepoints); printf("| +--------------------+------+------+------+ |\n"); printf("| |Manapunkte | 40 | %3.d | %3.d | |\n", manapoints - 40, manapoints); printf("| +--------------------+------+------+------+ |\n"); printf("| |Staerke | 9 | %3.d | %3.d | |\n", attack - 9, attack); printf("| +--------------------+------+------+------+ |\n"); printf("| |Verteidigung | 7 | %3.d | %3.d | |\n", defense - 7, defense); printf("| +--------------------+------+------+------+ |\n"); printf("| |Geschicklichkeit | 4 | %3.d | %3.d | |\n", geschick - 4, geschick); printf("| +--------------------+------+------+------+ |\n"); printf("| |Intelligenz | 2 | %3.d | %3.d | |\n", intelligence - 2, intelligence); printf("| +--------------------+------+------+------+ |\n"); printf("| |\n"); printf("| +-----------+-------------+ |\n"); printf("| | Okay! [1] | Beenden [0] | |\n"); printf("| +-----------+-------------+ |\n"); printf("| (Anderweitige Eingabe = Titelbildschirm) |\n"); printf("+----------------------------------------------+\n"); printf("Auswahl: "); scanf("%d", &main_aktion); if(main_aktion == 0){ // Titelbildschirm main(); }else if(main_aktion == 1){ // Spiel fängt dann erst richtig an einleitung_1(); }else main(); }
jo, zu den Variablen, ich habe dafür globale genommen, weil richtig viele Funktionen habe (ca. 200), und ich sonst den Überblick verliere
Wie gesagt, er zeigt Zufallszahlen an, genau wie ich es möchte, doch anstatt ner 0 zeigt er mir nichts an. Sonst passt alles
-
BraCay1 schrieb:
Wie gesagt, er zeigt Zufallszahlen an, genau wie ich es möchte, doch anstatt ner 0 zeigt er mir nichts an. Sonst passt alles
vielleichts liegts an diesem %3.d? schon mal was anderes probiert, %d z.b.? ääh, noch was: 'main' selber aufzurufen ist auch nicht so'ne gute idee.
-
aber mein ganzes prog ist darauf aufgebaut, das es am ende zu anderen Funktionen springt, und die main funktion ist dazu da, um bsw. ein neues spiel auszuwählen, ein spiel zuladen, optionen oder es zu beenden
hier die main
int main() { do{ printf("+----------------------------------------------+\n"); printf("| |\n"); printf("| 8888888888 888 888 |\n"); printf("| 888 888 888 |\n"); printf("| 888 888 888 |\n"); printf("| 8888888 8888b. 88888b. 888 .d88b. |\n"); printf("| 888 `88b 888 `88b 888 d8P Y8b |\n"); printf("| 888 888 888 888 88P 888 Y8b. v88 |\n"); printf("| 888 888 888 888 ,88P 888 Y8b. |\n"); printf("| 888 `Y888888 88888P' 888 `Y8888 |\n"); printf("| ____________________________________________ |\n"); printf("| |\n"); printf("| CHOICE YOUR DESTINY |\n"); printf("| |\n"); printf("+----------------------------------------------+\n"); printf("| +--------------------------+ |\n"); printf("| | Neues Spiel [3] | |\n"); printf("| +--------------------------+ |\n"); printf("| | Spiel laden [2] | |\n"); printf("| +--------------------------+ |\n"); printf("| | Option [1] | |\n"); printf("| +--------------------------+ |\n"); printf("| | Beenden [0] | |\n"); printf("| +--------------------------+ |\n"); printf("+----------------------------------------------+\n"); printf("Aktion: "); scanf("%d", &main_aktion); if(main_aktion == 3){ // Neues Spiel neues_spiel(); }else if(main_aktion == 2){ // Spiel laden }else if(main_aktion == 1){ // Option }else main_aktion = 0; }while(main_aktion != 0); }
Zu dem %3.d, ich brauche umbedingt, diese 3 vorkommestellen, weil sonst meine ganze tabelle auseinander bricht, bzw. unschön aussieht ...
glaubst du es liegt wirklich daran?
nebenbei habe ich noch ne andere Frage:
Ich hab aj Ansi-C in der Schule gelernt, und da hatten wir Linux-Rechner, dort gibt es die Befehle,
system("Clear"); && system("Reset");
Bei meinen Progs jedoch erkennt er die garnicht, sind diese Befehle nur für Linux gedacht? Wenn ja, wie lauten sie für Windows? Wenn nein, liegts an meinem Compiler? Hab den Bloodshed Dev-C++
Noch ne Frage zu den Systemen, wäre dies möglich:
if(Windowscompiler) Clear bzw. Resetbefehl für Windows; if else(Linuxcompiler) Clear bzw. Resetbefehl für Linux;
Is jetzt nurn Pseudocode, kanns jetzt auch schwer erklären hoffe ihr wisst was ich meine ^^
-
Zu clearscreen:
http://www.hs-augsburg.de/~sandman/c_von_a_bis_z/c_013_009.htm
ziemlich unten.MfG f.-th.
-
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);
- 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.
- du kannst die Dateeien einfach nehmen und in einer anderen Umgebung testen.
- 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.
- random_get_w6 ist einfacher zu verstehen als rand() % 5 + 1.
- 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.cPRIVAT: 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 mitPRIVAT ... 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/383997Hier 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/384005Hier 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/384008Hier test.cpp: ein einfaches Testmodul, das die bisherige Funktionalität testet.
http://pastie.org/384011Na 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/385027Viel 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)