Skip to content

Instantly share code, notes, and snippets.

@cleemy-desu-wayo
Last active November 27, 2025 14:49
  • Select an option

Select an option

study20251125_tetris (study in Processing 4)

study20251125_tetris (study in Processing 4)

screen shot of study20251125_tetris

this is just a study in Processing 4
ただの習作です


what's this

A Tetris-like falling-block puzzle game with gamepad support.
テトリスに似た落ちものパズルです。ゲームパッドにも対応してます。

This is a modified version of study20251026_tetris, which I released last month.
先月わたくしが公開した study20251026_tetris を改造したものです。

study20251026_tetris is here:
study20251026_tetris はこちら:
https://gist.github.com/cleemy-desu-wayo/ec1d6ce69b4a7e61efdcee71413ef07f

You may not be able to launch the game unless you install the "Game Control Plus" library first.
ライブラリの「Game Control Plus」を先に入れておかないと、ゲームの起動ができないかもしれません。

background image

the background image can be downloaded from the following link (Unsplash License):
背景画像は以下よりダウンロード可能(Unsplashライセンス):

how to play

  • LEFT key: move left
  • RIGHT key: move right
  • UP key: ratate R
  • DOWN key: move down
  • Z key: ratate R
  • X key: ratate L
  • P key: pause

(also supports gamepads)

major changes

major changes from study20251026_tetris:

  • added gamepad/controller support
  • enabled both clockwise and counterclockwise rotation
  • added rotation with Z and X keys
  • added score tracking and level progression features
  • added lock delay when pieces land
  • added landing visual effects
  • implemented flexible rotation (maybe not SRS-compliant)
  • added line clear effects
  • added game over animation
  • added PAUSE functionality
  • minor layout adjustments

study20251026_tetris からの主な変更点:

  • ゲームパッドをサポート
  • 左右両方向の回転が可能なようにした
  • ZキーとXキーでも回転が出来るようにした
  • スコア機能・レベル機能の追加
  • 接地(着地)時の固定猶予(lock delay)を追加
  • 接地(着地)時の視覚エフェクトを追加
  • 柔軟な回転に対応(おそらくSRS準拠にはなってない)
  • ライン消し成功時の視覚エフェクトを追加
  • ゲームオーバー時のアニメーションを追加
  • PAUSE機能を追加
  • レイアウト微調整

BTW, I wrote study20251026_tetris entirely from scratch as a way to learn Processing.
ちなみに study20251026_tetris については、Processingの勉強のためにフルスクラッチで書いたものです。

newer versions

possibly you will find the newer versions:
より新しいバージョンが、以下から見つかるかも:

https://gist.github.com/search?q=cleemy+desu+wayo+tetris

a video of this game running

https://x.com/metanagi/status/1993147684103094595

