C++ Spieleprogrammieren
-
ich kann dir ein tetris anbieten, musst es also nur noch verstehen und dann anpassen (aus source code lernt man am besten, eh? :D), compiliert mit visual studio.
// Copyright (c) 2008, rapso // All rights reserved. // // BSD license ( http://en.wikipedia.org/wiki/BSD_license ) // // http://www.c-plusplus.net/forum/viewforum-var-f-is-7.html /////////////////////////////////////////////////////////////////////////////// #include <assert.h> #include <stdio.h> #include <conio.h> #include <windows.h> #include <intrin.h> #pragma intrinsic(_rotl16) const unsigned int SIZE_X = 10+2; //orginal width + border const unsigned int SIZE_Y = 20+1; //orginal height + border const unsigned int GAME_SPEED = 250; //in ms const unsigned int ROW_SCORE = 10; //random number const unsigned int ROW_BORDER = 1|(1<<(SIZE_X-1)); //left and right border const unsigned int ROW_FILLED = (1<<SIZE_X)-1; //1<<8 == 0x100; //0x100-1 == 0xff; typedef unsigned short tdTetrad; enum EState { ES_START, ES_RESETGAME, ES_GAME, ES_LOSE, ES_END, ES_QUIT }; enum ETetrad { ET_INVALIDE = 0, ET_I = 0x0F00, ET_J = 0x0470, ET_L = 0x02e0, ET_O = 0x0660, ET_S = 0x06C0, ET_T = 0x04E0, ET_Z = 0x0C60, ET_COUNT = 7 }; unsigned int g_Score; tdTetrad g_Tetrad; unsigned int g_PosX; unsigned int g_PosY; unsigned short g_Field[SIZE_Y+3]; //too lazy to bound check in Valid(...) unsigned int g_LastPressedKey; void GotoPixel(unsigned int x,unsigned int y) { const HANDLE Out = GetStdHandle(STD_OUTPUT_HANDLE); const COORD Pos = {x,y}; SetConsoleCursorPosition(Out,Pos); } EState ProcessReset(const bool LogicStep) { GotoPixel(0,0); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");//clear screen :P printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); for(unsigned int a=0;a<SIZE_Y-1;a++) g_Field[a] = ROW_BORDER; g_Field[SIZE_Y-1] = ~0;//floor g_Score = 0; g_Tetrad = ET_INVALIDE; g_LastPressedKey = 0; return ES_GAME; } EState ProcessStart(const bool LogicStep) { printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");//clear screen :P GotoPixel(20,20); printf("TetriXTM"); GotoPixel(10,25); printf("Press SPACE to Start or ESCAPE to quit"); if(GetAsyncKeyState(VK_SPACE)) return ES_RESETGAME; return GetAsyncKeyState(VK_ESCAPE)?ES_END:ES_START; } bool Valid(const unsigned int PosX,const unsigned int PosY,tdTetrad Tetrad) { const unsigned int Mask = (_rotl16((Tetrad&0xf000)>>12,PosX)&g_Field[PosY+0])| (_rotl16((Tetrad&0x0f00)>> 8,PosX)&g_Field[PosY+1])| (_rotl16((Tetrad&0x00f0)>> 4,PosX)&g_Field[PosY+2])| (_rotl16((Tetrad&0x000f)>> 0,PosX)&g_Field[PosY+3]); return Mask==0; } tdTetrad Rotate(const tdTetrad Tetrad) { tdTetrad Ret = 0; for(unsigned int y=0;y<4;y++) for(unsigned int x=0;x<4;x++) Ret |= (Tetrad>>y+x*4&1)<<3-x+y*4; return Ret; } void Merge(unsigned short* pField,const unsigned int PosX,const unsigned int PosY,const tdTetrad Tetrad) { for(unsigned int y=0;y<SIZE_Y;y++) pField[y] = g_Field[y]; pField[PosY+0] = g_Field[PosY+0] | (((Tetrad&0xf000)>>12)<<PosX); if(PosY+1<SIZE_Y) pField[PosY+1] = g_Field[PosY+1] | (((Tetrad&0x0f00)>> 8)<<PosX); if(PosY+2<SIZE_Y) pField[PosY+2] = g_Field[PosY+2] | (((Tetrad&0x00f0)>> 4)<<PosX); if(PosY+3<SIZE_Y) pField[PosY+3] = g_Field[PosY+3] | (((Tetrad&0x000f)>> 0)<<PosX); } void CheckScore() { //start one row above the floor //topmost row is not taken into account for(unsigned int y=SIZE_Y-2;y>0;y--) { if(g_Field[y]==ROW_FILLED) { g_Score += ROW_SCORE; //collaps stack for(unsigned int y2=y;y2>0;y2--) g_Field[y2] = g_Field[y2-1]; g_Field[0] = ROW_BORDER; y++; //push by one to compensate the loop y-- } } } EState ProcessGame(const bool LogicStep) { if(GetAsyncKeyState(VK_ESCAPE)) return ES_LOSE; //spawn logic if(g_Tetrad==ET_INVALIDE) { switch(rand()%ET_COUNT) { case 0:g_Tetrad = ET_I;break; case 1:g_Tetrad = ET_J;break; case 2:g_Tetrad = ET_L;break; case 3:g_Tetrad = ET_O;break; case 4:g_Tetrad = ET_S;break; case 5:g_Tetrad = ET_T;break; case 6:g_Tetrad = ET_Z;break; } g_PosX = SIZE_X/2-2; //half is center of screen, sub 2 for half Tetrad width g_PosY = 0; if(!Valid(g_PosX,g_PosY,g_Tetrad)) return ES_LOSE; } //logic time step if(LogicStep) { if(Valid(g_PosX,g_PosY+1,g_Tetrad)) g_PosY++; else { Merge(g_Field,g_PosX,g_PosY,g_Tetrad); CheckScore(); g_Tetrad=ET_INVALIDE; } } else { //avoid same action while key still pressed if(!g_LastPressedKey || !GetAsyncKeyState(g_LastPressedKey)) { g_LastPressedKey = 0; if(GetAsyncKeyState(VK_LEFT) && Valid(g_PosX-1,g_PosY,g_Tetrad)) { g_PosX--; g_LastPressedKey = VK_LEFT; } if(GetAsyncKeyState(VK_RIGHT) && Valid(g_PosX+1,g_PosY,g_Tetrad)) { g_PosX++; g_LastPressedKey = VK_RIGHT; } if(GetAsyncKeyState(VK_UP) && Valid(g_PosX,g_PosY,Rotate(g_Tetrad))) { g_Tetrad = Rotate(g_Tetrad); g_LastPressedKey = VK_UP; } if(GetAsyncKeyState(VK_DOWN)) { while(Valid(g_PosX,g_PosY+1,g_Tetrad)) g_PosY++; CheckScore(); g_LastPressedKey = VK_DOWN; } } } //display { unsigned short DrawField[SIZE_Y]; Merge(DrawField,g_PosX,g_PosY,g_Tetrad); for(unsigned int y=0;y<SIZE_Y;y++) { GotoPixel(20,y); for(unsigned int x=0;x<SIZE_X;x++) printf(DrawField[y]&(1<<x)?"ÌÌ":" "); } } GotoPixel(0,2); printf("Score is:\n%8d",g_Score); return ES_GAME; } EState ProcessLose(const bool LogicStep) { printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");//clear screen :P GotoPixel(20,20); printf("Game Over"); GotoPixel(20,21); printf("your highscore was:",g_Score); GotoPixel(10,40); printf("Press SPACE to Start or ESCAPE to quit"); if(GetAsyncKeyState(VK_SPACE)) return ES_RESETGAME; return GetAsyncKeyState(VK_ESCAPE)?ES_END:ES_LOSE; } EState ProcessEnd(const bool LogicStep) { printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");//clear screen :P GotoPixel(20,20); printf("Thx for playing TetriXTM"); GotoPixel(15,40); Sleep(2500); return ES_QUIT; } int main(int argc,char* argv[]) { const unsigned int StartTime = GetTickCount(); unsigned int LastTick = 0; EState State = ES_START; while(State!=ES_QUIT) { const unsigned int CurrentTick = (GetTickCount()-StartTime)/GAME_SPEED; const bool LogicTick = LastTick!=CurrentTick; LastTick = CurrentTick; switch(State) { case ES_START: State = ProcessStart(LogicTick); break; case ES_RESETGAME: State = ProcessReset(LogicTick); break; case ES_GAME: State = ProcessGame(LogicTick); break; case ES_LOSE: State = ProcessLose(LogicTick); break; case ES_END: State = ProcessEnd(LogicTick); break; } Sleep(20);//to reduce flickering } return 0; }
-
ok thx ich werde mal den code durch büffeln...und ging sogar xD
-
numberone123 schrieb:
ok thx ich werde mal den code durch büffeln...und ging sogar xD
Damit du nicht die ganzen Grafikfunktionen selber schreiben musst bzw. um nicht solche ClearScreen-Übelkeiten zu übernehmen kann dir evtl. auch die Improved Console (http://ic.c-plusplus.net) helfen. Sie bietet einige Konsolenfunktionen an um etwas Farbe ins Spiel zu bringen. Bleibt aber trotzdem alles auf Zeichenebene.
MfG SideWinder
-
ja danke...
ja das clearscreen hab ich besser gelöst xD
system("CLS");
:p
edit:
hab noch ne frage wie kann ich denn die steuerung machen hab jetzt schon einen kopf der sich durch tasten druck sich verschiebt(naja tut er nicht ,cout xD) aber wie soll ich das mit dem feld und so machen und ich bräuchte noch hilfe für die steuerung hab da jetzt ne sehr besch***** steuerung eingebaut und ich weiß nicht weitervoid Bewegung(char b){ do{ if(!g_LastPressedKey || !GetAsyncKeyState(g_LastPressedKey)){ g_LastPressedKey = 0; if (GetAsyncKeyState(VK_RIGHT)){ for(i = 0;i < 1;i++){ cout << b; } g_LastPressedKey = VK_RIGHT; } else if(GetAsyncKeyState(VK_DOWN)){ for(i = 0; i < 1; i++){ cout << '\n' << b; } g_LastPressedKey = VK_DOWN; } } }while( s == 1); }
bei dem feld könnte ich auch hilfe gebrauchen...
Mfg,
numberone123
-
fang erst garnicht so an, logic und rendering sind zwei verschiedene stufen. du hast ja schon gut angefangen es in einzelne funktionen zu trennen, da wo "void Bewegung(char b)" drueber steht, sollte eine bewegung gemacht werden, kein zeichnen. sonst wirst du am ende durch den ganzen code steigen muessen falls du mal irgendwas am layout deines spieles aendern willst.
erst logic dann rendering, nicht beides zusammen!
also, fuer den anfang
typedef unsigned char tdPixel; tdPixel g_PixelBuffer[SIZE_X*SIZE_Y] void ClearBuffer() { memset(g_PixelBuffer,32,sizeof(g_PixelBuffer)); } void SetPixel(unsigned int x,unsigned int y,tdPixel c) { if(x<SIZE_X && y<SIZE_Y) g_PixelBuffer[x+y*SIZE_X]=c; } void Display() { //display { for(unsigned int y=0;y<SIZE_Y;y++) { GotoPixel(0,y); for(unsigned int x=0;x<SIZE_X;x++) printf("%c",DrawField[x+y*SIZE_X]); } }
du zeichnest ab jetzt in nichts anderes ausser in den pixelbuffer und wenn du fertig bist, gibst du das mittels Display() aus. (SIZE_X und SIZE_Y sollten entsprechend deines verlangen angepasst werden).
der logicloop sollte also in etwas so sein
while(..) { bearbeiteInput(); //z.b. if left then x--;etc logic(); //z.b. gegner bewegen oder bei tetris den block fallen lassen Zeichne(); //hier solltest du alles an spielinformation ins g_pixelbuffer stecken Display(); //am ende zeigst du das nur an, eventuel noch ein paar strings wie "score" }
ja, mein tetris war nur quick und dirty und ist nicht perfekt zum lernen, aber mit einer noch abstrakteren trennung von logik und rendering waere es fuer den anfang vielleicht noch konfuser.
wenn du das so sauber trennst, wird es dich spaeter vielleicht 10zeilen code kosten um das auf graphik umzubauen.
-
numberone123 schrieb:
ja danke...
ja das clearscreen hab ich besser gelöst xD
system("CLS");
:p
-
-
wtf ? xD
@rapso ja ok danke ich werde es mal so versuchen...
bin halt noch anfänger und nutzte dies eig zum noch lernen
kennt wer ne idee was ich so als anfänger schreiben könnte weil ich glaub das ist mir noch zu schwer hab schon nen taschenrechner geproggt aber dies geling zu leicht und suche eine kleine herrausforderung nicht so schwer wie snake aber auch nicht zu leicht wie eiene tacshcenrechenr...Mfg,
numberone123
-
Hm - ich weiss ja nicht was dein Taschenrechner so konnte, aber ich vermute mal, dass nur Einfache Berechnungen wie 5 * 3 möglich waren.
Du könntest den Taschenrechner z.B. erweitern, sodass er auch komplexere Ausdrücke auswerten kann, sowas in der Art wie (5 * 3) + 2 / ( -2 + (3 * 4) )Hm - sonst könntest du sowas wie Tic Tac Toe oder Schiffe versenken oder ähnliches mal probieren.
-
rapso schrieb:
#pragma intrinsic(_rotl16) enum ETetrad { ET_INVALIDE = 0, ET_I = 0x0F00, ET_J = 0x0470, ET_L = 0x02e0, ET_O = 0x0660, ET_S = 0x06C0, ET_T = 0x04E0, ET_Z = 0x0C60, ET_COUNT = 7 };
Weiss jemand was die Hex Zahlen und das #pragma intrinsic(_rotl16) bedeuten ?
Gruss Stewie G
-
-
Google2 schrieb:
Google halt
ja das #pragma intrinsic(_rotl16) weis ich jetzt, aber wieso werden Hex Zahlen verwendet man könnte man auch das so schreiben ?:
enum ETetrad { ET_INVALIDE = 0, ET_I = 3840, ET_J = 1136, ... }
Was bedeuten die Variablen ?
Gruss Google5 :p