Задание 17
Я тут немножечко подправил код.
Но в целом - отлично.
Запускаем и наслаждаемся тетрисом.
P.S.
Не забудь отрегулировать высоту консоли
Задание 16
Напиши свою реализацию методов left(), right(), up(), down() в классе Figure.
Подумай, что должны делать эти методы?
Задание 15
Напиши реализацию метода step в классе Tetris.
В методе надо переместить фигурку вниз на один шаг.
Если после перемещения положить фигурку на текущее место невозможно, то:
а) поднять ее обратно (up)
б) "приземлить" ее (landed)
в) удалить все "полные линии" в объекте field
г) создать новую фигурку взамен старой.
Задание 14
Напиши реализацию метода removeFullLines в классе Field
Надо
а) удалить все строки из матрицы, которые полностью заполнены (состоят из одних единиц)
б) сместить оставшиеся строки вниз
в) создать новые строки взамен отсутствующих.
ВАЖНО!
matrix[y][x] содержит элемент с координатами (x,y)
matrix[i] содержит i-ю строку
а) Мы можем удалить стоку:
matrix[i] = null
б)Скопировать [ссылку на] строку:
matrix[i+1] = matrix[i];
в)Создать новую строку:
matrix[i] = new int[width];
Задание 13
Теперь приступим к реализации созданных методов.
Напиши реализацию метода print в классе Field
а) Метод должен выводить на экран прямоугольник.
б) Высота прямоугольника равна height, ширина - width
в) Если данная клетка пустая - вывести точку, если не пустая - английский X
Подсказка:
if (matrix[y][x]==0) ...
Задание 12
В тетрисе мы управляем движением фигурки с помощью клавиатуры.
Тут есть 4 действия:
движение влево (кнопка влево)
движение вправо (кнопка влево)
поворот фигурки (цифра 5 на доп.клавиатуре справа)
падение вниз (пробел)
Мы будем обрабатывать ввод с клавиатуры в методе run() класса Tetris.
И тут у меня для тебя две новости: хорошая и плохая
Плохая новость состоит в том, что java не позволяет считать нажатые символы с клавиатуры,
пока пользователь не нажмет enter.
Не очень удобно, правда?
Хорошая новость состоит в том, я написал специальный класс (KeyboardObserver), который позволяет обойти это ограничение.
Так что ты можешь воспользоваться им.
Есть еще и отличная новость.
Ты до сих пор отлично справлялся, поэтому я помогу тебе немного.
Я написал за тебя методы:
а) createRandomFigure в FigureFactory
б) run в Tetris
Изучи их внимательно и переходи дальше.
Задание 11
Теперь создай класс FigureFactory.
С его помощью мы будем создавать фигуры различных форм.
Пока он будет содержать только один статический метод createRandomFigure:
public static Figure createRandomFigure(int x,int y)
Задание 10
Так же нам понадобятся методы для управления фигуркой.
Добавь в класс Figure методы:
left() - для движения фигурки влево.
right() - для движения фигурки вправо.
down() - для движения фигурки вниз.
up() - для движения фигурки вверх.
downMaximum() - падение фигурки в низ до дна.
rotate() - для поворота фигурки вокруг главной диагонали.
boolean isCurrentPositionAvailable() - проверка - может ли фигурка быть помещена в текущую позицию. Для теста захардкодь результат в true.
landed() - вызывается, когда фигурка достигла дна или уперлась в другую фигурку
Все ее занятые клетки теперь должны добавиться в Field.
Задание 9
Если ты обратил внимание, мы пишем программу "сверху вниз".
Сначала решили, какие классы нам нужны. Затем - какие методы.
А потом уже начнем писать код этих методов.
Таким образом мы разбиваем большую задачу на множество маленьких.
Когда код всех методов будет готов, останется только проверить - так ли все работает, как должно быть.
И если надо - внести некоторые изменения.
Задание 8
Теперь создадим костяк класса Figure.
Этот класс будет описывать падающую фигурку.
Нам понадобятся ее координаты и форма.
За координаты будут отвечать две переменные x и y.
За форму - матрица. Двумерный массив 3x3, состоящий из единиц и нулей.
Единицей мы обозначаем что клетка есть, нулем - что она пустая.
Добавь в класс Figure два поля поля: x типа int, y типа int.
Еще добавь двумерный массив: matrix(матрица) типа int[][].
Там же добавь getter'ы для созданных переменных.
Добавь конструктор с тремя параметрами x, y, matrix.
Задание 7
Нам понадобится еще 4 метода в классе Field:
а) print() - объект будет отрисовывать на экран свое текущее состояние;
б) removeFullLines() - будет удалять из матрицы полностью заполненные строки и сдвигать вышележащие строки вниз;
в) Integer getValue(int x, int y) - возвращает значение которое находится в матрице с координатами x и y;
г) void setValue(int x, int y, int value) - устанавливает переданное значение в ячейку массива (матрицы) с координатами x, y.
Задание 6
Теперь перейдем к классу Field.
Он будет отвечать за хранение данных о текущих занятых и свободных клетках на поле игры.
Добавь в класс Field два поля поля: width (ширина) типа int, height(высота) типа int.
Так же нам понадобится матрица - двумерный массив: matrix(матрица) типа int[][];
Там же добавь getter'ы для созданных переменных.
Добавь конструктор с двумя параметрами width и height. И не забудь про матрицу.
ВАЖНО!
Двумерный массив можно представить как массив массивов или как прямоугольную матрицу.
При этом первой координатой в массиве у нас будет номер строки, а второй - столбца.
Другими словами ячейка с координатами x, y - это matrix[y][x].
Задание 5
Теперь нужно создать объект самого Тетриса.
Добавь в класс Tetris статическую переменную game. (тип - Tetris, видимость - public)
Затем в методе main создай объект типа Тетрис и сохрани его в эту переменную.
Затем добавь вызов метода run.
Должно получиться что-то типа такого:
game = new Tetris();
game.run();
Задание 4
Так же еще нам понадобится пара методов.
Добавь в класс Tetris методы run() и step():
run() будет отвечать за всю игру.
А step() - за один шаг в игре.
Задание 3
Отлично! Теперь добавь в Tetris две переменные: field типа Field и figure типа Figure.
С их помощью мы будем хранить информацию о текущей фигурке и о состоянии дел на "поле клеток".
Так же добавь getter'ы для созданных переменных.
Задание 2
А еще нам понадобится метод main. Как же без него.
Добавь метод main в класс Tetris
Задание 1
Давай напишем игру Тетрис.
Наш Тетрис будет состоять из двух вещей: поля из клеток и фигурки, которая падает.
Поэтому для начала создай три класса: Field(поле с клетками), Figure(фигура) и сам Tetris.
package com.javarush.test.level22.lesson18.big01;
import java.awt.event.KeyEvent;
/**
* Класс Tetris - содержит основной функционал игры.
*/
public class Tetris {
public static Tetris game;
private Field field; //Поле с клетками
private Figure figure; //Фигурка
private boolean isGameOver; //Игра Окончена?
public Tetris(int width, int height) {
field = new Field(width, height);
figure = null;
}
public static void main(String[] args) throws Exception {
game = new Tetris(10, 20);
game.run();
}
/**
* Геттер переменной field.
*/
public Field getField() {
return field;
}
/**
* Сеттер для field
*/
public void setField(Field field) {
this.field = field;
}
/**
* Геттер переменной figure.
*/
public Figure getFigure() {
return figure;
}
/**
* Сеттер для figure
*/
public void setFigure(Figure figure) {
this.figure = figure;
}
/**
* Основной цикл программы.
* Тут происходят все важные действия
*/
public void run() throws Exception {
//Создаем объект "наблюдатель за клавиатурой" и стартуем его.
KeyboardObserver keyboardObserver = new KeyboardObserver();
keyboardObserver.start();
//выставляем начальное значение переменной "игра окончена" в ЛОЖЬ
isGameOver = false;
//создаем первую фигурку посередине сверху: x - половина ширины, y - 0.
figure = FigureFactory.createRandomFigure(field.getWidth() / 2, 0);
//пока игра не окончена
while (!isGameOver) {
//"наблюдатель" содержит события о нажатии клавиш?
if (keyboardObserver.hasKeyEvents()) {
//получить самое первое событие из очереди
KeyEvent event = keyboardObserver.getEventFromTop();
//Если равно символу 'q' - выйти из игры.
if (event.getKeyChar() == 'q') return;
//Если "стрелка влево" - сдвинуть фигурку влево
if (event.getKeyCode() == KeyEvent.VK_LEFT)
figure.left();
//Если "стрелка вправо" - сдвинуть фигурку вправо
else if (event.getKeyCode() == KeyEvent.VK_RIGHT)
figure.right();
//Если код клавиши равен 12 ("цифра 5 на доп. клавиатуре") - повернуть фигурку
else if (event.getKeyCode() == 12)
figure.rotate();
//Если "пробел" - фигурка падает вниз на максимум
else if (event.getKeyCode() == KeyEvent.VK_SPACE)
figure.downMaximum();
}
step(); //делаем очередной шаг
field.print(); //печатаем состояние "поля"
Thread.sleep(300); //пауза 300 миллисекунд - 1/3 секунды
}
//Выводим сообщение "Game Over"
System.out.println("Game Over");
}
public void step() {
//опускам фигурку вниз
figure.down();
//если разместить фигурку на текущем месте невозможно
if (!figure.isCurrentPositionAvailable()) {
figure.up(); //поднимаем обратно
figure.landed(); //приземляем
isGameOver = figure.getY() <= 1;//если фигурка приземлилась на самом верху - игра окончена
field.removeFullLines(); //удаляем заполненные линии
figure = FigureFactory.createRandomFigure(field.getWidth() / 2, 0); //создаем новую фигурку
}
}
}
package com.javarush.test.level22.lesson18.big01;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
public class KeyboardObserver extends Thread {
private Queue<KeyEvent> keyEvents = new ArrayBlockingQueue<KeyEvent>(100);
private JFrame frame;
@Override
public void run() {
frame = new JFrame("KeyPress Tester");
frame.setTitle("Transparent JFrame Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setUndecorated(true);
frame.setSize(400, 400);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setLayout(new GridBagLayout());
frame.setOpacity(0.0f);
frame.setVisible(true);
frame.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
//do nothing
}
@Override
public void focusLost(FocusEvent e) {
System.exit(0);
}
});
frame.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
//do nothing
}
public void keyReleased(KeyEvent e) {
//do nothing
}
public void keyPressed(KeyEvent e) {
keyEvents.add(e);
}
});
}
public boolean hasKeyEvents() {
return !keyEvents.isEmpty();
}
public KeyEvent getEventFromTop() {
return keyEvents.poll();
}
}
package com.javarush.test.level22.lesson18.big01;
/**
* Класс Figure описывает фигурку тетриса
*/
public class Figure {
//метрица которая определяет форму фигурки: 1 - клетка не пустая, 0 - пустая
private int[][] matrix;
//координаты
private int x;
private int y;
public Figure(int x, int y, int[][] matrix) {
this.x = x;
this.y = y;
this.matrix = matrix;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int[][] getMatrix() {
return matrix;
}
/**
* Поворачаиваем фигурку.
* Для простоты - просто вокруг главной диагонали.
*/
public void rotate() {
int[][] matrix2 = new int[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
matrix2[i][j] = matrix[j][i];
}
}
matrix = matrix2;
}
/**
* Двигаем фигурку влево.
* Проверяем не вылезла ли она за границу поля и/или не залезла ли на занятые клетки.
*/
public void left() {
x--;
if (!isCurrentPositionAvailable())
x++;
}
/**
* Двигаем фигурку вправо.
* Проверяем не вылезла ли она за границу поля и/или не залезла ли на занятые клетки.
*/
public void right() {
x++;
if (!isCurrentPositionAvailable())
x--;
}
/**
* Двигаем фигурку вверх.
* Используется, если фигурка залезла на занятые клетки.
*/
public void up() {
y--;
}
/**
* Двигаем фигурку вниз.
*/
public void down() {
y++;
}
/**
* Двигаем фигурку вниз до тех пор, пока не залезем на кого-нибудь.
*/
public void downMaximum() {
while (isCurrentPositionAvailable()) {
y++;
}
y--;
}
/**
* Проверяем - может ли фигурка находится на текущей позици:
* а) не вылазиет ли она за границы поля
* б) не залазиет ли она на занятые клетки
*/
public boolean isCurrentPositionAvailable() {
Field field = Tetris.game.getField();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (matrix[i][j] == 1) {
if (y + i >= field.getHeight())
return false;
Integer value = field.getValue(x + j, y + i);
if (value == null || value == 1)
return false;
}
}
}
return true;
}
/**
* Приземляем фигурку - добавляем все ее непустые клетки к клеткам поля.
*/
public void landed() {
Field field = Tetris.game.getField();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (matrix[i][j] == 1)
field.setValue(x + j, y + i, 1);
}
}
}
}
package com.javarush.test.level22.lesson18.big01;
/**
* Клсс FigureFactory отвечает за создание объектов-фигурок.
*/
public class FigureFactory {
/**
* Набор из шести шаблонов для фигурок
*/
public static final int[][][] BRICKS = {{
{1, 1, 0}, // X X
{0, 1, 1}, // X X
{0, 0, 0}}, { //
{1, 0, 0}, // X
{1, 1, 0}, // X X
{0, 1, 0}}, { // X
{0, 1, 0}, // X
{0, 1, 0}, // X
{0, 0, 0}}, { // X
{1, 1, 0}, // X X
{1, 1, 0}, // X X
{0, 0, 0}}, { //
{1, 1, 1}, // X X X
{0, 1, 0}, // X
{0, 0, 0}}, { //
{1, 1, 1}, // X X X
{1, 1, 1}, // X X X
{0, 0, 0}} //
};
/**
* Метод выбирает случайный шаблон и создает с ним новую фигурку.
*/
public static Figure createRandomFigure(int x, int y) {
int index = (int) (Math.random() * 6);
return new Figure(x, y, BRICKS[index]);
}
}
package com.javarush.test.level22.lesson18.big01;
import java.util.ArrayList;
/**
* Класс Field описывает "поле клеток" игры Тетрис
*/
public class Field {
//ширина и высота
private int width;
private int height;
//матрица поля: 1 - клетка занята, 0 - свободна
private int[][] matrix;
public Field(int width, int height) {
this.width = width;
this.height = height;
matrix = new int[height][width];
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int[][] getMatrix() {
return matrix;
}
/**
* Метод возвращает значение, которое содержится в матрице с координатами (x,y)
* Если координаты за пределами матрицы, метод возвращает null.
*/
public Integer getValue(int x, int y) {
if (x >= 0 && x < width && y >= 0 && y < height)
return matrix[y][x];
return null;
}
/**
* Метод устанавливает переданное значение(value) в ячейку матрицы с координатами (x,y)
*/
public void setValue(int x, int y, int value) {
if (x >= 0 && x < width && y >= 0 && y < height)
matrix[y][x] = value;
}
/**
* Метод печатает на экран текущее содержание матрицы
*/
public void print() {
//Создаем массив, куда будем "рисовать" текущее состояние игры
int[][] canvas = new int[height][width];
//Копируем "матрицу поля" в массив
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
canvas[i][j] = matrix[i][j];
}
}
//Копируем фигурку в массив, только непустые клетки
int left = Tetris.game.getFigure().getX();
int top = Tetris.game.getFigure().getY();
int[][] brickMatrix = Tetris.game.getFigure().getMatrix();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (top + i >= height || left + j >= width) continue;
if (brickMatrix[i][j] == 1)
canvas[top + i][left + j] = 2;
}
}
//Выводим "нарисованное" на экран, но начинаем с "границы кадра".
System.out.println("---------------------------------------------------------------------------\n");
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int index = canvas[i][j];
if (index == 0)
System.out.print(" . ");
else if (index == 1)
System.out.print(" X ");
else if (index == 2)
System.out.print(" X ");
else
System.out.print("???");
}
System.out.println();
}
System.out.println();
System.out.println();
}
/**
* Удаляем заполненные линии
*/
public void removeFullLines() {
//Создаем список для хранения линий
ArrayList<int[]> lines = new ArrayList<int[]>();
//Копируем все непустые линии в список.
for (int i = 0; i < height; i++) {
//подсчитываем количество единиц в строке - просто суммируем все ее значения
int count = 0;
for (int j = 0; j < width; j++) {
count += matrix[i][j];
}
//Если сумма строки не равно ее ширине - добавляем в список
if (count != width)
lines.add(matrix[i]);
}
//Добавляем недостающие строки в начало списка.
while (lines.size() < height) {
lines.add(0, new int[width]);
}
//Преобразуем список обратно в матрицу
matrix = lines.toArray(new int[height][width]);
}
}
Leave A Comment