Membuat game logika untuk platform game

Halo

Saya ingin berbagi cerita tentang kenalan saya dengan platform game Gameduino 3, serta sedikit pembicaraan tentang pemrograman game logika paling sederhana untuk platform ini, yang digunakan bersama dengan Arduino Uno.

Apa itu Gameduino 3? Gameduino 3 adalah papan ekspansi yang memungkinkan Anda untuk mengubah Arduino menjadi konsol game genggam (artinya ukuran). Yang mengejutkan saya, saya tidak dapat menemukan informasi terperinci di papan ini di hub. Saya ingin mengisi celah ini, terutama karena dewan, menurut pendapat saya, patut mendapat perhatian.

Sedikit sejarah


Penulis proyek yang disebut Gameduino adalah James Bowman, yang pada 2011 menciptakan versi pertama papan. Kemudian diposisikan sebagai modul VGA untuk Arduino. Dewan itu disebut Gameduino dan didasarkan pada keluarga Xilinx Spartan-3A dari logika FPGA yang dapat diprogram. Konektor untuk menghubungkan monitor VGA dan speaker stereo dipasang di papan. gambar

Fitur Gameduino (1):
  • Output video VGA dengan resolusi 400x300 piksel, 512 warna;
  • semua gamut warna diproses dalam FPGA dengan akurasi 15-bit;

gambar latar belakang:

  • bidang grafis latar belakang simbolis 512x512 piksel;
  • 256 karakter, masing-masing dengan palet 4-warna independen;
  • menerapkan efek pembungkus string teks dengan perataan piksel;

grafik foreground:

  • Setiap sprite memiliki resolusi 16x16 piksel;
  • setiap sprite dapat memiliki palet 256-, 16- atau 4-warna;
  • dukungan untuk rotasi 4 arah dan algoritma rotasi horisontal;
  • 96 sprite per garis raster, 1536 elemen tekstur per garis raster;
  • mekanisme untuk mendeteksi kemungkinan persimpangan sprite;

output audio:

  • 12-bit dual-channel synthesizer frekuensi;
  • Polifoni 64-suara dalam rentang frekuensi 10 - 8000 Hz.

Gambar ditampilkan pada layar monitor VGA standar dengan resolusi 400x300 piksel,
kompatibilitas dengan monitor VGA standar dengan resolusi 800x600 piksel dipertahankan.

Pada tahun 2013, versi kedua papan dirilis - Gameduino 2, di mana, tidak seperti versi sebelumnya, sudah ada layar sentuh resistif 4,3 inci dengan resolusi 480x272, akselerometer 3-sumbu, slot kartu memori microSD, dan output audio untuk headphone.

gambar

"Jantung" papan adalah pengontrol grafis EVE (Embedded Video Engine - dalam bahasa Rusia dapat diterjemahkan sebagai "modul video tertanam") FT800, yang memiliki kemampuan komputasi yang kuat, menggabungkan beberapa fungsi pada saat yang bersamaan: pembentukan gambar dan outputnya ke layar layar TFT, pemrosesan layar sentuh, generasi suara.

Diagram fungsional pengontrol grafis FT800
gambar

Blok fungsional berikut termasuk dalam struktur sirkuit mikro: pengontrol grafis, pengontrol audio, pengontrol panel sentuh resistif. Chip FT800 dirancang untuk mengontrol tampilan dengan resolusi hingga 512 x 512 piksel. FT800 juga mendukung LCD WQVGA (480 x 272) dan QVGA (320 x 240). EVE (Embedded Video Engine) FT800 adalah solusi turnkey untuk membuat antarmuka pengguna grafis. Microcircuit menghasilkan sinyal kontrol tampilan, memiliki fungsi grafis built-in untuk menampilkan titik, garis, gambar bitmap, tombol volume, teks, dll.


Struktur Sistem Berdasarkan Pengontrol Grafis FT800
Pembentukan gambar didasarkan pada seperangkat perintah (daftar tampilan), yang ditransmisikan oleh mikrokontroler kontrol ke FT800 melalui antarmuka I2C atau SPI (dalam Gameduino 2, komunikasi antara Arduino dan FT800 dilakukan melalui antarmuka SPI). Fitur FT800 secara signifikan mengurangi pengontrol host sistem.

gambar

Misalnya, untuk menampilkan sejumlah tombol, cukup untuk mentransfer satu perintah ke pengontrol grafis (empat kata 32-bit), dan FT800 akan secara mandiri membentuk gambar tombol-tombol ini pada layar layar TFT. Set perintah pengontrol grafis FTDI mencakup lebih dari 50 fungsi yang dapat digunakan untuk menampilkan berbagai gambar pada layar tampilan dengan berbagai efek.

Manual pemrograman pengontrol terperinci dan contoh-contoh bekerja dengan berbagai lingkungan desain dapat ditemukan di Catatan Aplikasi di situs web FTDI .

Di Rusia, deskripsi yang baik tentang fungsi, prinsip-prinsip umum dan contoh-contoh pekerjaan ada di sini .

