22496

ИСПОЛЬЗОВАНИЕ МНОГОПОТОЧНОСТИ ДЛЯ СОЗДАНИЯ ГРАФИЧЕСКОГО JAVA - ПЛАТФОРМЕРА

Курсовая

Информатика, кибернетика и программирование

Android - это открытая платформа, основанная на ядре Linux. Установлена она на тысячах устройствах широкого круга производителей. Android предоставляет приложения для всех видов оборудования, которые только можно представить в виде современных мобильных устройств

Русский

2014-11-24

1.36 MB

16 чел.

Министерство образования Республики Беларусь

Учреждение образования

«Гомельский государственный университет

имени Франциска Скорины»

Факультет математический

Кафедра вычислительной математики и программирования

ИСПОЛЬЗОВАНИЕ МНОГОПОТОЧНОСТИ

ДЛЯ СОЗДАНИЯ ГРАФИЧЕСКОГО

JAVA - ПЛАТФОРМЕРА

Курсовая работа

Исполнитель:

студент группы ПO-32            ________________ Плющ М. Д.

                       

Научный руководитель:

к.ф.-м.н., ассистент кафедры  ________________ Родионов А. А.

       

Гомель 2013

Содержание

[1] Содержание

[2] Введение

[3] 1 Начало работы с Java

[3.1] 1.1 Java Runtime Environment

[3.2] 1.2 Java development Kit

[3.3] 1.3 Документация Java API

[4] 2 Среда программирования Java

[4.1] 2.1 Компиляция исходного кода через командную строку

[4.2] 2.2 Java IDE

[5] 3. Основные моменты создания игрового 2d - приложения

[5.1] 3.1 Анимация при помощи таймера

[5.2] 3.2 Анимация при помощи потоков

[5.3] 3.3 Frame Per Second при использовании потоков

[5.4] 3.4Создание фрейма, вставка контейнера

[5.5] 3.5 Методы update() и draw()

[5.6] 3.6 Game State Manager

[5.7] 3.7 События кнопок

[6] 4. Графические примитивы

[6.1] 4.1 Прямая линия

[6.2] 4.2 Цвет

[6.3] 4.3 Цвет фона

[6.4] 4.4 Прямоугольник

[6.5] 4.5 Овал

[6.6] 4.6 Многоугольник

[6.7] 4.7 Объект точки

[6.8] 4.8 Изображение

[7] Заключение

[8] Список использованных источников

[9] Приложение A

[10]
Приложение Б

[11] Приложение В

[12] Приложение Г

[13] Приложение Д

[14] Приложение Е

[15] Приложение Ж

[16] Приложение З

[17] Приложение И

[18] Приложение К

[19] Приложение Л

[20] Приложение М

[21] Приложение Н

[22] Приложение О

[23] Приложение П

[24] Приложение Р


Введение

В настоящее время, сфера информационных технологий весьма активно развивается. Появляется огромное количество новых языков программирования, технологий и концепций. В сфере it количество рабочих мест постоянно растет. На сегодняшний день, в этой области наблюдается дефицит в высококвалифицированных специалистах.

Требования к программистам постоянно увеличиваются, за счет бурного развития отрасли. Так или иначе, первостепенной важности вопрос, который встает перед каждым it - специалистом - это правильный выбор языка программирования, в пределах которого программист будет совершенствоваться. Конечно же, профессионалы и близко не ограничиваются одним языком, но у каждого специалиста высокие познания развиты в пределах одного - двух языков. Если брать многочисленные статистики по языкам программирования, то первое место на данный момент по популярности и по востребованности, делят языки Java и PHP. Именно язык Java и был выбран мной для активного самостоятельного изучения. Далее мне хотелось бы упомянуть ключевые моменты этого интересного языка программирования.

Java - это язык программирования и вычислительная платформа, впервые выпущенная компанией Sun Microsystems в 1995 году. Это базовая технология, на которой основываются многие современные программы (в том числе утилиты, игры и бизнес-приложения). Java работает на более чем 850 млн. персональных компьютерах и в миллиардах устройств (в том числе в мобильных телефонах и в телевизорах) по всему миру. На языке java разрабатываются desktop-приложения различных уровней сложности, web - интерфейсы.

Одно из главных преимуществ java - это кроссплатформенность (независимость от архитектуры компьютера). Т.е. «программа, единожды написанная на Java, будет работать везде». В Java это достигается за счет виртуальной машины JVM (Java Virtual Machine), которая интерпретирует байт - код. Также JVM существенно повышает безопасность, поскольку в процессе работы можно оценить последствия каждой конкретной команды.

Кроме того, Java активно используется для создания мобильных приложений под операционную систему Android.

Android - это открытая платформа, основанная на ядре Linux. Установлена она на тысячах устройствах широкого круга производителей. Android предоставляет приложения для всех видов оборудования, которые только можно представить в виде современных мобильных устройств - цифровые компасы, видеокамеры, GPS, датчики, коммуникаторы, планшетные компьютеры, цифровые проигрыватели, нетбуки и многое другое.

Также, язык java активно используется для создания различных игровых приложений. Большая часть мобильных игр под операционную систему Symbian были написаны на java, благодаря технологии j2me.

J2ME - подмножество платформы Java для устройств, ограниченных в ресурсах, например: сотовых телефонов, карманных компьютеров, ресиверов цифрового телевидения, проигрывателей дисков Blu-ray. В настоящее время является устаревшей технологией из-за прихода на рынок Android и IOS.

В настоящее время, данный язык до сих пор держит лидирующие позиции в сфере мобильных приложений, благодаря ОС Android, которая на сегодня занимает огромный кусок рынка.

Немалую роль занимают и desktop - игры, написанные на java. В основном, разработка игр под pc ведется либо при помощи различных игровых Java библиотек (LWJGL - lightweight java game library, Slick 2D, libGDX и т.д.).

Однако, при написаний игрового движка «с нуля», игровые библиотеки не используются, а весь процесс разработки ведется на «чистом» Java SE.

Существует ошибочное мнение, что платформа Java не подходит для написания сложных игровых проектов. Программы, написанные на Java, имели репутацию более медленных и занимающих больше оперативной памяти, чем написанные на языке Си. Тем не менее, скорость выполнения программ, написанных на языке Java, была существенно улучшена с выпуском в 1997 - 1998 годах так называемого JIT-компилятора версии 1.1 в дополнение к другим особенностям языка, для поддержки лучшего анализа кода (такие как внутренние классы, класс StringBuffer, упрощенные логические вычисления и т. д.). Кроме того, была произведена оптимизация виртуальной машины Java - с 2000 года для этого используется виртуальная машина HotSpot. 

Еще одним преимуществом в сторону выбора Java для написания игр является возможность перевода Java - проектов в т.н. jar - файлы. JAR файл – это Java - архив (сокращение от англ. Java ARchive). Представляет собой обычный ZIP-архив, в котором содержится часть программы на языке Java. Jar файлы весьма удобны, т.к. их можно запустить абсолютно на любой ОС, на которой установлена JVM. Еще одно удобство jar - файла в том, что сложный проект, с глубокой иерархией, можно несколькими несложными действиями перевести во всего лишь один jar – файл, который можно будет свободно запускать.

Следует заметить, что на сегодняшний день на Java было написано множество разнообразных игр. Одна из самых популярных игр в мире, количество игроков в которой уже давно превысило 20 миллионов, была написана на Java. Я говорю, конечно же, об инди игре Minecraft.

Инди игры - это игры, которые были созданы независимыми разработчиками. Обычно такие игры отличаются оригинальной концепцией и неповторимым геймплеем. На сегодняшний день, популярность инди игр стремительно растет. Попробовать свои силы в разработке инди игры, может каждый. Обычно для таких целей выбирается либо язык C++, либо Java, т.к. именно на этих языках было написано большинство инди игр и они отлично себя зарекомендовали в данной сфере.

Официально - утвержденных методик написания инди игр на данный момент не существует. Однако крайне рекомендуется писать такие игры в соответствии с некоторыми общепринятыми принципами, которые будут рассмотрены далее.


1 Начало работы с Java

Для начала разработки приложений, нам необходимо установить специальные программные элементы, предназначенные для Java. Процесс установки будет рассмотрен ниже. Можно также установить Java документацию (Java API), которая в дальнейшем весьма поможет при разработке.

