ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SFML /3. 테트리스 게임오버 구현
    활동 기록 😵‍💫/SFML 테트리스 2023. 2. 19. 19:09

    게임 효과 구현 

    1.(기록)level 측정 -> 줄이 한줄 지워질때마다 level += 1

    메인에 int score = 0; 선언해주고 

     //한줄 차면 지우기 //
                            int k = M - 1;
                            for (int i = M - 1; i > 0; i--) {
                                int count = 0;
                                for (int j = 0; j < N; ++j) {
                                    if (board[i][j]) count++;
                                    board[k][j] = board[i][j];
                                }
                                if (count < N) k--;
                                else {
                                    score++;
                                }
                            }
                            dx = 0; delay = 0.3; rotate = false;

    한줄이 지워지면 score ++; 해주도록 했다. 

    게임을 진행 하는 중에도 확인 할 수 있도록

     Text scoreCurrent;
        scoreCurrent.setFont(font);
        scoreCurrent.setFillColor(Color::Red);
        scoreCurrent.setStyle(Text::Regular);
        scoreCurrent.setCharacterSize(30);
        scoreCurrent.setPosition(250, 30);

    선언해주고 출력코드 다음과 window.display(); 전에 

      currentScore = ("%f", score);
                            stringstream s;
                            s << currentScore;
                            scoreCurrent.setString("Score : " + s.str());
                            window.draw(scoreCurrent);

    넣어주었다. 

    처음에는 score을 위에 생성했던 textprint 함수로 사용 하려 했는데 

    계속해서 바뀌는 변수이기때문에 그렇게는 안되는거 같았다.

    그래서 다른 게임들의 예시를 찾아본 결과 증가시킨 score 값을 또 다른 int변수인 

    currentScore에 넣어주고... 이를 문자열로 바꾼 다음에 출력하는 형식으로 구현해보았다.

    생각해보니 굳이 score을 currentscore에 안넣어도 되지 않나 싶지만? 아무튼

     

     

    2. 게임종료 ->

    1)테트리스 블록과 프레임의 맨 위에 선을 하나 두어서(검은색으로) 충돌하면 종료되도록 

    구현하고 싶었다. 

    프레임이랑은 닿는데 선이랑은 경계 충돌이 잘 안되었다 왜일까...

    deadline이라는 선의 이미지를 가지고 경계 충돌을 해보았다. 

    #include <SFML/Graphics.hpp>
    #include <cassert>
    #include <stdio.h>
    #include <iostream>
    #include <windows.h>
    #include <time.h>
    #include <string>
    #include <exception>
    #include <conio.h>
    #include <stdlib.h>
    #include <cstdlib>
    #include <algorithm>
    #include <SFML/Config.hpp>
    #include <SFML/System/Export.hpp>
    #include <SFML/Audio.hpp>
    #include <sstream>
    #include <vector>
    #include <cmath>
    
    
    using namespace std;
    using namespace sf;
    bool automaticPlaying = true;
    
    //테트리스 메인 보드
    const int M = 20;
    const int N = 10;
    
    int board[M][N] = { 0 }; //새 벽돌을 만들어야 하는 상태
    
    struct  Point
    {
        int x, y;  //좌표
    };
    Point a[4];
    Point c[4];  //점이 4개 들어있는 배열 생성
    
    
    int tiles[7][4] = {
    1,3,5,7, //I
    2,4,5,7, //Z
    3,5,4,6, //S
    3,5,4,7, //T
    2,3,5,7, //L
    3,5,7,6, //J
    2,3,4,5, //O
    
    };
    
    bool check()
    {
        for (int i = 0; i < 4; ++i)
        {
            if (a[i].x < 0 || a[i].x >= N || a[i].y >= M) return false;
            else if (board[a[i].y][a[i].x]) return false;
        }
        return true;
    }
    
    const float windowWidth = 500;
    const float windowHeight = 500;
    
    
    
    
    
    //text 함수
    int textprint(Text& text, Font& font, int size, float x, float y, const Color& color, const Color& outColor, string p)
    
    {
        text.setFont(font);
        text.setCharacterSize(size);
        text.setPosition(x, y);
        text.setFillColor(color);
        text.setOutlineColor(outColor);
        text.setOutlineThickness(1.f);
        text.setString(p);
        return 0;
    
    
    }
    
    void gameover() {
        RenderWindow window(VideoMode(windowWidth, windowHeight), "gameover");
        window.setFramerateLimit(60);
    
        while (window.isOpen()) {
            window.clear(Color::White);
            window.display();
        }
    }
    
    int main() {
        srand(time(0));
    
    
    
        int score = 0;
        Text text;
        Text text2;
        Uint8 r = 0, g = 0, b = 0;
        string comment = "tetris Game Start";
        string hi = "PRESS THIS BUTTON TO START !"; //마우스를 누르면 게임시작
        int x = 0, y = 0;
    
        RenderWindow window(VideoMode(windowWidth, windowHeight), "tetris");
    
    
        window.setFramerateLimit(60);
    
        //시작화면 text
        Font font;
        if (!font.loadFromFile("C:/Users/User/Downloads/DungGeunMo TTF/DungGeunMo.ttf"))
            throw exception("font error");
        textprint(text, font, 100, 700, 30, Color::Green, Color::Black, comment);
        textprint(text2, font, 30, 50, 300, Color::Black, Color::Black, hi);
    
        Text scoreCurrent;
        scoreCurrent.setFont(font);
        scoreCurrent.setFillColor(Color::Red);
        scoreCurrent.setStyle(Text::Regular);
        scoreCurrent.setCharacterSize(30);
        scoreCurrent.setPosition(250, 30);
    
    
    
        Texture button;
        button.loadFromFile("C:/Users/User/OneDrive/바탕 화면/NicePng_start-button-png_1108890.png");
        Sprite button2(button);
        button2.setPosition(220, 370);
        button2.setScale(0.09f, 0.09f);
        SoundBuffer buffer;
    
        if (!buffer.loadFromFile("C:/Users/User/OneDrive/바탕 화면/bgm.wav")) {
            cout << "sound error" << endl;
        }
    
        Sound sound;
        sound.setBuffer(buffer);
        sound.play();
        sound.setLoop(true);
        window.display();
    
        while (window.isOpen()) {
    
            text.move(-1, 0);
    
            window.clear(Color::White);
            window.draw(text);
            window.draw(button2);
            window.draw(text2);
            window.display();
            Event event;
    
            while (window.pollEvent(event)) {
    
    
                switch (event.type) {
    
                case Event::Closed:
                    window.close();
                    break;
    
                case Event::MouseButtonPressed:
                    if (event.mouseButton.button == Mouse::Left) {
                        window.create(VideoMode(windowWidth, windowHeight), "tetris game");
    
                        button2.move(1000, 0);
                        text2.move(1000, 0);
    
    
    
                        time_t start, end;
                        double result;
                        start = time(NULL);
                        int level = 0;
    
                        Texture t1;
                        t1.loadFromFile("C:/Users/User/OneDrive/사진/tile.png");
    
    
    
                        Sprite tileSprite(t1);
    
    
                        Texture t2;
                        t2.loadFromFile("C:/Users/User/OneDrive/사진/line2.png");
                        Sprite deadline(t2);
                        deadline.setPosition(0, 27);
                        //tileSprite.setTextureRect(IntRect(0, 0, 18, 18));  //사각텍스쳐
    
                        Texture t3;
                        t3.loadFromFile("C:/Users/User/OneDrive/사진/frame.png");
                        Sprite frame;
                        frame.setTexture(t3);
    
    
    
                        int dx = 0;
                        bool rotate = false;
                        int colorNum = 1;
                        float timer = 0;
                        float delay = 0.3;
                        int currentScore = 0;
                        Clock clock;
                        bool collide;
    
    
                        a[0].x = 0; a[0].y = 1;
                        a[1].x = 1; a[1].y = 1;
                        a[2].x = 1; a[2].y = 2;
                        a[3].x = 1; a[3].y = 3;
    
    
                        while (window.isOpen()) {
                            float time = clock.getElapsedTime().asSeconds();  //초 단위로 경과시간을 얻음
                            clock.restart(); //시계 재시작
                            timer += time;  //시간 누적
    
                            Event e;
                            while (window.pollEvent(e)) {
                                if (e.type == Event::Closed)
                                    window.close();
                                if (e.type == Event::KeyPressed)
                                    if (e.key.code == Keyboard::Up) rotate = true;
                                    else if (e.key.code == Keyboard::Left) dx = -1;
                                    else if (e.key.code == Keyboard::Right) dx = 1;    //원래 dx =0이니까 왼쪽 -1 오른쪽 +1
                            }
    
                            if (Keyboard::isKeyPressed(Keyboard::Down)) delay = 0.05;
    
    
    
                            //이동//
                            for (int i = 0; i < 4; ++i) {
                                c[i] = a[i];
                                a[i].x += dx;    //좌우로
                            }
                            if (!check()) for (int i = 0; i < 4; ++i) a[i] = c[i];
    
                            // Rotate //
                            if (rotate)
                            {
                                Point p = a[1]; // center of rotation
                                for (int i = 0; i < 4; ++i)
                                {
                                    int x = a[i].y - p.y;
                                    int y = a[i].x - p.x;
                                    a[i].x = p.x - x;
                                    a[i].y = p.y + y;
                                }
                                if (!check()) for (int i = 0; i < 4; ++i) a[i] = c[i];
                            }
                            //시간//
                            if (timer > delay) {
                                for (int i = 0; i < 4; ++i) {
                                    c[i] = a[i]; a[i].y += 1;   //아래로
                                }
                                if (!check()) {
                                    for (int i = 0; i < 4; ++i)
                                        board[c[i].y][c[i].x] = colorNum;
                                    colorNum = 1 + rand() % 7;
                                    int n = rand() % 7;
                                    for (int i = 0; i < 4; ++i) {
                                        a[i].x = tiles[n][i] % 2;
                                        a[i].y = tiles[n][i] / 2;
                                    }
                                }
                                timer = 0;
                            }
    
                            //한줄 차면 지우기 //
                            int k = M - 1;
                            for (int i = M - 1; i > 0; i--) {
                                int count = 0;
                                for (int j = 0; j < N; ++j) {
                                    if (board[i][j]) count++;
                                    board[k][j] = board[i][j];
                                }
                                if (count < N) k--;
                                else {
                                    score++;
                                }
                            }
                            dx = 0; delay = 0.3; rotate = false;
    
                            //그리기//
                            window.clear(Color::Black);
    
    
    
    
    
                            for (int i = 0; i < M; ++i) {
                                for (int j = 0; j < N; ++j) {
                                    if (board[i][j] == 0) continue;
                                    tileSprite.setTextureRect(IntRect(board[i][j] * 18, 0, 18, 18));
                                    tileSprite.setPosition(j * 18, i * 18);
                                    tileSprite.move(28, 31);
                                    window.draw(tileSprite);
                                }
                            }
    
                            //출력//
    
                            for (int i = 0; i < 4; ++i) {
                                tileSprite.setTextureRect(IntRect(colorNum * 18, 0, 18, 18));
                                tileSprite.setPosition(a[i].x * 18, a[i].y * 18);
                                tileSprite.move(28, 31);
                                window.draw(tileSprite);
    
                                if (tileSprite.getGlobalBounds().intersects(deadline.getGlobalBounds())) {
                                    collide = true;
                                    while (collide == true) {
                                        window.create(VideoMode(windowWidth, windowHeight), "gameover");
                                        window.setFramerateLimit(60);
                                        while (window.isOpen()) {
                                            window.clear(Color::White);
                                            window.display();
    
                                        }
    
                                    }
                                }
                            }
    
                            currentScore = ("%f", score);
                            stringstream s;
                            s << currentScore;
                            scoreCurrent.setString("Score : " + s.str());
                            window.draw(scoreCurrent);
                            window.draw(deadline);
                            window.draw(frame);
                            window.display();
    
                        }
                      
    
                    }
    
                }
    
            }
    
        }
        return 0;
    
    }

    중괄호 때문에 고생했다.. return 0;은 메인 마지막... 기본이잖아....

    자꾸만 새로운 window를 마지막에 두면.. 게임대신 새로운 화면이 먼저 뜨고,

    반복문 안에 넣으면 게임이 렉먹어서 

    아무튼 저렇게 해놓았는데.. 되는줄 알았건만 

    언제 충돌감지가 되는건지 ;; 자꾸만 꺼진다... 아무래도 블록 사이즈에 따라 

    내려올때 닿는거 같다.. 그런데 위치를 올려도 안된다 !!!!!! (그럼 어떡하라고..)

    그리고 또다른 문제.. 아무래도 반복문 안에 넣어놔서 그런지

    윈도우 창이 새로 시작 되어도 응답없음으로 되어버리는...

    계속해본 결과,..... 

    I모양 블록 때문이었다....

    deadline을 (0,23)으로 하면 블록은 내려오는데 충돌 감지가 안되고 

    그 밑으로 내리면 무조건 I블록이 나올때 충돌.... 뫼비우스 ㅠㅠ

    2) 블록의 위치를 이용해서 멈추어야 겠다 생각했다 

    나는 블록의 위치가 전과 같을때를 구현하고 싶었는데 더 좋은 아이디어를 얻어서 이렇게 했다.

         //시간//
                            if (timer > delay) {
                                for (int i = 0; i < 4; ++i) {
                                    c[i] = a[i]; a[i].y += 1;   //아래로
                                }
                                if (!check()) {
                                    if ((preabc == abc) && preabc != 0 && preabc == 1) { //이전움직임과 현재 움직임 동일(움직임이 없다고 볼때), 0으로 선언했으니까 시작과 동시에 꺼지는 것 방지, 움직임 같을때도 꺼지니까 이전움직임 1일때 
                                        cout << "gameover" << endl;
                                        window.close();
                                    }
                                    for (int i = 0; i < 4; ++i) {
                                        board[c[i].y][c[i].x] = colorNum;
                                    }
                                    cout << "preabc :" << preabc << endl;
                                    colorNum = 1 + rand() % 7;
                                    int n = rand() % 7;
                                    for (int i = 0; i < 4; ++i) {
                                        a[i].x = tiles[n][i] % 2;
                                        a[i].y = tiles[n][i] / 2;
                                    }
                                    cout << "abc: " << abc << endl; 
                                    preabc = abc; //abc를 이전의 것으로 저장 
                                    abc = 0; //abc는 다시 세야되니까 초기화
    
                                }
    
                                timer = 0;
                                cout << abc++ << endl; //abc++
                            }

    int abc =0; //블록이 바닥에 닿을때까지의 움직임 수 

    int preabc = 0; //이전블록 

    오류가 없이 깔끔해서 정말 .. 역시 관록은 다르다 느꼈다... !!

    그 뒤로 잘 작동하여서 

    window.close(); 다음에 새로운 창을 만들어 주었는데 

    계속하여 응답없음이 뜬것이 반복문안에 만들어 주어서 그런것 같다 !!

    또 이벤트 클로즈를 안해주면 그런것 같기도 하다?

    그래서 제일 마지막 return0; 위에 게임오버 윈도우를 만들어 줬다.

    window.create(VideoMode(windowWidth, windowHeight), "Game Over");
        Text gameover;
        String over = "GAMEOVER !!";
        textprint(gameover, font, 70, 60, 30, Color::Magenta, Color::White, over);
        
        
        scoreCurrent.move(-100, 100);
        scoreCurrent.setFillColor(Color::White);
        while (window.isOpen()) {
            Event e;
            while (window.pollEvent(e)) {
    
                if (e.type == Event::Closed) {
                    window.close();
                    break;
                }
                window.clear(Color::Black);
                window.draw(gameover);
                
                window.draw(scoreCurrent);
                window.display();
            }
        }

    정말.... 처음에는 화면하나 만드는것도 어려웠는데 

    코드도 안보고 바로바로 아 이건 move로 옮겨야지 색은 setFill로 바꿔야지 하면서 ...

    쪼금 뿌듯했다 !!!‪‧˚₊*̥(* ⁰̷̴͈꒳⁰̷̴͈ )‧˚₊*̥‬

    그리고 마지막까지 어려웠다기 보단 성가신..? 점은 위치랑 크기 맞추는거였다..

    눈대중으로 숫자 줄이고 키우면서 맞추는 것이 ... 시간이 꽤 걸린것 같다. 

    시간이 좀더 있더라면 나중에 구현해보고 싶은 기능은 

    1. 다시 시작하기 버튼 

    2. score저장 기능? 근데 끄면 다 종료되는 거라서 이게 구현 가능 한지는 모르겟다..

     

Diseñada por Tistory.