Fitur Gameduino 2:
  • resolusi layar 480x272 piksel dalam warna 24-bit;
  • satu set perintah dengan gaya OpenGL;
  • hingga 2000 sprite dalam berbagai ukuran;
  • Memori video 256 KB;
  • rotasi sprite halus dan penskalaan dengan penyaringan bilinear;
  • lingkaran halus dan pola linier dalam perangkat keras - pemulusan 16x;
  • Decoding perangkat keras JPEG;
  • Render bawaan, gradien, teks, tombol dan tombol.

Suara dihasilkan melalui jack headphone yang diperkuat.
Sistem mendukung pemilihan sampel dan alat bawaan.

ROM pengontrol sudah terhubung:

  • font berkualitas tinggi (6 ukuran);
  • sampel 8 alat musik yang dimainkan oleh not MIDI;
  • sampel 10 suara perkusi.

Dan, tentu saja, Anda dapat memuat font dan suara Anda sendiri ke dalam 256 Kbytes RAM.

Menggunakan platform Arduino bukanlah prasyarat: papan Gameduino 2 dapat dihubungkan ke papan mikrokontroler atau mikrokontroler apa pun dengan antarmuka SPI.

Pada 2017, versi ketiga dewan dirilis - Gameduino 3, yang terlihat hampir identik dengan Gameduino 2. Alih-alih FT800, pengontrol grafis FT810 baru digunakan, yang memiliki kompatibilitas perangkat lunak mundur dengan FT800 (mis. Semua kode untuk Gameduino2 berfungsi pada Gameduino3). tetapi pada saat yang sama memiliki kemampuan komputasi 4 kali lebih besar, seperti decoding JPEG perangkat keras yang lebih cepat, decoding video, hingga 1 MB RAM, dll.

Fitur Gameduino 3:
  • decoder video untuk video layar penuh 30 fps;
  • RAM internal 1 megabyte;
  • konektor untuk kartu microSD dan output audio;
  • 4.3 "480x272 panel LCD kontras tinggi dengan layar sentuh resistif;
  • dukungan untuk peta yang dibuat menggunakan editor Tiled Map;
  • Unduh gambar PNG dari microSD;
  • decoding JPEG yang dipercepat;
  • potret perangkat keras / pergantian lanskap;
  • dukungan untuk Arduino, ESP8266 dan Teensy 3.2;
  • alat online untuk menyiapkan grafik, audio, font dan video;


James Bowman telah menerbitkan perpustakaan untuk proyeknya dengan banyak contoh yang berfungsi langsung. Perpustakaan saat ini yang berhasil saya temukan ada di sini . Panduan Pemrograman (dalam bahasa Inggris), di mana semuanya dijelaskan secara rinci. Banyak informasi berguna tentang cara menginstal IDE, dll., Dll.

Pemrograman


Entah bagaimana, berkeliaran di sekitar Bolshoi Internet Theatre , saya menemukan proyek yang menarik untuk Arduino - permainan logis “Kolom” , yang ditulis di bawah tampilan warna Cina yang biasa murah 128x160 piksel. Saya ingin mengulang game ini, tetapi di papan saya, saya akan menyebutnya FT810 (dengan nama GPU), yang saat itu sudah ada di tangan saya. Saya sudah berhasil mempelajari manual pemrograman dan contoh-contoh dari perpustakaan, jadi tangan saya hanya "gatal" karena keinginan untuk menulis sesuatu dari mereka sendiri. Yang saya segera mulai.

Hal pertama yang harus saya lakukan adalah menampilkan teks di layar.

Berkat kehadiran font bawaan, output teks ke layar cukup mudah. Berikut ini adalah sketsa demo dari perpustakaan (dengan komentar saya):

Buat sketsa helloworld.ino
#include <EEPROM.h> #include <SPI.h> #include <GD2.h> void setup() { Serial.begin(1000000); //   ,    1000000  GD.begin(0); //  ,   . } void loop() { GD.ClearColorRGB(0x103000); //    . GD.Clear(); //   (   ) GD.cmd_text( //  ,  GD.w / 2, //     2 GD.h / 2, //     2 31, //   OPT_CENTER, // ,         . "Hello world"); //   GD.swap(); //         (   ). } 
Akibatnya, kami sampai di sini teks yang indah:
gambar

Selanjutnya, perlu menggambar bentuk geometris, misalnya: garis.
Untuk menggambar garis, Anda harus menggunakan Begin (LINES) atau Begin (LINE_STRIP).
LINES bergabung dengan setiap pasangan simpul, sedangkan LINE_STRIP menggabungkan semua simpul bersama.

Saya akan memberikan sketsa demo berikut dari perpustakaan (dengan komentar saya):

Buat sketsa lines.ino
 #include <EEPROM.h> #include <SPI.h> #include <GD2.h> void setup() { Serial.begin(1000000); //   ,    1000000 . GD.begin(); //  ,   . } static void zigzag(int x) { GD.Vertex2ii(x - 10, 10); //     GD.Vertex2ii(x + 10, 60); //     () GD.Vertex2ii(x - 10, 110); GD.Vertex2ii(x + 10, 160); GD.Vertex2ii(x - 10, 210); GD.Vertex2ii(x + 10, 260); //     () } void loop() { GD.Clear(); //   (    - 0000000) GD.Begin(LINES); //     zigzag(140); //   zigzag   -   GD.Begin(LINE_STRIP); //     zigzag(240); GD.LineWidth(16 * 10); //        1/16 , .. 1/16 * 16 * 10 = 10  GD.Begin(LINE_STRIP); zigzag(340); GD.swap(); //         (   ). } 