1.1 Java Runtime Environment

«Java Runtime Environment» - минимальная реализация виртуальной машины, необходимая для исполнения Java – приложений, без компилятора и других средств разработки. Состоит из виртуальной машины - «Java Virtual Machine» и библиотеки Java - классов. JRE распространяется свободно и для большинства платформ может быть загружена с сайта Oracle.

При установке JRE, у нас появится возможность простого запуска приложений, написанных на Java, без возможности разработки. На данный момент, самые актуальная версия JRE - это JRE 7. Скачать JRE 7 можно по ссылке http://www.oracle.com/technetwork/java/javase/downloads/

На странице можно найти JRE под множество операционных систем и архитектур (рисунок 1.1).

Рисунок 1.1 – Выбор нужного JRE 7


1.2 Java development Kit

Java Development Kit - бесплатно распространяемый компанией Oracle Corporation (ранее Sun Microsystems) комплект разработчика приложений на языке Java, включающий в себя компилятор Java (javac), стандартные библиотеки классов Java, примеры,  различные утилиты и исполнительную систему Java (JRE). Для разработки необязательно качать JRE отдельно - достаточно будет JDK.

Самую последнюю версию JDK можно скачать по ссылке http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html. На странице можно выбрать нужную ОС и подходящую платформу (рисунок 1.2)

Рисунок 1.2 – Выбор нужного JDK 7

1.3 Документация Java API

Совершенно невозможно запомнить всю информацию, которая может понадобиться при написании программ. Например, даже стандартная библиотека Java содержит тысячи классов, содержащих огромное количество методов. Следовательно, очень важно уметь пользоваться интерактивной справочной системой, содержащей документацию об API, и находить нужные классы и методы. Документация является составной частью набора инструментальных средств JDK. Она представлена в формате HTML. Если перейти в браузере по ссылке http://docs.oracle.com/javase/6/docs/api/ то можно увидеть, что из себя представляет Java API.

Окно браузера разделено на три фрейма. В небольшом фрейме в верхнем левом углу отображаются имена всех доступных пакетов. Под ним во фрейме побольше перечислены все классы. (рисунок 1.3). Если щелкнуть кнопкой мыши на любом из имен классов, то информация об этом классе будет показана в большом фрейме, расположенном справа (рисунок 1.4).

Рисунок 1.3 – Окно с документацией по API


Рисунок 1.4 - Описание класса

Рисунок 1.5 – Перечень методов класса


Для того, чтобы скачать документацию Java API, можно перейти по ссылке http://www.oracle.com/technetwork/java/javase/documentation/java-se-7-doc-download-435117.html.


2 Среда программирования Java

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

Здесь два пути: либо осуществлять работу с JDK непосредственно через командную строку, либо использовать интегрированную среду разработки. Рассмотрим оба этих варианта.

2.1 Компиляция исходного кода через командную строку

Один из вариантов - это весь программный код вести в текстовом редакторе, а  затем через командную строку компилировать исходный Java - код в байт - код и после этот байт - код запускать.

Ниже представлена последовательность действий:

1. Сохраняем наш исходный код в формате *.java (рисунок 2.1).

2. Запускаем командную строку и входим в папку с исходным Java – кодом (рисунок 2.2).

3. При помощи команды javac, компилируем исходный Java - код в байт - код (рисунок 2.3).

4. Запускаем байт код при помощи команды java (рисунок 2.4).

Рисунок 2.1 – Сохранение исходного кода

Рисунок 2.2 - Путь до папки в командной строке

Рисунок 2.3 - Компиляция в байт - код

Рисунок 2.4 - Запуск байт - кода

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

2.2 Java IDE

Компилировать Java код из командной строки может показаться обременительным занятием, но, тем не менее, это важнейшее умение. Выполняя базовые шаги работы с JDK самостоятельно, можно получить лучшее представление о том, как работает среда разработки. Чаще всего, крайне рекомендуется перед тем, как устанавливать какое - либо IDE (integrated development environment), научиться работать с Java JDK через командную строку.

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

За последнее время такие среды стали настолько мощны и удобны, что теперь просто нет особого смысла обходиться без них.Самые распространенные IDE для Java на сегодняшний день – это : Eclipse, NetBeans и IDEA. У каждой из данных сред д есть свои преимущества и свои недостатки. Лично я остановился на IDE Eclipse по некоторым причинам. Во-первых, у данной среды весьма удобные горячие клавиши. Под нее в свободном доступе находится огромное количество инструментов для разработчика. Даже такая крупная корпорация, как Google лично написала для Eclipse немало инструментария. При разработке ,например, под ОС Android, лучшую IDE найти сложно – для Eclipse в свободном доступе находится весьма мощный Android SDK, на котором было разработано не одно поколение Android приложений.

Для того, чтобы скачать данную IDE, стоит перейти по ссылке http://www.eclipse.org/downloads/ и выбрать одну из предложенных решений (рисунок 2.5).

Рисунок 2.5 - Предлагаемые решений Eclipse

Всего предлагается около двенадцати решений для разработчиков различных направленностей. Как видим, выбор достаточно велик. Для своей задачи, я выбрал стандартный пакет Eclipse Standart. Далее выбираем нужную операционную систему с архитектурой (рисунок 2.6) и  начинается бесплатное скачивание данной среды.

Рисунок 2.6 - Выбор нужной ОС и архитектуры


3. Основные моменты создания игрового 2d - приложения

Краеугольным камнем любого игрового приложения является анимация. Анимация - это иллюзия движения слоев на экране, путем быстрой смены последовательности изображений. Все движения, действия, физика персонажей - это специальным образом запрограммированная быстрая смена изображений. Благодаря высокой вычислительной мощности компьютера, будет создаваться иллюзия прыжков, ходьбы, атак и т.д. Существует множество аспектов при «правильном» программирований 2d графики, основные из которых я затрону ниже.

3.1 Анимация при помощи таймера

При создании анимации на Java SE, существует 2 основных приема для смены изображений.

Самый распространенный - это использование Swing таймера. Это самый простой, но менее эффективный способ для создания Java игр. Для его реализации, наш класс обязан расширять интерфейс Action Listener и при этом реализовывать метод данного интерфейса - Action Performed. Данный метод вызывается всякий раз, когда происходит некоторое действие.  Правда, не всегда необходимо расширять Action Listener. Чаще всего, просто используются вложенные классы.

Сам таймер имеет вид: Timer ( int interval, ActionListener listener ).

Данный вызов создает таймер, передающий сообщение объекту listener. Сообщение передается по истечении интервала времени, продолжительность которого задается параметром interval.

Метод start() запускает таймер, который запускает метод actionPerformed() его слушателей.

Метод stop() останавливает таймер. После остановки таймера, метод actionPerformed() слушателей больше не вызывается.

3.2 Анимация при помощи потоков

Самый эффективный способ создания анимации - это использование потоков. В Java поток представлен классом Thread (полное имя java.lang.Thread), все потоки, в конечном счете наследуются от Thread. Есть два подхода к созданию своего потока:

унаследовать Thread;

реализовать Runnable (полное имя java.lang.Runnable).

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

Во втором случае определяется только основной процесс и инициализируются некоторые данные.

У класса Thread есть много полезных методов и приёмов, построенных на их применении. Большинство этих методов статические, то есть можно использовать их, не имея ссылки на объект класса Thread, что особенно удобно, если используется реализация Runnable вместо Thread.

Часто бывает нужно, чтобы поток отдал управление другим потокам в известный момент, например в конце итерации, для этого есть несколько методов, первый из них yield( ). Следующий метод – sleep( ), метод очень нужный, если известно, что поток нужно вызывать не часто по стандартам компьютерного времени. Например, раз в секунду или в минуту. Принимает в аргумент число миллисекунд.

Важно упомянуть о том, как же нормально остановить поток. Если необходимо остановить выполнение потока, то для этого есть два варианта:

нужно просто остановить;

нужно остановить и убедиться, что поток больше не работает.

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

3.3 Frame Per Second при использовании потоков

Самый легкий путь использования потоков при создании java - игры - это в бесконечном цикле организовать обновление и перерисовку различных событий. Такой способ имеет множество недостатков. Самый существенный - это использование слишком больших объемов памяти. Чтобы разумно использовать память при работе с потоками, в играх используется т.н. кадровая частота (FPS) - количество сменяемых кадров за единицу времени на мониторе. Минимальная кадровая частота для создания ощущения плавности движения составляет 12—18 кадров в секунду. FPS рассчитывается следующим образом:

