Pull to refresh

Пишем 2-d игру на Java #2

Reading time6 min
Views39K
Доброго времени суток всем! Продолжение 1-ого урока (ссылка на первый).

Задачи


Сегодня мы должны создать игрока, и реализовать в нем метод движения

Начало


Приступим. Для начала создадим новый класс Player.java.

Создадим несколько переменных, типа int. Первая это x, вторая это y, и третья speed. Пускай они будут приватными, во избежание дальнейших ошибок(установите сразу им значения, x=0, y=0, speed = любая скорость, я буду ставить 2). Также, создадим три метода — getSpeed(), getX() и getY(). Они выглядят так:

	public int getX() {
		return x;
	}
	
	public int getY() {
		return y;
	}
	
	public int getSpeed() {
		return speed;
	}

Они будут возвращать нам текущие x и y координаты нашего игрока, а также скорость игрока.

Теперь, нам нужен метод, отвечающий за движение. Так его и назовем — move. Пока у нас нету слушателя клавиш, по этому, просто напишем:

	public void move() {
		x+= speed;
	}

Возвращаемся в наш класс Main.java и в нем, создаем объект нашего игрока:

Player player = new Player();

Далее, в методе actionPerformed() пишем:

player.move();

Теперь, у нас при каждом обновлении таймера будет двигаться игрок. Но сейчас мы это движение не увидим, т.к. координаты игрока мы нигде не используем. Сейчас я коротко объясню как происходит движение на экране. Двигается не сама картинка нашего игрока, а задний план, который имитирует движение игрока. Так что, переходим в метод paint() и там, вместо координат отрисовки, вписываем player.getX() и player.getY(). Должно выйти так:

g.drawImage(img, player.getX(), player.getY(),frame.getWidth(), frame.getHeight(), null);

Запускаем, и видим что наш фон двигается.

Теперь я объясню, что мы не совсем корректно сделали.

Мы должны иметь две отдельные координаты — координата карты и координата игрока. Для чего это нужно? Сейчас мы слой отрисовываем относительно координаты игрока. В дальнейшем, у нас появятся объекты, находящиеся на карте, и проверять, взаимодействует ли с ними игрок будет проще, имея координаты карты. Надеюсь, объяснил понятно, а теперь мы это реализуем.

По-хорошему, мы должны иметь отдельный объект, Universe (Вселенная) в котором и будет содержаться информация о нашей карте(время суток, координаты и т.п.). В последующих уроках мы это сделаем, а пока, просто добавим в классе игрока две новые переменные: mapX и mapY, и сразу установим им значения, 0 и 0. И, как в случае с x и y, создадим 2 getter'a.

Теперь подкорректируем наш метод move().

Напишем вместо x+=speed это:

x-=speed;
mapX+=speed;

И вернемся к методу paint и поменяем

g.drawImage(img, player.getX(), player.getY(),frame.getWidth(), frame.getHeight(), null);

На:

g.drawImage(img, player.getMapX(), player.getMapY(),frame.getWidth(), frame.getHeight(), null);

Запускаем, и видим что ничего не поменялось. Это хороший знак, значит мы все правильно сделали.

Но мы видим, что картинка одна, и за ней остается след. И вот мы подошли к такой развилке. Мы можем создать игру, в которой мир будет постоянно расширяться, в зависимости оттого, в каком направлении идет игрок. Либо, сделать игру с определенного размера локацией, за пределы которой выйти нельзя, но локации смогут меняться. С какими проблемами я столкнулся, при создании этих двух вариантов. Если будем создавать игру без границ, то:

  1. Надо будет осуществить генерацию этого мира, и если у нее нет пределов, то её надо делать случайной, т.е. будут баги прорисовки, с которыми мы будем сталкиваться постоянно.

Если будет создавать игру, основанную на смене локаций, то:

  1. Либо она будет прямолинейной, с сюжетом, либо смена локаций будет случайна, но в таком случае возникает проблема номер 2.
  2. Локаций должно быть много, чтобы человеку не надоело играть в эту игру.

Под этой статьей находится опрос. Не забудьте проголосовать.

Хорошо, пока обойдемся без дополнительных слоев, создадим слушателя нажатий на клавиши.

В конструкторе класса Main.java допишем такую строку:

frame.addKeyListener(new KeyAdapter() {

});

Некоторые могут спросить, почему мы используем метод «добавить слушателя», а добавляем адаптер? Просто адаптер позволяет не использовать все три метода класса слушателя, а допустим, как в нашем случае, мы реализуем в нем только два:


			@Override
			public void keyPressed(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}

Первый метод отвечает за нажатие на клавишу, а второй за ее отпускание. Третий метод реагирует на короткое нажатие клавиши, а не задерживание её, как в первом методе. Теперь, чтобы у нас не получилась белеберда, и чтобы не усложнять себе жизнь, сделаем точно такие же два метода в классе игрока. И теперь в наших методах, находящихся в классе Main.java напишем:

В keyPressed:

player.keyPressed(e);

