シンプルなSFMLゲーム

SFMLライブラリを使用して、C ++でゲームの「タグ」を作成します。 15は、次のような有名なパズルです。









4x4の競技場では、1〜15の数字の15個のサイコロと1つの空きスペースがランダムに配置されます。 サイコロは一度に1つずつ、空の場所にのみ移動できます。 ゲームの目的は、その数に対応する順序で競技場にサイコロを構築することです。



それでは始めましょう。



Visual Studioを起動し、新しい空のプロジェクトを作成します。 好きな名前を付けることができます。「15」と呼びます。 このプロジェクトでは、新しいmain.cppファイルと空のmain関数を作成します。



// main.cpp int main() { return 0; }
      
      





次に、 sfml-dev.orgからSFMLライブラリをダウンロードして展開します。 解凍されたライブラリには、必要なフォルダが含まれています: includelibおよびbin追加インクルードディレクトリのC / C ++セクションのプロジェクトプロパティで、 インクルードフォルダーへのパスを追加します。









そこで、 Additional Library Directoriesの Linkerセクションで、 libフォルダーへのパスを追加します。









そして、 binディレクトリからDLLファイルをコピーし、プロジェクトのexeファイルがあるディレクトリに配置する必要があります。









さらに、[リンカー]セクションの[入力]セクションで、[ 追加の依存関係]で使用されるライブラリファイルを追加する必要があります。 この場合、3つのファイルを追加するだけで十分です:sfml-system-d.lib、sfml-window-d.lib、およびsfml-graphics-d.lib:









ファイル名の-d記号は、デバッグバージョンであり、デバッグ構成で使用する必要があることを意味します。 リリースバージョンの設定では、名前に-d文字を含まないファイルを指定する必要があります。



SFMLライブラリをVisual Studioプロジェクトに接続する方法についてはライブラリのWebサイトをご覧ください



次に、プロジェクトでライブラリを使用してみましょう。 ウィンドウを作成し、イベントループを開始します。



main.cpp
 // main.cpp #include <SFML/Graphics.hpp> int main() { //    600  600    60    sf::RenderWindow window(sf::VideoMode(600, 600), "15"); window.setFramerateLimit(60); sf::Event event; while (window.isOpen()) { while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); if (event.type == sf::Event::KeyPressed) { //    -    if (event.key.code == sf::Keyboard::Escape) window.close(); } } //      window.clear(); window.display(); } return 0; }
      
      







結果は、黒の背景で600 x 600ピクセルの正方形のウィンドウになります。









ウィンドウは、マウスを使用するか、Escキーを使用して通常の方法で閉じることができます。 キーボードキーストロークハンドラーもメッセージ処理ループに含まれています。



仕事に取りかかる前に、画面にテキストを表示するために何らかのフォントが必要です。 たとえば、フォントTrueType Calibriを使用しました。



これでゲームの作成を開始できます。



新しいゲームクラスを作成します。









クラスは、ゲームの操作と競技場のレンダリングを担当します。 これを行うには、SFMLライブラリのDrawableおよびTransformableクラスからクラスを継承します。



それで、クラスの説明を始めます



Game.h
 #pragma once #include <SFML/Graphics.hpp> const int SIZE = 4; //      const int ARRAY_SIZE = SIZE * SIZE; //   const int FIELD_SIZE = 500; //      const int CELL_SIZE = 120; //     enum class Direction { Left = 0, Right = 1, Up = 2, Down = 3 }; class Game : public sf::Drawable, public sf::Transformable { protected: int elements[ARRAY_SIZE]; int empty_index; bool solved; sf::Font font; public: Game(); void Init(); bool Check(); void Move(Direction direction); public: virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; };
      
      







まず、グラフィックスライブラリを接続します。



 #include <SFML/Graphics.hpp>
      
      





ここで、ゲームに必要ないくつかの定数を宣言します。



 const int SIZE = 4; //      const int ARRAY_SIZE = SIZE * SIZE; //    const int FIELD_SIZE = 500; //      const int CELL_SIZE = 120; //    
      
      





また、プレートの移動方向を決定する型enumを宣言します。



 enum class Direction { Left = 0, Right = 1, Up = 2, Down = 3 };
      
      





そして最後に、クラス自体:



 class Game : public sf::Drawable, public sf::Transformable { protected: int elements[ARRAY_SIZE]; int empty_index; bool solved; sf::Font font; public: Game(); void Init(); bool Check(); void Move(Direction direction); public: virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; };
      
      





私たちが持っている最も重要なことは、競技場の状態に対応する整数値を含む要素の配列です。 配列内の要素は、左から右、上から下へ、競技場の要素に対応します。つまり、配列の最初の4つの要素はフィールドの最初の行に対応し、2番目の4つの要素は2番目の行に対応します。