1. Заводим заранее 2 переменные:  базовый FPS и FPS для понимания его потоком. Второй вычисляется, как 1000 / FPS, это делается исключительно из-за того, что  основное разряд времени для работы с потоками - 1 миллисекунда

2. Когда поток входит в цикл,  фиксируем время его входа (startTime).

3. После выполняем все необходимые методы для обновления и прорисовки событий и сразу же после этого, находим время, затраченное потоком на выполнение этих методов (разность конца выполнения прорисовки и startTime).

4. Нужная нам величина - переменная wait, хранит оптимальное время, на которое нужно временно остановить поток (ввести в сон). Оно вычисляется, как разность между поточной FPS и временем, затраченным для выполнения потоком отрисовки. Именно на эту величину можно останавливать поток, не опасаясь, потери памяти и “подвисаний” игры.

Пример:

private int FPS = 100;

private long targetTime = 1000 / FPS;

while (running) {

  start = System.nanoTime();

 

  update();

  draw();

     elapsed = System.nanoTime() - start;

wait = targetTime - elapsed / 1000000;

 

if (wait < 0)

 wait = 5;

try {

 Thread.sleep(wait);

} catch (Exception e) {

 e.printStackTrace();

 }

}

3.4Создание фрейма, вставка контейнера

Окно верхнего уровня (т.е. окно, не содержащееся внутри другого окна) в языке Java называется фреймом (frame). В библиотеке AWT для такого окна предусмотрен класс Frame. В библиотеке Swing аналогом этого класса является JFrame. Класс

JFrame расширяет класс Frame и представляет собой один из немногих компонентов в библиотеке Swing, которые не отображаются на холсте (canvas).

По умолчанию фрейм имеет совершенно бесполезный размер - 0x0 пикселей, поэтому устанавливаем размер вручную методом setSize(int width, int height). И еще один важный момент - это определяем, что должно произойти, если пользователь закроет фрейм.

В данном случае программа должна завершить свою работу. Для этого используется

следующая строка кода:

frame. setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE)

Определяем вручную, т.к. по умолчанию ничего не определено.

В самом конце обязательно делаем окно видимым (true), иначе результата никакого не увидим :

setVisible(boolean vis);

Окно создано, но отображать оно ничего не будет.

Для этих целей используются контейнеры. Один из самых удобных - это JPanel. Чтобы получить доступ для работы с JPanel, соответствующий класс должен наследовать его.

Само добавление контейнера в наш фрейм, происходит с помощью метода setContentPane(contentPane obj)

3.5 Методы update() и draw() 

Данные два метода незаменимы при разработке игровых приложений. Поток постоянно вызывает эти методы для «чистоты» картинки. Если первоначально не согласовать порядок отрисовки, то на экране будут появляться графические аномалии, или события будут происходить не в своей очередности. Именно эти проблемы и предотвращают методы update и draw. Update никогда не взаимодействует напрямую с методами отрисовки нашего окна. Update мгновенно производит расчеты по всем изменениям, которые произошли в нашей игре. Уже после этого, за дело берется метод draw, который проходит по всем дочерним методам draw в различных игровых разделах и уже там взаимодействует напрямую с методом объекта Graphics - drawImage. Поток, в зависимости от заданного FPS, постоянно выполняет эти два метода, тем самым «обновляя» полностью все приложение. Здесь ключевым моментом является очередность. Нельзя размещать draw перед update, т.к. на экране тем самым окажется старый игровой контекст.

3.6 Game State Manager

GameStateManager - абстрактная структура, которая просто незаменима в игростроении. Именно она дает возможность размещать методы update и draw во всех разделах игры. Центральное место ее приходится на класс GameStateManager, у которого два ключевых поля - целочисленная переменная currentState и одномерный массив gameStates абстрактного класса GameState, который содержит абстрактные методы draw, update, init, keyPressed, keyReleased. В массиве gameStates содержатся объекты текущего состояния игры (Меню, Уровень1, Настройки и т.д.). currentState - идентификатор, указывающий на то, какой именно режим игры сейчас активен. Поток постоянно обновляет методы update и draw объекта GameStateManager, в которых, в свою очередь, обновляются эти же методы, только уже текущего режима из массива gameStates, вызываясь уже из самого этого объекта. Т.е. объект GameStateManager никогда напрямую не взаимодействует с режимами игры, а благодаря массиву gameStates, можно не беспокоиться, что активный объект режим сам своевременно не обновит себя, или не отрисует нужную информацию (рисунок 3.1).

Рисунок 3.1 - Схема GSM

3.7 События кнопок

Как и с update и draw, события кнопок передаются через объект GameStateManager. Само событие - это keyEvent. Оно же и будет передаваться через GameStateManager в нужный раздел, где и будет обрабатываться, в зависимости от контекста. Пример определения нажатой клавиши :

public void keyPressed(keyEvent key) {

 if ( key == VK_ENTER )

  System.out.printl(“The enter was pressed”);

}

Для слушания событий, класс должен реализовывать интерфейс KeyListener, переопределяя мутоды keyPressed  (keyEvent) ( клавиша нажата) и keyReleased(keyEvent) (клавиша отпущена). keyEvent - события клавиш при нажатии и отпускании клавиши, соответственно (рисунок 3.2).

Рисунок 3.2 - Работа с клавишами в GSM


4. Графические примитивы

Графику в Java обслуживают классы Graphics и Graphics2D. Работа с графикой осуществляется в графическом контексте элементов, унаследованных от класса Component. Понимать это можно так: на элементах управления, например, JFrame, JPanel, JButton и других, есть возможность рисовать. Такие элементы обладают графическим контекстом, в этом контексте мы и рисуем. Всё, что нарисовано в контексте, будет показано на элементе. Классы Graphics и Graphics2D необходимы для работы с графическим контекстом. Мы должны получить экземпляр такого класса и, используя его методы, рисовать. Получить экземпляр контекста можно в методе paint:

public void paint(Graphics g);

Данный метод наследуется из класса Component. Аргумент Graphics g создаётся системой, а мы берём его в готовом виде и используем для рисования. При создании элемента метод paint будет вызван автоматически.\

4.1 Прямая линия

Метод drawLine класса Graphics начертит прямую линию:

g.drawLine(x1, y1, x2, y2);

Здесь x1 и y2 - это координаты x и y начала линии, x2 и y2 - координаты конца линии.

4.2 Цвет

Метод setColor класса Graphics сделает текущим новый цвет:

Аргументы конструктора new Color(0, 0, 255) — это красный, зелёный и синий цвета соответственно (rgb). Как задать rgb цвета? В примере задан чисто синий цвет, т.к. значения других составляющих равны нулю. Вот  красный и зеленый цвета:

Color newColor = new Color(255, 0, 0);

Color newColor = new Color(0, 255, 0);

4.3 Цвет фона

Задать цвет фона можно методом setBackground:

mainFrame.setBackground(Color.white);

4.4 Прямоугольник

Нарисовать прямоугольник можно методом drawRect класса Graphics, где x1, y1 — это координаты верхнего левого угла прямоугольника;

x2, y2 -  высота прямоугольника:

g.drawRect(x1, y1, x2, y2);

4.5 Овал

Методом drawOval класса Graphics рисуется овал:

g.drawOval(20, 110, 150, 60);

Аргументы определяют прямоугольник, в который вписан овал.

4.6 Многоугольник

Методом drawPolygon класса Graphics:

int[] arrayX = {20, 100, 100, 250, 250, 20, 20, 50};

int[] arrayY = {180, 180, 200, 200, 220, 200, 200, 190};

Polygon poly = new Polygon(arrayX, arrayY, 8);

g.drawPolygon(poly);

Здесь создаём объект класса Polygon. arrayX — это х-координаты вершин многоугольника, arrayY - это y-координаты вершин многоугольника, 8 - число вершин многоугольника.

4.7 Объект точки

Для этого используем класс Point:

Point aPoint = new Point(50, 190);

аргументы - это x, y координаты.

4.8 Изображение

Для того, чтобы добавить изображение, нам необходим класс Graphics2D. Непосредственно создать объект класса Graphics2D не получится, поскольку графический контекст сильно зависит от конкретной графической платформы, этот класс сделан абстрактным. Самый очевидный выход - это явное приведение от Graphics:

