Question
Tetris Game AS3- I can't move the tetromino HORIZONTALLY, DOWN, ROTATE (need help!)
package {
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.KeyboardEvent;
import flash.display.DisplayObject;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
// The main class for the Tetris game, extends Sprite.
public class Main extends Sprite {
// Constant representing the size of a single tile (in pixels).
private const TS: uint = 20;
private const ROWS: uint = 20;
private const COLS: uint = 10;
// Array to numerically represent the game field.
private var fieldArray: Array;
// DisplayObject to graphically render the game field.
private var fieldSprite: Sprite;
// Arrays to store different types of tetrominoes and their colors.
private var tetrominoes: Array = new Array();
private var colors: Array = new Array();
// Sprite to represent the current tetromino.
private var tetromino: Sprite;
// Variables to store the current and next tetromino types, current rotation,
// and the current position of the tetromino on the game field.
private var currentTetromino: uint;
private var nextTetromino: uint;
private var currentRotation: uint;
private var tRow: int;
private var tCol: int;
private var gameOver: Boolean = false;
// Timer to control the falling speed of tetrominoes.
private var timeCount: Timer = new Timer(500);
// Constructor function for the Main class.
public function Main() {
generateField();
initTetrominoes();
// Generate the random value for the next tetromino
nextTetromino = Math.floor(Math.random() * 7);
generateTetromino();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKDown);
}
/**
* Function to generate the initial game field.
* The field is represented numerically by a 2D array (fieldArray)
* and visually by a Sprite (fieldSprite) added to the stage.
*/
private function generateField(): void {
// Define alternating colors for the game field tiles.
var colors: Array = new Array("0x444444", "0x555555");
// Initialize the fieldArray to store numerical representation of the game field.
fieldArray = new Array;
// Create a new Sprite to visually represent the game field and add it to the stage.
var fieldSprite: Sprite = new Sprite;
addChild(fieldSprite);
// Set the line style for the graphics of the fieldSprite.
fieldSprite.graphics.lineStyle(0, 0x000000);
// Set the position of fieldSprite to specific coordinates.
//fieldSprite.x = 29.05;
//fieldSprite.y = 18.05;
// Loop through rows and columns to initialize fieldArray and draw the game field.
for (var i: uint = 0; i < 20; i++) {
// Initialize a new array for each row in fieldArray.
fieldArray[i] = new Array;
for (var j: uint = 0; j < 10; j++) {
// Initialize each element in fieldArray to 0.
fieldArray[i][j] = 0;
// Use alternating colors to create a checkerboard pattern in the game field.
fieldSprite.graphics.beginFill(colors[(j % 2 + i % 2) % 2]);
// Draw a rectangle representing a tile in the game field.
fieldSprite.graphics.drawRect(TS * j, TS * i, TS, TS);
// End the fill for the current tile.
fieldSprite.graphics.endFill();
}
}
}
// Function to initialize different types of tetrominoes and their colors.
private function initTetrominoes(): void {
// I
tetrominoes[0] = [
[
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]
]
];
colors[0] = 0x00FFFF;
// T
tetrominoes[1] = [
[
[0, 0, 0, 0],
[1, 1, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[1, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
]
];
colors[1] = 0xAA00FF;
// L
tetrominoes[2] = [
[
[0, 0, 0, 0],
[1, 1, 1, 0],
[1, 0, 0, 0],
[0, 0, 0, 0]
],
[
[1, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 1, 0],
[1, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]
]
];
colors[2] = 0xFFA500;
// J
tetrominoes[3] = [
[
[1, 0, 0, 0],
[1, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
],
[
[0, 0, 0, 0],
[1, 1, 1, 0],
[0, 0, 1, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[0, 1, 0, 0],
[1, 1, 0, 0],
[0, 0, 0, 0]
]
];
colors[3] = 0x0000FF;
// Z
tetrominoes[4] = [
[
[0, 0, 0, 0],
[1, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]
],
[
[0, 0, 1, 0],
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 0, 0, 0]
]
];
colors[4] = 0xFF0000;
// S
tetrominoes[5] = [
[
[0, 0, 0, 0],
[0, 1, 1, 0],
[1, 1, 0, 0],
[0, 0, 0, 0]
],
[
[0, 1, 0, 0],
[0, 1, 1, 0],
[0, 0, 1, 0],
[0, 0, 0, 0]
]
];
colors[5] = 0x00FF00;
// O
tetrominoes[6] = [
[
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
];
colors[6] = 0xFFFF00;
}
// Generate the current tetromino
private function generateTetromino(): void {
// Check if the game is not over
if (!gameOver) {
// Generate a new random tetromino
currentTetromino = Math.floor(Math.random() * 7);
// Set the initial rotation for the current tetromino
currentRotation = 0;
// Set the initial row for the current tetromino to the top
tRow = 0;
// Adjust starting position based on the tetromino shape and the width of the game field
tCol = Math.floor((10 - tetrominoes[currentTetromino][0][0].length) / 2);
// Draw the current tetromino on the stage
drawTetromino();
// Check if the tetromino can fit in its starting position
if (canFit(tRow, tCol, currentRotation)) {
// Add timer listener for automatic downward movement
timeCount.addEventListener(TimerEvent.TIMER, onTime);
timeCount.start();
} else {
// If tetromino cannot fit, it's game over
gameOver = true;
}
}
}
// Draw the next tetromino
private function drawNext(): void {
// Remove the previous "next" sprite if it exists
if (getChildByName("next") != null) {
removeChild(getChildByName("next"));
}
// Create a new sprite for the next tetromino
var next_t: Sprite = new Sprite();
next_t.x = 300; // Set the x-coordinate for the display
next_t.name = "next"; // Set the name for identification
addChild(next_t); // Add the sprite to the display list
// Draw the next tetromino
next_t.graphics.lineStyle(0, 0x000000);
for (var i: int = 0; i < tetrominoes[nextTetromino][0][0].length; i++) {
for (var j: int = 0; j < tetrominoes[nextTetromino][0].length; j++) {
if (tetrominoes[nextTetromino][0][j][i] == 1) {
next_t.graphics.beginFill(colors[nextTetromino]);
next_t.graphics.drawRect(TS * i, TS * j, TS, TS);
next_t.graphics.endFill();
}
}
}
}
// Add the onTime function for the timer listener
private function onTime(e: TimerEvent): void {
// Check if tetromino can fit one row down
if (canFit(tRow + 1, tCol, currentRotation)) {
// Move the tetromino down by one row
tRow++;
placeTetromino();
} else {
// If it can't move down, land the tetromino and generate a new one
landTetromino();
generateTetromino();
}
}
// Function to draw the current tetromino on the stage.
private function drawTetromino(): void {
var ct: uint = currentTetromino;
tetromino = new Sprite();
addChild(tetromino);
tetromino.graphics.lineStyle(0, 0xCFCCF7);
// Loop to draw the individual tiles of the current tetromino.
for (var i: int = 0; i < tetrominoes[ct][currentRotation].length; i++) {
for (var j: int = 0; j < tetrominoes[ct][currentRotation][i].length; j++) {
if (tetrominoes[ct][currentRotation][i][j] == 1) { // Fix: Check if the value is equal to 1
tetromino.graphics.beginFill(colors[ct]);
tetromino.graphics.drawRect(TS * j, TS * i, TS, TS);
tetromino.graphics.endFill();
}
}
}
// Place the tetromino on the game field.
placeTetromino();
}
// Function to place the current tetromino at its current position.
private function placeTetromino(): void {
tetromino.x = tCol * TS;
tetromino.y = tRow * TS;
}
// Modify onKDown function to check for game over and handle horizontal movement
private function onKDown(e: KeyboardEvent): void {
// Check if the game is not over
if (!gameOver) {
switch (e.keyCode) {
case 37: // LEFT arrow key
if (canFit(tRow, tCol - 1, currentRotation)) {
// Move the current tetromino to the left
tCol--;
placeTetromino();
}
break;
case 38: // UP arrow key
// Rotate the tetromino (existing code)
var ct: uint = currentRotation;
var rot: uint = (ct + 1) % tetrominoes[currentTetromino].length;
if (canFit(tRow, tCol, rot)) {
currentRotation = rot;
removeChild(tetromino);
drawTetromino();
placeTetromino();
}
break;
case 39: // RIGHT arrow key
if (canFit(tRow, tCol + 1, currentRotation)) {
// Move the current tetromino to the right
tCol++;
placeTetromino();
}
break;
case 40: // DOWN arrow key
if (canFit(tRow + 1, tCol, currentRotation)) {
// Move the current tetromino down
tRow++;
placeTetromino();
} else {
landTetromino();
generateTetromino();
}
break;
}
}
}
// Modify landTetromino function to remove the timer listener
private function landTetromino(): void {
var ct: uint = currentTetromino;
var landed: Sprite;
for (var i: int = 0; i < tetrominoes[ct][currentRotation].length; i++) {
for (var j: int = 0; j < tetrominoes[ct][currentRotation][i].length; j++) {
// Check for tetromino piece
if (tetrominoes[ct][currentRotation][i][j] == 1) {
// Create a new sprite for the landed piece
landed = new Sprite();
// Add the landed piece to the display list
addChild(landed);
// Draw the landed piece
landed.graphics.lineStyle(0, 0x000000);
landed.graphics.beginFill(colors[currentTetromino]);
landed.graphics.drawRect(TS * (tCol + j), TS * (tRow + i), TS, TS);
landed.graphics.endFill();
// Set the name for easy identification
landed.name = "r" + (tRow + i) + "c" + (tCol + j);
// Update the fieldArray to mark the cell as occupied
fieldArray[tRow + i][tCol + j] = 1;
}
}
}
// Remove the timer listener
timeCount.removeEventListener(TimerEvent.TIMER, onTime);
// Stop the timer
timeCount.stop();
// Continue with line checking and removal
checkForLines();
}
private function rotateTetromino(): void {
var ct: uint = currentRotation;
var rot: uint = (ct + 1) % tetrominoes[currentTetromino].length;
if (canFit(tRow, tCol, rot)) {
currentRotation = rot;
removeChild(tetromino);
drawTetromino();
placeTetromino();
}
}
// Updated checkForLines function to manage remaining lines after removal
private function checkForLines(): void {
for (var i: int = 0; i < 20; i++) {
if (fieldArray[i].indexOf(0) == -1) {
// Remove the completed line
for (var j: int = 0; j < 10; j++) {
fieldArray[i][j] = 0;
removeChild(getChildByName("r" + i + "c" + j));
}
// Move down tetrominoes above the cleared line
for (j = i; j >= 0; j--) {
for (var k: int = 0; k < 10; k++) {
if (fieldArray[j][k] == 1) {
fieldArray[j][k] = 0;
fieldArray[j + 1][k] = 1;
// Move down the corresponding DisplayObject
getChildByName("r" + j + "c" + k).y += TS;
// Update the name based on the new position
getChildByName("r" + j + "c" + k).name = "r" + (j + 1) + "c" + k;
}
}
}
}
}
}
// Function to remove a completed line
private function removeLine(lineIndex: int): void {
// Loop through each column of the fieldArray
for (var j: int = 0; j < fieldArray[lineIndex].length; j++) {
// Remove the cell from the display
removeChild(getChildByName("r" + lineIndex + "c" + j));
}
}
// Function to move down lines above the removed line
private function moveLinesDown(removedLine: int): void {
// Loop through each row above the removed line
for (var i: int = removedLine; i > 0; i--) {
// Copy the row above to the current row
fieldArray[i] = fieldArray[i - 1].concat();
// Loop through each column
for (var j: int = 0; j < fieldArray[i].length; j++) {
// Update the name of the moved cell
var cell: DisplayObject = getChildByName("r" + (i - 1) + "c" + j);
if (cell) cell.name = "r" + i + "c" + j;
}
}
// Empty the top row
fieldArray[0] = new Array(10).fill(0);
}
private function canFit(row: int, col: int, side: uint): Boolean {
var ct: uint = currentTetromino;
for (var i: int = 0; i < tetrominoes[ct][side].length; i++) {
for (var j: int = 0; j < tetrominoes[ct][side][i].length; j++) {
if (tetrominoes[ct][side][i][j] == 1) {
// Check for boundaries
if (col + j < 0 || col + j > 9) {
return false;
}
// Check if the cell is occupied by another tetromino
if (fieldArray[row + i][col + j] == 1) {
return false;
}
}
}
}
return true;
}
}
}