Baris di layar:

gambar

Dari menggambar garis, mari kita beralih ke menggambar persegi panjang.

Untuk menggambar persegi panjang, gunakan Begin (RECTS) dan atur sudut kebalikan dari persegi panjang. Urutan kedua sudut itu tidak masalah. Rectangles digambar dengan sudut bulat, menggunakan lebar garis saat ini sebagai jari-jari sudut. Sudut membulat melampaui batas persegi panjang, sehingga meningkatkan jari-jari sudut mengarah ke peningkatan jumlah piksel. Contoh ini menggambar persegi panjang 420 × 20 tiga kali dengan jari-jari sudut meningkat.

Sketsa rectangles.ino
 #include <EEPROM.h> #include <SPI.h> #include <GD2.h> void setup() { Serial.begin(1000000); //   ,    1000000 . GD.begin(); //  ,   . } void loop() { GD.Clear(); //   (    - 0000000) GD.Begin(RECTS); //     GD.Vertex2ii(30, 30); //       GD.Vertex2ii(450, 50); //       GD.LineWidth(16 * 10); //         1/16 , .. 1/16 * 16 * 10 = 10  GD.Vertex2ii(30, 120); //       GD.Vertex2ii(450, 140); //       GD.LineWidth(16 * 20); //         1/16 , .. 1/16 * 16 * 20 = 20  GD.Vertex2ii(30, 220); //       GD.Vertex2ii(450, 230); //       GD.swap(); //         (   ) } 

Hasil:
gambar

Mari kita lanjutkan menggambar lingkaran - dasar tombol sentuh di masa depan. Mari kita kembali ke contoh pertama dengan teks dan menambahkan beberapa baris ke loop ().

Buat sketsa dengan menggambar lingkaran berwarna
 #include <EEPROM.h> #include <SPI.h> #include <GD2.h> void setup() { Serial.begin(1000000); //   ,    1000000  GD.begin(0); //  ,   . } void loop() { GD.ClearColorRGB(0x103000); //    . GD.Clear(); //   (   ) GD.cmd_text( //  ,  GD.w / 2, //     2 GD.h / 2, //     2 31, //   OPT_CENTER, // ,          "Hello world"); //   GD.PointSize(16 * 30); //    ()    1/16 , .. 1/16 * 16 * 30 = 30  GD.Begin(POINTS); //     () GD.ColorRGB(0xff8000); //   orange GD.Vertex2ii(220, 100); //   ()   220,100 GD.ColorRGB(0x0080ff); //   teal GD.Vertex2ii(260, 170); //   ()   260,170 GD.swap(); //         (   ) } 

Hasil:



Untuk bekerja dengan tombol sentuh, perlu mengatur pengolahan klik pada layar sentuh. Ini dilakukan sebagai berikut. Saya akan menjelaskan secara singkat prinsipnya. Setiap piksel (titik) pada layar memiliki warna. Ini juga memiliki nilai tag yang tidak terlihat, yang dapat ditetapkan ke titik (atau seluruh objek seperti garis, lingkaran, persegi panjang, dll.) Dan selanjutnya digunakan untuk mendeteksi sentuhan objek ini. Sketsa berikut menunjukkan contoh pengaturan nilai tag untuk lingkaran berwarna ke 100 dan 101.

Sketsa Layar Sentuh
 #include <EEPROM.h> #include <SPI.h> #include <GD2.h> void setup() { Serial.begin(1000000); //   ,    1000000  GD.begin(0); //  ,   . } void loop() { GD.ClearColorRGB(0x103000); //    . GD.Clear(); //   (   ) GD.cmd_text( //  ,  GD.w / 2, //     2 GD.h / 2, //     2 31, //   OPT_CENTER, // ,          "Hello world"); //   GD.PointSize(16 * 30); //    ()    1/16 , .. 1/16 * 16 * 30 = 30  GD.Begin(POINTS); //     () GD.ColorRGB(0xff8000); //   orange GD.Tag(100); //       () GD.Vertex2ii(220, 100); //   ()   220,100 GD.ColorRGB(0x0080ff); //   teal GD.Tag(101); //       () GD.Vertex2ii(260, 170); //   ()   260,170 GD.swap(); //         (   ) GD.get_inputs(); //     if(GD.inputs.tag > 0) //      Serial.println(GD.inputs.tag); //       “”  } 

Sekarang, ketika sistem mendeteksi menyentuh lingkaran apa pun, ia melaporkan kode sensornya, dalam hal ini 100 atau 101. Ketika Anda mengklik objek (lingkaran berwarna) pada layar di jendela port serial, nilai tag yang sesuai dengan objek yang ditekan akan ditampilkan:



Saya memberikan contoh operasi dasar, menggunakan yang sudah memungkinkan untuk melanjutkan pembuatan game dengan aman. Tentu saja, game itu tidak dibuat dari awal, tetapi kode kerja yang sudah jadi diambil sebagai dasar, yang diadaptasi (sambil mempertahankan grafik), sehingga versi asli dari game itu sangat mirip dengan aslinya.

