Recently I have finished my first bigger Android project. I made a little Tetris App.
Here is a picture of my app in emulator:
My Tetris has following functions:
- fast drop down
- play tetris music
- start/pause button
- Gameover Screen
- View which shows next Piece
- "Levels" if you reach a particular score, level will be increase and pieces drops faster down
- saving and loading highscore
I post the most important part of my code, if you need more code please see my git repo(https://github.com/Linus1905/Android-Tetris).
Piece.java:
public class Piece {
public int colorCode;
public int x1, y1;
public int x2, y2;
public int x3, y3;
public int x4, y4;
private Piece piece;
/*
creates a copy Instances of Piece
*/
public Piece(Piece piece) {
this.piece = piece;
this.x1= piece.x1; this.x2= piece.x2;
this.x3= piece.x3; this.x4= piece.x4;
this.y1= piece.y1; this.y2= piece.y2;
this.y3= piece.y3; this.y4= piece.y4;
}
/*
creates a Piece depending on colorCode
*/
public Piece(int f) {
switch (f) { // Square
case 1:
x1 = 0; y1 = 7;
x2 = 0; y2 = 8;
x3 = 1; y3 = 7;
x4 = 1; y4 = 8;
colorCode = 1;
break;
case 2: // z Piece
x1 = 0;y1 = 7;
x2 = 0;y2 = 8;
x3 = 1;y3 = 8;
x4 = 1;y4 = 9;
colorCode = 2;
break;
case 3: // I Piece
x1 = 0;y1 = 6;
x2 = 0;y2 = 7;
x3 = 0;y3 = 8;
x4 = 0;y4 = 9;
colorCode = 3;
break;
case 4: // T Piece
x1 = 0;y1 = 8;
x2 = 1;y2 = 7;
x3 = 1;y3 = 8;
x4 = 1;y4 = 9;
colorCode = 4;
break;
case 5: // S Piece
x1 = 0;y1 = 7;
x2 = 0;y2 = 8;
x3 = 1;y3 = 6;
x4 = 1;y4 = 7;
colorCode = 5;
break;
case 6: // J Piece
x1 = 0;y1 = 7;
x2 = 0;y2 = 8;
x3 = 0;y3 = 9;
x4 = 1;y4 = 9;
colorCode = 6;
break;
case 7: // L Piece
x1 = 0;y1 = 7;
x2 = 0;y2 = 8;
x3 = 0;y3 = 9;
x4 = 1;y4 = 7;
colorCode = 7;
break;
}
}
public void move(int x, int y) {
x1 = x1 + x;
y1 = y1 + y;
x2 = x2 + x;
y2 = y2 + y;
x3 = x3 + x;
y3 = y3 + y;
x4 = x4 + x;
y4 = y4 + y;
}
/*
turns piece around x1|y1 coordinates
*/
public void turnPiece() {
int tmp_x1, tmp_y1;
int tmp_x2, tmp_y2;
int tmp_x3, tmp_y3;
tmp_x1 = turn_AroundX1(y2);
tmp_y1 = turn_AroundY1(x2);
x2 = tmp_x1;
y2 = tmp_y1;
tmp_x2 = turn_AroundX1(y3);
tmp_y2 = turn_AroundY1(x3);
x3 = tmp_x2;
y3 = tmp_y2;
tmp_x3 = turn_AroundX1(y4);
tmp_y3 = turn_AroundY1(x4);
x4 = tmp_x3;
y4 = tmp_y3;
}
public int turn_AroundX1(int y) {
return x1 + y - y1;
}
public int turn_AroundY1(int x) {
return y1 - x + x1;
}
public int getMinXCoordinate(int x1, int x2, int x3, int x4) {
return Math.min(Math.min(x1,x2),Math.min(x3,x4));
}
}
GameBoard.java :
import android.graphics.Color;
import android.graphics.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class GameBoard {
private final int boardHeight =30;
private final int boardWidth=16;
private int gameBoard[][]=new int[boardHeight][boardWidth];
private final Random random = new Random();
private ArrayList<Piece> pieceList = new ArrayList<Piece>();
private final int number_of_Pieces = 7;
public GameBoard() {
pieceList.add(new Piece(random.nextInt(number_of_Pieces)+1));
pieceList.add(new Piece(random.nextInt(number_of_Pieces)+1));
}
/*
returns Color of x,y gameBoard cell
*/
public int codeToColor(int x, int y) {
if(gameBoard[x][y]==0) return Color.parseColor("#FFFF00"); // Yellow
if(gameBoard[x][y]==1) return Color.parseColor("#00FF00"); ; // Square Green
if(gameBoard[x][y]==2) return Color.parseColor("#FF00FF"); ; // zpiece Magenta
if(gameBoard[x][y]==3) return Color.parseColor("#0000FF"); ; // ipiece Blue
if(gameBoard[x][y]==4) return Color.parseColor("#00FFFF"); ; // tpiece Cyan
if(gameBoard[x][y]==5) return Color.parseColor("#ffbf00"); ; // spiece Orange
if(gameBoard[x][y]==6) return Color.parseColor("#BEBEBE"); ; // jpiece gray
if(gameBoard[x][y]==7) return Color.parseColor("#FF0000"); ; // lpiece Red
return -1;
}
public void clearGameBoard() {
for(int i=0; i<boardHeight; i++) {
for(int j=0; j<boardWidth; j++) {
gameBoard[i][j]= 0;
}
}
}
public ArrayList<Piece> getPieceList(){
return pieceList;
}
public Piece getCurrentPiece() {
return pieceList.get(pieceList.size() - 2);
}
public Piece getNextPiece() {
return pieceList.get(pieceList.size()-1);
}
private void placePiece(Piece currentPiece) {
gameBoard[currentPiece.x1][currentPiece.y1] = currentPiece.colorCode;
gameBoard[currentPiece.x2][currentPiece.y2] = currentPiece.colorCode;
gameBoard[currentPiece.x3][currentPiece.y3] = currentPiece.colorCode;
gameBoard[currentPiece.x4][currentPiece.y4] = currentPiece.colorCode;
}
private void deletePiece(Piece currentPiece) {
gameBoard[currentPiece.x1][currentPiece.y1] = 0;
gameBoard[currentPiece.x2][currentPiece.y2] = 0;
gameBoard[currentPiece.x3][currentPiece.y3] = 0;
gameBoard[currentPiece.x4][currentPiece.y4] = 0;
}
/*
checks if Piece can move in direction x|y
copy Piece and try to move it, return true
if it can move
*/
private boolean piece_Can_Move(Piece currentPiece, int x, int y) {
int tmp =0;
/*
copy piece coordinates
*/
Point p1 = new Point(currentPiece.x1, currentPiece.y1);
Point p2 = new Point(currentPiece.x2, currentPiece.y2);
Point p3 = new Point(currentPiece.x3, currentPiece.y3);
Point p4 = new Point(currentPiece.x4, currentPiece.y4);
Point tmp1 = new Point(currentPiece.x1+x, currentPiece.y1+y);
Point tmp2 = new Point(currentPiece.x2+x, currentPiece.y2+y);
Point tmp3 = new Point(currentPiece.x3+x, currentPiece.y3+y);
Point tmp4 = new Point(currentPiece.x4+x, currentPiece.y4+y);
ArrayList<Point> tmpPieceCoordinates = new ArrayList<Point>();
tmpPieceCoordinates.add(tmp1);
tmpPieceCoordinates.add(tmp2);
tmpPieceCoordinates.add(tmp3);
tmpPieceCoordinates.add(tmp4);
for(Point p : tmpPieceCoordinates ) {
if(p.x< boardHeight && p.y>=0 && p.y< boardWidth && gameBoard[p.x][p.y]==0) {
tmp++;
}
else if(p.equals(p1) || p.equals(p2) || p.equals(p3) || p.equals(p4)) {
tmp++;
}
}
if(tmp==4) {
return true;
}
return false;
}
/*
copy current Piece and check if it can rotate
if true return true
*/
private boolean piece_Can_Rotate(Piece currentPiece) {
int tmp =0;
ArrayList<Point> tmpPieceCoordinates = new ArrayList<Point>();
Piece tmpStein = new Piece(currentPiece);
Point p1 = new Point(currentPiece.x1, currentPiece.y1);
Point p2 = new Point(currentPiece.x2, currentPiece.y2);
Point p3 = new Point(currentPiece.x3, currentPiece.y3);
Point p4 = new Point(currentPiece.x4, currentPiece.y4);
tmpStein.turnPiece();
Point tmp1 = new Point(tmpStein.x1, tmpStein.y1);
Point tmp2 = new Point(tmpStein.x2, tmpStein.y2);
Point tmp3 = new Point(tmpStein.x3, tmpStein.y3);
Point tmp4 = new Point(tmpStein.x4, tmpStein.y4);
tmpPieceCoordinates .add(tmp1);
tmpPieceCoordinates .add(tmp2);
tmpPieceCoordinates .add(tmp3);
tmpPieceCoordinates .add(tmp4);
for(Point p : tmpPieceCoordinates ) {
if(p.x< boardHeight && p.x>=0 && p.y>=0 && p.y< boardWidth && gameBoard[p.x][p.y]==0) {
tmp++;
}
else if(p.equals(p1) || p.equals(p2) || p.equals(p3) || p.equals(p4)) {
tmp++;
}
}
if(tmp==4) { /* all four little squares are correct*/
return true;
}
return false;
}
private boolean can_Move_Left(Piece currentPiece) {
if(piece_Can_Move(currentPiece, 0, -1)==true) {
return true;
}
return false;
}
private boolean can_Move_Right(Piece currentPiece){
if(piece_Can_Move(currentPiece, 0,1) == true) {
return true;
}
return false;
}
public boolean can_Move_Down(Piece currentPiece) {
if(piece_Can_Move(currentPiece, 1,0)==true) {
return true;
}
return false;
}
private void movePiece(Piece currentPiece, int x, int y) {
deletePiece(currentPiece);
currentPiece.move(x, y);
placePiece(currentPiece);
}
public void moveRight(Piece currentPiece){
if(can_Move_Right(currentPiece)==true) {
movePiece(currentPiece, 0, 1);
}
}
public void moveLeft(Piece currentPiece){
if(can_Move_Left(currentPiece)==true) {
movePiece(currentPiece, 0, -1);
}
}
public void moveDown(Piece currentPiece) {
if(can_Move_Down(currentPiece)==true) {
movePiece(currentPiece, 1, 0);
}
}
public void fastDrop(Piece currentPiece) {
deletePiece(currentPiece);
while(can_Move_Down(currentPiece)==true) {
moveDown(currentPiece);
}
placePiece(currentPiece);
}
/*
turn all pieces until square piece
*/
public void rotatePiece(Piece currentPiece) {
if(piece_Can_Rotate(currentPiece)==true && currentPiece.colorCode!=1) {
deletePiece(currentPiece);
currentPiece.turnPiece();
placePiece(currentPiece);
}
placePiece(currentPiece);
}
public int clearRows() {
int deletedRowIndex;
int deletedRows=0;
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for (int i = 0; i < boardHeight; i++) {
for (int j = boardWidth - 1; j >= 0; j--) {
if (gameBoard[i][j]==0) { // Row not full
break;
}
if (j == 0) {
deletedRowIndex = i;
arrayList.add(deletedRowIndex);
deletedRows++;
deleteRow(deletedRowIndex);
}
}
}
if (deletedRows >= 1) {
int highestRow = Collections.min(arrayList); // highest Row which can be cleared
int[][] gameBoardCopy = new int[highestRow][boardWidth];
for (int i = 0; i < gameBoardCopy.length; i++) {
for (int j = 0; j < gameBoardCopy[1].length; j++) {
gameBoardCopy[i][j] = gameBoard[i][j];
}
}
for (int i = 0; i < gameBoardCopy.length; i++) {
for (int j = 0; j < gameBoardCopy[1].length; j++) {
gameBoard[i+deletedRows][j] = gameBoardCopy[i][j];
}
}
}
return deletedRows;
}
public void deleteRow(int r){
for (int i = 0; i < boardWidth; i++) {
gameBoard[r][i] =0;
}
}
public boolean checkGameOver(Piece spielStein) {
if(can_Move_Down(spielStein) == false && spielStein.getMinXCoordinate(
spielStein.x1, spielStein.x2, spielStein.x3, spielStein.x4)<=1) {
return true;
}
return false;
}
public int getBoardHeight() {
return this.boardHeight;
}
public int getBoardWidth() {
return this.boardWidth;
}
}
Tetris.java:
public class Tetris extends View implements View.OnClickListener{
private MediaPlayer mediaPlayer;
private GameBoard gameBoard;
private MainActivity mainActivity;
private ImageButton rotateButton;
private ImageButton rightButton;
private ImageButton downButton;
private ImageButton leftButton;
private Timer timer = new Timer();
private Random random = new Random();
private ArrayList<Piece> pieceList;
private NextPieceView nextPieceView;
private TextView currentLevelTextView;
private TextView highscoreLevelTextView;
private TextView currentPunkteTextView;
private Points points;
private final int score=10;
private int timerPeriod =250;
private int level=0;
private boolean pause;
public Tetris(Context context, NextPieceView nextPieceView, GameBoard
gameBoard) {
super(context);
this.mainActivity = (MainActivity) context;
this.nextPieceView=nextPieceView;
this.gameBoard = gameBoard;
pause = mainActivity.getPause();
pieceList = gameBoard.getPieceList();
mediaPlayer = mainActivity.getMediaPlayer();
points = new Points(context);
currentLevelTextView = mainActivity.getCurrentLevelTextView();
highscoreLevelTextView = mainActivity.getHighscoreLevelTextView();
currentPunkteTextView = mainActivity.getPointTextView();
currentLevelTextView.append("0");
currentPunkteTextView.append("0");
highscoreLevelTextView.append(""+points.loadHighscore());
rotateButton = mainActivity.getRotateButton();
rightButton = mainActivity.getRightButton();
downButton = mainActivity.getDownButton();
leftButton = mainActivity.getLeftButton();
rotateButton .setOnClickListener(this);
rightButton.setOnClickListener(this);
downButton.setOnClickListener(this);
leftButton.setOnClickListener(this);
gameLoop();
}
public void gameLoop() {
timer.schedule(new TimerTask() {
@Override
public void run() {
mainActivity.runOnUiThread(new TimerTask() {
@Override
public void run() {
if(gameOver()==false && mainActivity.getPause()==false ) {
gameBoard.moveDown(gameBoard.getCurrentPiece());
if (gameBoard.can_Move_Down(gameBoard.getCurrentPiece()) == false) {
int deletedRows = gameBoard.clearRows();
gameBoard.clearRows();
pieceList.remove(gameBoard.getCurrentPiece());
pieceList.add(new Piece(random.nextInt(7) + 1));
nextPieceView.invalidate();
if (deletedRows> 0) {
points.setCurrentPoints(points.getCurrentPoints() + deletedRows * score);
int p = points.getCurrentPoints();
points.setLevel();
currentPunkteTextView.setText("Points:" +" "+ p);
currentLevelTextView.setText("Level" +" "+ points.getLevel());
if (points.getLevel() > points.loadHighscore()) {
points.writeHighscore();
highscoreLevelTextView.setText("Highscore:" +" "+ points.getLevel());
}
}
if(points.getLevel()>level) {
level++;
timerPeriod = timerPeriod - (points.getLevel()*20);
timer.cancel();
timer = new Timer();
gameLoop();
}
}
invalidate();
}
}
});
}
}, 0, timerPeriod);
}
public boolean gameOver() {
if( gameBoard.checkGameOver(gameBoard.getCurrentPiece())==true ) {
timer.cancel();
pieceList.clear();
gameBoard.clearGameBoard();
mainActivity.setPause(true);
mediaPlayer.stop();
showGameOverScreen();
return true;
}
return false;
}
public void showGameOverScreen() {
Intent intent = new Intent(this.getContext(), GameOverScreen.class);
getContext().startActivity(intent);
}
/*
change colorCode to spezific Color and paint on Gameboard
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint p = new Paint();
for (int x = 0; x < gameBoard.getBoardHeight(); x++) {
for (int y = 0; y < gameBoard.getBoardWidth(); y++) {
int color = gameBoard.codeToColor(x,y);
p.setColor(color);
canvas.drawRect(y*30, x*30, y*30+30, x*30+30,p);
}
}
}
/*
control falling pieces with buttons
*/
@Override
public void onClick(View v) {
if(mainActivity.getPause()==false) {
switch(v.getId()) {
case R.id.rightButton:
gameBoard.moveRight(gameBoard.getCurrentPiece());
invalidate();
break;
case R.id.downButton:
gameBoard.fastDrop(gameBoard.getCurrentPiece());
invalidate();
break;
case R.id.leftButton:
gameBoard.moveLeft(gameBoard.getCurrentPiece());
invalidate();
break;
case R.id.rotateButton:
gameBoard.rotatePiece(gameBoard.getCurrentPiece());
invalidate();
break;
}
}
}
}
MainActivity:
import android.graphics.Color;
import android.media.MediaPlayer;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button buttonStart;
private ImageButton rotateButton;
private ImageButton rightButton;
private ImageButton downButton;
private ImageButton leftButton;
private TextView pointTextView;
private TextView highscoreLevelTextView;
private TextView currentLevelTextView;
private Tetris tetris;
private NextPieceView nextPieceView;
private boolean pause = true;
private MediaPlayer mediaPlayer;
private int stopMediaplayer;
private GameBoard gameBoard = new GameBoard();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource( this, Uri.parse("android.resource://com.example.admin.tetris/raw/tetrismusik"));
mediaPlayer.prepare();
} catch (Exception e) {e.printStackTrace();}
buttonStart = (Button) findViewById(R.id.buttonstart);
rotateButton = (ImageButton) findViewById(R.id.rotateButton);
rightButton = (ImageButton) findViewById(R.id.rightButton);
downButton = (ImageButton) findViewById(R.id.downButton);
leftButton = (ImageButton) findViewById(R.id.leftButton);
pointTextView = (TextView) findViewById(R.id.textViewPunkte);
highscoreLevelTextView= (TextView) findViewById(R.id.textViewHighscore);
currentLevelTextView = (TextView) findViewById(R.id.levelText);
nextPieceView = new NextPieceView(this, gameBoard);
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(300,300);
nextPieceView.setLayoutParams(params1);
RelativeLayout relativeSteinAnzeige = (RelativeLayout) findViewById(R.id.relativelayout1);
nextPieceView.setBackgroundColor(Color.YELLOW);
relativeSteinAnzeige.addView(nextPieceView);
tetris = new Tetris(this,nextPieceView, gameBoard);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(480, 900);
tetris.setLayoutParams(params);
RelativeLayout relativeTetris = (RelativeLayout) findViewById(R.id.relativelayout);
tetris.setBackgroundColor(Color.YELLOW);
relativeTetris.addView(tetris);
buttonStart.setOnClickListener(new View.OnClickListener() {
int tmp=0;
@Override
public void onClick(View v) {
stopMediaplayer = mediaPlayer.getCurrentPosition();
tmp++;
if(buttonStart.getText().equals("Start")) {
buttonStart.setText("Pause");
pause = false;
if(tmp==1) {
mediaPlayer.start();
mediaPlayer.setLooping(true);
} else if(tmp>1) {
mediaPlayer.seekTo(stopMediaplayer);
mediaPlayer.start();
}
}
else if(buttonStart.getText().equals("Pause")) {
buttonStart.setText("Start");
pause = true;
mediaPlayer.pause();
}
}
});
}
@Override
public void onRestart() {
super.onRestart();
pause = false;
mediaPlayer.seekTo(stopMediaplayer);
mediaPlayer.start();
}
@Override
public void onPause() {
super.onPause();
pause = true;
mediaPlayer.stop();
mediaPlayer.pause();
stopMediaplayer = mediaPlayer.getCurrentPosition();
}
@Override
public void onDestroy() {
super.onDestroy();
finish();
}
public ImageButton getRightButton() { return rightButton;}
public ImageButton getDownButton() { return downButton;}
public ImageButton getLeftButton() { return leftButton;}
public ImageButton getRotateButton() { return rotateButton; }
public boolean getPause() { return pause;}
public void setPause(boolean pause) { this.pause=pause;}
public TextView getHighscoreLevelTextView() { return highscoreLevelTextView;
}
public TextView getPointTextView() { return pointTextView; }
public TextView getCurrentLevelTextView() { return currentLevelTextView;}
public MediaPlayer getMediaPlayer() { return mediaPlayer; }
}
All help and suggestions would be really appreciated