Graphics2D g2 = (Graphics2D) g;

У объекта Graphics2D имеется очень полезный метод - drawImage(), который, собственно, и рисует изображение на экран. К примеру:

g2.drawImage( image, 0, 0, width, height, null);


Заключение

В ходе курсовой работы, были изучены основные элементы программирования на Java. Также были рассмотрены основные принципы работы с игровыми приложениями. Данное игровое приложение было оптимизировано с потоками для рационального использования памяти. Также использовалась весьма удобная игровая структура GameStateManager, которая обеспечивает распределение нагрузки между всеми игровыми разделами. В итоге, для демонстрации приобретенных навыков, был разработан игровой платформер без использования сторонних библиотек.

В ходе нажатии пользователем определенных клавиш, игровой персонаж совершает соответствующие действия: ходьба, прыжок, полет,  удар рукой, стрельба. Уровни создавались в текстовом редакторе, при помощи специального редактора. Персонаж не может выйти за пределы уровня, контактирую со стенами, потолком и полом. В качестве ресурсов использовались .gif, .jpg и .png файлы. Также файл уровней .map, который представляет собой числовую последовательность , каждая цифра которого «символически» обозначает некоторый участок картинки из файла titles.

Игровой платформер состоит из нескольких пакетов: GameState, Entity, Main, TileMap. GameState - соответствующие режимы игры. В Entity находятся классы для персонажей игры и их доступные действия. Из пакета Main начинается запуск игры, идет полная настройка фрейма с добавлением к нему панели. Именно отсюда берут свое начало потоки. В пакете TileMap идет загрузка карты, ее прорисовка и работа с фоном.

Данное приложение можно использовать на абсолютно любых desktop - устройствах, на которых установлена JVM. Запуск идет из jar файла. В дальнейшем планируется перевод  проекта в веб и на ОС Android.


Список использованных источников

  1.  Andrew Davison, Killer Game Programming in Java - «O'Reilly Media», Май 2005, - 998 c.
  2.  Java2. Библиотека профессионала Том 1. Основы, 8 - е издание - «Sun microsystems», 2010 - 820 c.
  3.  Эккель, Б. Философия Java. / Б. Эккель. – СПб. : Питер, 2001. – 880 с.
  4.  Шилдт, Г. Искусство программирования на JAVA / Г. Шилдт, Дж. Холмс. – М. : Вильямс, 2005. – 331 с.


Приложение A

Описание класса Game пакета Main

package Main;

import javax.swing.JFrame;

public class Game {

 public static void main(String[] args) {

 JFrame window = new JFrame("Game");

 window.setContentPane(new GamePanel());

 window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

 window.setResizable(false);

 window.pack();

 window.setVisible(true);

}

}


Приложение Б

Описание класса GamePanel пакета Main

package Main;

import java.awt.*;

import java.awt.image.BufferedImage;

import java.awt.event.*;

import javax.swing.JPanel;

import GameState.GameStateManager;

@SuppressWarnings("serial")

public class GamePanel extends JPanel implements Runnable, KeyListener {

 // размеры

 public static final int WIDTH = 800;

 public static final int HEIGHT = 600;

 public static final int SCALE = 2;

 // поток

 private Thread thread;

 private boolean running;

 private int FPS = 100;

 private long targetTime = 1000 / FPS;

 // изображение

 private BufferedImage image;

 private Graphics2D g;

 // game state manager

 private GameStateManager gsm;

 public GamePanel() {

 super();

 setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));

 setFocusable(true);

 requestFocus();

}

 public void addNotify() {

 super.addNotify();

 if (thread == null) {

  thread = new Thread(this);

  addKeyListener(this);

  thread.start();

 }

}

 private void init() {

image = new BufferedImage(WIDTH, HEIGHT,

BufferedImage.TYPE_INT_RGB);

 g = (Graphics2D) image.getGraphics();

 running = true;

 gsm = new GameStateManager();

}

 public void run() {

 init();

 

 long start;

 long elapsed;

 long wait;

 // game loop

 while (running) {

  start = System.nanoTime();

  update();

  draw();

  drawToScreen();

  elapsed = System.nanoTime() - start;

  wait = targetTime - elapsed / 1000000;

  if (wait < 0)

   wait = 5;

  try {

   Thread.sleep(wait);

  } catch (Exception e) {

   e.printStackTrace();

  }

 }

}

 private void update() {

 gsm.update();

}

 private void draw() {

 gsm.draw(g);

}

 private void drawToScreen() {

 Graphics g2 = getGraphics();

 g2.drawImage(image, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, null);

 g2.dispose();

}

 public void keyTyped(KeyEvent key) {

}

 public void keyPressed(KeyEvent key) {

 gsm.keyPressed(key.getKeyCode());

}

 public void keyReleased(KeyEvent key) {

 gsm.keyReleased(key.getKeyCode());

}

}


Приложение В

Описание класса GameState пакета GameState

package GameState;

public abstract class GameState {

 protected GameStateManager gsm;

 public abstract void init();

 public abstract void update();

 public abstract void draw(java.awt.Graphics2D g);

 public abstract void keyPressed(int k);

 public abstract void keyReleased(int k);

}


Приложение Г

Описание класса GameStateManager пакета GameState

package GameState;

import java.util.ArrayList;

public class GameStateManager {

 

 private GameState[] gameStates;

 private int currentState;

 public static final int NUMGAMESTATES = 2;

 public static final int MENUSTATE = 0;

 public static final int LEVEL1STATE = 1;

 

 public GameStateManager() {

 gameStates = new GameState[NUMGAMESTATES];

 currentState = MENUSTATE;

 loadState(currentState);

}

 

 private void loadState(int state) {

 if(state == MENUSTATE)

  gameStates[state] = new MenuState(this);

 if(state == LEVEL1STATE)

  gameStates[state] = new Level1State(this);

}

 

 private void unloadState(int state) {

 gameStates[state] = null;

}

 

 public void setState(int state) {

 unloadState(currentState);

 currentState = state;

 loadState(currentState);

 //gameStates[currentState].init();

}

 

 public void update() {

 try {

  gameStates[currentState].update();

 } catch(Exception e) {}

}

 

 public void draw(java.awt.Graphics2D g) {

 try {

  gameStates[currentState].draw(g);

 } catch(Exception e) {}

}

 

 public void keyPressed(int k) {

 gameStates[currentState].keyPressed(k);

}

 

 public void keyReleased(int k) {

 gameStates[currentState].keyReleased(k);

}

}


Приложение Д

Описание класса Level1State пакета GameState

package GameState;

import Main.GamePanel;

import TileMap.*;

import Entity.*;

import java.awt.*;

import java.awt.event.KeyEvent;

import java.util.ArrayList;

public class Level1State extends GameState {

 

 private TileMap tileMap;

 private Background bg;

 private Player player;

 private ArrayList<Explosion> explosions;

 private HUD hud;

 

 public Level1State(GameStateManager gsm) {

 this.gsm = gsm;

 init();

}

 

 public void init() {

 

 tileMap = new TileMap(30);

 tileMap.loadTiles("/Tilesets/grasstileset2.gif");

 tileMap.loadMap("/Maps/level1.map");

 tileMap.setPosition(0, 0);

 tileMap.setTween(1);

 

 bg = new Background("/Backgrounds/back.gif", 0.1);

 player = new Player(tileMap);

 player.setPosition(100, 100);

 

 explosions = new ArrayList<Explosion>();

 

 hud = new HUD(player);

   

}

 

 public void update() {

 

 // обновление персонажа

 player.update();

 tileMap.setPosition(

  GamePanel.WIDTH / 2 - player.getx(),

  GamePanel.HEIGHT / 2 - player.gety()

 );    

 

 // фон

 bg.setPosition(tileMap.getx(), tileMap.gety());

 

 

 

 // обновление взрывов

 for(int i = 0; i < explosions.size(); i++) {

  explosions.get(i).update();

  if(explosions.get(i).shouldRemove()) {

   explosions.remove(i);

   i--;

  }

 }

 

}

 