Versi pertama dari game:



Setelah bermain selama beberapa hari, saya ingin mengubah sesuatu dalam desain, misalnya, untuk menambahkan beberapa yang lain, tidak biasa, bukan latar belakang putih. Dan kemudian saya ingat satu contoh dari perpustakaan, di mana langit berbintang melintas di latar belakang:

Sketsa Slot slotgag.ino
 #include <EEPROM.h> #include <SPI.h> #include <GD2.h> #include "slotgag_assets.h" //      void setup() { Serial.begin(1000000); GD.begin(); LOAD_ASSETS(); //     } void loop() { GD.Clear(); //   ( ,     ) GD.ColorMask(1, 1, 1, 0); //        R, G, B,   GD.Begin(BITMAPS); //      GD.BitmapHandle(BACKGROUND_HANDLE); //   - GD.BitmapSize(NEAREST, REPEAT, REPEAT, 480, 272); //        GD.Vertex2ii(0, 0, BACKGROUND_HANDLE); //      0,0 GD.ColorMask(1, 1, 1, 1); GD.ColorRGB(0xa0a0a0); GD.Vertex2ii(240 - GAMEDUINO_WIDTH / 2, 136 - GAMEDUINO_HEIGHT / 2, GAMEDUINO_HANDLE); static int x = 0; GD.LineWidth(20 * 16); GD.BlendFunc(DST_ALPHA, ONE); GD.Begin(LINES); GD.Vertex2ii(x, 0); GD.Vertex2ii(x + 100, 272); x = (x + 20) % 480; //' }a GD.swap(); } 

Lihat:



Agar tidak menyelidiki hutan, saya tidak mengomentari seluruh kode, tetapi hanya berkomentar pada baris kode yang diperlukan yang saya salin ke dalam sketsa kerja saya.

Untuk menambahkan gambar langit berbintang sebagai latar belakang, saya harus melakukan hal berikut: pertama, ubah warna hitam garis dan teks menjadi putih (sehingga terlihat pada latar belakang hitam), tulis file slotgag.gd2 ke kartu micro SD, yang menyimpan gambar, tambahkan slotgag_assets.h ke folder proyek dan tambahkan 8 baris kode yang diperlukan ke sketsa.

Akibatnya, game memperoleh formulir ini:



Dan tentu saja, game seperti apa tanpa desain suara? Tetap menambahkan efek suara, terutama karena mereka disajikan dalam kualitas dan variasi yang baik.

Gameduino 2/3 memiliki dua sistem suara. Yang pertama - synthesizer - dapat menghasilkan satu set suara tetap dan not musik. Sebuah synthesizer berguna untuk menambahkan suara dengan cepat ke suatu proyek, tetapi karena rangkaian suara sudah diperbaiki, itu tidak terlalu fleksibel. Yang kedua adalah reproduksi sampel. Ini mereproduksi suara sampel dari memori utama dalam berbagai format. Sistem ini jauh lebih fleksibel, tetapi Anda harus menyiapkan dan memuat sampel ke dalam RAM.

Saya menggunakan synthesizer suara tetap. The synthesizer menyediakan beberapa suara "perkusi" pendek, terutama untuk digunakan dalam antarmuka pengguna. Untuk memainkan suara, Anda harus memanggil GD.play () dengan pengidentifikasi suara. Daftar lengkap suara yang tersedia:

Klik
BERALIH
Cowbell
NOTCH
Hihat
Kickdrum
Pop
Clack
CHACK

Ringkasan


Hasilnya berupa sketsa:

