mirror of
https://github.com/felixalbrigtsen/sfml_tetris
synced 2025-01-18 07:16:44 +01:00
Add files via upload
This commit is contained in:
parent
31cf987f1e
commit
5719c6c8ca
BIN
Pixelmania.ttf
Normal file
BIN
Pixelmania.ttf
Normal file
Binary file not shown.
201
main.cpp
Normal file
201
main.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
//Felix Albrigtsen 2020
|
||||
//Graphics and loops are contained in this file.
|
||||
//All game logic is defined in tetris.cpp
|
||||
|
||||
#include "tetris.hpp"
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <math.h>
|
||||
|
||||
#define SCALE 50
|
||||
#define MENUWIDTH SCALE*BOARDWIDTH
|
||||
#define MENUMARGIN 80
|
||||
#define MENUTEXT 30
|
||||
#define NPIECESIZE (MENUWIDTH-(2*MENUMARGIN))
|
||||
|
||||
#define MOVES_PER_TICK 2
|
||||
|
||||
int target_delay = 650; //Miliseconds between each fall
|
||||
|
||||
sf::Color colors[] = {
|
||||
sf::Color::Black,
|
||||
sf::Color::Cyan,
|
||||
sf::Color::Blue,
|
||||
sf::Color(255,128,0),
|
||||
sf::Color::Yellow,
|
||||
sf::Color::Green,
|
||||
sf::Color::Magenta,
|
||||
sf::Color::Red
|
||||
};
|
||||
|
||||
sf::RenderWindow window(sf::VideoMode(BOARDWIDTH*SCALE + MENUWIDTH, BOARDHEIGHT*SCALE),"Tetris");
|
||||
sf::Font font;
|
||||
sf::Text txt_level;
|
||||
sf::Text txt_lines;
|
||||
sf::Text txt_score;
|
||||
sf::Text txt_next;
|
||||
sf::RectangleShape nextSquare;
|
||||
sf::RectangleShape NPieceRect;
|
||||
|
||||
static int NPieceBlockSize = NPIECESIZE / 6;
|
||||
|
||||
void drawBoard(std::array<int, BOARDSIZE> board) {
|
||||
for (int i = 0; i < BOARDSIZE; i++) {
|
||||
int x = i % BOARDWIDTH;
|
||||
int y = (i - x) / BOARDWIDTH;
|
||||
|
||||
sf::RectangleShape square(sf::Vector2f(SCALE, SCALE));
|
||||
if (board[i] != 0) {
|
||||
square.setOutlineThickness(-3);
|
||||
} else {
|
||||
square.setOutlineThickness(-1);
|
||||
}
|
||||
|
||||
square.setOutlineColor(sf::Color(50,50,50));
|
||||
|
||||
square.setPosition(x*SCALE, y*SCALE);
|
||||
|
||||
square.setFillColor(colors[board[i]]);
|
||||
window.draw(square);
|
||||
}
|
||||
}
|
||||
|
||||
void drawInfo(std::array<int, 4> info) {
|
||||
// In order: Level, Lines, Score, Next piece
|
||||
int level = info[0];
|
||||
int lines = info[1];
|
||||
int points = info[2];
|
||||
int nextPiece = info[3];
|
||||
|
||||
target_delay = (650 * pow(0.92, level));
|
||||
|
||||
txt_level.setString("LEVEL " + std::to_string(level));
|
||||
txt_lines.setString(std::to_string(lines) + " LINES");
|
||||
txt_score.setString(std::to_string(points) + " POINTS");
|
||||
|
||||
window.draw(txt_level);
|
||||
window.draw(txt_lines);
|
||||
window.draw(txt_score);
|
||||
|
||||
window.draw(nextSquare);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int x = TETROMINOES[info[3]][0][i][0];
|
||||
int y = TETROMINOES[info[3]][0][i][1];
|
||||
|
||||
NPieceRect.setFillColor(colors[info[3]+1]);
|
||||
//NPieceRect.setPosition((BOARDWIDTH*SCALE)+MENUMARGIN+(x*NPieceBlockSize) + (NPIECESIZE/4), MENUMARGIN + (y*NPieceBlockSize)+ (NPIECESIZE/3));
|
||||
int xpos = (BOARDWIDTH*SCALE)+MENUMARGIN+(x*NPieceBlockSize);
|
||||
int ypos = MENUMARGIN + (y*NPieceBlockSize);
|
||||
|
||||
//Correct positioning
|
||||
switch(info[3]) {
|
||||
case 0: //I
|
||||
xpos += NPieceBlockSize;
|
||||
ypos += NPieceBlockSize*1.5;
|
||||
break;
|
||||
case 3: //Sqare / O
|
||||
xpos += NPieceBlockSize;
|
||||
ypos += NPieceBlockSize*2;
|
||||
break;
|
||||
default:
|
||||
xpos += NPieceBlockSize*1.5;
|
||||
ypos += NPieceBlockSize*2;
|
||||
break;
|
||||
}
|
||||
|
||||
NPieceRect.setPosition(xpos, ypos);
|
||||
window.draw(NPieceRect);
|
||||
}
|
||||
}
|
||||
|
||||
void initText() {
|
||||
font.loadFromFile("Pixelmania.ttf");
|
||||
|
||||
//Initialize text objects for displaying progress
|
||||
txt_level.setFont(font);
|
||||
txt_level.setCharacterSize(MENUTEXT);
|
||||
txt_level.setPosition(MENUMARGIN + (BOARDWIDTH*SCALE), NPIECESIZE + MENUMARGIN*2);
|
||||
txt_level.setFillColor(sf::Color::White);
|
||||
|
||||
txt_lines.setFont(font);
|
||||
txt_lines.setCharacterSize(MENUTEXT);
|
||||
txt_lines.setPosition(MENUMARGIN + (BOARDWIDTH*SCALE), NPIECESIZE + MENUMARGIN*3);
|
||||
txt_lines.setFillColor(sf::Color::White);
|
||||
|
||||
txt_score.setFont(font);
|
||||
txt_score.setCharacterSize(MENUTEXT);
|
||||
txt_score.setPosition(MENUMARGIN + (BOARDWIDTH*SCALE), NPIECESIZE + MENUMARGIN*4);
|
||||
txt_score.setFillColor(sf::Color::White);
|
||||
|
||||
//Initialize the everything neccessary to display the next piece
|
||||
nextSquare.setSize(sf::Vector2f(NPIECESIZE, NPIECESIZE));
|
||||
nextSquare.setPosition((BOARDWIDTH*SCALE)+MENUMARGIN, MENUMARGIN);
|
||||
nextSquare.setOutlineThickness(-12);
|
||||
nextSquare.setOutlineColor(sf::Color::White);
|
||||
nextSquare.setFillColor(sf::Color::Black);
|
||||
|
||||
NPieceRect.setSize(sf::Vector2f(NPieceBlockSize, NPieceBlockSize));
|
||||
NPieceRect.setOutlineThickness(-3);
|
||||
NPieceRect.setOutlineColor(sf::Color(200,200,200));
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Tetris game;
|
||||
game.init();
|
||||
initText();
|
||||
|
||||
drawBoard(game.getBoard());
|
||||
drawInfo(game.getInfo());
|
||||
|
||||
sf::Clock clock;
|
||||
|
||||
int tickCount = 0;
|
||||
|
||||
while (window.isOpen()) {
|
||||
sf::Event event;
|
||||
|
||||
while (window.pollEvent(event)) {
|
||||
if (event.type == sf::Event::Closed) {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
window.clear(sf::Color(100,100,100));
|
||||
|
||||
bool keys[] = {
|
||||
sf::Keyboard::isKeyPressed(sf::Keyboard::Left),
|
||||
sf::Keyboard::isKeyPressed(sf::Keyboard::Right),
|
||||
sf::Keyboard::isKeyPressed(sf::Keyboard::Up),
|
||||
sf::Keyboard::isKeyPressed(sf::Keyboard::Down),
|
||||
sf::Keyboard::isKeyPressed(sf::Keyboard::Space)
|
||||
};
|
||||
|
||||
game.tick(keys, (tickCount == MOVES_PER_TICK));
|
||||
|
||||
if (tickCount == MOVES_PER_TICK) {
|
||||
tickCount = 0;
|
||||
} else {
|
||||
tickCount += 1;
|
||||
}
|
||||
|
||||
//Display game board and stats
|
||||
drawBoard(game.getBoard());
|
||||
drawInfo(game.getInfo());
|
||||
|
||||
//Delay to maintain correct speed
|
||||
sf::Time framePeriod = clock.getElapsedTime();
|
||||
while (framePeriod.asMilliseconds() < target_delay / MOVES_PER_TICK) {
|
||||
framePeriod = clock.getElapsedTime();
|
||||
}
|
||||
clock.restart();
|
||||
|
||||
|
||||
|
||||
window.display();
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
13
makefile
Normal file
13
makefile
Normal file
@ -0,0 +1,13 @@
|
||||
LINKER_FLAGS = -lsfml-graphics -lsfml-window -lsfml-system
|
||||
|
||||
main: main.cpp
|
||||
g++ -c main.cpp
|
||||
|
||||
game: tetris.cpp tetris.hpp
|
||||
g++ -c tetris.cpp -o game.o
|
||||
|
||||
out: main game
|
||||
g++ main.o game.o -o out $(LINKER_FLAGS)
|
||||
|
||||
clean:
|
||||
rm main.o game.o out
|
206
tetris.cpp
Normal file
206
tetris.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
//Felix Albrigtsen 2020
|
||||
//All game logic, but no graphics are defined in this file
|
||||
|
||||
#include "tetris.hpp"
|
||||
#include <array>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
|
||||
void Tetris::newPiece() {
|
||||
cury = 0;
|
||||
curx = (BOARDWIDTH / 2)-1;
|
||||
curr = 0;
|
||||
|
||||
curPiece = nextPiece;
|
||||
nextPiece = rand() % 7;
|
||||
|
||||
if (staticBoard[BOARDWIDTH*1.5] != 0) { gameOver = true; }
|
||||
}
|
||||
|
||||
void Tetris::init() {
|
||||
srand((unsigned) time(NULL));
|
||||
staticBoard.fill(0);
|
||||
newPiece();
|
||||
lines = 0;
|
||||
points = 0;
|
||||
level = 0;
|
||||
levelProgress = 0;
|
||||
nextPiece = rand() % 7;
|
||||
}
|
||||
|
||||
std::array<int, BOARDSIZE> Tetris::getBoard() {
|
||||
//Returns staticboard and the current piece
|
||||
std::array<int, BOARDSIZE> board = staticBoard;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int xpos = curx + TETROMINOES[curPiece][curr][i][0];
|
||||
int ypos = cury + TETROMINOES[curPiece][curr][i][1];
|
||||
int index = (ypos * BOARDWIDTH) + xpos;
|
||||
board[index] = curPiece+1;
|
||||
}
|
||||
return board;
|
||||
}
|
||||
|
||||
std::array<int, 4> Tetris::getInfo() {
|
||||
//Return Level, Lines, Score, Next piece
|
||||
std::array<int, 4> info = {level, lines, points, nextPiece };
|
||||
return info;
|
||||
}
|
||||
|
||||
void Tetris::clearRows() {
|
||||
int prevlines = lines;
|
||||
|
||||
for (int line = 0; line < BOARDHEIGHT; line++) {
|
||||
bool full = true;
|
||||
for (int i = 0; i < BOARDWIDTH; i++) {
|
||||
if (staticBoard[ (line*BOARDWIDTH) + i ] == 0) {
|
||||
full = false;
|
||||
}
|
||||
}
|
||||
if (full) {
|
||||
|
||||
lines += 1;
|
||||
levelProgress += 1;
|
||||
if (levelProgress > level) {
|
||||
level += 1;
|
||||
levelProgress = 0;
|
||||
}
|
||||
|
||||
//Move everything down one line:
|
||||
int startIndex = (line*BOARDWIDTH) - 1;
|
||||
for (int i = startIndex; i > 0; i--) {
|
||||
staticBoard[i + BOARDWIDTH] = staticBoard[i];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
switch (lines - prevlines) {
|
||||
case 1:
|
||||
points += 40 * (level+1);
|
||||
break;
|
||||
case 2:
|
||||
points += 100 * (level+1);
|
||||
break;
|
||||
case 3:
|
||||
points += 300 * (level+1);
|
||||
break;
|
||||
case 4:
|
||||
points += 1200 * (level+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool Tetris::testLanded() {
|
||||
//Returns true if a part of the tetromino is directly above another piece
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int xpos = curx + TETROMINOES[curPiece][curr][i][0];
|
||||
int ypos = cury + TETROMINOES[curPiece][curr][i][1] + 1;
|
||||
int index = (ypos * BOARDWIDTH) + xpos;
|
||||
if (staticBoard[index] != 0 || ypos == BOARDHEIGHT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Tetris::lock() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int xpos = curx + TETROMINOES[curPiece][curr][i][0];
|
||||
int ypos = cury + TETROMINOES[curPiece][curr][i][1];
|
||||
int index = (ypos * BOARDWIDTH) + xpos;
|
||||
staticBoard[index] = curPiece+1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Tetris::move(bool left, bool right) {
|
||||
//Returns true if the move was legal
|
||||
int newx;
|
||||
if (right && !left) {
|
||||
newx = curx + 1;
|
||||
} else if (!right && left) {
|
||||
newx = curx - 1;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool possible = true;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int xpos = newx + TETROMINOES[curPiece][curr][i][0];
|
||||
int ypos = cury + TETROMINOES[curPiece][curr][i][1];
|
||||
int index = (ypos * BOARDWIDTH) + xpos;
|
||||
if (staticBoard[index] != 0) { possible = false; }
|
||||
if (xpos < 0 || xpos >= BOARDWIDTH) { possible = false; }
|
||||
}
|
||||
if (possible) {
|
||||
curx = newx;
|
||||
}
|
||||
return possible;
|
||||
}
|
||||
|
||||
void Tetris::rotate() {
|
||||
//Try to rotate
|
||||
int newr = (curr == 3) ? 0 : curr + 1; //Add one, wrap 4 -> 0
|
||||
bool possible = true;
|
||||
bool kickleft = false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int xpos = curx + TETROMINOES[curPiece][newr][i][0];
|
||||
int ypos = cury + TETROMINOES[curPiece][newr][i][1];
|
||||
int index = (ypos * BOARDWIDTH) + xpos;
|
||||
if (staticBoard[index] != 0 || xpos >= BOARDWIDTH || xpos < 0) {
|
||||
possible = false;
|
||||
}
|
||||
}
|
||||
if (possible) {
|
||||
curr = newr;
|
||||
} else {
|
||||
//If normal rotation is not possible, try to kick off the edge
|
||||
int oldr = curr;
|
||||
curr = newr;
|
||||
bool hasMoved = false;
|
||||
|
||||
hasMoved |= move(true, false);
|
||||
if (!hasMoved) {
|
||||
hasMoved |= move(false, true);
|
||||
}
|
||||
if (!hasMoved) { //No kicking was possible
|
||||
curr = oldr;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Tetris::tick(bool keys[], bool fallfast) {
|
||||
//Main Game Loop
|
||||
//Takes these inputs:
|
||||
// keys[] = boolean, true uf these keys are held down, in order: left, right, up, down, space
|
||||
// fall = boolean, true if this is a "gravity tick". Otherwise, only right/left and rotation is performed
|
||||
|
||||
if (gameOver) { return; }
|
||||
|
||||
//Left/Right
|
||||
move(keys[0], keys[1]);
|
||||
|
||||
//Rotate
|
||||
if (keys[2]) { rotate(); }
|
||||
|
||||
//Instant fall
|
||||
if (keys[4]) {
|
||||
bool landed = testLanded();
|
||||
while (!landed) {
|
||||
landed = testLanded();
|
||||
if (!landed) { cury += 1; }
|
||||
}
|
||||
}
|
||||
|
||||
if (fallfast || keys[3]) {
|
||||
bool landed = testLanded();
|
||||
if (landed) {
|
||||
lock();
|
||||
clearRows();
|
||||
newPiece();
|
||||
} else {
|
||||
cury += 1;
|
||||
}
|
||||
}
|
||||
}
|
92
tetris.hpp
Normal file
92
tetris.hpp
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef TETRIS_H
|
||||
#define TETRIS_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#define BOARDWIDTH 10
|
||||
#define BOARDHEIGHT 20
|
||||
|
||||
const int BOARDSIZE = BOARDWIDTH*BOARDHEIGHT;
|
||||
|
||||
//According to the official SRS system
|
||||
const uint_fast8_t TETROMINOES[7][4][4][2] = { //[Shape][Rotation][Block][0=x 1=y]
|
||||
{ /* I */
|
||||
{ {0,1}, {1,1}, {2,1}, {3,1} },
|
||||
{ {2,0}, {2,1}, {2,2}, {2,3} },
|
||||
{ {0,2}, {1,2}, {2,2}, {3,2} },
|
||||
{ {1,0}, {1,1}, {1,2}, {1,3} }
|
||||
},
|
||||
{ /* J */
|
||||
{ {0,0}, {0,1}, {1,1}, {2,1} },
|
||||
{ {2,0}, {1,0}, {1,1}, {1,2} },
|
||||
{ {0,1}, {1,1}, {2,1}, {2,2} },
|
||||
{ {1,0}, {1,1}, {1,2}, {0,2} }
|
||||
},
|
||||
{ /* L */
|
||||
{ {0,1}, {1,1}, {2,1}, {2,0} },
|
||||
{ {0,0}, {1,0}, {1,1}, {1,2} },
|
||||
{ {0,2}, {0,1}, {1,1}, {2,1} },
|
||||
{ {1,0}, {1,1}, {1,2}, {2,2} }
|
||||
},
|
||||
{ /* Square */
|
||||
{ {1,0}, {1,1}, {2,0}, {2,1} },
|
||||
{ {1,0}, {1,1}, {2,0}, {2,1} },
|
||||
{ {1,0}, {1,1}, {2,0}, {2,1} },
|
||||
{ {1,0}, {1,1}, {2,0}, {2,1} }
|
||||
},
|
||||
{ /* S */
|
||||
{ {0,1}, {1,1}, {1,0}, {2,0} },
|
||||
{ {1,0}, {1,1}, {2,1}, {2,2} },
|
||||
{ {0,2}, {1,2}, {1,1}, {2,1} },
|
||||
{ {0,0}, {0,1}, {1,1}, {1,2} }
|
||||
},
|
||||
{ /* Z */
|
||||
{ {0,0}, {1,0}, {1,1}, {2,1} },
|
||||
{ {2,0}, {2,1}, {1,1}, {1,2} },
|
||||
{ {0,1}, {1,1}, {1,2}, {2,2} },
|
||||
{ {1,0}, {1,1}, {0,1}, {0,2} }
|
||||
},
|
||||
{ /* T */
|
||||
{ {0,1}, {1,1}, {2,1}, {1,0} },
|
||||
{ {1,0}, {1,1}, {1,2}, {2,1} },
|
||||
{ {0,1}, {1,1}, {2,1}, {1,2} },
|
||||
{ {1,0}, {1,1}, {1,2}, {0,1} }
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
class Tetris {
|
||||
private:
|
||||
//std::array<std::array<char, BOARDWIDTH>, BOARDHEIGHT> staticBoard;
|
||||
std::array<int, BOARDSIZE> staticBoard;
|
||||
|
||||
int curPiece;
|
||||
int curx;
|
||||
int cury;
|
||||
int curr;
|
||||
int nextPiece;
|
||||
|
||||
int lines;
|
||||
int points;
|
||||
int level;
|
||||
int levelProgress;
|
||||
|
||||
bool gameOver;
|
||||
|
||||
//Functions
|
||||
void newPiece();
|
||||
bool testLanded();
|
||||
void clearRows();
|
||||
void lock();
|
||||
void rotate();
|
||||
bool move(bool right, bool left);
|
||||
bool fall();
|
||||
|
||||
public:
|
||||
std::array<int, BOARDSIZE> getBoard();
|
||||
void tick(bool keys[], bool fall);
|
||||
void init();
|
||||
std::array<int, 4> getInfo();
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user