/*
* study20251125_tetris
* written by cleemy desu wayo / Licensed under CC0 1.0
* 2025-11-25
* ----
* this is just a study in Processing
*
* the following preparations are required:
* 1. create a new folder (directory) named "study20251125_tetris"
* 2. in the new folder, save this Processing sketch as "study20251125_tetris.pde"
* 3. in the new folder, save image as "cassidy-james-blaede-UBcXr0DcTAY-unsplash.jpg"
* (download from https://unsplash.com/photos/UBcXr0DcTAY )
* 4. install "Game Control Plus" library
* (in IDE, Sketch -> Import Library -> Manage Libraries...)
* 5. edit the lines marked with "change this line as needed" if necessary
*
* the author, cleemy desu wayo, has tested this sketch only on Processing 4.4.10 running on Linux.
*
* ----
* possibly the updated version is here:
*
* https://gist.github.com/search?q=cleemy+desu+wayo+tetris
*
* old version is here:
*
* - study20251026_tetris
* https://gist.github.com/cleemy-desu-wayo/ec1d6ce69b4a7e61efdcee71413ef07f
*
* ----
* how to play
* LEFT key: move left
* RIGHT key: move right
* UP key: ratate R
* DOWN key: move down
* Z key: ratate R
* X key: ratate L
* P key: pause
*
* (and gamepads are also available)
*
* ----
* see also (technical matters around gamepad)
* https://note.com/ozora_electric/n/n17c0bafae69e
* https://note.com/ozora_electric/n/n82481de7e00e
* https://zawaworks.hatenablog.com/entry/2020/12/04/210613
* https://qiita.com/hextomino/items/8f6e0a08b15148a5d718
*
*/
import org.gamecontrolplus.*;
Boolean isGamePadEnabled = true; // change this line as needed
// (if you are seeing gamepad-related errors
// and don't need a gamepads, set this to false)
int padNum = 0; // change this line as needed
String hatName = ""; // change this line as needed
String sliderXName = "x"; // change this line as needed
String sliderYName = "y"; // change this line as needed
int btnANum = 1; // change this line as needed
int btnBNum = 2; // change this line as needed
int btnSNum = 9; // change this line as needed
char KeyPChar = 'p'; // change this line as needed
char KeyZChar = 'z'; // change this line as needed
char KeyXChar = 'x'; // change this line as needed
String fontName = "Noto Sans Mono"; // change this line as needed
int debugBarricadeMode = 0; // change this line as needed
// (for debug, 0: normal (no barricade) 1-: barricade is enable)
// change these values if you adjust the difficulty
int initialLevel = 1;
float levelInterval = 3000.0;
int[] addScore = {100, 300, 800, 2000};
float[] speedByLevel = {0.0 , // level 0 (ignored)
2000.0 , 1500.0 , 1100.0 , 950.0 , 700.0 , // level 1-5
600.0 , 500.0 , 400.0 , 320.0 , 240.0 , // level 6-10
170.0 , 100.0 , 60.0 , 20.0 , 0.0 // level 10-15
};
PGraphics pg;
int gameMode = 102; // 0: title (not implemented)
// 101: normal 102: fixing & stand by 103: deleting (line clear)
// 104: pause
// 200: game over
int lastGameMode;
int drawCnt = 0;
int lastRedraw = -1;
int screenWidth = 1280;
int screenHeight = 720;
int leftMargin = 360;
int topMargin = 118;
int blockSize = 20;
int blockMargin = 3;
int minoRotate;
int minoX; // X position
int minoY; // Y position
float downInterval = 0.0; // millisec
float asobiInterval = 500.0; // millisec
float lastAsobiResetTime = -1.0; // millisec (result of millis())
float standbyInterval = 300.0; // millisec
float lastStandbyStartTime = -1.0; // millisec (result of millis())
float lastDeleteStartTime = -1.0; // millisec (result of millis())
float gameOverStartTime = -1.0; // millisec (result of millis())
int lastMinoRotate = -1;
int lastMinoX = -1;
int lastMinoY = -1;
Boolean newMinoDrawDone = true;
int matrixSizeX = 14;
int matrixSizeY = 22;
int[][] blockMatrix = new int[matrixSizeX][matrixSizeY];
int[][] fixingBlock = new int[matrixSizeX][matrixSizeY];
int mino;
int nextMino;
int[][][][] minoBlock = {
// 0: T
{{{0, 0}, {-1, 0}, {0, -1}, {1, 0}},
{{0, 0}, {0, -1}, {1, 0}, {0, 1}},
{{0, 0}, {-1, 0}, {0, 1}, {1, 0}},
{{0, 0}, {-1, 0}, {0, -1}, {0, 1}}},
// 1: I
{{{0, 0}, {-1, 0}, {1, 0}, {2, 0}},
{{0, 0}, {0, -1}, {0, 1}, {0, 2}},
{{0, 0}, {-1, 0}, {1, 0}, {2, 0}},
{{0, 0}, {0, -1}, {0, 1}, {0, 2}}},
// 2: O
{{{0, 0}, {1, 0}, {0, -1}, {1, -1}},
{{0, 0}, {1, 0}, {0, -1}, {1, -1}},
{{0, 0}, {1, 0}, {0, -1}, {1, -1}},
{{0, 0}, {1, 0}, {0, -1}, {1, -1}}},
// 3: S
{{{0, 0}, {-1, 0}, {0, -1}, {1, -1}},
{{0, 0}, {0, -1}, {1, 0}, {1, 1}},
{{0, 0}, {-1, 0}, {0, -1}, {1, -1}},
{{0, 0}, {0, -1}, {1, 0}, {1, 1}}},
// 4: Z
{{{0, 0}, {-1, -1}, {0, -1}, {1, 0}},
{{0, 0}, {1, -1}, {1, 0}, {0, 1}},
{{0, 0}, {-1, -1}, {0, -1}, {1, 0}},
{{0, 0}, {1, -1}, {1, 0}, {0, 1}}},
// 5: J
{{{0, 0}, {-1, -1}, {-1, 0}, {1, 0}},
{{0, 0}, {1, -1}, {0, -1}, {0, 1}},
{{0, 0}, {-1, 0}, {1, 0}, {1, 1}},
{{0, 0}, {0, -1}, {0, 1}, {-1, 1}}},
// 6: L
{{{0, 0}, {-1, 0}, {1, 0}, {1, -1}},
{{0, 0}, {0, -1}, {0, 1}, {1, 1}},
{{0, 0}, {-1, 1}, {-1, 0}, {1, 0}},
{{0, 0}, {-1, -1}, {0, -1}, {0, 1}}}
};
int deletingLineNum = 0;
int[] deletingLines = {-1, -1, -1, -1};
Boolean deleteDone = false;
int intentFixingCnt = 0;
Boolean pauseTextDrawDone = false;
ControlDevice pad;
ControlHat hat;
ControlSlider sliderX, sliderY;
ControlButton btnA, btnB, btnS;
Boolean isHatFound = false;
Boolean isSliderXFound = false;
Boolean isSliderYFound = false;
Boolean isBtnAFound = false;
Boolean isBtnBFound = false;
Boolean isBtnSFound = false;
Boolean isKeyLeftPressed = false;
Boolean isKeyLeftJustReleased = true;
Boolean isPadLeftJustReleased = false;
Boolean isKeyRightPressed = false;
Boolean isKeyRightJustReleased = true;
Boolean isPadRightJustReleased = false;
Boolean isKeyUpPressed = false;
Boolean isKeyDownPressed = false;
Boolean isKeyUpJustReleased = true;
Boolean isPadUpJustReleased = false;
Boolean isKeyPPressed = false;
Boolean isKeyPJustReleased = true;
Boolean isKeyZPressed = false;
Boolean isKeyZJustReleased = true;
Boolean isKeyXPressed = false;
Boolean isKeyXJustReleased = true;
float moveLeftStartTime = -1.0; // millisec (result of millis())
float moveRightStartTime = -1.0; // millisec (result of millis())
float moveLeftRightHoldCharge = 160.0; // millisec
float lastMoveDownTime = -1.0; // millisec (result of millis())
float lastMoveLeftRightTime = -1.0; // millisec (result of millis())
Boolean isMoveDownEnabled = true;
Boolean isBtnAJustReleased = true;
Boolean isBtnBJustReleased = true;
Boolean isBtnSJustReleased = true;
int level = initialLevel;
int score = 0;
int highScore = 0;
void setup() {
if (isGamePadEnabled) {
setupGamePad();
}
size(1280, 720);
noStroke();
PImage bg = loadImage("cassidy-james-blaede-UBcXr0DcTAY-unsplash.jpg");
pg = createGraphics(1280, 720);
pg.beginDraw();
pg.image(bg, 0, 0, 1280, 720);
pg.endDraw();
frameRate(60);
gameReset();
}
void setupGamePad () {
ControlIO control;
int foundGamePadNum = 0;
int btnNum = 0;
String tmpHatName = "";
control = ControlIO.getInstance(this);
foundGamePadNum = control.getNumberOfDevices();
if (foundGamePadNum == 0) {
println("[gamepad info] number of gamepad: " + foundGamePadNum + " (not found)");
isGamePadEnabled = false;
return;
} else {
println("[gamepad info] number of gamepad: " + foundGamePadNum);
}
println("[gamepad info] found gamepad: " + control.getDevices());
pad = control.getDevice(padNum);
println("[gamepad info] pad: " + pad.getName());
btnNum = pad.getNumberOfButtons();
println("[gamepad info] number of button: " + btnNum);
int i = 0;
for (ControlInput input : pad.getInputs()) {
if (input instanceof ControlHat) {
if (tmpHatName == "") {
tmpHatName = input.getName();
}
println("[gamepad info] inputs-" + i + " -- hat : " + tmpHatName);
} else if (input instanceof ControlSlider) {
println("[gamepad info] inputs-" + i + " -- slider : " + input.getName());
} else if (input instanceof ControlButton) {
println("[gamepad info] inputs-" + i + " -- button : " + input.getName());
}
i++;
}
if (hatName == "") {
hat = pad.getHat(tmpHatName);
} else {
hat = pad.getHat(hatName);
}
if (hat instanceof ControlHat) {
isHatFound = true;
println("[gamepad info] hat : " + hat.getName() + " -- " + hat);
} else {
println("[gamepad info] hat : (not found)");
}
sliderX = pad.getSlider(sliderXName);
if (sliderX instanceof ControlSlider) {
isSliderXFound = true;
println("[gamepad info] sliderX : " + sliderX.getName() + " -- " + sliderX);
} else {
println("[gamepad info] sliderX : (not found)");
}
sliderY = pad.getSlider(sliderYName);
if (sliderY instanceof ControlSlider) {
isSliderYFound = true;
println("[gamepad info] sliderY : " + sliderY.getName() + " -- " + sliderY);
} else {
println("[gamepad info] sliderY : (not found)");
}
if (btnANum < btnNum) {
btnA = pad.getButton(btnANum);
}
if (btnA instanceof ControlButton) {
isBtnAFound = true;
println("[gamepad info] btnA : " + btnA.getName() + " -- " + btnA);
} else {
println("[gamepad info] btnA : (not found)");
}
if (btnBNum < btnNum) {
btnB = pad.getButton(btnBNum);
}
if (btnB instanceof ControlButton) {
isBtnBFound = true;
println("[gamepad info] btnB : " + btnB.getName() + " -- " + btnB);
} else {
println("[gamepad info] btnB : (not found)");
}
if (btnSNum < btnNum) {
btnS = pad.getButton(btnSNum);
}
if (btnS instanceof ControlButton) {
isBtnSFound = true;
println("[gamepad info] btnS : " + btnS.getName() + " -- " + btnS);
} else {
println("[gamepad info] btnS : (not found)");
}
}
void draw() {
Boolean isBlackout = false;
drawCnt++;
if (drawCnt % 100000 == 0) {
println("[" + getTimeString() + "] drawCnt: " + drawCnt);
}
if (drawCnt % 60 == 0) {
//println("level: " + level + " -- downInterval: " + downInterval); // for debug
// sliderX and sliderY info (for debug)
if (isSliderXFound && isSliderYFound) {
if (abs(sliderX.getValue()) > 0.1 || abs(sliderY.getValue()) > 0.1) {
//println("sliderX:" + sliderX.getValue() + " ---- sliderY: " + sliderY.getValue());
}
}
}
// pause
if (isBtnSFound && !btnS.pressed() && !isBtnSJustReleased) {
isBtnSJustReleased = true;
}
if (!isKeyPPressed && !isKeyPJustReleased) {
isKeyPJustReleased = true;
}
if ((isBtnSFound && btnS.pressed() && isBtnSJustReleased) || (isKeyPPressed && isKeyPJustReleased)) {
if (gameMode >= 101 && gameMode <= 103) {
lastGameMode = gameMode;
gameMode = 104;
pauseTextDrawDone = false;
} else if (gameMode == 104) {
gameMode = lastGameMode;
}
isBtnSJustReleased = false;
isKeyPJustReleased = false;
}
// title or something
if (gameMode < 100) {
// fixing & stand by
} else if (gameMode == 102) {
if (millis() > lastStandbyStartTime + standbyInterval) {
gameMode = 101;
newMinoDrawDone = false;
//clearCurrentMino();
// game over
if (checkBlock(mino, minoX, minoY, minoRotate) == 0) {
startGameOver();
}
putCurrentMino();
}
// deleting
} else if (gameMode == 103) {
float deleteTime = 0.0;
float fixingTime = 300.0;
if (deletingLineNum == 1) {
deleteTime = 140.0;
fixingTime = 190.0;
if (!deleteDone) {
if (millis() <= lastDeleteStartTime + 95) {
int deleteBlockNum = floor(10.0 / 95 * (millis() - lastDeleteStartTime));
if (deleteBlockNum > 10) { deleteBlockNum = 10; }
for (int i = 2; i <= 1 + deleteBlockNum; i++) {
blockMatrix[i][deletingLines[0]] = 0;
}
} else {
blockMatrix[11][deletingLines[0]] = 0;
}
}
} else if (deletingLineNum == 2) {
deleteTime = 270.0;
fixingTime = 340.0;
if (!deleteDone) {
if (millis() <= lastDeleteStartTime + 230) {
int deleteBlockNum = floor(10.0 / 230 * (millis() - lastDeleteStartTime));
if (deleteBlockNum > 10) { deleteBlockNum = 10; }
for (int i = 2; i <= 1 + deleteBlockNum; i++) {
blockMatrix[i][deletingLines[0]] = 0;
blockMatrix[13 -i][deletingLines[1]] = 0;
}
} else {
blockMatrix[11][deletingLines[0]] = 0;
blockMatrix[2][deletingLines[1]] = 0;
}
}
} else if (deletingLineNum == 3) {
deleteTime = 950.0;
fixingTime = 1300.0;
} else if (deletingLineNum == 4) {
deleteTime = 1900.0;
fixingTime = 2650.0;
}
if (!deleteDone) {
if (millis() > lastDeleteStartTime + deleteTime) {
for(int i = 0; i < 4; i++) {
deleteLine(deletingLines[i]);
}
deleteDone = true;
score += addScore[deletingLineNum - 1];
}
}
if (millis() > lastDeleteStartTime + fixingTime) {
gameMode = 101;
putCurrentMino();
newMinoDrawDone = false;
deletingLineNum = 0;
deleteDone = false;
}
// pause
} else if (gameMode == 104) {
if (!pauseTextDrawDone) {
fill(255, 255, 255, 223);
rect(280, 200, 480, 300);
textFont(createFont(fontName, 50, true));
textAlign(CENTER, CENTER);
fill(63, 63, 63, 127);
text("P A U S E", 280, 200, 480, 300);
pauseTextDrawDone = true;
}
return;
// normal game mode
} else if (gameMode == 101) {
Boolean isSliderXLeft = false;
Boolean isSliderXRight = false;
Boolean isSliderYUp = false;
Boolean isSliderYDown = false;
if (isSliderXFound) {
if (sliderX.getValue() < -0.6) {
isSliderXLeft = true;
} else if (sliderX.getValue() > 0.6) {
isSliderXRight = true;
}
}
if (isSliderYFound) {
if (sliderY.getValue() < -0.9) {
isSliderYUp = true;
} else if (sliderY.getValue() > 0.6) {
isSliderYDown = true;
}
}
// move down (by intention)
if (millis() > lastMoveLeftRightTime + 150) {
if (((isHatFound && !hat.down()) || !isHatFound) &&
!isSliderYDown &&
!isKeyDownPressed) {
isMoveDownEnabled = true;
intentFixingCnt = 0;
}
if (isMoveDownEnabled) {
if ((isHatFound && hat.down()) || isSliderYDown || isKeyDownPressed) {
moveMinoDown();
}
}
}
// move left
if (millis() > lastMoveDownTime + 90) {
if (((isHatFound && !hat.left()) || !isHatFound) &&
!isSliderXLeft &&
!isPadLeftJustReleased) {
isPadLeftJustReleased = true;
}
if (isKeyLeftPressed && (isKeyLeftJustReleased || (millis() - moveLeftStartTime > moveLeftRightHoldCharge))) {
if (isKeyLeftJustReleased) {
moveLeftStartTime = millis();
isKeyLeftJustReleased = false;
}
moveMinoLeft();
}
if (((isHatFound && hat.left()) || isSliderXLeft) &&
(isPadLeftJustReleased || (millis() - moveLeftStartTime > moveLeftRightHoldCharge))) {
if (isPadLeftJustReleased) {
moveLeftStartTime = millis();
isPadLeftJustReleased = false;
}
moveMinoLeft();
}
}
// move right
if (millis() > lastMoveDownTime + 90) {
if (((isHatFound && !hat.right()) || !isHatFound) &&
!isSliderXRight &&
!isPadRightJustReleased) {
isPadRightJustReleased = true;
}
if (isKeyRightPressed && (isKeyRightJustReleased || (millis() - moveRightStartTime > moveLeftRightHoldCharge))) {
if (isKeyRightJustReleased) {
moveRightStartTime = millis();
isKeyRightJustReleased = false;
}
moveMinoRight();
}
if (((isHatFound && hat.right()) || isSliderXRight) &&
(isPadRightJustReleased || (millis() - moveRightStartTime > moveLeftRightHoldCharge))) {
if (isPadRightJustReleased) {
moveRightStartTime = millis();
isPadRightJustReleased = false;
}
moveMinoRight();
}
}
// rotate
if (((isHatFound && !hat.up()) || !isHatFound) &&
!isSliderYUp &&
!isPadUpJustReleased) {
isPadUpJustReleased = true;
}
if (isKeyUpPressed && isKeyUpJustReleased) {
rotateMino(0);
isKeyUpJustReleased = false;
}
if (((isHatFound && hat.up()) || isSliderYUp) && isPadUpJustReleased) {
rotateMino(0);
isPadUpJustReleased = false;
}
// rotate (by button A)
if (isBtnAFound) {
if (!btnA.pressed() && !isBtnAJustReleased) {
isBtnAJustReleased = true;
}
if (btnA.pressed() && isBtnAJustReleased) {
rotateMino(0);
isBtnAJustReleased = false;
}
}
// rotate (by button B)
if (isBtnBFound) {
if (!btnB.pressed() && !isBtnBJustReleased) {
isBtnBJustReleased = true;
}
if (btnB.pressed() && isBtnBJustReleased) {
rotateMino(1);
isBtnBJustReleased = false;
}
}
// rotate (by Z key)
if (isKeyZPressed && isKeyZJustReleased) {
rotateMino(0);
isKeyZJustReleased = false;
}
// rotate (by X key)
if (isKeyXPressed && isKeyXJustReleased) {
rotateMino(1);
isKeyXJustReleased = false;
}
// move down naturally and set next mino
if (isMoveDownEnabled &&
((isHatFound && hat.down()) || isSliderYDown || isKeyDownPressed)) {
clearCurrentMino();
if (checkBlock(mino, minoX, minoY + 1, minoRotate) == 0) {
intentFixingCnt++;
if (intentFixingCnt > 6) {
setNextMino();
} else {
putCurrentMino();
}
} else {
putCurrentMino();
}
} else if (millis() > lastAsobiResetTime + downInterval) {
clearCurrentMino();
// move down naturally
if (checkBlock(mino, minoX, minoY + 1, minoRotate) == 1) {
minoY++;
putCurrentMino();
lastAsobiResetTime = millis();
} else if (millis() > lastAsobiResetTime + asobiInterval) {
setNextMino();
} else {
putCurrentMino();
}
}
// game over
} else if (gameMode == 200) {
int floodState = 0;
if (millis() < gameOverStartTime + 2000) {
} else if (millis() < gameOverStartTime + 3900) {
floodState = floor((millis() - 2000 - gameOverStartTime) / 40);
if (floodState < 0 ) {
floodState = 0;
} else if (floodState > 41) {
floodState = 41;
}
if (floodState <= 20) {
for (int i = 2; i < matrixSizeX - 2; i++) {
blockMatrix[i][20 - floodState] = 1;
}
} else {
for (int i = 2; i < matrixSizeX - 2; i++) {
blockMatrix[i][floodState - 21] = 0;
}
}
} else if (millis() > gameOverStartTime + 5100) {
gameReset();
gameMode = 101;
}
}
// draw
if ((gameMode == 101 &&
(!newMinoDrawDone || lastMinoRotate != minoRotate || lastMinoX != minoX || lastMinoY != minoY)) ||
gameMode == 102 || gameMode == 103 || gameMode == 200) {
float tmpTime = millis() - lastDeleteStartTime;
float tmpAlpha = 0.0;
// draw bg
if (gameMode == 103) {
if (deletingLineNum == 3) {
if (tmpTime < 310) {
if (drawCnt % 6 < 3) {
image(pg, 0, 0);
} else {
background(0);
}
} else {
image(pg, 0, 0);
}
} else if (deletingLineNum == 4) {
if (tmpTime < 512) {
tmpAlpha = tmpTime / 2.0;
if (tmpAlpha > 255.0) { tmpAlpha = 255.0; }
image(pg, 0, 0);
fill(0, 0, 0, tmpAlpha);
rect(0, 0, screenWidth, screenHeight);
} else if (tmpTime < 1100) {
isBlackout = true;
if (drawCnt % 6 < 3) {
background(255);
} else {
background(0);
}
} else if (tmpTime < 2500 ) {
background(0);
} else if (tmpTime < 2627 ) {
tmpAlpha = 255 - (tmpTime - 2500) * 2;
if (tmpAlpha < 0.0) { tmpAlpha = 0.0; }
image(pg, 0, 0);
fill(0, 0, 0, tmpAlpha);
rect(0, 0, screenWidth, screenHeight);
} else {
image(pg, 0, 0);
}
} else {
image(pg, 0, 0);
}
} else if (gameMode == 200) {
if (millis() < gameOverStartTime + 2000) {
tmpAlpha = 127.0 / 2000 * (millis() - gameOverStartTime);
if (tmpAlpha > 127.0) { tmpAlpha = 127.0; }
} else if (millis() > gameOverStartTime + 4500) {
tmpAlpha = 127.0 - 127.0 / 300 * (millis() - 4500 - gameOverStartTime);
if (tmpAlpha < 0.0) { tmpAlpha = 0.0; }
} else {
tmpAlpha = 127.0;
}
image(pg, 0, 0);
fill(0, 0, 0, tmpAlpha);
rect(0, 0, screenWidth, screenHeight);
} else {
image(pg, 0, 0);
}
// draw all blocks of main area
if (!isBlackout) {
fill(0, 0, 0, 127);
rect(320, 70, 400, 600);
}
fill(255, 255, 255, 255);
for (int i = 0; i < matrixSizeX; i++) {
for (int j = 0; j < matrixSizeY; j++) {
if (blockMatrix[i][j] == 1) {
if (!(gameMode == 103 && !deleteDone &&
((deletingLineNum == 4 && tmpTime > 1090) ||
(deletingLineNum == 3 && tmpTime > 390)) &&
i > 1 && i < matrixSizeX - 2 &&
(deletingLines[0] == j || deletingLines[1] == j ||
deletingLines[2] == j || deletingLines[3] == j))) {
square(leftMargin + (blockSize + blockMargin) * i,
topMargin + (blockSize + blockMargin) * j,
blockSize);
}
}
}
}
newMinoDrawDone = true;
// draw next mino
int nextMinoMarginX = 875;
int nextMinoMarginY = 168;
if (nextMino == 1) {
nextMinoMarginX -= 12;
nextMinoMarginY -= 10;
} else if (nextMino == 2) {
nextMinoMarginX -= 12;
}
if (!isBlackout) {
fill(0, 0, 0, 127);
rect(805, 90, 160, 160);
}
if (gameMode == 101 ||
(gameMode == 102 && millis() > lastStandbyStartTime + standbyInterval - 100)) {
fill(255, 255, 255, 255);
for (int i = 0; i < 4; i++) {
square(nextMinoMarginX + (blockSize + blockMargin) * minoBlock[nextMino][0][i][0],
nextMinoMarginY + (blockSize + blockMargin) * minoBlock[nextMino][0][i][1],
blockSize);
}
}
// draw score
levelReset();
if (!isBlackout) {
fill(0, 0, 0, 127);
rect(790, 330, 190, 292);
}
if (!(gameMode == 103 && deletingLineNum == 4 && tmpTime >= 512)) {
fill(255, 255, 255, 191);
if (gameMode == 103 && deletingLineNum == 4 && tmpTime < 512) {
fill(255, 255, 255, (191.0 / 512 * (512 - tmpTime)));
}
textFont(createFont(fontName, 17, true));
textAlign(LEFT, CENTER);
text("LEVEL", 820, 365, 165, 45);
textAlign(RIGHT, CENTER);
text(str(level), 785, 392, 165, 45);
textAlign(LEFT, CENTER);
text("SCORE", 820, 440, 165, 45);
textAlign(RIGHT, CENTER);
text(str(score), 785, 467, 165, 45);
textAlign(LEFT, CENTER);
text("HIGH SCORE", 820, 515, 165, 45);
if (score > highScore) {
highScore = score;
}
textAlign(RIGHT, CENTER);
text(str(highScore), 785, 542, 165, 45);
}
lastMinoRotate = minoRotate;
lastMinoY = minoY;
lastMinoX = minoX;
lastRedraw = drawCnt;
}
if (gameMode == 102) {
drawEffectFixingBlock();
}
}
void gameReset() {
mino = int(random(7));
nextMino = int(random(7));
minoX = 6;
minoY = 1;
minoRotate = 0;
score = 0;
levelReset();
println("[" + getTimeString() + "] game reset");
for (int i = 0; i < matrixSizeX; i++) {
for (int j = 0; j < matrixSizeY; j++) {
if (j == matrixSizeY - 1) {
if (i >= 1 && i < matrixSizeX - 1) {
blockMatrix[i][j] = 1;
} else {
blockMatrix[i][j] = 0;
}
} else {
if (i == 1 || i == matrixSizeX - 2) {
blockMatrix[i][j] = 1;
} else {
blockMatrix[i][j] = 0;
}
}
}
}
if (debugBarricadeMode != 0) {
putBarricade();
}
}
void levelReset() {
level = floor(score / levelInterval) + initialLevel;
if (level > speedByLevel.length -1) {
level = speedByLevel.length -1;
}
downInterval = speedByLevel[level];
}
void clearCurrentMino() {
int x, y;
for (int i = 0; i < 4; i++) {
x = minoX + minoBlock[mino][minoRotate][i][0];
y = minoY + minoBlock[mino][minoRotate][i][1];
blockMatrix[x][y] = 0;
}
}
void putCurrentMino() {
int x, y;
for (int i = 0; i < 4; i++) {
x = minoX + minoBlock[mino][minoRotate][i][0];
y = minoY + minoBlock[mino][minoRotate][i][1];
blockMatrix[x][y] = 1;
}
}
void setNextMino() {
putCurrentMino();
setFixingBlock();
gameMode = 102;
// check aligned lines
checkAlignedLines();
// next mino
mino = nextMino;
nextMino = int(random(7));
minoX = 6;
minoY = 1;
minoRotate = 0;
isMoveDownEnabled = false;
intentFixingCnt = 0;
if (mino == 1) {
minoY = 0;
}
lastAsobiResetTime = millis();
lastStandbyStartTime = millis();
newMinoDrawDone = false;
}
// return value: 0 -> ng 1 -> ok
int checkBlock(int mino, int pos, int step, int rotate) {
int result = 1;
int x, y;
for (int i = 0; i < 4; i++) {
x = pos + minoBlock[mino][rotate][i][0];
y = step + minoBlock[mino][rotate][i][1];
if (x >= 0 && x < matrixSizeX && y >= 0 && y < matrixSizeY) {
if (blockMatrix[x][y] == 1) {
result = 0;
}
} else {
result = 0;
}
}
return result;
}
void checkAlignedLines() {
Boolean isAlignedLine;
deletingLineNum = 0;
for (int i = 0; i < 4; i++) {
deletingLines[i] = -1;
}
for (int i = 0; i < matrixSizeY - 1; i++) {
isAlignedLine = true;
for (int j = 2; j <= matrixSizeX - 2; j++) {
if (blockMatrix[j][i] == 0) {
isAlignedLine = false;
}
}
if (isAlignedLine) {
deletingLines[deletingLineNum] = i;
gameMode = 103;
deletingLineNum++;
}
}
if (deletingLineNum > 0) {
// for debug
//println("deletingLines: " +
// deletingLines[0] + ", " + deletingLines[1] + ", " +
// deletingLines[2] + ", " + deletingLines[3]);
lastDeleteStartTime = millis();
}
}
void deleteLine(int line) {
for (int i = 2; i < matrixSizeX - 2; i++) {
for (int j = line; j >= 1; j--) {
blockMatrix[i][j] = blockMatrix[i][j - 1];
}
blockMatrix[i][0] = 0;
}
}
void setFixingBlock() {
int x, y;
for (int i = 0; i < matrixSizeX; i++) {
for (int j = 0; j < matrixSizeY; j++) {
fixingBlock[i][j] = 0;
}
}
for (int i = 0; i < 4; i++) {
x = minoX + minoBlock[mino][minoRotate][i][0];
y = minoY + minoBlock[mino][minoRotate][i][1];
fixingBlock[x][y] = 1;
}
}
void drawEffectFixingBlock() {
for (int i = 0; i < matrixSizeX; i++) {
for (int j = 0; j < matrixSizeY; j++) {
if (fixingBlock[i][j] == 1) {
if (millis() < lastStandbyStartTime + 240) {
if (drawCnt % 6 < 3) {
fill(91, 63, 63);
} else {
fill(255, 255, 255);
}
}
if (millis() < lastStandbyStartTime + 255) {
fill(63 + 192 / 255.0 * (millis() - lastStandbyStartTime),
79 + 176 / 255.0 * (millis() - lastStandbyStartTime),
127 + 127 / 255.0 * (millis() - lastStandbyStartTime));
}
square(leftMargin + (blockSize + blockMargin) * i,
topMargin + (blockSize + blockMargin) * j,
blockSize);
}
}
}
}
void startGameOver() {
gameOverStartTime = millis();
gameMode = 200;
}
void keyPressed() {
if (keyCode == LEFT) {
isKeyLeftPressed = true;
} else if (keyCode == RIGHT) {
isKeyRightPressed = true;
} else if (keyCode == DOWN) {
isKeyDownPressed = true;
} else if (keyCode == UP) {
isKeyUpPressed = true;
} else if (key == KeyPChar) {
isKeyPPressed = true;;
} else if (key == KeyZChar) {
isKeyZPressed = true;
} else if (key == KeyXChar) {
isKeyXPressed = true;
}
}
void keyReleased() {
if (keyCode == LEFT) {
isKeyLeftPressed = false;
isKeyLeftJustReleased = true;
} else if (keyCode == RIGHT) {
isKeyRightPressed = false;
isKeyRightJustReleased = true;
} else if (keyCode == DOWN) {
isKeyDownPressed = false;
} else if (keyCode == UP) {
isKeyUpPressed = false;
isKeyUpJustReleased = true;
} else if (key == KeyPChar) {
isKeyPPressed = false;
isKeyPJustReleased = true;
} else if (key == KeyZChar) {
isKeyZPressed = false;
isKeyZJustReleased = true;
} else if (key == KeyXChar) {
isKeyXPressed = false;
isKeyXJustReleased = true;
}
}
// move left
void moveMinoLeft() {
clearCurrentMino();
if (checkBlock(mino, minoX - 1, minoY, minoRotate) == 1) {
minoX--;
lastAsobiResetTime = millis();
}
putCurrentMino();
lastMoveLeftRightTime = millis();
}
// move right
void moveMinoRight() {
clearCurrentMino();
if (checkBlock(mino, minoX + 1, minoY, minoRotate) == 1) {
minoX++;
lastAsobiResetTime = millis();
}
putCurrentMino();
lastMoveLeftRightTime = millis();
}
// move down (by intention)
void moveMinoDown() {
clearCurrentMino();
if (checkBlock(mino, minoX, minoY + 1, minoRotate) == 1) {
minoY++;
score += 1;
lastAsobiResetTime = millis();
}
putCurrentMino();
lastMoveDownTime = millis();
}
// rotate mino
// mode == 0 -> rotate R
// mode == 1 -> rotate L
void rotateMino(int mode) {
int rotateCandidate = 0;
int[][] candidate = {{-1, minoX, minoY},
{-1, minoX - 1, minoY},
{-1, minoX + 1, minoY},
{1, minoX - 2, minoY},
{1, minoX + 2, minoY},
{-1, minoX, minoY - 1},
{-1, minoX - 1, minoY - 1},
{-1, minoX + 1, minoY - 1},
{1, minoX - 2, minoY - 1},
{1, minoX + 2, minoY - 1},
{1, minoX, minoY + 1}};
Boolean isSliderXRight = false;
Boolean isSliderXLeft = false;
if (isSliderXFound) {
if (sliderX.getValue() > 0.6) {
isSliderXRight = true;
} else if (sliderX.getValue() < -0.6) {
isSliderXLeft = true;
}
}
// if the left is pressed, the left takes priority
if ((isHatFound && hat.left()) || isSliderXLeft || isKeyLeftPressed) {
if (mino == 1) {
candidate[0][1] = minoX - 1;
candidate[1][1] = minoX - 2;
candidate[2][1] = minoX;
candidate[3][1] = minoX + 1;
candidate[4][1] = minoX + 1;
candidate[5][1] = minoX - 1;
candidate[6][1] = minoX - 2;
candidate[7][1] = minoX;
candidate[8][1] = minoX + 1;
candidate[9][1] = minoX + 1;
}
// if the right is pressed, the right takes priority
} else if ((isHatFound && hat.right()) || isSliderXRight || isKeyRightPressed) {
candidate[1][1] = minoX + 1;
candidate[2][1] = minoX - 1;
candidate[3][1] = minoX + 2;
candidate[4][1] = minoX - 2;
candidate[6][1] = minoX + 1;
candidate[7][1] = minoX - 1;
candidate[8][1] = minoX + 2;
candidate[9][1] = minoX - 2;
if (mino == 1) {
candidate[0][1] = minoX + 1;
candidate[1][1] = minoX + 2;
candidate[2][1] = minoX;
candidate[3][1] = minoX - 1;
candidate[4][1] = minoX - 1;
candidate[5][1] = minoX + 1;
candidate[6][1] = minoX + 2;
candidate[7][1] = minoX;
candidate[8][1] = minoX - 1;
candidate[9][1] = minoX - 1;
}
}
clearCurrentMino();
if (mode == 0) {
rotateCandidate = minoRotate + 1;
} else if (mode == 1) {
rotateCandidate = minoRotate - 1;
}
if (rotateCandidate > 3) { rotateCandidate = 0; }
if (rotateCandidate < 0) { rotateCandidate = 3; }
//println(" -- candidate check start --"); // for debug
for (int i = 0; i < candidate.length; i++) {
if (candidate[i][0] == -1 || candidate[i][0] == mino) {
// for debug
//println("candidate[" + i + "] -- minoX: " + candidate[i][1] + " minoY: " +candidate[i][2]);
if (checkBlock(mino, candidate[i][1], candidate[i][2], rotateCandidate) == 1) {
minoRotate = rotateCandidate;
minoX = candidate[i][1];
minoY = candidate[i][2];
lastAsobiResetTime = millis();
break;
}
}
}
putCurrentMino();
}
String getTimeString() {
String result = "";
result += nf(hour(), 2) + ":";
result += nf(minute(), 2) + ":";
result += nf(second(), 2);
return result;
}
// for debug
void putBarricade() {
// barricade at the top
if (debugBarricadeMode == 1) {
for (int i = 3; i <= 10; i++) {
if (i % 2 == 1) {
blockMatrix[i][8] = 1;
} else {
blockMatrix[i][10] = 1;
}
}
// barricade at the top
} else if (debugBarricadeMode == 2) {
for (int i = 2; i <= 11; i++) {
if (i != 7) {
blockMatrix[i][11] = 1;
}
}
blockMatrix[2][8] = 1;
blockMatrix[3][8] = 1;
blockMatrix[10][8] = 1;
blockMatrix[11][8] = 1;
// tetris or triple chance
} else if (debugBarricadeMode == 101) {
for (int i = 2; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[10][17] = 0;
// tetris chance (tense situation)
} else if (debugBarricadeMode == 102) {
for (int i = 2; i <= 10; i++) {
for (int j = 4; j <= 20; j++) {
blockMatrix[i][j] = 1;
}
}
// tetris chance (tense situation)
} else if (debugBarricadeMode == 103) {
for (int i = 3; i <= 11; i++) {
for (int j = 4; j <= 20; j++) {
blockMatrix[i][j] = 1;
}
}
// split triple chance
} else if (debugBarricadeMode == 201) {
for (int i = 2; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[5][18] = 0;
// split triple chance
} else if (debugBarricadeMode == 202) {
for (int i = 2; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[10][18] = 0;
// split double chance
} else if (debugBarricadeMode == 301) {
for (int i = 2; i <= 10; i++) {
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[10][19] = 0;
// spin chance (T)
} else if (debugBarricadeMode == 401 || debugBarricadeMode == 402) {
for (int i = 2; i <= 11; i++) {
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
if (debugBarricadeMode == 401) {
blockMatrix[6][18] = 0;
} else {
blockMatrix[8][18] = 0;
}
blockMatrix[7][18] = 0;
blockMatrix[6][19] = 0;
blockMatrix[7][19] = 0;
blockMatrix[8][19] = 0;
blockMatrix[2][20] = 0;
// spin chance (T)
} else if (debugBarricadeMode == 403 || debugBarricadeMode == 404) {
for (int i = 2; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
if (debugBarricadeMode == 403) {
blockMatrix[5][17] = 0;
} else {
blockMatrix[7][17] = 0;
}
blockMatrix[6][17] = 0;
blockMatrix[5][18] = 0;
blockMatrix[6][18] = 0;
blockMatrix[7][18] = 0;
blockMatrix[6][19] = 0;
// spin chance (T)
} else if (debugBarricadeMode == 405) {
for (int i = 2; i <= 11; i++) {
blockMatrix[i][16] = 1;
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[8][16] = 0;
blockMatrix[9][16] = 0;
blockMatrix[6][17] = 0;
blockMatrix[7][17] = 0;
blockMatrix[8][17] = 0;
blockMatrix[9][17] = 0;
blockMatrix[6][18] = 0;
blockMatrix[7][18] = 0;
blockMatrix[8][18] = 0;
blockMatrix[6][19] = 0;
blockMatrix[7][19] = 0;
blockMatrix[7][20] = 0;
// spin chance (S or Z)
} else if (debugBarricadeMode == 411) {
for (int i = 2; i <= 11; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[3][17] = 0;
blockMatrix[4][17] = 0;
blockMatrix[3][18] = 0;
blockMatrix[4][18] = 0;
blockMatrix[2][19] = 0;
blockMatrix[3][19] = 0;
blockMatrix[4][19] = 0;
blockMatrix[9][17] = 0;
blockMatrix[10][17] = 0;
blockMatrix[9][18] = 0;
blockMatrix[10][18] = 0;
blockMatrix[9][19] = 0;
blockMatrix[10][19] = 0;
blockMatrix[11][19] = 0;
blockMatrix[6][20] = 0;
// korogashi test (J or L)
} else if (debugBarricadeMode == 501) {
for (int i = 3; i <= 10; i++) {
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[3][17] = 1;
blockMatrix[10][17] = 1;
// flexible rotation test (I)
} else if (debugBarricadeMode == 601) {
for (int i = 3; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[2][10] = 1;
blockMatrix[3][10] = 1;
blockMatrix[10][10] = 1;
blockMatrix[11][10] = 1;
blockMatrix[3][15] = 1;
blockMatrix[3][16] = 1;
blockMatrix[4][16] = 1;
blockMatrix[5][16] = 1;
blockMatrix[7][17] = 0;
blockMatrix[8][15] = 1;
blockMatrix[8][16] = 1;
blockMatrix[9][16] = 1;
blockMatrix[10][16] = 1;
blockMatrix[7][17] = 0;
// flexible rotation test (I or J or L)
} else if (debugBarricadeMode >= 602 && debugBarricadeMode <= 603) {
for (int i = 3; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[2][10] = 1;
blockMatrix[3][10] = 1;
blockMatrix[10][10] = 1;
blockMatrix[11][10] = 1;
blockMatrix[10][16] = 1;
blockMatrix[9][17] = 0;
blockMatrix[3][16] = 1;
blockMatrix[4][17] = 0;
if (debugBarricadeMode == 603) {
blockMatrix[2][13] = 1;
blockMatrix[3][13] = 1;
blockMatrix[4][13] = 1;
blockMatrix[9][13] = 1;
blockMatrix[10][13] = 1;
blockMatrix[11][13] = 1;
}
// flexible rotation test (I or J or L)
} else if (debugBarricadeMode >= 611 && debugBarricadeMode <= 613) {
for (int i = 3; i <= 10; i++) {
blockMatrix[i][17] = 1;
blockMatrix[i][18] = 1;
blockMatrix[i][19] = 1;
blockMatrix[i][20] = 1;
}
blockMatrix[2][10] = 1;
blockMatrix[3][10] = 1;
blockMatrix[10][10] = 1;
blockMatrix[11][10] = 1;
blockMatrix[9][16] = 1;
blockMatrix[10][16] = 1;
blockMatrix[8][17] = 0;
blockMatrix[3][16] = 1;
blockMatrix[4][16] = 1;
blockMatrix[5][17] = 0;
if (debugBarricadeMode == 612) {
blockMatrix[6][14] = 1;
blockMatrix[6][15] = 1;
blockMatrix[7][14] = 1;
blockMatrix[7][15] = 1;
} else if (debugBarricadeMode == 613) {
blockMatrix[3][14] = 1;
blockMatrix[4][14] = 1;
blockMatrix[9][14] = 1;
blockMatrix[10][14] = 1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment