Создание игры на Java без сторонних библиотек, часть первая



    Введение и подготовка



    Привет хаброжители. Данный пост является «рерайтом» моего поста для песочницы. На этот раз я постараюсь охватить больше тем, чем тогда.

    Почему Java?

    Ничего объективного я тут не скажу, а скажу лишь то, что я люблю этот язык, и мне нравиться писать на нем. Да, на Java нет игр AAA-класса, но Java предоставляет огромные возможности, больше кол-во встроенных средств и быстроту написания кода.

    IDE

    Начнем с выбора IDE. Я являюсь фанатом Eclipse и посоветую вам его.
    Если же почему-то вам он не понравился, вы можете использовать NetBeans, Intellij IDEA или командную строку и ваш любимый редактор.

    JDK

    И скачаем JDK последней версии: JDK 7u4

    Скорее всего проблем с установкой IDE у вас не возникнет, а если у вас 64-битная система, все же посоветую устанавливать 32-битный Eclipse, так как иногда бывают ошибки и Eclipse у вас просто не запустится.

    Под катом мы приступим к созданию игры.


    Класс Game


    Итак, создаем проект, в нем класс Game(попутно создав в нем точку входа). Данный класс должен наследовать класс Canvas и реализовать интерфейс Runnable:

    public class Game extends Canvas implements Runnable {
    	private static final long serialVersionUID = 1L;
    
    	public void run() { //функция run появляется после того, как мы добавили "implements Runnable"
    	}
    
    	public static void main(String[] args) {
    	}
    }
    


    Создадим переменную running типа Boolean, которая, как вы уже догадались будет показывать нам запущена ли игра, или нет.

    Создадим функцию start() и в ней мы будем создавать новый поток и переводить running в true:

    public void start() {
    	running = true;
    	new Thread(this).start();
    }
    


    Создадим три функции — update(long delta), render() и init(). Я надеюсь что их значение вам понятно. В функции run() создадим главный игровой цикл, перед ним будем вызывать init(), а в нем самом render() и update(). Так же мы будем вычислять разницу между кадрами(delta time).

    public void run() {
    	long lastTime = System.currentTimeMillis();
    	long delta;
    	
    	init();
    		
    	while(running) {
    		delta = System.currentTimeMillis() - lastTime;
    		lastTime = System.currentTimeMillis();	
    		update(delta);
    		render();
    	}
    }
    	
    public void init() {
    		
    }
    	
    public void render() {
    
    }
    	
    public void update(long delta) {
    		
    }
    


    Пока поработаем над функцией render().

    public void render() {
    	BufferStrategy bs = getBufferStrategy(); 
    	if (bs == null) {
    		createBufferStrategy(2); //создаем BufferStrategy для нашего холста
    		requestFocus();
    		return;
    	}
    		
    	Graphics g = bs.getDrawGraphics(); //получаем Graphics из созданной нами BufferStrategy
    	g.setColor(Color.black); //выбрать цвет
    	g.fillRect(0, 0, getWidth(), getHeight()); //заполнить прямоугольник 
    	g.dispose();
    	bs.show(); //показать
    }
    


    Вам наверное уже не терпится запустить и попробовать, но не спешите. Мы должны создать фрейм и добавить наш холст на него. Заодно и объявим три переменных.

    public static int WIDTH = 400; //ширина
    public static int HEIGHT = 300; //высота
    public static String NAME = "TUTORIAL 1"; //заголовок окна
    
    public static void main(String[] args) {
    	Game game = new Game();
    	game.setPreferredSize(new Dimension(WIDTH, HEIGHT));
    
    	JFrame frame = new JFrame(Game.NAME);
    	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //выход из приложения по нажатию клавиши ESC
    	frame.setLayout(new BorderLayout());
    	frame.add(game, BorderLayout.CENTER); //добавляем холст на наш фрейм
    	frame.pack();
    	frame.setResizable(false);
    	frame.setVisible(true);
    
    	game.start();
    }
    




    Примерно вот так выглядит наш класс Game сейчас.

    Класс Sprite



    Создадим новый класс Sprite. Поскольку этот класс небольшой, я сразу приведу весь его код с комментариями:

    import java.awt.Graphics;
    import java.awt.Image;
    
    public class Sprite {
    	private Image image; //изображение
    	
    	public Sprite(Image image) {
    		this.image = image;
    	}
    	
    	public int getWidth() { //получаем ширину картинки
    		return image.getWidth(null);
    	}
    
    	public int getHeight() { //получаем высоту картинки
    		return image.getHeight(null);
    	}
    	
    	public void draw(Graphics g,int x,int y) { //рисуем картинку
    		g.drawImage(image,x,y,null);
    	}
    }
    


    Сразу же проверим работоспособность. Возьмем эту картинку и скопируем ее в папку с нашим классом Sprite. Добавим функцию getSprite() в класс Game(временно).



    public Sprite getSprite(String path) {
    	BufferedImage sourceImage = null;
    		
    	try {
    		URL url = this.getClass().getClassLoader().getResource(path);
    		sourceImage = ImageIO.read(url);
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    
    	Sprite sprite = new Sprite(Toolkit.getDefaultToolkit().createImage(sourceImage.getSource()));
    		
    	return sprite;
    }
    


    Добавим нашу картинку в папку assets(папку создать в корне проекта), саму папку надо добавить в build path.

    Далее создаем переменную hero типа Sprite. В функции init() инициализируем ее. В Функции render() рисуем:

    //в "шапку"
    public static Sprite hero;
    
    //в init()
    hero = getSprite("man.png");
    
    //в render() после g.fillRect(0, 0, getWidth(), getHeight());
    hero.draw(g, 20, 20);
    


    Результат:



    Весь код Game.java

    Input


    Для обработки инпута мы создадим класс, наследующий KeyAdapter:

    private class KeyInputHandler extends KeyAdapter {
    
    }
    

    Тут же и объявим две переменных в шапке класса Game:

    private boolean leftPressed = false;
    private boolean rightPressed = false; 
    


    Внутри класса KeyInputHandler создадим две функции:

    public void keyPressed(KeyEvent e) { //клавиша нажата
    	if (e.getKeyCode() == KeyEvent.VK_LEFT) {
    		leftPressed = true;
    	}
    	if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
    		rightPressed = true;
    	}	
    } 	
    public void keyReleased(KeyEvent e) { //клавиша отпущена
    	if (e.getKeyCode() == KeyEvent.VK_LEFT) {
    		leftPressed = false;
    	}
    	if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
    		rightPressed = false;
    	}
    }
    


    Теперь в функции init() добавим следующее:

    addKeyListener(new KeyInputHandler());
    


    Создадим переменные x и y для героя(так как пока что мы еще не написали класс Entity). Сделаем чтобы герой всегда рисовался на этих координатах.

    private static int x = 0;
    private static int y = 0;
    
    hero.draw(g, x, y);
    

    А теперь в функции update() будем проверять нажаты ли клавиши и изменять x-координату.

    public void update(long delta) {
    	if (leftPressed == true) {
    		x--;
    	}
    	if (rightPressed == true) {
    		x++;
    	}
    }
    




    Он двигается!

    Спасибо за внимание.

    P.S. Ссылка на github
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 49

      0
      Статья понравилась, очень жаль что в свое время эксперементировал не с Java, а с Delphi, в результате через некоторое время ему настал конец в геймдеве.

      P.S. При комбинации клавиш «Alt + Print Screen» будет захвачено только активное окно и не будет необходимости резать скриншоты.
        0
        Спасибо, насчет скриншотов учту, а сейчас фоткал с помощью Gyazo.
        • UFO just landed and posted this here
            +1
            А я как раз таки наоборот, очень рад что не связался с джавой =) на делфи, кстати, можно легко писать казуалки, в том числе и тридешные (ну это я еще о том времени, когда люди ставили игры на комп, а не играли в социалках), да и в любом случае, нативный код будет быстрее чем джавовский через ВМ, а в играх это довольно критично.

            И в качестве оффтопика (надеюсь меня не забрасают тапками, но меня действительно волнует эта тема). Кроме того что в играх важна скорость реакции, она так же важна и в любых других приложениях, а в особенности мне, как программисту, хочется скорости и гибкости среды разработки, в которой я работаю (пишу на флеше и js). Так вот, я не понимаю, как стали столь популярными IDE на основе еклипса? С огромнейшим контекстным меню, с кучей всяких не особо полезных и не всегда работающих штук, аля комбайн. Может быть я как то не правильно пользуюсь ими? Даже вебшторм подтормаживает, когда в процессе прокрутки попробовать открыть контекстное меню (MBA 2011). Быть может правильно использовать данные программы для того же C++ или самой Java? Но непонятно, почему тогда те же JetBrains используют ее для своих продуктов? И самым недоразумением во всей этой компании для меня является Flash Builder. Почему Adobe не написали на том же ейр удобный и заточенный именно под флеш инструмент? Еще в эклипсе предусмотрен механизм установки плагинов, но почему он такой не прозрачный и непонятный? почему нет маркета какого-то по категориям например?
            Заранее благодарю за ответ на мою эмоциональную простыню и готов признать, что я предосудительно отношусь к ПО на java в случае приведения рациональных аргументов)
              0
              Про eclipse ничего не скажу, а вот netbeans — это моё все, очень удобная IDE.
              А по поводу производительности, думаю здесь немного разные задачи у нативного приложения и java. Что что нативное выиграет в скорости, то java отыграет в переносимости.
                +1
                Переносимость? Почему у меня кроссплатфореммый саблим не тормозил даже на лоуэнд ноуте? Соответственно не тормозит и сейчас. ГТК приложения в основном не тормозят, да и имеют родной (более-менее) интерфейс под операционку. Даже редактор на яваскрипте в браузере не тормозит (правда на старом ноуте тормозило). Кроме того, есть переносимое Моно, QT. В чем проблема джавы? Почему она не ускоряется? Или может это проблема движка редактора текста? Может он тормозит? Потому что на джаве есть быстрое ПО (по крайней мере я думаю что ВиртуалБокс именно на джаве написан). Ведь довольно обидно покупать ноут за стопицот денег и все равно видеть, как на нем тормозит текстовый редактор…
                  0
                  Витуалбокс написан не на джаве. Программная виртуализация в нём основана на Qemu, а графический интерфейс написан с использованием Qt.
                  0
                  Тоже раньше был поклонником NetBeans, пока не начал изучать Android. Со временем как то стало привычно.
                  +4
                  Первое: JetBrains делает свои среды разработки не на основе Эклипс — у них, как и у NetBeans, все свое.

                  Почему Эклипс:

                  — он бесплатный, исходники открытые — бери и делай свою IDE. Писать IDE с нуля — дело сложное и хлопотное, если для компании важно время выхода среды разработки на рынок, то Eclipse — как раз для нее.
                  — есть куча документации, а также готовых плагинов с открытыми исходниками — сделать свой плагин для этой среды также достаточно легко.
                  — Eclipse — стандарт в мире корпоративной разработки. Весьма вероятно, что он уже стоит у разработчиков, а значит и поставить ваш плагин им будет нетак и сложно.
                  Eclipse Marketplace — аппстор для плагинов, доступен и из самого Эклипса, и через веб.

                  Да, у него есть проблемы с производительностью, но если честно, я не знаю ни одной современной среды разработки, которая бы не тормозила. И Visual Studio, и JetBrains IDEs, и НетБины, и Эклипс — все они весьма задумчивы даже на хорошем железе (4 ядра i7, 16 гиг оперативы — все тормозят, не переживайте). Не термозят легковесные редакторы — тот же SubLime или N++, — или среды разработки попроще — например, Code::Blocks или в какой-то степени XCode — но они и умеют гораздо меньше, чем «титаны».
                    +1
                    Спасибо за исчерпывающий ответ
                  0
                  Ух ты. Alt+PrntScrn работает даж в старых виндах?
                    0
                    Ну смотря что считать за старые винды. На XP работало
                      –1
                      Век живи, век учись…
                        +1
                        В 95-й точно работало, в 3.1 не проверял :-)
                      +2
                      очень жаль что в свое время эксперементировал не с Java, а с Delphi, в результате через некоторое время ему настал конец в геймдеве.

                      Так вот из-за кого это…
                        0
                        Блин, спасибо! :)
                        +3
                        Почему бы для разработки игр на Java не использовать libGDX? Просто потом можно будет при желании не очень сложно портировать на Android. К тому же в этом движке много чего предусмотрено для разработки игр.
                          +1
                          Я бы отнес ИЛ-2 Штурмовик к играм ААА-класа! Как по мне, так на 2001 год вполне себе ААА, главное, проект «наш» и написан на Java!
                            +1
                            Насколько я знаю, там только часть написана на Java…
                              +1
                              Только скрипты на Java, если не изменяет память. А все остальное православно написано — на C++.
                              • UFO just landed and posted this here
                                  0
                                  откуда такая информация?
                                    +1
                                    Когда-то, в далеком детстве, эта игра падала с ошибкой java'овской. Ну и в процессе гугления и были обнаружены обсуждения по поводу использования Java в игре. Вот так я и узнал. :)

                                    К слову, сейчас вбил в гугл и вот в этом review написано:

                                    Next urban legend says, that IL-2 is coded in Java. Again, this assumption is based on rumors only, and has no substance. Java is used in IL-2, but just in small part. C++ is mostly used in coding this baby.
                                      +1
                                      На самом деле, на C++ там написан только менеджмент ресурсов и рендер, всё остальное на яве.
                                0
                                Это всё или будет продолжение?
                                  +2
                                  Естественно будет. Звук, больше игровой логики, все дела.
                                  +2
                                  Вы бы хоть рассказали, что за игру пишите и что хотите получить в конечном итоге, а то сразу после выбора IDE:
                                  Итак, создаем проект, в нем класс Game(попутно создав в нем точку входа).
                                    0
                                    Ну, так как в этом уроке из игровой логики практически ничего нет, я не думаю что это очень важно. Во второй части уже явно будет видно что это платформер.
                                    • UFO just landed and posted this here
                                      0
                                      Minecraft же на java вроде бы. Вполне себе ААА-класс.
                                        +6
                                        Майнкрафт — инди-игра. Класс игр определяется отнюдь не количеством игроков, в неё играющих.
                                          0
                                          А чем он определяется? Есть какая-то общепринятая классификация?
                                            +1
                                            Могу предположить, что определяется бюджетом, как главным фактором, но это все маркетинг. Сейчас полно треша, который относит себя к играм ААА-класа.
                                              0
                                              Факторов много, это и бюджет, и количество продаж, и рейтинг, и всякие новые технические плюшки и тд., но по большей части все это субъективно.
                                            +1
                                            Майнкрафт стартанул как инди игра, но как по мне, так сейчас ее смело можно ставить в один ряд с другими играми ААА-класса.
                                          +17
                                          Извините, но ваш код ужасен. Начиная от общей лапшеватости, икэп-комментариев вида
                                          public static int WIDTH = 400; //ширина
                                          public static int HEIGHT = 300; //высота
                                          //...
                                          g.setColor(Color.black); //выбрать цвет
                                          


                                          и заканчивая некорректной многопоточной работой:

                                          private boolean running;
                                          //...
                                          while(running) {
                                          //...
                                          


                                          Ваша статья учит гораздо больше плохому, чем хорошему. Возможно, прежде чем её писать, стоило изучить теорию и получить какой-нибудь более-менее существенный опыт.
                                            –3
                                            Соглашусь, да, коментарии не очень. Но насчет второго: у Нотча наблюдается точно такая же конструкция. О его навыках я высокого мнения. Может и не стоит оглядываться на других, но все же…
                                              +6
                                              Не стоит, не стоит. Надо самому понимать каждый символ в своей программе. Конкретно while(running) приводит к тому, что ваша програмка жрет 100% времени ЦП, даже когда ей нечего делать. И нигде в программе нет running=false, даже пожаловаться на отсутствие синхронизации не получается :). То есть фактически while(true).

                                              Я от геймдева далек, но мне кажется, основные проблемы там как большому количеству пользователей взаимодействовать с еще более большим количеством объектов, некоторые из них сами активные. И while(true) там совершенно недопустим — тормознет всех.
                                                0
                                                Ознакомьтесь, пожалуйста.
                                                  +1
                                                  notch обычно пишет прототипы, которые потом сильно допиливаются (если возникает нужда).
                                                  Из текущего кода например вычисление delta в game loop, не взлетит на нормальном проекте.
                                                  Как только рендер и(или) игровой цикл начнут расти по времени, а gc будет активно работать, на разных машинах вы получите в лучшем случае — дерганную картинку и не играбильность, а в худшем — ошибки синхронизации игровой логики.
                                                  Если почитать тот-же www.java-gaming.org/, то там это момент обсуждался очень долго. В том числе еще тогда, когда там notch активно писал, а minecraft был прототипом.
                                                +9
                                                Статья очень слабая.
                                                1 цикл для рендера и для просчета, это никто уже сейчас не оценит.
                                                Собственно поддерживаю gvsmirnov.
                                                Такие статьи не для Game Development, а просто для программок, которым надо вывести графику в какое либо место.
                                                  +1
                                                  Создадим три функции — update(long delta), render() и init(). Я надеюсь что их значение вам понятно.

                                                  Тем кому понятно, этот код не нужен, а тем кому не понятно, конечно статья может была бы интересна, но для них не все понятно. Вы на какой уровень читатель рассчитывали?
                                                    +1
                                                    читателей*
                                                    0
                                                    Мне кажется, код этот был создан под впечатлением Нотчевского Minicraft. Однако следует учесть, что та игра делалась на конкурсе за 48 часов, и поэтому образцом хорошего стиля служить не может. Хотя, конечно, общие принципы там верные.
                                                      0
                                                      Добавим нашу картинку в папку assets(папку создать в корне проекта), саму папку надо добавить в build path.

                                                      А можно поподробнее про то, как это сделать? Я добавил в Project -> Properties -> Java Build Path -> Source папку assets, но, тем не менее, строка

                                                      URL url = this.getClass().getClassLoader().getResource(path);

                                                      выбрасывает null. Что я делаю не так?
                                                        0
                                                        Попробуйте скачать проект с гитхаба, он то точно запуститься, хоть я и удивлен что у вас не работает это.
                                                        0
                                                        Я так понял, пример идет без использования OpenGL, т.е. просто рисование по канве.
                                                        А нет ли проблем с framerate при таком способе? Возможно ли удерживать 50-60 кадров в секунду в динамических сценах? (При условии, что компьютер в принципе поддерживает подобный FPS в других программах)
                                                          0
                                                          Не пишите такие комментарии в коде никогда. По программе и так видно, что она делает в каждой строке кода. Лучше писать, зачем она это делает.

                                                          Например
                                                          g.fillRect(0, 0, getWidth(), getHeight()); //заполнить прямоугольник

                                                          Лучше написать что-то типа //делаем черный фон

                                                          Ну и JavaDoc тоже не просто так придумали.
                                                            0
                                                            На самом деле хочу поблагодарить за статейку. Очень коротко и понятно! Сам просто с java вообще никогда не работал, а тут попросили на скоряк для девушки в универе сделать простецкую игру чтобы сбивать самолеты. Но в инете прям отвратно кусками инфу собирать, особено когда с java не работал. Статья очень помогла и за 2 часика игрушка была готова! Еще раз спасибо!

                                                            Only users with full accounts can post comments. Log in, please.