 public void draw(Graphics2D g) {

 

 // нарисовать фон

 bg.draw(g);

 

 // нарисовать карту

 tileMap.draw(g);

 

 // нарсиовать персонажа

 player.draw(g);

 

 

 // нарисовать взрыв

 for(int i = 0; i < explosions.size(); i++) {

  explosions.get(i).setMapPosition(

   (int)tileMap.getx(), (int)tileMap.gety());

  explosions.get(i).draw(g);

 }

 

 //нарисовать панель здоровья

 hud.draw(g);

 

 }

 

 public void keyPressed(int k) {

 if(k == KeyEvent.VK_LEFT) player.setLeft(true);

 if(k == KeyEvent.VK_RIGHT) player.setRight(true);

 if(k == KeyEvent.VK_UP) player.setUp(true);

 if(k == KeyEvent.VK_DOWN) player.setDown(true);

 if(k == KeyEvent.VK_W) player.setJumping(true);

 if(k == KeyEvent.VK_E) player.setGliding(true);

 if(k == KeyEvent.VK_R) player.setScratching();

 if(k == KeyEvent.VK_F) player.setFiring();

}

 

 public void keyReleased(int k) {

 if(k == KeyEvent.VK_LEFT) player.setLeft(false);

 if(k == KeyEvent.VK_RIGHT) player.setRight(false);

 if(k == KeyEvent.VK_UP) player.setUp(false);

 if(k == KeyEvent.VK_DOWN) player.setDown(false);

 if(k == KeyEvent.VK_W) player.setJumping(false);

 if(k == KeyEvent.VK_E) player.setGliding(false);

 }

 

}


Приложение Е

Описание класса MenuState пакета GameState

package GameState;

import TileMap.Background;

import java.awt.*;

import java.awt.event.KeyEvent;

public class MenuState extends GameState {

 private Background bg;

 private int currentChoice = 0;

 private String[] options = { "Start", "Help", "Quit" };

 private Color titleColor;

 private Font titleFont;

 private Font font;

 public MenuState(GameStateManager gsm) {

 this.gsm = gsm;

 try {

  bg = new Background("/Backgrounds/back.jpg", 1);

  bg.setVector(-0.2, 0);

  titleColor = new Color(128, 0, 0);

  titleFont = new Font("Charlemagne Std", Font.PLAIN, 28);

  font = new Font("Charlemagne Std", Font.PLAIN, 12);

 } catch (Exception e) {

  e.printStackTrace();

 }

}

 public void init() {

}

 public void update() {

 bg.update();

}

 public void draw(Graphics2D g) {

 bg.draw(g);

 g.setColor(titleColor);

 g.setFont(titleFont);

 g.drawString("Student dragon", 200, 90);

 g.setFont(font);

 for (int i = 0; i < options.length; i++) {

  if (i == currentChoice) {

   g.setColor(Color.BLACK);

  } else {

   g.setColor(Color.RED);

  }

  g.drawString(options[i], 324, 140 + (int) (i * 15 * 1.2));

 }

}

 private void select() {

 if (currentChoice == 0) {

  gsm.setState(GameStateManager.LEVEL1STATE);

 }

 if (currentChoice == 1) {

  // help

 }

 if (currentChoice == 2) {

  System.exit(0);

 }

}

 public void keyPressed(int k) {

 if (k == KeyEvent.VK_ENTER) {

  select();

 }

 if (k == KeyEvent.VK_UP) {

  currentChoice--;

  if (currentChoice == -1) {

   currentChoice = options.length - 1;

  }

 }

 if (k == KeyEvent.VK_DOWN) {

  currentChoice++;

  if (currentChoice == options.length) {

   currentChoice = 0;

  }

 }

}

 public void keyReleased(int k) {

 }

}


Приложение Ж

Описание класса Background пакета TileMap

package TileMap;

import Main.GamePanel;

import java.awt.*;

import java.awt.image.*;

import javax.imageio.ImageIO;

public class Background {

 

 private BufferedImage image;

 private double x;

 private double y;

 private double dx;

 private double dy;

 private double moveScale;

 

 public Background(String s, double ms) {

 

 try {

  image = ImageIO.read(

   getClass().getResourceAsStream(s)

  );

  moveScale = ms;

 }

 catch(Exception e) {

  e.printStackTrace();

 }

 

}

 

 public void setPosition(double x, double y) {

 this.x = (x * moveScale) % GamePanel.WIDTH;

 this.y = (y * moveScale) % GamePanel.HEIGHT;

}

 

 public void setVector(double dx, double dy) {

 this.dx = dx;

 this.dy = dy;

}

 

 public void update() {

 x += dx;

 y += dy;

}

 

 public void draw(Graphics2D g) {

 

 g.drawImage(image, (int)x, (int)y, null);  

 if(x < 0) {

  g.drawImage(

   image,

   (int)x + GamePanel.WIDTH,

   (int)y,

   null

  );

 }

 if(x > 0) {

  g.drawImage(

   image,

   (int)x - GamePanel.WIDTH,

   (int)y,

   null

  );

 }

 if (x <= -GamePanel.WIDTH) {

  x += GamePanel.WIDTH;

  

 }

}

 

}


Приложение З

Описание класса Tile пакета TileMap

import java.awt.image.BufferedImage;

public class Tile {

 

 private BufferedImage image;

 private int type;

 

 // типы тайлов

 public static final int NORMAL = 0;

 public static final int BLOCKED = 1;

 

 public Tile(BufferedImage image, int type) {

 this.image = image;

 this.type = type;

}

 

 public BufferedImage getImage() { return image; }

 public int getType() { return type; }

 

}


Приложение И

Описание класса TileMap пакета TileMap

import java.awt.*;

import java.awt.image.*;

import java.io.*;

import javax.imageio.ImageIO;

import Main.GamePanel;

public class TileMap {

 // позиция

 private double x;

 private double y;

 // границы

 private int xmin;

 private int ymin;

 private int xmax;

 private int ymax;

 private double tween;

 // карта

 private int[][] map;

 private int tileSize;

 private int numRows;

 private int numCols;

 private int width;

 private int height;

 // тайлы

 private BufferedImage tileset;

 private int numTilesAcross;

 private Tile[][] tiles;

 // рисования

 private int rowOffset;

 private int colOffset;

 private int numRowsToDraw;

 private int numColsToDraw;

 public TileMap(int tileSize) {

 this.tileSize = tileSize;

 numRowsToDraw = GamePanel.HEIGHT / tileSize + 2;

 numColsToDraw = GamePanel.WIDTH / tileSize + 2;

 tween = 0.07;

}

 public void loadTiles(String s) {

 try {

  tileset = ImageIO.read(getClass().getResourceAsStream(s));

  numTilesAcross = tileset.getWidth() / tileSize;

  tiles = new Tile[2][numTilesAcross];

  BufferedImage subimage;

  for (int col = 0; col < numTilesAcross; col++) {

subimage = tileset.getSubimage(col * tileSize, 0, tileSize, tileSize);

   tiles[0][col] = new Tile(subimage, Tile.NORMAL);

   subimage = tileset.getSubimage(col * tileSize,

tileSize, tileSize, tileSize);

   tiles[1][col] = new Tile(subimage, Tile.BLOCKED);

  }

 } catch (Exception e) {

  e.printStackTrace();

 }

}

 public void loadMap(String s) {

 try {

  InputStream in = getClass().getResourceAsStream(s);

  BufferedReader br = new BufferedReader(

new InputStreamReader(in)

);

  numCols = Integer.parseInt(br.readLine());

  numRows = Integer.parseInt(br.readLine());

  map = new int[numRows][numCols];

  width = numCols * tileSize;

  height = numRows * tileSize;

  xmin = GamePanel.WIDTH - width - 100;

  xmax = 0;

  ymin = GamePanel.HEIGHT - height;//here

  ymax = 0;

  String delims = "\\s+";

  for (int row = 0; row < numRows; row++) {

   String line = br.readLine();

   String[] tokens = line.split(delims);

   for (int col = 0; col < numCols; col++) {

    map[row][col] = Integer.parseInt(tokens[col]);

   }

  }

 } catch (Exception e) {

  e.printStackTrace();

 }

}

 public int getTileSize() {

 return tileSize;

}

 public double getx() {

 return x;

}

 public double gety() {

 return y;

}

 public int getWidth() {

 return width;

}

 public int getHeight() {

 return height;

}

 public int getType(int row, int col) {

 int rc = map[row][col];

 int r = rc / numTilesAcross;

 int c = rc % numTilesAcross;

 return tiles[r][c].getType();

}

