C++ Spieleprogrammieren



  • Hallo,
    also wollte ein sehr simples Spiel proggen(z. B. Snake) hab Anfängerkenntnisse(schleifen,funktionen,nen bisschen oop,usw.)

    wäre nett wenn z.B. einer ein Tut zeigt oder mir eine Erklärung schreibt wie man vorgeht also kein code oder so, wär auch noch schön wenn es auf Snake bezogen ist.



  • Naja, ein Spiel zu proggen kann relativ(!) simpel bis schwierig sein. Um Herr von Briest zu zitieren: "Das ist ein weites Feld".

    Um dir genauer helfen zu können, müsste ich wissen, was du dir da so vorgestellt hast. Snake könnte man durchaus auch als Konsolenanwendung realisieren. Wahrscheinlich willst du stattdessen lieber eine "Normale" Windowsanwendung mit grafischer Benutzeroberfläche. Man könnte mithilfe der Winapi ein Fenster erstellen und dann direkt darauf Zeichnen. (Eventuell gibt es da auch Bibliotheken, mit denen das dann einfacher geht.)

    Um Spiele mit OpenGl oder DirectX zu Programmieren, baraucht man noch einiges mehr als nur die Grundlagen.

    Hier ist ein Winapi-Tutorial. Auf seite 8 wird erklärt, wie man Objekte zeichnen kann.

    http://www.win-api.de/tutorials.php

    MfG, Jochen



  • Nein,Nein genau das wollte ich,eine Konsolenanwendung, ganz simpel fürs erste...
    keine Api die ist viel zu schwer für den Anfang.

    Denn soweit bin ich noch net ganz, um was mit winapi zumachen...

    Mfg,
    numberone123


  • Mod

    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 weiter

    void 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


  • Mod

    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 🙄

    http://msdn.microsoft.com/de-de/library/t5e2f3sc.aspx

    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


Log in to reply