次に、移動ごとに計算される2つの変数はempty_index (空きセルに対応する配列内のインデックス)と解決 (パズルが解決したことを示す記号)です。



さらに、フォント変数はクラスで設定され、ウィンドウにテキストを表示するときに使用されるフォントを決定します。



次に、クラスのメソッドの実装を記述しましょう。



Game.cpp
 #include "Game.h" Game::Game() { //      font.loadFromFile("calibri.ttf"); Init(); } void Game::Init() { //    for (int i = 0; i < ARRAY_SIZE - 1; i++) elements[i] = i + 1; //        empty_index = ARRAY_SIZE - 1; elements[empty_index] = 0; //     = 0 solved = true; } bool Game::Check() { //    for (unsigned int i = 0; i < ARRAY_SIZE; i++) { if (elements[i] > 0 && elements[i] != i + 1) return false; } return true; } void Game::Move(Direction direction) { //       int col = empty_index % SIZE; int row = empty_index / SIZE; //          int move_index = -1; if (direction == Direction::Left && col < (SIZE - 1)) move_index = empty_index + 1; if (direction == Direction::Right && col > 0) move_index = empty_index - 1; if (direction == Direction::Up && row < (SIZE - 1)) move_index = empty_index + SIZE; if (direction == Direction::Down && row > 0) move_index = empty_index - SIZE; //      if (empty_index >= 0 && move_index >= 0) { int tmp = elements[empty_index]; elements[empty_index] = elements[move_index]; elements[move_index] = tmp; empty_index = move_index; } solved = Check(); } void Game::draw(sf::RenderTarget& target, sf::RenderStates states) const { states.transform *= getTransform(); sf::Color color = sf::Color(200, 100, 200); //     sf::RectangleShape shape(sf::Vector2f(FIELD_SIZE, FIELD_SIZE)); shape.setOutlineThickness(2.f); shape.setOutlineColor(color); shape.setFillColor(sf::Color::Transparent); target.draw(shape, states); //       shape.setSize(sf::Vector2f(CELL_SIZE - 2, CELL_SIZE - 2)); shape.setOutlineThickness(2.f); shape.setOutlineColor(color); shape.setFillColor(sf::Color::Transparent); //        sf::Text text("", font, 52); for (unsigned int i = 0; i < ARRAY_SIZE; i++) { shape.setOutlineColor(color); text.setFillColor(color); text.setString(std::to_string(elements[i])); if (solved) { //      shape.setOutlineColor(sf::Color::Cyan); text.setFillColor(sf::Color::Cyan); } else if (elements[i] == i + 1) { //        text.setFillColor(sf::Color::Green); } //   ,   if (elements[i] > 0) { //      sf::Vector2f position(i % SIZE * CELL_SIZE + 10.f, i / SIZE * CELL_SIZE + 10.f); shape.setPosition(position); //     text.setPosition(position.x + 30.f + (elements[i] < 10 ? 15.f : 0.f), position.y + 25.f); target.draw(shape, states); target.draw(text, states); } } }
      
      