 public void setTween(double d) {

 tween = d;

}

 public void setPosition(double x, double y) {

 this.x += (x - this.x) * tween;

 this.y += (y - this.y) * tween;

 fixBounds();

 colOffset = (int) -this.x / tileSize;

 rowOffset = (int) -this.y / tileSize;

}

 private void fixBounds() {

 if (x < xmin)

  x = xmin;

 if (y < ymin)

  y = ymin;

 if (x > xmax)

  x = xmax;

 if (y > ymax)

  y = ymax;

}

 public void draw(Graphics2D g) {

for (int row = rowOffset; row < rowOffset + numRowsToDraw; row++) {

  if (row >= numRows)

   break;

  for (int col = colOffset; col < colOffset +

numColsToDraw; col++)

{

   if (col >= numCols)

    break;

   if (map[row][col] == 0)

    continue;

   int rc = map[row][col];

   int r = rc / numTilesAcross;

   int c = rc % numTilesAcross;

   g.drawImage(tiles[r][c].getImage(),

(int) x + col * tileSize, 

(int) y + row * tileSize, null);

  }

 }

}

}


Приложение К

Описание класса Animation пакета Entity

package Entity;

import java.awt.image.BufferedImage;

public class Animation {

 private BufferedImage[] frames;

 private int currentFrame;

 private long startTime;

 private long delay;

 private boolean playedOnce;

 public Animation() {

 playedOnce = false;

}

 public void setFrames(BufferedImage[] frames) {

 this.frames = frames;

 currentFrame = 0;

 startTime = System.nanoTime();

 playedOnce = false;

}

 public void setDelay(long d) {

 delay = d;

}

 public void setFrame(int i) {

 currentFrame = i;

}

 public void update() {

 if (delay == -1)

  return;

 long elapsed = (System.nanoTime() - startTime) / 1000000;

 if (elapsed > delay) {

  currentFrame++;

  startTime = System.nanoTime();

 }

 if (currentFrame == frames.length) {

  currentFrame = 0;

  playedOnce = true;

 }

}

 public int getFrame() {

 return currentFrame;

}

 public BufferedImage getImage() {

 return frames[currentFrame];

}

 public boolean hasPlayedOnce() {

 return playedOnce;

}

}


Приложение Л

Описание класса Explosion пакета Entity

package Entity;

import java.awt.Graphics2D;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;

public class Explosion {

 

 private int x;

 private int y;

 private int xmap;

 private int ymap;

 

 private int width;

 private int height;

 

 private Animation animation;

 private BufferedImage[] sprites;

 

 private boolean remove;

 

 public Explosion(int x, int y) {

 

 this.x = x;

 this.y = y;

 

 width = 30;

 height = 30;

 

 try {

  

  BufferedImage spritesheet = ImageIO.read(

   getClass().getResourceAsStream(

    "/Sprites/Enemies/explosion.gif"

   )

  );

  

  sprites = new BufferedImage[6];

  for(int i = 0; i < sprites.length; i++) {

   sprites[i] = spritesheet.getSubimage(

    i * width,

    0,

    width,

    height

   );

  }

  

 }

 catch(Exception e) {

  e.printStackTrace();

 }

 

 animation = new Animation();

 animation.setFrames(sprites);

 animation.setDelay(70);

 

}

 

 public void update() {

 animation.update();

 if(animation.hasPlayedOnce()) {

  remove = true;

 }

}

 

 public boolean shouldRemove() { return remove; }

 

 public void setMapPosition(int x, int y) {

 xmap = x;

 ymap = y;

}

 

 public void draw(Graphics2D g) {

 g.drawImage(

  animation.getImage(),

  x + xmap - width / 2,

  y + ymap - height / 2,

  null

 );

}

}


Приложение М

Описание класса FireBall пакета Entity

package Entity;

import Main.GamePanel;

import TileMap.TileMap;

import java.awt.*;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;

public class FireBall extends MapObject {

 

 private boolean hit;

 private boolean remove;

 private BufferedImage[] sprites;

 private BufferedImage[] hitSprites;

 

 public FireBall(TileMap tm, boolean right) {

 

 super(tm);

 

 facingRight = right;

 

 moveSpeed = 3.8;

 if(right) dx = moveSpeed;

 else dx = -moveSpeed;

 

 width = 30;

 height = 30;

 cwidth = 14;

 cheight = 14;

 

 // load sprites

 try {

  

  BufferedImage spritesheet = ImageIO.read(

   getClass().getResourceAsStream(

    "/Sprites/Player/fireball.gif"

   )

  );

  

  sprites = new BufferedImage[4];

  for(int i = 0; i < sprites.length; i++) {

   sprites[i] = spritesheet.getSubimage(

    i * width,

    0,

    width,

    height

   );

  }

  

  hitSprites = new BufferedImage[3];

  for(int i = 0; i < hitSprites.length; i++) {

   hitSprites[i] = spritesheet.getSubimage(

    i * width,

    height,

    width,

    height

   );

  }

  

  animation = new Animation();

  animation.setFrames(sprites);

  animation.setDelay(70);

  

 }

 catch(Exception e) {

  e.printStackTrace();

 }

 

}

 

 public void setHit() {

 if(hit) return;

 hit = true;

 animation.setFrames(hitSprites);

 animation.setDelay(70);

 dx = 0;

}

 

 public boolean shouldRemove() {

 return remove;

}

 

 public void update() {

 checkTileMapCollision();

 setPosition(xtemp, ytemp);

 if(dx == 0 && !hit) {

  setHit();

 }

 

 animation.update();

 if(hit && animation.hasPlayedOnce()) {

  remove = true;

 }

 

}

 

 public void draw(Graphics2D g) {

 setMapPosition();

 

 super.draw(g);

 

}

 

}


Приложение Н

Описание класса HUD пакета Entity

package Entity;

import java.awt.*;

import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;

public class HUD {

 

 private Player player;

 

 private BufferedImage image;

 private Font font;

 

 public HUD(Player p) {

 player = p;

 try {

  image = ImageIO.read(

   getClass().getResourceAsStream(

    "/HUD/hud.gif"

   )

  );

  font = new Font("Arial", Font.PLAIN, 14);

 }

 catch(Exception e) {

  e.printStackTrace();

 }

}

 

 public void draw(Graphics2D g) {

 

 g.drawImage(image, 0, 10, null);

 g.setFont(font);

 g.setColor(Color.WHITE);

 g.drawString(

  player.getHealth() + "/" + player.getMaxHealth(),

  30,

  25

 );

 g.drawString(

  player.getFire() / 100 + "/" + player.getMaxFire() / 100,

  30,

  45

 );

 

}

 

}


Приложение О

Описание класса MapObject пакета Entity

package Entity;

import Main.GamePanel;

import TileMap.TileMap;

import TileMap.Tile;

import java.awt.Rectangle;

public abstract class MapObject {

 

 

 protected TileMap tileMap;

 protected int tileSize;

 protected double xmap;

 protected double ymap;

 

 // позиция

 protected double x;

 protected double y;

 protected double dx;

 protected double dy;

 

 // измерение

 protected int width;

 protected int height;

 

 // коллизия

 protected int cwidth;

 protected int cheight;

 

 // еще коллизия

 protected int currRow;

 protected int currCol;

 protected double xdest;

 protected double ydest;

 protected double xtemp;

 protected double ytemp;

 protected boolean topLeft;

 protected boolean topRight;

 protected boolean bottomLeft;

 protected boolean bottomRight;

 

 // анимация

 protected Animation animation;

 protected int currentAction;

 protected int previousAction;

 protected boolean facingRight;

 

 // движение

 protected boolean left;

 protected boolean right;

 protected boolean up;

 protected boolean down;

 protected boolean jumping;

 protected boolean falling;

 

 // свойства движения

 protected double moveSpeed;

 protected double maxSpeed;

 protected double stopSpeed;

 protected double fallSpeed;

 protected double maxFallSpeed;

 protected double jumpStart;

 protected double stopJumpSpeed;

 

 

 public MapObject(TileMap tm) {

 tileMap = tm;

 tileSize = tm.getTileSize();

}

 

 public boolean intersects(MapObject o) {

 Rectangle r1 = getRectangle();

 Rectangle r2 = o.getRectangle();

 return r1.intersects(r2);

}

 

