LoginSignup
9
9

More than 3 years have passed since last update.

ゲームプログラミング:C++でテトリスを作ってみた

Last updated at Posted at 2020-06-14

概要

「エンジニアやっているのに、そこそこの開発規模(個人レベル)でゲーム作ったことなかったな」
そう思って、ゲームを作ろうと思いました。
「いきなりGUIとか考慮すると、完成までにモチベーションを使い果たしてしまうのではないか?」
というわけで、コンソール上で動作するゲームを作成することにしました。

以下を参考にしながらテトリスを作成しました。
館長、ありがとう!!!
 https://youtu.be/iosmmQvhyzM

とりあえずうまく動作していそうです。

スクリーンショット (50).png

コード

せっかくなので、コードを公開します。
ご自由にお試し下さい。
ホントはコメントとかネーミングとかもうちょっと考えて、初学者の方が分かるようにしたいと思っていますが・・・

C++的に記述できていないところも多い(vector使っていないとか)ので、ちょこちょこ修正していこうと思います。
回帰テストせなアカンで!!!

※コードレビューコメント頂けますと幸いです

source.cpp
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

static const int FIELD_WIDTH = 12;
static const int FIELD_HEIGHT = 22;

static const int MINO_WIDTH = 4;
static const int MINO_HEIGHT = 4;

enum
{
    MINO_TYPE_I,
    MINO_TYPE_O,
    MINO_TYPE_S,
    MINO_TYPE_Z,
    MINO_TYPE_J,
    MINO_TYPE_L,
    MINO_TYPE_T,
    MINO_TYPE_MAX
};

enum
{
    MINO_ANGLE_0,
    MINO_ANGLE_90,
    MINO_ANGLE_180,
    MINO_ANGLE_270,
    MINO_ANGLE_MAX
};

char field[FIELD_HEIGHT][FIELD_WIDTH] = { 0 };

char displayBuffer[FIELD_HEIGHT][FIELD_WIDTH] = { 0 };

char minoShapes[MINO_TYPE_MAX][MINO_ANGLE_MAX][MINO_WIDTH][MINO_HEIGHT] =
{
    // MINO_TYPE_I
    {
        // MINO_ANGLE_0
        {
            0, 1, 0, 0,
            0, 1, 0, 0,
            0, 1, 0, 0,
            0, 1, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 0, 0, 0,
            0, 0, 0, 0,
            1, 1, 1, 1,
            0, 0, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 1, 0,
            0, 0, 1, 0,
            0, 0, 1, 0,
            0, 0, 1, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 0, 0,
            1, 1, 1, 1,
            0, 0, 0, 0,
            0, 0, 0, 0
        }
    },

    // MINO_TYPE_O
    {
        // MINO_ANGLE_0
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        }
    },

    // MINO_TYPE_S
    {
        // MINO_ANGLE_0
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            1, 1, 0, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 1, 0, 0,
            0, 1, 1, 0,
            0, 0, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            1, 1, 0, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 0, 0,
            0, 1, 0, 0,
            0, 1, 1, 0,
            0, 0, 1, 0
        }
    },

    // MINO_TYPE_Z
    {
        // MINO_ANGLE_0
        {
            0, 0, 0, 0,
            1, 1, 0, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 0, 0, 0,
            0, 0, 1, 0,
            0, 1, 1, 0,
            0, 1, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 0, 1, 1,
            0, 0, 0, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 1, 0,
            0, 1, 1, 0,
            0, 1, 0, 0,
            0, 0, 0, 0
        }
    },

    // MINO_TYPE_J
    {
        // MINO_ANGLE_0
        {
            0, 0, 1, 0,
            0, 0, 1, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 0, 0, 0,
            1, 1, 1, 0,
            0, 0, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 1, 0, 0,
            0, 1, 0, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 0, 0,
            0, 1, 0, 0,
            0, 1, 1, 1,
            0, 0, 0, 0
        }
    },

    // MINO_TYPE_L
    {
        // MINO_ANGLE_0
        {
            0, 1, 0, 0,
            0, 1, 0, 0,
            0, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 0, 0, 0,
            0, 0, 1, 0,
            1, 1, 1, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 0, 0,
            0, 1, 1, 0,
            0, 0, 1, 0,
            0, 0, 1, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 0, 0,
            0, 1, 1, 1,
            0, 1, 0, 0,
            0, 0, 0, 0
        }
    },

    // MINO_TYPE_T
    {
        // MINO_ANGLE_0
        {
            0, 0, 0, 0,
            1, 1, 1, 0,
            0, 1, 0, 0,
            0, 0, 0, 0
        },
        // MINO_ANGLE_90
        {
            0, 0, 0, 0,
            0, 1, 0, 0,
            0, 1, 1, 0,
            0, 1, 0, 0
        },
        // MINO_ANGLE_180
        {
            0, 0, 0, 0,
            0, 0, 1, 0,
            0, 1, 1, 1,
            0, 0, 0, 0
        },
        // MINO_ANGLE_270
        {
            0, 0, 1, 0,
            0, 1, 1, 0,
            0, 0, 1, 0,
            0, 0, 0, 0
        }
    }
};