Sketsa Columns.ino
 #include <SPI.h> #include <GD2.h> #include <avr/eeprom.h> #include "slotgag_assets.h" #define TAG_BUTTON_LEFT 201 #define TAG_BUTTON_RIGHT 202 #define TAG_BUTTON_ROT 203 #define TAG_BUTTON_DROP 204 #define X_BUTTON_LEFT 50 #define Y_BUTTON_LEFT 222 #define X_BUTTON_RIGHT 430 #define Y_BUTTON_RIGHT 222 #define X_BUTTON_ROT 430 #define Y_BUTTON_ROT 50 #define X_BUTTON_DROP 50 #define Y_BUTTON_DROP 50 // Color definitions #define BLACK 0x000000 #define RED 0xFF0000 #define GREEN 0x00FF00 #define BLUE 0x0000FF #define YELLOW 0xFFFF00 #define MAGENTA 0xFF00FF #define CYAN 0x00FFFF #define WHITE 0xFFFFFF #define DISPLAY_MAX_X 480 #define DISPLAY_MAX_Y 272 #define MaxX 8 #define MaxY 17 #define SmeX 3 #define SmeY 3 #define razmer 18 #define NumCol 6 #define MaxLevel 8 #define NextLevel 80 #define DISP_LEFT ((DISPLAY_MAX_X - MaxX*razmer)/2 - 2) #define DISP_RIGHT ((DISPLAY_MAX_X + MaxX*razmer)/2 + 2) #define DISP_TOP ((DISPLAY_MAX_Y - (MaxY-4)*razmer)/2 - 2) #define DISP_BOT ((DISPLAY_MAX_Y + (MaxY-4)*razmer)/2 + 2) uint8_t MasSt[MaxX][MaxY], MasTmp[MaxX][MaxY], fignext[3]; uint8_t Level=1, dx, dy, tr, flfirst=1; uint32_t MasCol[]={WHITE, BLACK, RED, BLUE, GREEN, YELLOW, MAGENTA, CYAN}; unsigned long Counter, Score=0, TScore=0, Record=0, myrecord; uint16_t tempspeed = 1000; bool fl, Demo=true, Arbeiten=false, FlZ=false; int8_t x,y; int8_t mmm [4][2]={{-1,0},{0,-1},{1,0},{0,1}}; uint16_t MasSpeed[MaxLevel]={500,450,400,350,300,250,200,100}; uint8_t state_game = 0; unsigned long time_count; byte prevkey; uint32_t btn_color = 0xff0000; /****************************************************************************************************************/ void setup(void) { Serial.begin(1000000); Serial.println("Columns"); GD.begin(); LOAD_ASSETS(); GD.BitmapHandle(BACKGROUND_HANDLE); GD.BitmapSize(NEAREST, REPEAT, REPEAT, 480, 272); randomSeed(analogRead(5)); myrecord = eeprom_read_byte((unsigned char *)1); time_count = millis() + 1000; } static struct { byte t, note; } pacman[] = { { 0, 71 }, { 2, 83 }, { 4, 78 }, { 6, 75 }, { 8, 83 }, { 9, 78 }, { 12, 75 }, { 16, 72 }, { 18, 84 }, { 20, 79 }, { 22, 76 }, { 24, 84 }, { 25, 79 }, { 28, 76 }, { 32, 71 }, { 34, 83 }, { 36, 78 }, { 38, 75 }, { 40, 83 }, { 41, 78 }, { 44, 75 }, { 48, 75 }, { 49, 76 }, { 50, 77 }, { 52, 77 }, { 53, 78 }, { 54, 79 }, { 56, 79 }, { 57, 80 }, { 58, 81 }, { 60, 83 }, { 255, 255 } }; //================================================== void loop(void) { GD.get_inputs(); byte key = GD.inputs.tag; int8_t VAL = 0; if (prevkey == 0x00) { switch (key) { case TAG_BUTTON_LEFT: VAL = -1; break; case TAG_BUTTON_RIGHT: VAL = 1; break; case TAG_BUTTON_ROT: if (!FlZ) { GD.play(HIHAT); byte aa=MasSt[x][y]; MasSt[x][y]=MasSt[x][y+2]; MasSt[x][y+2]=MasSt[x][y+1]; MasSt[x][y+1]=aa; } break; case TAG_BUTTON_DROP: if (Arbeiten) { if (!FlZ) { tempspeed=50; GD.play(NOTCH); } } else { GD.play(CLICK); Demo=false; NewGame(); } break; } } prevkey = key; if (VAL!=0 && fig_shift(VAL) && !FlZ) { for (byte i=0;i<3;i++) { MasSt[x+VAL][y+i]=MasSt[x][y+i]; MasSt[x][y+i]=0; } x=x+VAL; } ProcGame(); ViewStacan(); GD.swap(); } //================================================== // redraw one square void ViewQuad(byte i,byte j,byte mycolor) { if (j<3) return; uint16_t wy=DISP_TOP + SmeY+(j-3)*razmer-j; uint16_t wx=DISP_LEFT + SmeX+i*razmer-i; if (mycolor!=0) { GD.LineWidth(16*1); GD.ColorRGB(WHITE); GD.Begin(LINE_STRIP); GD.Vertex2ii(wx,wy); GD.Vertex2ii(wx + razmer-1,wy); GD.Vertex2ii(wx + razmer-1,wy + razmer-1); GD.Vertex2ii(wx,wy + razmer-1); GD.Vertex2ii(wx,wy); GD.Begin(RECTS); GD.ColorRGB(MasCol[mycolor]); GD.Vertex2ii(wx+1, wy+1); GD.Vertex2ii(wx+1 + razmer-2 - 1, wy+1 + razmer-2 - 1); } else { } } //================================================== void ViewStacan(void) { char myStr2[5]; // Draw background fone GD.Clear(); GD.ColorMask(1, 1, 1, 0); GD.Begin(BITMAPS); GD.BitmapHandle(BACKGROUND_HANDLE); GD.BitmapSize(NEAREST, REPEAT, REPEAT, 480, 272); GD.Vertex2ii(0, 0, BACKGROUND_HANDLE); // Print text GD.ColorRGB(WHITE); GD.cmd_text(DISP_LEFT - 30, DISP_TOP + 3, 27, OPT_CENTER, "LEVEL"); GD.cmd_text(DISP_RIGHT + 30, DISP_TOP + 3, 27, OPT_CENTER, "NEXT"); GD.cmd_text(DISP_RIGHT + 30, DISP_TOP + 100, 27, OPT_CENTER, "SCORE"); GD.cmd_text(DISP_LEFT - 30, DISP_TOP + 100, 27, OPT_CENTER, "TOP"); // Print digit Score GD.ColorRGB(RED); sprintf(myStr2,"%05d",Score ); GD.cmd_text(DISP_RIGHT + 30, DISP_TOP + 130, 27, OPT_CENTER, myStr2); // Print digit Top sprintf(myStr2,"%05d",myrecord ); GD.cmd_text(DISP_LEFT - 30, DISP_TOP + 130, 27, OPT_CENTER, myStr2); // Print digit Level sprintf(myStr2,"%02d",Level ); GD.cmd_text(DISP_LEFT - 30, DISP_TOP + 40, 31, OPT_CENTER, myStr2); // Draw color squares for (byte j=3;j<MaxY;j++) for (byte i=0;i<MaxX;i++) ViewQuad(i,j,MasSt[i][j]); // Draw Next Figure for (byte i=0;i<3;i++) { GD.ColorRGB(WHITE); GD.Begin(LINE_STRIP); GD.LineWidth(16*1); GD.Vertex2ii(DISP_RIGHT + 15, DISP_TOP + 20 + razmer*ii); GD.Vertex2ii(DISP_RIGHT + 15 + razmer-1, DISP_TOP + 20 + razmer*ii); GD.Vertex2ii(DISP_RIGHT + 15 + razmer-1, DISP_TOP + 20 + razmer*ii + razmer-1); GD.Vertex2ii(DISP_RIGHT + 15, DISP_TOP + 20 + razmer*ii + razmer-1); GD.Vertex2ii(DISP_RIGHT + 15, DISP_TOP + 20 + razmer*ii); GD.Begin(RECTS); GD.ColorRGB(MasCol[fignext[i]]); GD.Vertex2ii(DISP_RIGHT+15+1, DISP_TOP+20+razmer*i-i+1); GD.Vertex2ii(DISP_RIGHT+15+1+razmer-2-1, DISP_TOP+20+razmer*i-i+1+razmer-2-1); } // Draw "stacan" GD.ColorRGB(WHITE); GD.Begin(LINE_STRIP); GD.LineWidth(16*1); GD.Vertex2ii(DISP_LEFT + 1, DISP_TOP); GD.Vertex2ii(DISP_LEFT + 1, DISP_BOT); GD.Vertex2ii(DISP_LEFT + 1 + razmer*MaxX+5-MaxX - 1, DISP_BOT); GD.Vertex2ii(DISP_LEFT + 1 + razmer*MaxX+5-MaxX - 1, DISP_TOP); // Draw 9 vertical lines for (byte i=0; i<9; i++) { GD.ColorRGB(WHITE); GD.Begin(LINE_STRIP); GD.LineWidth(16*1); GD.Vertex2ii(DISP_LEFT + 3 + razmer*ii, DISP_TOP); GD.Vertex2ii(DISP_LEFT + 3 + razmer*ii, DISP_BOT - 2); } // Draw 1 horizontal line GD.ColorRGB(WHITE); GD.Begin(LINE_STRIP); GD.Vertex2ii(DISP_LEFT + 3, DISP_BOT - 2); GD.Vertex2ii(DISP_LEFT + 3 + razmer*MaxX-MaxX - 1, DISP_BOT - 2); // Draw "Game Over" if (!Demo && !Arbeiten) { GD.Begin(RECTS); GD.ColorRGB(WHITE); GD.Vertex2ii((DISP_LEFT + DISP_RIGHT)/2 - 60, (DISP_TOP + DISP_BOT)/2 - 40); GD.Vertex2ii((DISP_LEFT + DISP_RIGHT)/2 + 60, (DISP_TOP + DISP_BOT)/2 + 40); GD.ColorRGB(BLACK); GD.Vertex2ii((DISP_LEFT + DISP_RIGHT)/2 - 58, (DISP_TOP + DISP_BOT)/2 - 38); GD.Vertex2ii((DISP_LEFT + DISP_RIGHT)/2 + 58, (DISP_TOP + DISP_BOT)/2 + 38); GD.ColorRGB(RED); GD.cmd_text((DISP_LEFT + DISP_RIGHT)/2, (DISP_TOP + DISP_BOT)/2 - 20, 30, OPT_CENTER, "GAME"); GD.cmd_text((DISP_LEFT + DISP_RIGHT)/2, (DISP_TOP + DISP_BOT)/2 + 20, 30, OPT_CENTER, "OVER"); } // Draw Buttons GD.Begin(POINTS); GD.PointSize(16*50); // Set size of buttons (50 pix) GD.ColorRGB(btn_color); // Set fone color of buttons GD.Tag(TAG_BUTTON_LEFT); // Set TAG for BUTTON_LEFT GD.Vertex2ii( X_BUTTON_LEFT, Y_BUTTON_LEFT); // Place BUTTON1 GD.Tag(TAG_BUTTON_RIGHT); // Set TAG for BUTTON_RIGHT GD.Vertex2ii(X_BUTTON_RIGHT, Y_BUTTON_RIGHT); // Place BUTTON2 GD.Tag(TAG_BUTTON_ROT); // Set TAG for BUTTON_ROT GD.Vertex2ii( X_BUTTON_ROT, Y_BUTTON_ROT); // Place BUTTON3 GD.Tag(TAG_BUTTON_DROP); // Set TAG for BUTTON_DROP GD.Vertex2ii(X_BUTTON_DROP, Y_BUTTON_DROP); // Place BUTTON4 // Draw figures in buttons circles GD.Tag(255); GD.ColorRGB(0xffff00); GD.LineWidth(16*2); GD.Begin(LINE_STRIP); GD.Vertex2ii(X_BUTTON_LEFT + 30, Y_BUTTON_LEFT - 20); GD.Vertex2ii(X_BUTTON_LEFT, Y_BUTTON_LEFT - 20); GD.Vertex2ii(X_BUTTON_LEFT, Y_BUTTON_LEFT - 40); GD.Vertex2ii(X_BUTTON_LEFT - 40, Y_BUTTON_LEFT); GD.Vertex2ii(X_BUTTON_LEFT, Y_BUTTON_LEFT + 40); GD.Vertex2ii(X_BUTTON_LEFT, Y_BUTTON_LEFT + 20); GD.Vertex2ii(X_BUTTON_LEFT + 30, Y_BUTTON_LEFT + 20); GD.Vertex2ii(X_BUTTON_LEFT + 30, Y_BUTTON_LEFT - 20); GD.Begin(LINE_STRIP); GD.Vertex2ii(X_BUTTON_RIGHT - 30, Y_BUTTON_RIGHT - 20); GD.Vertex2ii(X_BUTTON_RIGHT, Y_BUTTON_RIGHT - 20); GD.Vertex2ii(X_BUTTON_RIGHT, Y_BUTTON_RIGHT - 40); GD.Vertex2ii(X_BUTTON_RIGHT + 40, Y_BUTTON_RIGHT); GD.Vertex2ii(X_BUTTON_RIGHT, Y_BUTTON_RIGHT + 40); GD.Vertex2ii(X_BUTTON_RIGHT, Y_BUTTON_RIGHT + 20); GD.Vertex2ii(X_BUTTON_RIGHT - 30, Y_BUTTON_RIGHT + 20); GD.Vertex2ii(X_BUTTON_RIGHT - 30, Y_BUTTON_RIGHT - 20); GD.Begin(LINE_STRIP); GD.Vertex2ii(X_BUTTON_ROT - 40, Y_BUTTON_ROT); GD.Vertex2ii(X_BUTTON_ROT, Y_BUTTON_ROT - 40); GD.Vertex2ii(X_BUTTON_ROT + 40, Y_BUTTON_ROT); GD.Vertex2ii(X_BUTTON_ROT, Y_BUTTON_ROT + 40); GD.Vertex2ii(X_BUTTON_ROT - 40, Y_BUTTON_ROT); GD.Begin(LINE_STRIP); if (Arbeiten) { GD.Vertex2ii(X_BUTTON_DROP - 40, Y_BUTTON_DROP - 10); GD.Vertex2ii(X_BUTTON_DROP + 40, Y_BUTTON_DROP - 10); GD.Vertex2ii(X_BUTTON_DROP, Y_BUTTON_DROP + 30); GD.Vertex2ii(X_BUTTON_DROP - 40, Y_BUTTON_DROP - 10); } else { GD.Vertex2ii(X_BUTTON_DROP - 10, Y_BUTTON_DROP - 40); GD.Vertex2ii(X_BUTTON_DROP + 30, Y_BUTTON_DROP); GD.Vertex2ii(X_BUTTON_DROP - 10, Y_BUTTON_DROP + 40); GD.Vertex2ii(X_BUTTON_DROP - 10, Y_BUTTON_DROP - 40); } } //================================================== void ClearMas(byte MasStx[MaxX][MaxY]) { for (byte j=0;j<MaxY;j++) for (byte i=0;i<MaxX;i++) MasStx[i][j]=0; } //================================================== void Sosed(int i,int j,int dx,int dy, byte mode) { int nx=i+dx; int ny=j+dy; if (nx>=0 && ny>=0 && nx<MaxX && ny<MaxY && MasSt[nx][ny]==MasSt[i][j]) { if (mode==1) MasTmp[i][j]++; else if (mode==2 && (MasTmp[nx][ny]>1 || MasTmp[i][j]>2 )) { MasTmp[nx][ny]=3; MasTmp[i][j]=3; } else { if (mode==3 && MasTmp[nx][ny]==3) { if (MasTmp[i][j]!=3) { MasTmp[i][j]=3; fl=true; } } } } } //================================================== void Sos(int i,int j, byte mode) { for (byte k=0;k<4;k++) Sosed(i,j,mmm[k][0],mmm[k][1],mode); } //================================================== // create next figure void GetNext(void) { x=3; y=0; for (byte i=0;i<3;i++) { if (!Demo) MasSt[x][i]=fignext[i]; fignext[i]=random(NumCol)+2; } if (!Demo) { Counter++; if (Counter==NextLevel) { Counter=0; Level++; if (Level>MaxLevel) Level=MaxLevel; } tempspeed=MasSpeed[Level-1]; } } //================================================== // find onecolor elements bool FindFull(void) { byte i,j,k; bool res; res=false; for (byte k=2;k<8;k++) { // by every color ClearMas(MasTmp); for (j=3;j<MaxY;j++) for (i=0;i<MaxX;i++) if (MasSt[i][j]==k) Sos(i,j,1); for (j=3;j<MaxY;j++) for (i=0;i<MaxX;i++) if (MasTmp[i][j]>1) Sos(i,j,2); do { fl=false; for (j=3;j<MaxY;j++) for (i=0;i<MaxX;i++) if (MasTmp[i][j]>0) Sos(i,j,3); } while (fl); for (j=3;j<MaxY;j++) for (i=0;i<MaxX;i++) if (MasTmp[i][j]==3) { MasSt[i][j]=1; TScore++; } } return(res); } //================================================ // move figure down bool fig_drop(int dy) { if (dy>0 && !FlZ) { if (y+dy+2>MaxY-1 || MasSt[x+dx][y+dy+2]>0) { if (y<3) { gameover(); } else { return true; } } else { if (y+dy+dy+2>MaxY-1 || MasSt[x+dx][y+dy+dy+2]>0) { GD.play(COWBELL); } for (byte i=0;i<3;i++) MasSt[x][y+2-i+dy]=MasSt[x][y+2-i]; MasSt[x][y]=0; y=y+dy; } } return(false); } //================================================ // move figure left/right (shift) bool fig_shift(int dx) { if (x+dx<0 || x+dx>MaxX-1) { GD.play(COWBELL); return(false); } if (dx!=0) { if (MasSt[x+dx][y+dy+2]==0) { if (x+dx+dx<0 || x+dx+dx>MaxX-1) GD.play(COWBELL); else GD.play(CHACK); return(true); } else { GD.play(COWBELL); return(false); } } return(false); } //================================================== // State-machine void ProcGame(void) { byte i,j,k; bool res = false; if (time_count < millis()) { if (Arbeiten) time_count = millis() + tempspeed; else time_count = millis() + 1000; switch (state_game) { // Demo case 0: Score=0; GetNext(); for (byte j=3;j<MaxY;j++) for (byte i=0;i<MaxX;i++) MasSt[i][j]=random(6)+2; state_game = 1; TScore=0; break; case 1: FindFull(); if (TScore>0) { FlZ=true; time_count = millis() + 500; } state_game = 2; break; case 2: for (j=0;j<MaxY;j++) { for (i=0;i<MaxX;i++) { while (MasSt[i][MaxY-1-j]==1) { for (k=0;k<MaxY-2-j;k++) MasSt[i][MaxY-1-kj] = MasSt[i][MaxY-2-kj]; res=true; } } } if(res) { if (TScore>7) Score=Score+TScore+(TScore-8)*2; else Score=Score+TScore; state_game = 1; } else { state_game = 0; } break; // Arbeiten case 3: if (fig_drop(1)) { tempspeed=MasSpeed[Level-1]; TScore=0; FindFull(); if (TScore>0) { GD.play(KICKDRUM); FlZ=true; state_game = 4; } else { FlZ=false; GetNext(); } } break; case 4: for (j=0;j<MaxY;j++) { for (i=0;i<MaxX;i++) { while (MasSt[i][MaxY-1-j]==1) { for (k=0;k<MaxY-2-j;k++) MasSt[i][MaxY-1-kj] = MasSt[i][MaxY-2-kj]; res=true; } } } if(res) { if (TScore>7) Score=Score+TScore+(TScore-8)*2; else Score=Score+TScore; state_game = 5; FlZ=true; GD.play(CLACK); } else { state_game = 3; FlZ=false; time_count = millis() + 100; } break; case 5: state_game = 3; FlZ=false; break; default: break; } } } //================================================ // start new game void NewGame() { Score = 0; FlZ = false; ClearMas(MasSt); Arbeiten = true; GetNext(); Counter = 0; Level = 1; tempspeed = MasSpeed[0]; Record = myrecord; state_game = 3; } //================================================ // draw "GAME OVER" void gameover() { if (Arbeiten==true) { GD.play(SWITCH); Arbeiten=false; if (Score>myrecord) { myrecord=Score; eeprom_write_byte((unsigned char *) 1, myrecord); } } } 
slotgag_assets.h
 #define LOAD_ASSETS() GD.safeload("slotgag.gd2"); #define BACKGROUND_HANDLE 0 #define BACKGROUND_WIDTH 256 #define BACKGROUND_HEIGHT 256 #define BACKGROUND_CELLS 1 #define GAMEDUINO_HANDLE 1 #define GAMEDUINO_WIDTH 395 #define GAMEDUINO_HEIGHT 113 #define GAMEDUINO_CELLS 1 #define ASSETS_END 220342UL static const shape_t BACKGROUND_SHAPE = {0, 256, 256, 0}; static const shape_t GAMEDUINO_SHAPE = {1, 395, 113, 0}; 

Saya percaya bahwa saya menyelesaikan tugas membuat sketsa kerja pertama saya untuk forum ini. Saya harap setidaknya satu orang tertarik untuk membaca cerita saya. Kritik dan komentar dipersilahkan. Rencana tidak berhenti, terus maju dan, tentu saja, berbagi pengalaman, pengetahuan.

Untuk menunjukkan pengoperasian papan, saya memposting video dengan suara (Perhatian! Suara keras!).
Terima kasih atas perhatian anda

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


All Articles