Game SFML sederhana

Kami akan membuat "tag" game di C ++ menggunakan perpustakaan SFML. Lima belas adalah teka-teki terkenal yang terlihat seperti ini:


Di lapangan bermain berukuran 4x4, 15 dadu dengan angka dari 1 hingga 15 dan satu ruang kosong berada secara acak. Anda hanya dapat memindahkan dadu satu per satu dan hanya ke tempat kosong. Tujuan dari permainan ini adalah untuk membangun dadu di lapangan bermain dalam urutan yang sesuai dengan jumlah mereka.

Jadi mari kita mulai.

Luncurkan Visual Studio dan buat proyek kosong baru. Anda dapat memberi nama apa pun yang Anda inginkan, saya memanggil "15". Dalam proyek ini, buat file main.cpp baru dan fungsi utama kosong:

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

Selanjutnya, unduh perpustakaan SFML dari sfml-dev.org dan buka paketnya . Perpustakaan yang belum dibuka berisi folder yang kita butuhkan: include , lib and bin . Di properti proyek di bagian C / C ++ di Direktori Sertakan Tambahan, tambahkan path ke folder include :


Di sana, di bagian Linker di Direktori Perpustakaan Tambahan, tambahkan path ke folder lib :


Dan dari direktori bin Anda perlu menyalin file DLL dan meletakkannya di direktori dengan file exe dari proyek kami:


Selain itu, di bagian Linker, di bagian Input, Anda perlu menambahkan file perpustakaan yang digunakan dalam Ketergantungan Tambahan . Dalam kasus kami, tambahkan saja tiga file: sfml-system-d.lib, sfml-window-d.lib dan sfml-graphics-d.lib:


Simbol -d dalam nama file berarti bahwa itu adalah versi debug dan harus digunakan dalam konfigurasi Debug. Dalam pengaturan versi rilis, Anda harus menentukan file tanpa karakter -d dalam nama.

Instruksi yang baik tentang menghubungkan perpustakaan SFML ke proyek Visual Studio ada di situs web perpustakaan.

Sekarang mari kita coba gunakan perpustakaan di proyek kita. Buat jendela dan mulai loop acara:

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; } 


Hasilnya akan menjadi jendela persegi berukuran 600 x 600 piksel dengan latar belakang hitam:


Jendela dapat ditutup dengan mouse, atau melalui tombol Esc. Handler keystroke keyboard juga termasuk dalam loop pemrosesan pesan.

Sebelum memulai bisnis, kita perlu semacam font untuk menampilkan teks di layar. Sebagai contoh, saya mengambil font TrueType Calibri.

Sekarang kita bisa mulai membuat game kita.

Buat kelas Game baru:


Kelas akan bertanggung jawab untuk pengoperasian game dan untuk rendering lapangan bermain. Untuk melakukan ini, kita akan mewarisi kelas kita dari kelas Drawable dan Transformable dari perpustakaan SFML.

Jadi, kami mulai menggambarkan kelas kami

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; }; 


Pertama, kami menghubungkan perpustakaan Grafik:

 #include <SFML/Graphics.hpp> 

Di sini kami mendeklarasikan beberapa konstanta yang diperlukan untuk game:

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

Kami juga mendeklarasikan tipe enum kami, yang menentukan arah pergerakan pelat:

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

Dan akhirnya, kelas itu sendiri:

 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; }; 

Hal terpenting yang kita miliki di dalamnya adalah array elemen yang berisi nilai integer yang sesuai dengan keadaan lapangan bermain. Elemen-elemen dalam array sesuai dengan elemen-elemen dari lapangan bermain dari kiri ke kanan, dari atas ke bawah, yaitu, 4 elemen pertama dari array sesuai dengan baris pertama dari lapangan, 4 elemen kedua ke baris kedua, dll.

Selanjutnya, dua variabel yang akan dihitung setiap gerakan adalah empty_index (indeks dalam larik yang sesuai dengan sel bebas) dan dipecahkan (tanda bahwa teka-teki dipecahkan).

Selain itu, variabel font diatur dalam kelas, yang menentukan font yang akan digunakan saat menampilkan teks di jendela.

Sekarang kita akan menulis implementasi metode kelas kita.

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); } } } 


Konstruktor kelas memuat font dari file eksternal dan memanggil metode inisialisasi game:

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

Metode inisialisasi game mengisi array dengan elemen-elemen dalam urutan yang benar dan menetapkan tanda puzzle yang dipecahkan:

 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; } 

Ya, awalnya game akan diinisialisasi sebagai diselesaikan, dan sebelum dimulainya permainan kita akan mencampur dadu menggunakan gerakan acak.

Metode berikut memeriksa apakah puzzle dipecahkan dan mengembalikan hasil cek:

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

Dan akhirnya, metode yang mengimplementasikan gerakan lempeng dalam game:

 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(); } 

Metode terakhir dari kelas adalah metode yang menarik bidang bermain:

menggambar
 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); } } } 


Dalam metode rendering, hal pertama yang kami gunakan adalah transformasi koordinat, dengan mengalikannya dengan matriks transformasi. Ini diperlukan untuk dapat mengatur koordinat lapangan bermain kami. Selanjutnya, menggunakan objek RectangleShape perpustakaan SFML, menggambar perbatasan lapangan bermain dan perbatasan masing-masing mati dalam permainan. Pada dadu kami juga menggambar teks dengan nomor pelat. Selain itu, jika puzzle dipecahkan, maka warna dadu dibuat berbeda.

Saatnya untuk kembali ke fungsi utama:

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; } 


Pertama, muat font dan buat objek Teks untuk menampilkan baris teks dengan penetapan tombol. Selanjutnya, buat objek permainan kami dan atur posisi lapangan ke titik dengan koordinat (50,50) - ini adalah cara kami indentasi dari tepi jendela.

Saya memutuskan untuk mengontrol permainan melalui keyboard, jadi untuk setiap penekanan tombol panah, kami memanggil metode Pindahkan pada objek permainan untuk memindahkan piring ke arah yang sesuai.

Menekan tombol F2 adalah awal dari gim baru, jadi dalam penangan acara ini kami menginisialisasi ulang gim (yang akan mengarah pada penempatan dadu di tempat mereka), dan juga menetapkan nilai penghitung gerak ke 100. Penghitung ini digunakan lebih lanjut untuk menjalankan gerakan dalam arah acak, hingga tidak akan diatur ulang, dan dadu tidak akan bercampur. Dengan demikian, kita pasti akan mendapatkan kondisi puzzle yang terpecahkan.

Pada dasarnya itu semua, kompilasi, assemble, jalankan:



Pada artikel ini, saya menunjukkan bagaimana Anda dapat dengan cepat membuat game C ++ sederhana menggunakan perpustakaan SFML. Namun, arsitektur programnya sendiri jauh dari ideal. Pada artikel selanjutnya, kami akan mencoba melakukan sesuatu tentang itu.

Source: https://habr.com/ru/post/id449596/


All Articles