クラスコンストラクターは、外部ファイルからフォントをロードし、ゲーム初期化メソッドを呼び出します。



 Game::Game() { //      font.loadFromFile("calibri.ttf"); Init(); }
      
      





ゲームの初期化メソッドは、配列を正しい順序で要素で満たし、解決されたパズルの記号を設定します。



 void Game::Init() { //    for (int i = 0; i < ARRAY_SIZE - 1; i++) elements[i] = i + 1; //   -     empty_index = ARRAY_SIZE - 1; elements[empty_index] = 0; //     = 0 solved = true; }
      
      





はい、最初はゲームは解決済みとして初期化され、ゲームの開始前にランダムな動きを使用してサイコロを混ぜます。



次のメソッドは、パズルが解決されたかどうかをチェックし、チェックの結果を返します。



 bool Game::Check() { //    for (unsigned int i = 0; i < ARRAY_SIZE; i++) { if (elements[i] > 0 && elements[i] != i + 1) return false; } return true; }
      
      





そして最後に、ゲームでプレートの動きを実装する方法:



 void Game::Move(Direction direction) { //       int col = empty_index % SIZE; int row = empty_index / SIZE; //          int move_index = -1; if (direction == Direction::Left && col < (SIZE - 1)) move_index = empty_index + 1; if (direction == Direction::Right && col > 0) move_index = empty_index - 1; if (direction == Direction::Up && row < (SIZE - 1)) move_index = empty_index + SIZE; if (direction == Direction::Down && row > 0) move_index = empty_index - SIZE; //      if (empty_index >= 0 && move_index >= 0) { int tmp = elements[empty_index]; elements[empty_index] = elements[move_index]; elements[move_index] = tmp; empty_index = move_index; } solved = Check(); }
      
      





クラスの最後のメソッドは、競技場を描くメソッドです。



描く
 void Game::draw(sf::RenderTarget& target, sf::RenderStates states) const { states.transform *= getTransform(); sf::Color color = sf::Color(200, 100, 200); //     sf::RectangleShape shape(sf::Vector2f(FIELD_SIZE, FIELD_SIZE)); shape.setOutlineThickness(2.f); shape.setOutlineColor(color); shape.setFillColor(sf::Color::Transparent); target.draw(shape, states); //       shape.setSize(sf::Vector2f(CELL_SIZE - 2, CELL_SIZE - 2)); shape.setOutlineThickness(2.f); shape.setOutlineColor(color); shape.setFillColor(sf::Color::Transparent); //        sf::Text text("", font, 52); for (unsigned int i = 0; i < ARRAY_SIZE; i++) { shape.setOutlineColor(color); text.setFillColor(color); text.setString(std::to_string(elements[i])); if (solved) { //      shape.setOutlineColor(sf::Color::Cyan); text.setFillColor(sf::Color::Cyan); } else if (elements[i] == i + 1) { //        text.setFillColor(sf::Color::Green); } //   ,   if (elements[i] > 0) { //      sf::Vector2f position(i % SIZE * CELL_SIZE + 10.f, i / SIZE * CELL_SIZE + 10.f); shape.setPosition(position); //     text.setPosition(position.x + 30.f + (elements[i] < 10 ? 15.f : 0.f), position.y + 25.f); //    target.draw(shape, states); //    target.draw(text, states); } } }
      
      







レンダリングメソッドで最初に使用するのは、変換行列を乗算することによる座標変換です。 これは、競技場の座標を設定できるようにするために必要です。 次に、SFMLライブラリのRectangleShapeオブジェクトを使用して、ゲームのプレイフィールドの境界線と各ダイの境界線を描画します。 サイコロには、プレート番号付きのテキストも描画します。 さらに、パズルが解決されると、サイコロの色が異なって行われます。



メイン関数に戻る時が来ました:



main.cpp
 // main.cpp #include <SFML/Graphics.hpp> #include "Game.h" int main() { //    600  600    60    sf::RenderWindow window(sf::VideoMode(600, 600), "15"); window.setFramerateLimit(60); sf::Font font; font.loadFromFile("calibri.ttf"); //     sf::Text text("F2 - New Game / Esc - Exit / Arrow Keys - Move Tile", font, 20); text.setFillColor(sf::Color::Cyan); text.setPosition(5.f, 5.f); //    Game game; game.setPosition(50.f, 50.f); sf::Event event; int move_counter = 0; //       while (window.isOpen()) { while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); if (event.type == sf::Event::KeyPressed) { //    -    if (event.key.code == sf::Keyboard::Escape) window.close(); if (event.key.code == sf::Keyboard::Left) game.Move(Direction::Left); if (event.key.code == sf::Keyboard::Right) game.Move(Direction::Right); if (event.key.code == sf::Keyboard::Up) game.Move(Direction::Up); if (event.key.code == sf::Keyboard::Down) game.Move(Direction::Down); //   if (event.key.code == sf::Keyboard::F2) { game.Init(); move_counter = 100; } } } //     ,    if (move_counter-- > 0) game.Move((Direction)(rand() % 4)); //      window.clear(); window.draw(game); window.draw(text); window.display(); } return 0; }
      
      







最初に、フォントをロードし、Textオブジェクトを作成して、キーの割り当てとともにテキスト文字列を表示します。 次に、ゲームオブジェクトを作成し、フィールドの位置を座標(50.50)のポイントに設定します。これがウィンドウの端からインデントする方法です。



キーボードでゲームを制御することにしたので、矢印キーを押すたびに、ゲームオブジェクトでMoveメソッドを呼び出して、対応する方向にプレートを移動します。



F2キーを押すと新しいゲームが開始されるので、このイベントのハンドラーでゲームを再度初期化し(その場所にサイコロを配置します)、移動のカウンターの値を100に設定します。このカウンターは、リセットされず、サイコロが混ざりません。 したがって、パズルの解決された状態を確実に取得します。



基本的にはすべて、コンパイル、アセンブル、実行です:







この記事では、SFMLライブラリーを使用して簡単なC ++ゲームをすばやく作成する方法を示しました。 ただし、プログラム自体のアーキテクチャは理想からはほど遠いものです。 次の記事では、それについて何かをしようとします。



All Articles