I'm making 3D tetris in p5.js. But right now I have a bug that makes the pieces fall through the bottom! I'm not sure why that is because i wrote a function, "fallingPieceIsLegal" that should prevent this. Thanks so much for your help!
//Audrey Zheng
//3D Tetris
var cx;
var cy;
// set board dimensions and margin
var rows = 15;
var cols = 10;
var margin = 50;
let emptyColor;
//make board
var state = new Array();
for (var i = 0; i < cols; i ++) {
state.push(emptyColor);
}
var board = new Array();
for (var i = 0; i< rows; i ++) {
board.push(state);
}
//seven "standard" pieces (tetrominoes)
var iPiece = [
[ true, true, true, true]
];
var jPiece = [
[ true, false, false ],
[ true, true, true]
];
var lPiece = [
[ false, false, true],
[ true, true, true]
];
var oPiece = [
[ true, true],
[ true, true]
];
var sPiece = [
[ false, true, true],
[ true, true, false ]
];
var tPiece = [
[ false, true, false ],
[ true, true, true]
];
var zPiece = [
[ true, true, false ],
[ false, true, true]
];
var tetrisPieces = [ iPiece, jPiece, lPiece, oPiece, sPiece, tPiece, zPiece ];
var tetrisPieceColors = [ "green", "pink", "orange", "yellow", "purple", "blue", "red" ];
//the falling piece
var fallingPiece;
var fallingPieceCols;
var fallingPieceCol;
var fallingPieceRow;
function setup() {
frameRate(10);
createCanvas(550, 800);
background(220);
// osc = new p5.TriOsc();
// osc.freq(880.0);
// osc.amp(0.1);
// osc.start();
cx = width/2;
cy = width/2;
newFallingPiece();
//fallingPieceIsLegal();
}
function draw() {
fill(255,255,220);
rect(0,0,width,height);
emptyColor = color(255,0,0,63);
drawBoard(rows,cols, 450,675);
placeFallingPiece();
rectMode(CORNER);
drawFallingPiece();
if (moveFallingPiece(-1,0) == false) {
placeFallingPiece();
newFallingPiece();
}
}
function newFallingPiece(){
fallingPiece = random(tetrisPieces);
fallingPieceCols = fallingPiece[0].length;
fallingPieceCol = cols/2 - Math.floor(fallingPieceCols/2);
fallingPieceRow = 0;
//console.log(fallingPiece);
}
function placeFallingPiece() {
for (var r = 0; r < fallingPiece.length; r ++) {
for (var c = 0; c < fallingPiece[0].length; c ++) {
if (fallingPiece[r][c]) {
board[r + fallingPieceRow][c + fallingPieceCol] = "magenta";
}
//print("hi");
}
}
}
function drawFallingPiece() {
for (var r = 0; r < fallingPiece.length; r ++) {
for (var c = 0; c < fallingPiece[0].length; c ++) {
if (fallingPiece[r][c]) {
var bnds = getCellBounds(r + fallingPieceRow, c + fallingPieceCol, 450, 675);
fill(255);
rect(bnds[0],bnds[2],45,45);
var tetrisCube = new cube(bnds[0],bnds[2], true);
//systems.push(tetrisCube);
tetrisCube.display();
//fill(230,245,255);
rect(bnds[0], bnds[2], 45,45);
}
//print("hi");
}
}
}
function fallingPieceIsLegal() {
for (var r = 0; r < fallingPiece.length; r++) {
for (var c = 0; c < fallingPieceCols; c++) {
if (fallingPiece[r][c] == true) {
if ((c + fallingPieceCol > cols - 1) || (c + fallingPieceCol < 0)) {
return false;
}
if (r + fallingPieceRow > rows -1) {
return false;
}
}
}
}
return true;
}
function moveFallingPiece(drow, dcol) {
if ((drow == 0) && (dcol == -1)) { //move left
fallingPieceCol -= 1;
if (fallingPieceIsLegal() == false) {
fallingPieceCol += 1;
print('hi');
}
}
if ((drow == 0) && (dcol == 1)) { //move right
fallingPieceCol += 1;
if (fallingPieceIsLegal() == false) {
print("yo");
fallingPieceCol -= 1;
}
}
if ((drow == -1) && (dcol == 0)) { //move down
fallingPieceRow += 1;
if (fallingPieceIsLegal() == false) {
fallingPieceRow -= 1;
return false;
}
return true;
}
}
function rotate1(L) {
var result = [];
var a;
for (var col = L[0].length -1; col > -1; col--) {
//print("yeet");
var result1 = [];
for (var row = 0; row < L.length; row++) {
a = L[row][col];
result1.push(a);
print(a);
}
result.push(result1);
}
return result;
}
function rotateFallingPiece() {
fallingPiece = rotate1(fallingPiece);
fallingPieceCols = fallingPiece[0].length;
if (fallingPieceIsLegal == false) {
for (var i = 0; i < 3; i ++) {
fallingPiece = rotate1(fallingPiece);
fallingPieceCols = fallingPiece[0].length;
}
}
print(fallingPiece);
}
function getCellBounds(row,col, width,height) {
var gridWidth = width - 2 * margin;
var gridHeight = height - 2 * margin;
var x0 = margin + width * col/ cols;
var x1 = margin + width * (col + 1)/ cols;
var y0 = margin + height * row / rows;
var y1 = margin + height * (row + 1)/ rows;
return [x0,x1,y0,y1];
}
//console.log(getCellBounds(0,0, 450,600));
function drawBoard(rows, cols, width,height) {
for (var row = 0; row < rows; row ++) {
for (var col = 0; col < cols; col++) {
drawCell(row,col,width,height);
}
}
}
function drawCell(row, col, width, height) {
var bounds = getCellBounds(row,col, width, height);
x0 = bounds[0];
x1 = bounds[1];
y0 = bounds[2];
y1 = bounds[3];
rectMode(CORNER);
var cellCube = new cube(x0 ,y0, false);
cellCube.display();
//quad(x0,y0, x0,y0 + 40, x0+40, y0+40, x0 + 40, y0 );
}
function cube(x,y, isSolid) { //the cube
this.x = x;
this.y = y;
this.width = 45;
this.NW =[this.x, this.y];
this.NE = [x+this.width, this.y];
this.SE = [this.x+this.width, y+this.width];
this.SW = [this.x, y+this.width];
this.larger = new square(x,y,this.width, this.width);
this.smaller = new square(x + (cx -x) * 0.25, y + (cy - y) *0.25, this.width * 0.75, this.width * 0.75);
this.NWs =[(this.x + (cx - this.x) * 0.20), this.y + (cy - this.y) * 0.20];
this.NEs = [(this.x + (cx - this.x) * 0.20) + (this.width * 0.8), this.y + (cy - this.y) * 0.20];
this.SEs = [(this.x + (cx - this.x) * 0.20) + (this.width * 0.8), this.y + (cy - this.y) * 0.20 + (this.width * 0.8)];
this.SWs = [(this.x + (cx - this.x) * 0.20), this.y + (cy - this.y) * 0.20 +(this.width * 0.8)];
this.display = function() {
//rect(this.x, this.y, this.width, this.width);
//rect(this.x + (cx - this.x) * 0.20, this.y + (cy - this.y) * 0.20, this.width * 0.8, this.width * 0.8);
stroke(127);
//bigger square
line(this.x,this.y, this.x + this.width, this.y);
line(this.x,this.y, this.x, this.y + this.width);
line(this.x + this.width, this.y, this.x + this.width, this.y + this.width);
line(this.x, this.y+ this.width, this.x + this.width, this.y + this.width);
//smaller square
line(this.x + (cx - this.x) * 0.20, this.y + (cy - this.y) * 0.20, (this.x + (cx - this.x) * 0.20 )+ this.width * 0.8, this.y + (cy - this.y) * 0.20);
line(this.x + (cx - this.x) * 0.20, this.y + (cy - this.y) * 0.20, this.x + (cx - this.x) * 0.20, this.y + (cy - this.y) * 0.20 + this.width * 0.8);
line(this.x + (cx - this.x) * 0.20 + this.width * 0.8, this.y + (cy - this.y) * 0.20,this.x + (cx - this.x) * 0.20 + this.width * 0.8,this.y + (cy - this.y) * 0.20 + this.width * 0.8);
line(this.x + (cx - this.x) * 0.20, this.y + (cy - this.y) * 0.20 + this.width * 0.8, (this.x + (cx - this.x) * 0.20 )+ this.width * 0.8, this.y + (cy - this.y) * 0.20 + this.width * 0.8);
if (isSolid == false) {
line(this.NW[0], this.NW[1], this.NWs[0], this.NWs[1]);
line(this.NE[0], this.NE[1], this.NEs[0], this.NEs[1]);
line(this.SE[0], this.SE[1], this.SEs[0], this.SEs[1]);
line(this.SW[0], this.SW[1], this.SWs[0], this.SWs[1]);
}
if (isSolid) {
noStroke();
fill(230);
quad(this.SW[0], this.SW[1], this.SE[0], this.SE[1], this.SEs[0], this.SEs[1], this.SWs[0], this.SWs[1]); //bottom
quad(this.NE[0], this.NE[1], this.NEs[0], this.NEs[1], this.SEs[0], this.SEs[1], this.SE[0], this.SE[1]); // right
quad(this.NW[0], this.NW[1], this.NWs[0], this.NWs[1], this.SWs[0], this.SWs[1], this.SW[0], this.SW[1]); //fill left
quad(this.NE[0], this.NE[1], this.NEs[0], this.NEs[1], this.NWs[0], this.NWs[1], this.NW[0], this.NW[1]); //fill top
fill(240);
quad(this.NE[0], this.NE[1], this.SE[0], this.SE[1], this.SW[0], this.SW[1], this.NW[0], this.NW[1]);
}
};
}
function square(x,y,w,h) {
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.getCorners = function() {
var NW =[x-w/2,y-h/2];
//print(NW);
var NE = [x+w/2, y-h/2];
var SE = [x+w/2, y-h/2];
var SW = [x-w/2, y+h/2];
return [NW,NE,SE,SW];
};
}
function keyPressed() {
if (keyCode == LEFT_ARROW) {
moveFallingPiece(0, -1);
}
if (keyCode == RIGHT_ARROW) {
moveFallingPiece(0,1);
}
if (keyCode == DOWN_ARROW) {
moveFallingPiece(-1,0);
}
if (keyCode == UP_ARROW) {
rotateFallingPiece();
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>p5.js vers 0.7.1, Edit index.html to Change This Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/addons/p5.dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/addons/p5.sound.js"></script>
<script src="sketch.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
The placeFallingPiece function is quite similar to drawFallingPiece, only rather than draw the cells, it loads the corresponding cells of the fallingPiece onto the board with the fallingPieceColor. In this way, the piece is placed on the board.
Wow, that looks like a lot of work ! You might want to either simplify or start fresh to solve your problem. (You can always make it more complex later, but if it doesn't work as you need to ,it's best to make easy to tweak/change first).
Unfortunately I won't be able to provide a snippet for every single problem, but I can point a few things that might help you fix your bug:
if (moveFallingPiece(-1,0) == false) {
(e.g. create a class and instances (objects) , solve any collisions (be it with the bottom of the screen or other pieces) and add multiple objects to the array as collisions are detected to form full rows (gain points) or stack up)
I would suggest doing a 2D version with p5.js first, then simply change the p5.js renderer to WEBGL
and swap your rect()
calls for box()
calls (taking into account they're drawn from centre and using push()
/pop()
calls to isolate coordinate spaces for each box). (A bit further down the line you might want to look at the MVC pattern and have the rendering/view separate from the data and control logic, allowing you to change from a 2D to 3D Tetris if you want to)