 public Rectangle getRectangle() {

 return new Rectangle(

   (int)x - cwidth,

   (int)y - cheight,

   cwidth,

   cheight

 );

}

 

 public void calculateCorners(double x, double y) {

 int leftTile = (int)(x - cwidth / 2) / tileSize;

 int rightTile = (int)(x + cwidth /2 - 1) / tileSize;

 int topTile = (int)(y - cheight / 2) / tileSize;

 int bottomTile = (int)(y + cheight / 2 - 1) / tileSize;

 

 int tl = tileMap.getType(topTile, leftTile);

 int tr = tileMap.getType(topTile, rightTile);

 int bl = tileMap.getType(bottomTile, leftTile);

 int br = tileMap.getType(bottomTile, rightTile);

 

 topLeft = tl == Tile.BLOCKED;

 topRight = tr == Tile.BLOCKED;

 bottomLeft = bl == Tile.BLOCKED;

 bottomRight = br == Tile.BLOCKED;

 

}

 

 public void checkTileMapCollision() {

 

 currCol = (int)x / tileSize;

 currRow = (int)y / tileSize;

 

 xdest = x + dx;

 ydest = y + dy;

 

 xtemp = x;

 ytemp = y;

 

 calculateCorners(x, ydest);

 if(dy < 0) {

  if(topLeft || topRight) {

   dy = 0;

   ytemp = currRow * tileSize + cheight / 2;

  }

  else {

   ytemp += dy;

  }

 }

 if(dy > 0) {

  if(bottomLeft || bottomRight) {

   dy = 0;

   falling = false;

   ytemp = (currRow + 1) * tileSize - cheight / 2;

  }

  else {

   ytemp += dy;

  }

 }

 

 calculateCorners(xdest, y);

 if(dx < 0) {

  if(topLeft || bottomLeft) {

   dx = 0;

   xtemp = currCol * tileSize + cwidth / 2;

  }

  else {

   xtemp += dx;

  }

 }

 if(dx > 0) {

  if(topRight || bottomRight) {

   dx = 0;

   xtemp = (currCol + 1) * tileSize - cwidth / 2;

  }

  else {

   xtemp += dx;

  }

 }

 

 if(!falling) {

  calculateCorners(x, ydest + 1);

  if(!bottomLeft && !bottomRight) {

   falling = true;

  }

 }

 

}

 

 public int getx() { return (int)x; }

 public int gety() { return (int)y; }

 public int getWidth() { return width; }

 public int getHeight() { return height; }

 public int getCWidth() { return cwidth; }

 public int getCHeight() { return cheight; }

 

 public void setPosition(double x, double y) {

 this.x = x;

 this.y = y;

}

 public void setVector(double dx, double dy) {

 this.dx = dx;

 this.dy = dy;

}

 

 public void setMapPosition() {

 xmap = tileMap.getx();

 ymap = tileMap.gety();

}

 

 public void setLeft(boolean b) { left = b; }

 public void setRight(boolean b) { right = b; }

 public void setUp(boolean b) { up = b; }

 public void setDown(boolean b) { down = b; }

 public void setJumping(boolean b) { jumping = b; }

 

 public boolean notOnScreen() {

 return x + xmap + width < 0 ||

  x + xmap - width > GamePanel.WIDTH ||

  y + ymap + height < 0 ||

  y + ymap - height > GamePanel.HEIGHT;

}

 

 public void draw(java.awt.Graphics2D g) {

 if(facingRight) {

  g.drawImage(

   animation.getImage(),

   (int)(x + xmap - width / 2),

   (int)(y + ymap - height / 2),

   null

  );

 }

 else {

  g.drawImage(

   animation.getImage(),

   (int)(x + xmap - width / 2 + width),

   (int)(y + ymap - height / 2),

   -width,

   height,

   null

  );

 }

}

 

}


Приложение П

Описание класса Player пакета Entity

package Entity;

import TileMap.*;

import java.util.ArrayList;

import javax.imageio.ImageIO;

import java.awt.*;

import java.awt.image.BufferedImage;

import java.util.HashMap;

public class Player extends MapObject {

 

 // действия игрока

 private int health;

 private int maxHealth;

 private int fire;

 private int maxFire;

 private boolean dead;

 private boolean flinching;

 private long flinchTimer;

 

 // стрельба

 private boolean firing;

 private int fireCost;

 private int fireBallDamage;

 private ArrayList<FireBall> fireBalls;

 

 // удар рукой

 private boolean scratching;

 private int scratchDamage;

 private int scratchRange;

 

 // полет

 private boolean gliding;

 

 // анимация

 private ArrayList<BufferedImage[]> sprites;

 private final int[] numFrames = {

 2, 8, 1, 2, 4, 2, 5

};

 

 // id анимации

 private static final int IDLE = 0;

 private static final int WALKING = 1;

 private static final int JUMPING = 2;

 private static final int FALLING = 3;

 private static final int GLIDING = 4;

 private static final int FIREBALL = 5;

 private static final int SCRATCHING = 6;

 

 

 public Player(TileMap tm) {

 

 super(tm);

 

 width = 30;

 height = 30;

 cwidth = 30;

 cheight = 30;

 

 moveSpeed = 0.3;

 maxSpeed = 1.6;

 stopSpeed = 0.4;

 fallSpeed = 0.15;

 maxFallSpeed = 4.0;

 jumpStart = -4.8;

 stopJumpSpeed = 0.3;

 

 facingRight = true;

 

 health = maxHealth = 5;

 fire = maxFire = 2500;

 

 fireCost = 200;

 fireBallDamage = 5;

 fireBalls = new ArrayList<FireBall>();

 

 scratchDamage = 8;

 scratchRange = 40;

 

 // загрузка спрайтов

 try {

  

  BufferedImage spritesheet = ImageIO.read(

   getClass().getResourceAsStream(

    "/Sprites/Player/playersprites.gif"

   )

  );

  

  sprites = new ArrayList<BufferedImage[]>();

  for(int i = 0; i < 7; i++) {

   

   BufferedImage[] bi =

    new BufferedImage[numFrames[i]];

   

   for(int j = 0; j < numFrames[i]; j++) {

    

    if(i != SCRATCHING) {

     bi[j] = spritesheet.getSubimage(

       j * width,

       i * height,

       width,

       height

     );

    }

    else {

     bi[j] = spritesheet.getSubimage(

       j * width * 2,

       i * height,

       width * 2,

       height

     );

    }

    

   }

   

   sprites.add(bi);

   

  }

  

 }

 catch(Exception e) {

  e.printStackTrace();

 }

 

 animation = new Animation();

 currentAction = IDLE;

 animation.setFrames(sprites.get(IDLE));

 animation.setDelay(400);

 

}

 

 public int getHealth() { return health; }

 public int getMaxHealth() { return maxHealth; }

 public int getFire() { return fire; }

 public int getMaxFire() { return maxFire; }

 

 public void setFiring() {

 firing = true;

}

 public void setScratching() {

 scratching = true;

}

 public void setGliding(boolean b) {

 gliding = b;

}

 

 

 public void hit(int damage) {

 if(flinching) return;

 health -= damage;

 if(health < 0) health = 0;

 if(health == 0) dead = true;

 flinching = true;

 flinchTimer = System.nanoTime();

}

 

 private void getNextPosition() {

 

 // движение

 if(left) {

  dx -= moveSpeed;

  if(dx < -maxSpeed) {

   dx = -maxSpeed;

  }

 }

 else if(right) {

  dx += moveSpeed;

  if(dx > maxSpeed) {

   dx = maxSpeed;

  }

 }

 else {

  if(dx > 0) {

   dx -= stopSpeed;

   if(dx < 0) {

    dx = 0;

   }

  }

  else if(dx < 0) {

   dx += stopSpeed;

   if(dx > 0) {

    dx = 0;

   }

  }

 }

 

 // во время удара запрет движение

 if(

 (currentAction == SCRATCHING || currentAction == FIREBALL) &&

 !(jumping || falling)) {

  dx = 0;

 }

 

 // прыжок

 if(jumping && !falling) {

  dy = jumpStart;

  falling = true;

 }

 

 // падение

 if(falling) {

  

  if(dy > 0 && gliding) dy += fallSpeed * 0.1;

  else dy += fallSpeed;

  

  if(dy > 0) jumping = false;

  if(dy < 0 && !jumping) dy += stopJumpSpeed;

  

  if(dy > maxFallSpeed) dy = maxFallSpeed;

  

 }

 

}

 