В keyReleased:

player.keyReleased(e);

Готово! Теперь у нас есть слушатель нажатий на клавиши. Давайте проверим его работоспособность, просто написав в каждом из методов в классе Player.java такую строку:

System.out.println(e.getKeyCode());

Запускаем программу, смотрим в нашу консоль, и нажимаем на клавиши.

Если при каждом нажатии на клавишу выписываются цифры в консоль, это означает что вы сделали все правильно. Не бойтесь, запоминать все эти клавиши не надо, за нас это уже сделал класс KeyEvent.

Теперь создадим энум, который будет отвечать за направление игрока. Для тех, кто не знает, энум, это такая переменная, которая может иметь несколько значений, которые мы заранее прописали. Создаем энум, под названием Direction. В него, через запятую, пишем такие значения: UP, DOWN, LEFT, RIGHT, NONE. Теперь, в классе Player создаем новую переменную типа Direction, которую назовем playerDirection (сразу поставьте значение Direction.NONE). Теперь, сделаем так, чтобы при нажатии соответствующих кнопок, направление игрока изменялось. Кстати, мы всегда будем работать с методами keyPressed и keyReleased из класса Player.java, в классе Main.java их не трогайте. Так вот, для начала, в методе keyPressed объявим переменную, типа int, которая равна e.getKeyCode(). Мы делаем это для удобства, чтобы в дальнейшем каждый раз не прописывать одно и то же. Так вот, дальше пишем:

if(key == KeyEvent.VK_W) {
			playerDirection = Direction.UP;
		}
		if(key == KeyEvent.VK_S) {
			playerDirection = Direction.DOWN;
		}
		if(key == KeyEvent.VK_A) {
			playerDirection = Direction.LEFT;
		}
		if(key == KeyEvent.VK_D) {
			playerDirection = Direction.RIGHT;
		}

Это условия, как будет меняться наше направление игрока в зависимости от нажатых клавиш.

Далее, изменим метод move. Он должен будет выглядеть так:

switch(playerDirection) {
		case UP:
			mapY+=speed;
			break;
		case DOWN:
			mapY-=speed;
			break;
		case LEFT:
			mapX+=speed;
			break;
		case RIGHT:
			mapX-=speed;
			break;
		default:
			break;
		}

Для тех, кто не знает, switch это тоже самое что и if, только в более удобной форме. Запускаем программу, и любуемся!

На сегодня все. Опять же, оставляю весь код, для тех, кто запутался.

Player.java


import java.awt.event.KeyEvent;

public class Player {
	
	private int x = 0;
	private int y = 0;
	private int speed = 2;
	private int mapX = 0;
	private int mapY = 0;
	
	private Direction playerDirection = Direction.NONE;
	
	public void move() {
		switch(playerDirection) {
		case UP:
			mapY+=speed;
			break;
		case DOWN:
			mapY-=speed;
			break;
		case LEFT:
			mapX+=speed;
			break;
		case RIGHT:
			mapX-=speed;
			break;
		default:
			break;
		}
	}
	
	public void keyPressed(KeyEvent e) {
		int key = e.getKeyCode();
		if(key == KeyEvent.VK_W) {
			playerDirection = Direction.UP;
		}
		if(key == KeyEvent.VK_S) {
			playerDirection = Direction.DOWN;
		}
		if(key == KeyEvent.VK_A) {
			playerDirection = Direction.LEFT;
		}
		if(key == KeyEvent.VK_D) {
			playerDirection = Direction.RIGHT;
		}
	}

	public void keyReleased(KeyEvent e) {
		System.out.println(e.getKeyCode());
	}
	
	public int getX() {
		return x;
	}
	
	public int getY() {
		return y;
	}
	
	public int getSpeed() {
		return speed;
	}
	
	public int getMapX() {
		return mapX;
	}
	
	public int getMapY() {
		return mapY;
	}
}

Main.java


import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main extends JPanel implements ActionListener {
	
	Image img = new ImageIcon("2.png").getImage();
	
	Timer timer = new Timer(20, this);
	
	Player player = new Player();
	
	JFrame frame;
	
	public Main(JFrame frame) {
		this.frame = frame;
		timer.start();
		frame.addKeyListener(new KeyAdapter() {


			@Override
			public void keyPressed(KeyEvent e) {
				// TODO Auto-generated method stub
				player.keyPressed(e);
			}

			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				player.keyReleased(e);
			}
			
		});
	}
	
	public void paint(Graphics g) {
		g.drawImage(img, player.getMapX(), player.getMapY(),frame.getWidth(), frame.getHeight(), null);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		repaint();
		player.move();
	}
	
}

Display.java


Никак не поменялся.

Спасибо за внимание! Не забудьте проголосовать.
Only registered users can participate in poll. Log in, please.
Дальнейшее изменение игры
64.1% Игра не будет иметь границ25
35.9% Игра будет основана на смене локаций14
39 users voted. 24 users abstained.
Tags:
Hubs:
-2
Comments4

Articles