Merhabalar, daha önce yapmış olduğumuz arduino ile oled kullanımı ve joystick kullanımı örneklerinden faydalanarak tetris oyununu yapacağız.
Arduino ile yapılacak en eğlenceli projelerden biri tetris oyunu olabilir. Hemen hemen hepimizin yakından tanıdığı tetris oyunu dijital dünyaya adımda dijital oyunların temellerini atmıştır. Bizde bu uygulamada hem ilkleri anacağız hemde arduinoyu sadece öğrenip bırakmayarak eğlenceli bir proje yapmak için kullanacağız.
Bu uygulamada standart tetrislerde olan buton kullanımından sıyrılarak oyunu joystick ile oynayacağız. Joystick modülü içinde bulunan buton oyunu başlatacaktır. Sağ veya sola yönlendirmek için X eksenini, aşağı ve rotasyon değişimi için Y eksenini kullanacağız.
Siz de verilen kodlarda tanımlı olan buzzer’ı uygulamaya eklemeye çalışınız. Bu uygulamada biz Arduino Uno’yu kullandık. Dilerseniz Arduino Nano benzeri bir kartı kullanabilirsiniz. Bir 3D yazıcıya sahipseniz bu uygulamayı ürün haline de getirebilirsiniz.

Kullanılan Malzemeler:

Arduino Tetris Oyunu Bağlantı Şeması:

Aşağıda bulunan bağlantı şemasına göre bağlantılarımızı yapalım. Pin sıralamasına dikkat edelim. Pin sıralaması üreticiden üreticiye değişebilmektedir. Vcc ve GND bağlantıları ters bağlandığı zaman oled ekranımız zarar görür.

Arduino Tetris Oyunu Kodu:

Öncelikle Oyun için gerekli kütüphaneleri indirelim. Arduino IDE de Taslak->Dosya Ekle menüsüne tıklıyoruz. Daha indirmiş olduğumuz kütüphane dosyalarından “TetrisTheme.cpp” ve “dpad.cpp” ‘yi ekliyoruz. Aşağıda bulunan kodları Arduino Uno kartımıza atıyoruz.


/*
* Bu tetris uygulaması https://github.com/AJRussell/Tiny-Tetris 'dan Faydalanılarak geliştirilmiştir.
* https://create.arduino.cc/projecthub/BADFEED/tetris-clone-with-oled-ssd1306-i2c-for-arduino-nano-uno-8d9395 ilgili linkten orjinal sürümüne gidebilirsiniz.
akademi.robolinkmarket.com
*/
#include <Wire.h>
#include "TetrisTheme.cpp"
#include "dpad.cpp"
#define OLED_ADDRESS 0x3C //you may need to change this, this is the OLED I2C address.
#define OLED_COMMAND 0x80
#define OLED_DATA 0x40
#define OLED_DISPLAY_OFF 0xAE
#define OLED_DISPLAY_ON 0xAF
#define OLED_NORMAL_DISPLAY 0xA6
#define OLED_INVERSE_DISPLAY 0xA7
#define OLED_SET_BRIGHTNESS 0x81
#define OLED_SET_ADDRESSING 0x20
#define OLED_HORIZONTAL_ADDRESSING 0x00
#define OLED_VERTICAL_ADDRESSING 0x01
#define OLED_PAGE_ADDRESSING 0x02
#define OLED_SET_COLUMN 0x21
#define OLED_SET_PAGE 0x22
// the tetris blocks
const byte Blocks[7][2] PROGMEM = {
{ 0B01000100, 0B01000100 },
{ 0B11000000, 0B01000100 },
{ 0B01100000, 0B01000100 },
{ 0B01100000, 0B00000110 },
{ 0B11000000, 0B00000110 },
{ 0B01000000, 0B00001110 },
{ 0B01100000, 0B00001100 }
};
// the numbers for score, To do: create letter fonts
const byte NumberFont[10][8] PROGMEM = {
{ 0x00, 0x1c, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x1c },
{ 0x00, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x08 },
{ 0x00, 0x3e, 0x02, 0x04, 0x18, 0x20, 0x22, 0x1c },
{ 0x00, 0x1c, 0x22, 0x20, 0x18, 0x20, 0x22, 0x1c },
{ 0x00, 0x10, 0x10, 0x3e, 0x12, 0x14, 0x18, 0x10 },
{ 0x00, 0x1c, 0x22, 0x20, 0x20, 0x1e, 0x02, 0x3e },
{ 0x00, 0x1c, 0x22, 0x22, 0x1e, 0x02, 0x04, 0x18 },
{ 0x00, 0x04, 0x04, 0x04, 0x08, 0x10, 0x20, 0x3e },
{ 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c },
{ 0x00, 0x0c, 0x10, 0x20, 0x3c, 0x22, 0x22, 0x1c }
};
// "Tiny Tetris" upside-down text binarized from http://www.dcode.fr/binary-image
const byte welcomeScreen[16][5] PROGMEM = {
B01110011, B10100010, B00100011, B11100010, B00000000,
B10001001, B00100010, B00100000, B00100010, B00000000,
B10000001, B00100010, B00100000, B00100010, B00000000,
B01110001, B00011110, B00100001, B11100010, B00000000,
B00001001, B00100010, B00100000, B00100010, B00000000,
B10001001, B00100010, B00100000, B00100010, B00000000,
B01110011, B10011110, B11111011, B11101111, B10000000,
B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B10001000, B10111000, B10000000, B00000000,
B00000000, B10001100, B10010000, B10000000, B00000000,
B00000000, B10001100, B10010000, B10000000, B00000000,
B00000001, B01001010, B10010000, B10000000, B00000000,
B00000010, B00101001, B10010000, B10000000, B00000000,
B00000010, B00101001, B10010000, B10000000, B00000000,
B00000010, B00101000, B10111011, B11100000, B00000000
};
// Tetris Illustration upside-down image binarized from http://www.dcode.fr/binary-image
const byte tetrisLogo[40][8] PROGMEM = {
B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111, B11111111,
B11101101, B10111111, B11111111, B11111111, B11111111, B01111111, B11111001, B11100111,
B11101101, B00110100, B11111111, B11111111, B11111110, B01110011, B11110001, B11100111,
B10111000, B01010101, B11111111, B11111111, B11111000, B01110011, B11100001, B11100111,
B10011110, B10110011, B10110011, B11100011, B11100100, B00100011, B11100011, B11110011,
B10001111, B00010011, B00110001, B11110001, B11110100, B00100011, B11100011, B11110011,
B10001111, B00000111, B01110001, B11110000, B11110010, B00110011, B11100011, B11110001,
B10001111, B00000110, B01100001, B11111000, B11111010, B00000001, B11000001, B11100001,
B10000110, B00001110, B11100000, B11111000, B01111001, B00000001, B11000000, B11000001,
B10000110, B00001100, B11100000, B11111100, B01111001, B00000001, B11000000, B00000001,
B10000110, B00001100, B11110000, B11111100, B01111001, B00000000, B10000000, B00000001,
B10000110, B00001100, B11110000, B01111100, B01111001, B00000000, B10000000, B00000001,
B10000110, B00000110, B11110000, B01111100, B01111001, B00000000, B10000000, B00000001,
B10000110, B00000111, B01111000, B01111000, B01110010, B00000000, B10000000, B00000001,
B10001101, B00000011, B00111000, B01111000, B01110010, B00000000, B00000000, B00000001,
B10011001, B10000011, B10111000, B01111000, B11110100, B00000000, B00000000, B00000001,
B10011001, B10000001, B10011100, B01110001, B11101100, B00000000, B00000000, B00000001,
B10001001, B00000000, B11111100, B01110001, B11011000, B00000000, B00000000, B00000001,
B10001011, B00000000, B01111100, B01100011, B10110000, B00000000, B00000000, B00000001,
B10000110, B00000000, B00110100, B11100111, B01100000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011110, B11100110, B01000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00001110, B11001100, B10000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000110, B11011011, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000111, B11010010, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000011, B10100100, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000001, B11111000, B00000000, B00000000, B00110000, B00000001,
B10000000, B00000000, B00000000, B11110000, B00000000, B00000000, B00110000, B00000001,
B10000000, B00000000, B00000000, B11010000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B01110000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B10000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000011, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000011, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B01100000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B11110000, B00000000, B00000000, B00000000, B00010001,
B10000000, B00000000, B00000000, B11001000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000001, B10001000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000001, B10001000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B10010000, B00000000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000000, B11110000, B00001000, B00000000, B00000000, B00000001
};
// Tetris Brick upside-down image binarized from http://www.dcode.fr/binary-image
const byte brickLogo[36][8] PROGMEM= {
B10000000, B00000000, B00000000, B00000000, B00000000, B11111111, B11111100, B00000001,
B10000000, B00000111, B11111100, B11111111, B11111110, B11111111, B11111100, B00000001,
B10000011, B11111111, B11111110, B11111111, B11111111, B01111111, B11111110, B00000001,
B10000011, B11111111, B11111110, B01111111, B11111111, B00111111, B11111111, B00000001,
B10000011, B11111111, B11111111, B01111111, B11111111, B10111111, B11111111, B10000001,
B10001001, B11111111, B11111111, B00111111, B11111111, B10011111, B11111111, B10000001,
B10001101, B11111111, B11111111, B10111111, B11111111, B11001111, B11111111, B11000001,
B10001101, B11111111, B11111111, B10011111, B11111111, B11101111, B11111111, B11100001,
B10001100, B11111111, B11111111, B11011111, B11111111, B11100111, B11111111, B11110001,
B10001110, B11111111, B11111111, B11001111, B11111111, B11110111, B11111111, B11110001,
B10001110, B11111111, B11111111, B11101111, B11111111, B11111011, B11111111, B00000001,
B10001110, B01111111, B11111111, B11101111, B11111111, B11100000, B00000000, B00010001,
B10001111, B01111111, B11111111, B11100100, B00000000, B00000001, B11111111, B11110001,
B10001111, B00111111, B10000000, B00000000, B00111111, B11111011, B11111111, B11110001,
B10011111, B00000000, B00000111, B11110111, B11111111, B11110011, B11111111, B11100001,
B10001111, B00111111, B11111111, B11100111, B11111111, B11110111, B11111111, B11000001,
B10001111, B00111111, B11111111, B11101111, B11111111, B11100111, B11111111, B11000001,
B10001111, B01111111, B11111111, B11101111, B11111111, B11101111, B11111111, B10000001,
B10001111, B01111111, B11111111, B11001111, B11111111, B11001111, B11111111, B10000001,
B10000111, B01111111, B11111111, B11011111, B11111111, B11011111, B11111111, B00000001,
B10000110, B01111111, B11111111, B11011111, B11111111, B11011111, B11111111, B00000001,
B10000110, B01111111, B11111111, B10011111, B11111111, B10111111, B11111110, B00000001,
B10000010, B11111111, B11111111, B10111111, B11111111, B10111111, B11111000, B00000001,
B10000010, B11111111, B11111111, B10111111, B11111111, B00110000, B00000000, B00000001,
B10000010, B11111111, B11111111, B00111111, B11100000, B00000000, B00000000, B00000001,
B10000000, B11111111, B11111111, B00000000, B00000110, B00000000, B00000000, B00000001,
B10000000, B11111111, B11000000, B00000111, B11111110, B00000000, B00000000, B00000001,
B10000000, B10000000, B00001110, B01111111, B11111100, B00000000, B00000000, B00000001,
B10000000, B00000000, B00111110, B11111111, B11111100, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011110, B11111111, B11111100, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011100, B11111111, B11111000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00011101, B11111111, B11111000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00001101, B11111111, B11110000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00001001, B11111111, B11110000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000011, B11111111, B11100000, B00000000, B00000000, B00000001,
B10000000, B00000000, B00000011, B11110000, B00000000, B00000000, B00000000, B00000001
};
#define KEY_MIDDLE 0
#define KEY_LEFT 1
#define KEY_RIGHT 2
#define KEY_DOWN 3
#define KEY_ROTATE 4
#define PIEZO_PIN 3
#define LED_PIN 13
struct PieceSpace {
byte umBlock[4][4];
char Row;
char Coloum;
};
//Globals, is a mess. To do: tidy up and reduce glogal use if possible
byte pageArray[8] = { 0 };
byte scoreDisplayBuffer[8][6] = { { 0 }, { 0 } };
byte nextBlockBuffer[8][2] = { { 0 }, { 0 } };
bool optomizePageArray[8] = { 0 };
byte blockColoum[10] = { 0 };
byte tetrisScreen[14][25] = { { 1 } , { 1 } };
PieceSpace currentPiece = { 0 };
PieceSpace oldPiece = { 0 };
byte nextPiece = 0;
//keyPress key = { 0 };
bool gameOver = false;
unsigned long moveTime = 0;
int pageStart = 0;
int pageEnd = 0;
int score = 0;
int acceleration = 0;
int level = 0;
int levellineCount = 0;
int dropDelay = 1000;
int lastKey = 0;
// I2C
void OLEDCommand(byte command) {
Wire.beginTransmission(OLED_ADDRESS);
Wire.write(OLED_COMMAND);
Wire.write(command);
Wire.endTransmission();
}
void OLEDData(byte data) {
Wire.beginTransmission(OLED_ADDRESS);
Wire.write(OLED_DATA);
Wire.write(data);
Wire.endTransmission();
}
void setup() {
Serial.begin(9600);
while (!Serial);
Wire.begin();
Wire.setClock(400000);
pinMode(PIEZO_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
OLEDCommand(OLED_DISPLAY_OFF);
delay(20);
OLEDCommand(OLED_DISPLAY_ON);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
delay(20);
OLEDCommand(0x8D);
delay(20);
OLEDCommand(0x14);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
fillTetrisScreen(0);
randomSeed(analogRead(7)); /// To do: create a decent random number generator.
pinMode(JoyX, INPUT);
pinMode(JoyY, INPUT);
pinMode(Button, INPUT_PULLUP);
// blink led
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(200);
digitalWrite(LED_PIN, HIGH);
delay(50);
digitalWrite(LED_PIN, LOW);
}
void fillTetrisArray(byte value) {
for (char r = 0; r < 24; r++) {
for (char c = 0; c < 14; c++) {
tetrisScreen[c][r] = value;
}
}
for (char r = 21; r < 24; r++) {
for (char c = 0; c < 14; c++) {
tetrisScreen[c][r] = 0;
}
}
}
void fillTetrisScreen(byte value) {
for (int r = 1; r < 21; r++) {
for (int c = 2; c < 12; c++) {
tetrisScreen[c][r] = value;
}
}
}
void drawTetrisScreen() {
for (byte r = 1; r < 21; r++) {
//loop through rows to see if there is data to be sent
for (byte c = 2; c < 12; c++) {
if ((tetrisScreen[c][r] == 2) | (tetrisScreen[c][r] == 3)) {
//send line to screen
for (byte i = 0; i < 10; i++) {
blockColoum[i] = tetrisScreen[i + 2][r];
//clear delete block
if (tetrisScreen[i + 2][r] == 3) tetrisScreen[i + 2][r] = 0;
}
drawTetrisLine((r – 1) * 6);
break; break;
}
}
}
}
void drawTetrisTitle(bool blank = false) {
byte byteval;
//set Vertical addressing mode and column – page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand( 50 ); //Set column start
OLEDCommand( 66 ); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand( 1 ); //Set page start
OLEDCommand( 5 ); //Set page end
for (int r = 0; r <16; r++) {
for (int c = 4; c >=0; c–) {
if(blank) {
OLEDData(0);
}else {
byteval = pgm_read_byte(&welcomeScreen[r][c]);
OLEDData(byteval);
}
}
}
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand( 1 ); //Set column start
OLEDCommand( 42 ); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand( 0 ); //Set page start
OLEDCommand( 7 ); //Set page end
for (int r = 0; r <40; r++) {
for (int c = 7; c >=0; c–) {
if(blank) {
OLEDData(0);
}else {
byteval = pgm_read_byte(&tetrisLogo[r][c]);
OLEDData(byteval);
}
}
}
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand( 75 ); //Set column start
OLEDCommand( 116 ); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand( 0 ); //Set page start
OLEDCommand( 7 ); //Set page end
for (int r = 0; r <36; r++) {
for (int c = 7; c >=0; c–) {
if(blank) {
OLEDData(0);
}else {
byteval = pgm_read_byte(&brickLogo[r][c]);
OLEDData(byteval);
}
}
}
//brickLogo[36][8]
}
void drawTetrisLine(byte x) {
//fill array with blocks based on blockRow
//clear page and Optimize array
memset(optomizePageArray, 0, 8); ///review this… declare them here? interesting question…
memset(pageArray, 0, 8);
x++; // up one
//*********Column 0***********
//draw block
if (blockColoum[0] == 2 | blockColoum[0] == 1) {
pageArray[0] = pageArray[0] | B11111001;
optomizePageArray[0] = 1;
}
//delete block
if (blockColoum[0] == 3) {
pageArray[0] = pageArray[0] | B00000001; //create side wall
pageArray[0] = pageArray[0] & B00000111;
optomizePageArray[0] = 1;
}
//*********Column 1***********
if (blockColoum[1] == 2 | blockColoum[1] == 1) {
pageArray[1] = pageArray[1] | B00111110;
optomizePageArray[1] = 1;
}
//delete block
if (blockColoum[1] == 3) {
pageArray[1] = pageArray[1] & B11000001;
optomizePageArray[1] = 1;
}
//*********Column 2***********
if (blockColoum[2] == 2 | blockColoum[2] == 1) {
pageArray[1] = pageArray[1] | B10000000;
optomizePageArray[1] = 1;
pageArray[2] = pageArray[2] | B00001111;
optomizePageArray[2] = 1;
}
//delete block
if (blockColoum[2] == 3) {
pageArray[1] = pageArray[1] & B01111111;
optomizePageArray[1] = 1;
pageArray[2] = pageArray[2] & B11110000;
optomizePageArray[2] = 1;
}
//*********Column 3***********
if (blockColoum[3] == 2 | blockColoum[3] == 1) {
pageArray[2] = pageArray[2] | B11100000;
optomizePageArray[2] = 1;
pageArray[3] = pageArray[3] | B00000011;
optomizePageArray[3] = 1;
}
//delete block
if (blockColoum[3] == 3) {
pageArray[2] = pageArray[2] & B00011111;
optomizePageArray[2] = 1;
pageArray[3] = pageArray[3] & B11111100;
optomizePageArray[3] = 1;
}
//*********Column 4***********
if (blockColoum[4] == 2 | blockColoum[4] == 1) {
pageArray[3] = pageArray[3] | B11111000;
optomizePageArray[3] = 1;
}
//delete block
if (blockColoum[4] == 3) {
pageArray[3] = pageArray[3] & B00000111;
optomizePageArray[3] = 1;
}
//*********Column 5***********
if (blockColoum[5] == 2 | blockColoum[5] == 1) {
pageArray[4] = pageArray[4] | B00111110;
optomizePageArray[4] = 1;
}
//delete block
if (blockColoum[5] == 3) {
pageArray[4] = pageArray[4] & B11000001;
optomizePageArray[4] = 1;
}
//*********Column 6***********
if (blockColoum[6] == 2 | blockColoum[6] == 1) {
pageArray[4] = pageArray[4] | B10000000;
optomizePageArray[4] = 1;
pageArray[5] = pageArray[5] | B00001111;
optomizePageArray[5] = 1;
}
//delete block
if (blockColoum[6] == 3) {
pageArray[4] = pageArray[4] & B01111111;
optomizePageArray[4] = 1;
pageArray[5] = pageArray[5] & B11110000;
optomizePageArray[5] = 1;
}
//*********Column 7***********
if (blockColoum[7] == 2 | blockColoum[7] == 1) {
pageArray[5] = pageArray[5] | B11100000;
optomizePageArray[5] = 1;
pageArray[6] = pageArray[6] | B00000011;
optomizePageArray[6] = 1;
}
if (blockColoum[7] == 3) {
pageArray[5] = pageArray[5] & B00011111;
optomizePageArray[5] = 1;
pageArray[6] = pageArray[6] & B11111100;
optomizePageArray[6] = 1;
}
//*********Column 8***********
if (blockColoum[8] == 2 | blockColoum[8] == 1) {
pageArray[6] = pageArray[6] | B11111000;
optomizePageArray[6] = 1;
}
//delete block
if (blockColoum[8] == 3) {
pageArray[6] = pageArray[6] & B00000111;
optomizePageArray[6] = 1;
}
//*********Column 9***********
if (blockColoum[9] == 2 | blockColoum[9] == 1) {
pageArray[7] = pageArray[7] | B10111110;
optomizePageArray[7] = 1;
}
if (blockColoum[9] == 3) {
pageArray[7] = pageArray[7] | B10000000;//create side wall
pageArray[7] = pageArray[7] & B11000001;
optomizePageArray[7] = 1;
}
//Optimize – figure out what page array has data
for (int page = 0; page < 8; page++) {
if (optomizePageArray[page]) {
//block found set page start
pageStart = page;
break;
}
}
for (int page = 7; page >= 0; page–) {
if (optomizePageArray[page]) {
//block found set page end
pageEnd = page;
break;
}
}
//set Vertical addressing mode and column – page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(x);
OLEDCommand(x + 4);
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(pageStart);
OLEDCommand(pageEnd);
//send the array 5 times
for (int c = 0; c < 5; c++) {
for (int p = pageStart; p <= pageEnd; p++) {
OLEDData(pageArray[p]);
}
}
}
void loadPiece(byte pieceNumber, byte row, byte coloum, bool loadScreen) {
//load the piece from piece array to screen
byte pieceRow = 0;
byte pieceColoum = 0;
byte c = 0;
// load piece from progmem
byte byte_in;
bool piece_out[4][4];
byte piece_bit[2] = {0,0};
for(int i=0;i<2;i++) {
byte_in = pgm_read_byte(&Blocks[pieceNumber-1][i]);
for( byte mask = 1; mask; mask <<=1) {
if(mask & byte_in) {
piece_out[piece_bit[0]][piece_bit[1]] = 1;
} else {
piece_out[piece_bit[0]][piece_bit[1]] = 0;
}
piece_bit[1]++;
if(piece_bit[1]>=4) {
piece_bit[1]=0;
piece_bit[0]++;
}
}
}
memcpy(currentPiece.umBlock, piece_out, 16);
currentPiece.Row = row;
currentPiece.Coloum = coloum;
if (loadScreen) {
oldPiece = currentPiece;
for (c = coloum; c < coloum + 4; c++) {
for (int r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
}
}
void drawPiece() {
char coloum;
char row;
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
// delete blocks first
coloum = oldPiece.Coloum;
row = oldPiece.Row;
for (c = coloum; c < coloum + 4; c++) {
for (char r = row; r < row + 4; r++) {
if (oldPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 3;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
//draw new blocks
pieceRow = 0;
pieceColoum = 0;
c = 0;
coloum = currentPiece.Coloum;
row = currentPiece.Row;
for (c = coloum; c < coloum + 4; c++) {
for (char r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 2;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
}
void drawLandedPiece() {
char coloum;
char row;
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
// Landed pieces are 1
coloum = currentPiece.Coloum;
row = currentPiece.Row;
for (c = coloum; c < coloum + 4; c++) {
for (int r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) tetrisScreen[c][r] = 1;
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
processCompletedLines();
}
bool led = true;
void RotatePiece() {
byte i, j;
byte umFig[4][4] = { 0 };
memcpy(oldPiece.umBlock, currentPiece.umBlock, 16);
oldPiece.Row = currentPiece.Row;
oldPiece.Coloum = currentPiece.Coloum;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 4; ++j) {
umFig[j][i] = currentPiece.umBlock[4 – i – 1][j];
}
}
oldPiece = currentPiece;
memcpy(currentPiece.umBlock, umFig, 16);
if (checkColloision()) currentPiece = oldPiece;
// no need for this…
if (led) {
digitalWrite(LED_PIN, HIGH);
led = false;
}
delay(1);
digitalWrite(LED_PIN, LOW);
if (led == false) {
digitalWrite(LED_PIN, LOW);
led = true;
}
}
bool movePieceDown() {
bool pieceLanded = false;
char rndPiece = 0;
oldPiece = currentPiece;
currentPiece.Row = currentPiece.Row1;
//check collision
if (checkColloision()) {
// its at the bottom make it a landed piece and start new piece
currentPiece = oldPiece; // back to where it was
drawLandedPiece();
pieceLanded = true;
}
if (pieceLanded) {
loadPiece(nextPiece, 19, 4, false);
acceleration = 0;
if (checkColloision()) {
gameOver = true;
} else {
loadPiece(nextPiece, 19, 4, true);
acceleration = 0;//reset acceleration as there is a new piece
}
nextPiece = random(1, 8);
setNextBlock(nextPiece);
}
}
void movePieceLeft() {
oldPiece = currentPiece;
currentPiece.Coloum = currentPiece.Coloum1;
//check collision
if (checkColloision()) {
currentPiece = oldPiece; // back to where it was
}
}
void movePieceRight() {
oldPiece = currentPiece;
currentPiece.Coloum = currentPiece.Coloum + 1;
//check collision
if (checkColloision()) {
currentPiece = oldPiece; // back to where it was
}
}
bool checkColloision() {
byte pieceRow = 0;
byte pieceColoum = 0;
char c = 0;
char coloum = currentPiece.Coloum;
char row = currentPiece.Row;
//scan across piece and translate to Tetris array and check Collisions.
for (c = coloum; c < coloum + 4; c++) {
for (char r = row; r < row + 4; r++) {
if (currentPiece.umBlock[pieceColoum][pieceRow]) {
if (tetrisScreen[c][r] == 1) return true; //is it on landed blocks?
}
pieceRow++;
}
pieceRow = 0;
pieceColoum++;
}
return false;
}
void processCompletedLines() {
char rowCheck = 0;
char coloumCheck = 0;
bool fullLine = false;
bool noLine = true;
char linesProcessed = 0;
char clearedLines = 0;
char topRow = 0;
char bottomRow = 0;
char currentRow = 0;
int amountScored = 0;
if (currentPiece.Row < 1)bottomRow = 1;
else bottomRow = currentPiece.Row;
for (int rowCheck = bottomRow; rowCheck < currentPiece.Row + 4; rowCheck++) {
bool fullLine = true;
for (coloumCheck = 2; coloumCheck < 12; coloumCheck++) {
if (tetrisScreen[coloumCheck][rowCheck] == 0) {
fullLine = false;
break;
}
}
if (fullLine) {
//make line values 3's and render
for (char c = 2; c < 12; c++) {
tetrisScreen[c][rowCheck] = 3;
}
bottomRow = rowCheck + 1;
//line is now all 0's
linesProcessed++;
delay(77); // animation 🙂
}
drawTetrisScreen();
}
//******all lines are 0's and have been removed from the screen
if (linesProcessed) {
clearedLines = linesProcessed;
while (clearedLines) {
for (currentRow = 1; currentRow < 20; currentRow++) {
noLine = true;
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][currentRow]) noLine = false;
}
if (noLine) {
//move all lines down
for (int r = currentRow + 1; r < 20; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r]) tetrisScreen[c][r – 1] = 2;
else tetrisScreen[c][r – 1] = 3;
}
}
}
}
//make the 2's 1's
for (char r = 1; r < 24; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r] == 2)tetrisScreen[c][r] = 1;
}
}
clearedLines–;
drawTetrisScreen();
tone(PIEZO_PIN, 1000, 50);
delay(60);
tone(PIEZO_PIN, 2000, 50);
delay(50);
tone(PIEZO_PIN, 500, 50);
delay(60);
}
}
// ************** process score *******************
switch (linesProcessed) {
case 1: amountScored = 40 * (level + 1); break;
case 2: amountScored = 100 * (level + 1); break;
case 3: amountScored = 300 * (level + 1); break;
case 4: amountScored = 1200 * (level + 1);
//do 4 line affect
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(20);
OLEDCommand(OLED_NORMAL_DISPLAY);
break;
}
//score animation
for (long s = score; s < score + amountScored; s = s + (5 * (level + 1))) {
setScore(s, false);
}
score = score + amountScored;
setScore(score, false);
//****update level line count
levellineCount = levellineCount + linesProcessed;
if (levellineCount > 10) {
level++;
levellineCount = 0;
//do level up affect
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
for (int i = 250; i < 2500; i += 100) {
tone(PIEZO_PIN, i, 5);
delay(5);
tone(PIEZO_PIN, i / 2, 5);
delay(10);
}
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(100);
OLEDCommand(OLED_NORMAL_DISPLAY);
}
//make the 2's 1's
for (char r = bottomRow; r <= topRow; r++) {
for (char c = 2; c < 12; c++) {
if (tetrisScreen[c][r]) {
tetrisScreen[c][r] = 1;
}
}
}
}
void tetrisScreenToSerial() {
//for debug
for (int r = 0; r < 24; r++) {
for (int c = 0; c < 14; c++) {
Serial.print(tetrisScreen[c][r], DEC);
}
Serial.println();
}
Serial.println();
}
bool processKeys() {
bool keypressed = true;
int leftRight = 300 – acceleration;
int rotate = 700;
int down = 110 – acceleration;
int dpadpos = Dpad::getPos();
//Serial.println(dpadpos);
switch(dpadpos) {
case KEY_LEFT:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, leftRight);
}
movePieceLeft();
break;
case KEY_RIGHT:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, leftRight);
}
movePieceRight();
break;
case KEY_DOWN:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, down);
}
movePieceDown();
break;
case KEY_ROTATE:
if( Dpad::DoDebounce() ) {
acceleration = Dpad::setAccel(acceleration, rotate);
}
RotatePiece();
break;
default:
acceleration = 0;
processKey = true;
Debounce = 0;
keypressed = false;
break;
}
if (keypressed) {
drawPiece();
drawTetrisScreen();
}
}
void setScore(long score, bool blank)
{
// this is a kludge. To do: create a proper system for rendering numbers and letters.
long ones = (score % 10);
long tens = ((score / 10) % 10);
long hundreds = ((score / 100) % 10);
long thousands = ((score / 1000) % 10);
long tenthousands = ((score / 10000) % 10);
long hunderedthousands = ((score / 100000) % 10);
//create the score in upper left part of the screen
byte font = 0;
char bytes_out[8];
memset(scoreDisplayBuffer, 0, sizeof scoreDisplayBuffer);
//****************score digit 6****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hunderedthousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | bytes_out[i] >> 1;
}
//****************score digit 5****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tenthousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][0] = scoreDisplayBuffer[i][0] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | bytes_out[i] >> 1;
}
//****************score digit 4****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[thousands][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][1] = scoreDisplayBuffer[i][1] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | bytes_out[i] >> 1;
}
//****************score digit 3****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[hundreds][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][2] = scoreDisplayBuffer[i][2] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | bytes_out[i] >> 1;
}
//****************score digit 2****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[tens][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][3] = scoreDisplayBuffer[i][3] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | bytes_out[i] >> 1;
}
//****************score digit 1****************
for (int v = 0; v<8; v++) bytes_out[v] = pgm_read_byte(&NumberFont[ones][v]);
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][4] = scoreDisplayBuffer[i][4] | (bytes_out[i] << 6);
}
//write the number to the Score buffer
for (int i = 0; i < 8; i++)
{
scoreDisplayBuffer[i][5] = scoreDisplayBuffer[i][5] | bytes_out[i] >> 1;
}
//set Vertical addressing mode and column – page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(120); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(5); //Set page end
for (int p = 0; p < 8; p++)
{
for (int c = 0; c <6; c++)
{
if (blank) OLEDData(0);
else OLEDData(scoreDisplayBuffer[p][c]);
}
}
}
void setNextBlock(byte pieceNumber) {
memset(nextBlockBuffer, 0, sizeof nextBlockBuffer); //clear buffer
switch (pieceNumber) {
case 1:
//************l piece – 1 *************
for (int k = 2; k < 6; k++) {
nextBlockBuffer[k][0] = B01110111;
nextBlockBuffer[k][1] = B01110111;
}
break;
case 2:
//************J piece – 2 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B01110000;
}
break;
case 3:
//************L piece – 3 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
break;
case 4:
//************O piece – 4 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
break;
case 5:
//************S piece – 5 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B00000000;
nextBlockBuffer[k][1] = B11101110;
}
break;
case 6:
//************T piece – 6 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B01110111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B00000000;
nextBlockBuffer[k][1] = B00001110;
}
break;
case 7:
//************Z piece – 7 *************
for (int k = 0; k < 3; k++) {
nextBlockBuffer[k][0] = B01110000;
nextBlockBuffer[k][1] = B00000111;
}
for (int k = 4; k < 7; k++) {
nextBlockBuffer[k][0] = B11101110;
nextBlockBuffer[k][1] = B00000000;
}
break;
}
//set Vertical addressing mode and column – page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(120); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(6); //Set page start
OLEDCommand(7); //Set page end
for (int p = 0; p < 8; p++) {
for (int c = 0; c < 2; c++) {
OLEDData(nextBlockBuffer[p][c]);
}
}
}
void drawBottom() {
//set Vertical addressing mode and column – page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(0); //Set column start
OLEDCommand(0); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(7); //Set page end
for (int c = 0; c < 8; c++) {
OLEDData(255);
}
}
void drawSides() {
//set Vertical addressing mode and column – page start end
OLEDCommand(OLED_SET_ADDRESSING);
OLEDCommand(OLED_VERTICAL_ADDRESSING);
OLEDCommand(OLED_SET_COLUMN);
OLEDCommand(0); //Set column start
OLEDCommand(127); //Set column end
OLEDCommand(OLED_SET_PAGE);
OLEDCommand(0); //Set page start
OLEDCommand(7); //Set page end
for (int r = 0; r < 128; r++) {
for (int c = 0; c < 8; c++) {
if (c == 0) OLEDData(1);
else if (c == 7) OLEDData(128);
else OLEDData(0);
}
}
}
void loop() {
//main loop code
//To do: create high score system that savees to EEprom
gameOver = false;
score = 0;
fillTetrisArray(1); //fill with 1's to make border
fillTetrisScreen(2);
drawTetrisScreen();
delay(200);
fillTetrisScreen(3);
drawTetrisScreen();
delay(200);
drawSides();
drawBottom();
// tetrisScreenToSerial();
OLEDCommand(OLED_INVERSE_DISPLAY);
delay(200);
OLEDCommand(OLED_NORMAL_DISPLAY);
loadPiece(random(1, 8), 20, 5, true);
drawTetrisScreen();
nextPiece = random(1, 8);
setNextBlock(nextPiece);
setScore(0, false);
delay(300);
setScore(0, true);
delay(300);
setScore(0, false);
byte rnd = 0;
drawTetrisTitle(false);
TetrisTheme::start();
while(songOn) TetrisTheme::tetrisThemePlay();
drawTetrisTitle(true);
drawSides();
drawBottom();
setScore(0, false);
for(int i=1;i<10;i++) {
nextPiece = random(1, 8);
setNextBlock(nextPiece);
delay(100);
}
while (!gameOver) {
movePieceDown();
drawPiece();
drawTetrisScreen();
moveTime = millis();
while (millis() – moveTime < (dropDelay – (level * 50))) {
processKeys();
}
}
}

Tetris oyunumuz hazır!!! Bu veya buna benzer projelerini #projebaşlasın etiketiyle paylaşmayı unutma 🙂
Farklı proje önerilerinizi veya sorularınızı yorum olarak bırakabilirsiniz.

CEVAP VER

Lütfen yorumunuzu giriniz!
Lütfen isminizi buraya giriniz