void display();
bool isHit(int argMinoX, int argMinoY, int argMinoType, int argMinoAngle);
void resetMino();

int minoType = 0;
int minoAngle = 0;
int minoX = 0;
int minoY = 0;

int main()
{
    // create wall and bottom
    for (int i = 0; i < FIELD_HEIGHT; ++i)
    {
        field[i][0] = 1;
        field[i][FIELD_WIDTH - 1] = 1;
    }

    for (int i = 0; i < FIELD_WIDTH; ++i)
    {
        field[FIELD_HEIGHT - 1][i] = 1;
    }

    resetMino();
    time_t t = time(NULL);
    while (1)
    {
        if (_kbhit())
        {
            switch (_getch())
            {
                //case 'w':
            case 's':
                if (!isHit(minoX, minoY + 1, minoType, minoAngle))
                {
                    ++minoY;
                }
                break;
            case 'a':
                if (!isHit(minoX - 1, minoY, minoType, minoAngle))
                {
                    --minoX;
                }
                break;
            case 'd':
                if (!isHit(minoX + 1, minoY, minoType, minoAngle))
                {
                    ++minoX;
                }
                break;
            case 0x20:
                if (!isHit(minoX, minoY, minoType, (minoAngle + 1) % MINO_ANGLE_MAX))
                {
                    minoAngle = (minoAngle + 1) % MINO_ANGLE_MAX;
                }
                break;
            }
            display();
        }

        if (time(NULL) != t)
        {
            t = time(NULL);

            if (isHit(minoX, minoY + 1, minoType, minoAngle))
            {
                for (int i = 0; i < MINO_HEIGHT; ++i)
                {
                    for (int j = 0; j < MINO_WIDTH; ++j)
                    {
                        field[minoY + i][minoX + j] |= minoShapes[minoType][minoAngle][i][j];
                    }
                }

                // erase block
                for (int i = 0; i < FIELD_HEIGHT - 1; ++i)
                {
                    bool isLineFilled = true;
                    for (int j = 1; j < FIELD_WIDTH - 1; ++j)
                    {
                        if ( 1 != field[i][j] )
                        {
                            isLineFilled = false;
                        }
                    }

                    if ( true == isLineFilled )
                    {
                        for (int j = i; j > 0; --j)
                        {
                            memcpy(field[j], field[j - 1], FIELD_WIDTH);
                        }
                    }
                }

                resetMino();
            }
            else
            {
                ++minoY;
            }

            display();
        }
    }

    _getch();
    return 0;
}

void display()
{
    // write displayBuffer
    memcpy(displayBuffer, field, sizeof(field));

    for (int i = 0; i < MINO_HEIGHT; ++i)
    {
        for (int j = 0; j < MINO_WIDTH; ++j)
        {
            displayBuffer[minoY + i][minoX + j] |= minoShapes[minoType][minoAngle][i][j];
        }
    }

    // display block on console
    system("cls");

    for (int i = 0; i < FIELD_HEIGHT; ++i)
    {
        for (int j = 0; j < FIELD_WIDTH; ++j)
        {
            if (1 == displayBuffer[i][j])
            {
                std::cout << "■";
            }
            else
            {
                std::cout << " ";
            }
        }
        std::cout << std::endl;
    }
}

bool isHit(int argMinoX, int argMinoY, int argMinoType, int argMinoAngle)
{
    for (int i = 0; i < MINO_HEIGHT; ++i)
    {
        for (int j = 0; j < MINO_WIDTH; ++j)
        {
            if ( minoShapes[argMinoType][argMinoAngle][i][j] && field[argMinoY + i][argMinoX + j] )
            {
                return true;
            }
        }
    }
    return false;
}

void resetMino()
{
    minoX = 5;
    minoY = 0;
    minoType = rand() % MINO_TYPE_MAX;
    minoAngle = rand() % MINO_ANGLE_MAX;
}
9
9
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9