 public void update() {

 

 // обновление позиции

 getNextPosition();

 checkTileMapCollision();

 setPosition(xtemp, ytemp);

 

 // проверка остановки атаки

 if(currentAction == SCRATCHING) {

  if(animation.hasPlayedOnce()) scratching = false;

 }

 if(currentAction == FIREBALL) {

  

  if(animation.hasPlayedOnce()) firing = false;

 }

 

 // стрельба

 fire += 1;

 if(fire > maxFire) fire = maxFire;

 if(firing && currentAction != FIREBALL) {

  if(fire > fireCost) {

   fire -= fireCost;

   FireBall fb = new FireBall(tileMap, facingRight);

   fb.setPosition(x, y);

   fireBalls.add(fb);

  }

 }

 

 // обновить огонь

 for(int i = 0; i < fireBalls.size(); i++) {

  fireBalls.get(i).update();

  if(fireBalls.get(i).shouldRemove()) {

   fireBalls.remove(i);

   i--;

  }

 }

 

 

 if(flinching) {

  long elapsed =

   (System.nanoTime() - flinchTimer) / 1000000;

  if(elapsed > 1000) {

   flinching = false;

  }

 }

 

 // установить анимацию

 if(scratching) {

  if(currentAction != SCRATCHING) {

   currentAction = SCRATCHING;

   animation.setFrames(sprites.get(SCRATCHING));

   animation.setDelay(50);

   width = 60;

  }

 }

 else if(firing) {

  if(currentAction != FIREBALL) {

   currentAction = FIREBALL;

   animation.setFrames(sprites.get(FIREBALL));

   animation.setDelay(100);

   width = 30;

  }

 }

 else if(dy > 0) {

  if(gliding) {

   if(currentAction != GLIDING) {

    currentAction = GLIDING;

    animation.setFrames(sprites.get(GLIDING));

    animation.setDelay(100);

    width = 30;

   }

  }

  else if(currentAction != FALLING) {

   currentAction = FALLING;

   animation.setFrames(sprites.get(FALLING));

   animation.setDelay(100);

   width = 30;

  }

 }

 else if(dy < 0) {

  if(currentAction != JUMPING) {

   currentAction = JUMPING;

   animation.setFrames(sprites.get(JUMPING));

   animation.setDelay(-1);

   width = 30;

  }

 }

 else if(left || right) {

  if(currentAction != WALKING) {

   currentAction = WALKING;

   animation.setFrames(sprites.get(WALKING));

   animation.setDelay(40);

   width = 30;

  }

 }

 else {

  if(currentAction != IDLE) {

   currentAction = IDLE;

   animation.setFrames(sprites.get(IDLE));

   animation.setDelay(400);

   width = 30;

  }

 }

 

 animation.update();

 

 // установить направление

 if(currentAction != SCRATCHING && currentAction != FIREBALL) {

  if(right) facingRight = true;

  if(left) facingRight = false;

 }

 

}

 

 public void draw(Graphics2D g) {

 

 setMapPosition();

 

 // отрисовка огня

 for(int i = 0; i < fireBalls.size(); i++) {

  fireBalls.get(i).draw(g);

 }

 

 // нарисовать игрока

 if(flinching) {

  long elapsed =

   (System.nanoTime() - flinchTimer) / 1000000;

  if(elapsed / 100 % 2 == 0) {

   return;

  }

 }

 

 super.draw(g);

 

}

 

}


Приложение Р

Результат разработанного приложения

Рисунок Р.1 - Персонаж на стоит на месте

Рисунок Р.2 - Прыжок

Рисунок Р.3 - Персонаж летит

Рисунок Р.4 - Стрельба огнем

Рисунок Р.5 - Разрушение огня

 

Рисунок Р.6 - Удар когтем

Рисунок Р.7 - Невозможность пройти через потолок

Рисунок Р.8 - Окраина уровня


 

А также другие работы, которые могут Вас заинтересовать

65712. Податкова політика російського царату в українському селі у другій половині ХІХ – на початку ХХ ст.: історичний аспект 143.5 KB
  Трансформаційні процеси в аграрному секторі економіки України кінця ХХ – початку ХХІ ст. стимулюють зростання значного інтересу як дослідників-фахівців, так і широкої громадськості до специфічних особливостей перебігу аграрних перетворень, що мали місце в українському селі у минулому.
65713. Теплоізоляційні матеріали на основі модифікованих лужних алюмосилікатних композицій, здатних до спучування 927 KB
  Отже враховуючи все вищезазначене особливої актуальності набуває питання щодо розробки принципово нових видів теплоізоляційних матеріалів на основі модифікованих лужних алюмосилікатних композицій здатних до спучування які можуть бути виготовлені з використанням недорогої...
65714. РОЗВИТОК ТВОРЧИХ ЗДІБНОСТЕЙ УЧНІВ ОСНОВНОЇ ШКОЛИ ЗАСОБАМИ ГРАФІЧНИХ ЗАДАЧ З КРЕСЛЕННЯ 184 KB
  Звідси виникає перше протиріччя суть якого проявляється у важливості і значущості розвитку творчих здібностей школярів і відсутності методичної основи забезпечення цього процесу на засадах розв’язування творчих графічних задач на уроках креслення.
65715. Особистісна реалізованість людини в онтогенезі 382 KB
  Мета дослідження теоретико-методологічне обґрунтування та емпіричне вивчення феномену особистісної реалізованості людини в онтогенезі. Відповідно до зазначеної мети поставлено такі завдання: здійснити теоретичний аналіз...
65716. Патогенез і терапія псоріазу з різним ступенем тяжкості ендогенної інтоксикації та артеріальної гіпертензії 529.5 KB
  Виявлення захворювання серцевосудинної системи у хворих на псоріаз уже на початкових стадіях має велике практичне значення тому що розширює перспективи ефективної вторинної профілактики. Обраний напрямок досліджень пов'язаний з науковою діяльністю і входить...
65717. МОЖЛИВОСТІ ЧЕРЕЗСТРАВОХІДНОЇ ТА ТРАНСТОРАКАЛЬНОЇ ЕХОКАРДІОГРАФІЇ ДЛЯ ОПТИМІЗАЦІЇ ТЕРМІНУ АНТИКОАГУЛЯНТНОЇ ТЕРАПІЇ У ХВОРИХ НА ФІБРИЛЯЦІЮ-ТРІПОТІННЯ ПЕРЕДСЕРДЬ НЕКЛАПАННОЇ ЕТІОЛОГІЇ 1.04 MB
  Одним з найбільш небезпечних ускладнень у хворих ФП є розвиток тромбоемболічних ускладнень ТЕУ. Але підготовка хворих до відновлення синусового ритму продовжує залишатися актуальною проблемою: рекомендовані терміни терапії непрямими антикоагулянтами довготривалі...
65718. Перестрахування як механізм забезпечення фінансової стійкості страхової компанії 372 KB
  Одним з найбільш дієвих багатофункціональних та раціональних інструментів здатних забезпечити стабільну діяльність страхової компанії виступає перестрахування оскільки лібералізація світового ринку фінансових послуг надає можливість проводити як розміщення так і прийняття ризиків в межах країни та за кордоном.
65719. ПРОКУРОР ЯК СУБ’ЄКТ КРИМІНАЛЬНО-ПРОЦЕСУАЛЬНОЇ ДІЯЛЬНОСТІ 194 KB
  Метою наукового дослідження є розробка питань місця прокурора в кримінальнопроцесуальній діяльності та її законодавчого врегулювання. Для досягнення цієї мети автором були поставлені такі завдання: з’ясувати сутність діяльності прокурора як суб’єкта кримінального процесу в Україні...
65720. ФОРМУВАННЯ СРАТЕГІЙ МАРКЕТИНГОВОГО МЕНЕДЖМЕНТУ ТУРИСТИЧНИХ ПІДПРИЄМСТВ 237 KB
  Специфіка процесу надання туристичних послуг пов'язана не тільки з його органічним поєднанням із розвитком ресурсів конкретних географічних територій але і з необхідністю відтворення належного зв'язку між різними підприємствами даної території в процесі обслуговування туристів.