Доброго дня всем!
Сегодня я хочу Вам рассказать как написать игру под android исходя из уроков которые я писал здесь.
Когда я писал эту игру у меня возникала масса вопросов по поводу зацикливания спрайтов так что бы они появлялись через определенное время, так же были проблемы с обнаружением столкновений двух спрайтов и более, все эти вопросы я сегодня хочу осветить в этом посте так как в интернете я не нашел нормального ответа на мои вопросы и пришлось делать самому. Урок будет включать в себя предыдущие части которые если Вы не читали желательно прочесть.
Если уже читали эти статьи и знаете что и как, тогда добро пожаловать под кат. Будем писать игру.
Игра должна представлять из себя поле (сцену) на котором располагается ниндзя и призраки. Нинзя должен защищать свою базу от этих призраков стреляя по ним.
Пример такой игры можно посмотреть в android market'e. Хотя я сильно замахнулся, у нас будет только похожая идея.
Вот как будет выглядеть игра:
Создаем проект. Запускаем Eclipse — File — Android Project — Defens — Main.java.
Открываем наш файл Main.java и изменяем весь код на код который ниже:
Main.java
Код ниже говорит нашей главной функции что запускать нужно не *.xml файл темы, а класс который у нас является самой сценой.
Дальше Вам нужно создать класс GameView.java который будет служить для нас главным классом на котором будет производится прорисовка всех объектов. Так же в этом классе будет находится и наш поток в котором будет обрабатываться прорисовка объектов в потоке для уменьшения нагрузки игры на процессор. Вот как будет выглядеть класс когда на сцене у нас ничего не происходит:
GameView.java
Из комментариев надеюсь понятно какая функция что делает. Этот класс является базовым по этому в нем мы будем производиться все действия (функции) которые будут происходить в игре, но для начало нам нужно сделать еще несколько классов Переходи к следующему пункту — создание спрайтов.
Спрайты это маленькие картинки в 2D-играх, которые передвигаются. Это могут быть человечки, боеприпасы или даже облака. В этой игре мы будем иметь три различных типа спрайта: Нинзя , призрак , и снаряд .
Сейчас мы будем использовать не анимированные спрайты но в будущем я вставлю спрайты в проэкт, если тянет научиться делать спрайты прошу во второй урок по созданию игры под android.
Теперь загрузите эти картинки в папку res/drawable для того что бы Eclipse мог удивить эти картинки и вставить в Ваш проект.
Следующий рисунок должен визуально помочь понять как будет располагаться игрок на экране.
Скучная картинка… Давайте лучше создадим этого самого игрока.
Нам нужно разместить спрайт на экране, как это сделать? Создаем класс Player.java и записываем в него следующее:
Player.java
Все очень просто и понятно, наш игрок будет стоять на месте и ничего не делать, кроме как стрелять по врагу но стрельба будет реализована в классе пуля (снаряд), который будем делать дальше.
Создаем еще один файл классов и назовем его Bullet.java, этот класс будет определять координаты полета, скорость полета и другие параметры пули. И так, создали файл, и пишем в него следующее:
Bullet.java
Из комментариев должно быть понятно что пуля выполняет только одно действие — она должна лететь по направлению указанному игроком.
Для того что бы нарисовать эти два класса которые мы создали, нам нужно отредактировать код в классе GameView.java, добавить несколько методов которые будут возвращать нам наши рисунки. Полностью весь код я писать не буду, буду приводить только код нужных мне методов.
Для начала нам нужно создать объекты классов Bullet и Player для того что бы отобразить их на экране, для этого создадим список пуль, что бы они у нас никогда не заканчивались, и обычный объект класса игрока.
Шапка GameView
Дальше нам нужно присвоить картинки нашим классам, находим конструктор GameView и вставляем в самый конец две строчки:
GameView.java — Конструктор GameView
И в методе onDraw(Canvas c); делаем видимыми эти спрайты. Проходим по всей коллекции наших элементов сгенерировавшихся в списке.
GameView,java
А для того что бы пули начали вылетать при нажатии на экран, нужно создать метод createSprites(); который будет возвращать наш спрайт.
GameView.java
Ну и в конце концов создаем еще один метод — onTouch(); который собственно будет отлавливать все касания по экрану и устремлять пулю в ту точку где было нажатия на экран.
GameView.java
Если хотите сделать что бы нажатие обрабатывалось не единоразово, т.е. 1 нажатие — 1 пуля, а 1 нажатие — и пока не отпустишь оно будет стрелять, нужно удалить if(e.getAction() == MotionEvent.ACTION_DOWN) { }
и оставить только ball.add(createSprite(R.drawable.bullet));.
Все, запускаем нашу игру и пробуем стрелять. Должно выйти вот такое:
Для того что бы нам не было скучно играться, нужно создать врагов. Для этого нам придется создать еще один класс который будет называться Enemy.java и который будет уметь отображать и направлять нашего врага на нашу базу. Класс довольно простой по этому смотрим код ниже:
Enemy.java
И так что происходит в этом классе? Рассказываю: мы объявили жизненно важные переменные для нашего врага, высота ширина и координаты. Для размещения их на сцене я использовал класс Random() для того что бы когда они будут появляться на сцене, появлялись на все в одной точке, а в разных точках и на разных координатах. Скорость так же является у нас рандомной что бы каждый враг шел с разной скоростью, скорость у нас начинается с 0 и заканчивается 10, 10 — максимальная скорость которой может достигнуть враг. Двигаться они будут с права налево, для того что бы они не были сразу видны на сцене я закинул их на 900 пикселей за видимость экрана. Так что пока они дойдут можно уже будет подготовиться по полной к атаке.
Дальше нам нужно отобразить врага на сцене, для этого в классе GameView.java делаем следующее:
Создаем список врагов для того что бы они никогда не заканчивались и создаем битмап который будет содержать спрайт:
Шапка GameView
Далее создаем новый поток для задания скорости появления врагов на экране:
Шапка GameView
И имплементируем класс Runuble, вот как должна выглядеть инициализация класса GameView:
Теперь у Вас еклипс требует создать метод run(), создайте его, он будет иметь следующий вид:
В самом низу класса GameView
Здесь мы создаем поток который будет создавать спрайт от 0 до 2000 милисекунд или каждые 0, 1 или 2 секунды.
Теперь в конструкторе в самом конце пишем инициализируем наш спрайт с классом для отображения на сцене:
Конструктор GameView
Ну и конечно же нам нужно объявить эти методы в onDraw(); Вот значит и пишем в нем следующее:
Метод onDraw() в GameView
Снова проходим по коллекции врагов с помощью итератора и проверяем — если враг зашел за предел в 1000 пикселей — удаляем его, так как если мы не будем удалять у нас пямять закакается и телефон зависнет, а нам такие проблемы не нужны. Все игра готова для запуска.
Запускаем нашу игру и что мы увидим? А вот что:
Но что я вижу? О нет!!! Пули никак не убивают наших призраков что же делать? А я Вам скажу что делать, нам нужно создать метод который будет образовывать вокруг каждого спрайта — прямоугольник и будет сравнивать их на коллизии. Следующая тема будет об этом.
И так, у нас есть спрайт, у нас есть сцена, у нас все это даже движется красиво, но какая польза от всего этого когда у нас на сцене ничего не происходит кроме хождения туда сюда этих спрайтов? :)
С этой функцией я навозился по полной, даже как-то так выходило что психовал и уходил гулять по улице)) Самый трудный метод, хотя выглядеть совершенно безобидно…
Ладно, давайте уже создадим этот метод и не будем много разглагольствовать… Где то в конце класса GameView создаем метод testCollision() и пишем следующий код:
В самом низу класса GameView.java
И так, что у нас происходит в этом методе? Мы создаем один итератор и запускаем цикл для просмотра всей коллекции спрайтов, и говорим что каждый следующий спрайт пули будет первым.
Дальше создаем еще один итератор с другим списком спрайтов и снова переопределяем и говорим что каждый следующий спрайт врага будет первым. И создаем оператор ветвления — if() который собственно и проверяет на столкновения наши спрайты. В нем я использовал математическую функцию модуль (abs) которая возвращает мне абсолютное целое от двух прямоугольников.
Внутри ифа происходит сравнения двух прямоугольников Модуль от (Пуля по координате Х минус координата врага по координате Х меньше либо равен ширина пули плюс ширина врага / 2 (делим на два для нахождения центра прямоугольника)) и (Модуль от (Пуля по координате У минус координата врага по координате У меньше либо равен ширина пули плюс ширина врага / 2 (делим на два для нахождения центра прямоугольника)));
И в конце всего, если пуля таки достала до врага — мы удаляем его со сцены с концами.
Ну и для того что бы эта функция стала работать записываем её в метод run() который находится в классе GameThread, ниже нашего метода рисования onDraw(). И в surfaceCreated() добавляем thred.start() для запуска отрисовки объектов.
Вот что у нас получается после запуска приложения:
Сегодня я хочу Вам рассказать как написать игру под android исходя из уроков которые я писал здесь.
Когда я писал эту игру у меня возникала масса вопросов по поводу зацикливания спрайтов так что бы они появлялись через определенное время, так же были проблемы с обнаружением столкновений двух спрайтов и более, все эти вопросы я сегодня хочу осветить в этом посте так как в интернете я не нашел нормального ответа на мои вопросы и пришлось делать самому. Урок будет включать в себя предыдущие части которые если Вы не читали желательно прочесть.
- Пишем игру под Android: Часть 1 — Рисуем картинки на SurfaceView
- Пишем игру под Android: Часть 2 — Создаем первый спрайт
- Пишем игру под Android: Часть 3 — Спрайтовая анимация, работа с несколькими спрайтами
- Пишем игру под Android: Часть 4 — onTouchEvent и определение столкновений
- Пишем игру под Android: Часть 5 — Создание полноценной 2D игры
- Пишем игру под Android: Часть 6: Добавление звука
- Пишем игру под Android: Часть 7: Меню для игры и окно приветствия
- Пишем игру под Android: Часть 8: Фоновая музыка в игре
Если уже читали эти статьи и знаете что и как, тогда добро пожаловать под кат. Будем писать игру.
Постановка задачи:
Игра должна представлять из себя поле (сцену) на котором располагается ниндзя и призраки. Нинзя должен защищать свою базу от этих призраков стреляя по ним.
Пример такой игры можно посмотреть в android market'e. Хотя я сильно замахнулся, у нас будет только похожая идея.
Вот как будет выглядеть игра:
Начало разработки
Создаем проект. Запускаем Eclipse — File — Android Project — Defens — Main.java.
Открываем наш файл Main.java и изменяем весь код на код который ниже:
Main.java
public class Main extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// если хотим, чтобы приложение постоянно имело портретную ориентацию
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// если хотим, чтобы приложение было полноэкранным
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// и без заголовка
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
}
}
Код ниже говорит нашей главной функции что запускать нужно не *.xml файл темы, а класс который у нас является самой сценой.
setContentView(new GameView(this));
Дальше Вам нужно создать класс GameView.java который будет служить для нас главным классом на котором будет производится прорисовка всех объектов. Так же в этом классе будет находится и наш поток в котором будет обрабатываться прорисовка объектов в потоке для уменьшения нагрузки игры на процессор. Вот как будет выглядеть класс когда на сцене у нас ничего не происходит:
GameView.java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import towe.def.GameView.GameThread;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView
{
/**Объект класса GameLoopThread*/
private GameThread mThread;
public int shotX;
public int shotY;
/**Переменная запускающая поток рисования*/
private boolean running = false;
//-------------Start of GameThread--------------------------------------------------\\
public class GameThread extends Thread
{
/**Объект класса*/
private GameView view;
/**Конструктор класса*/
public GameThread(GameView view)
{
this.view = view;
}
/**Задание состояния потока*/
public void setRunning(boolean run)
{
running = run;
}
/** Действия, выполняемые в потоке */
public void run()
{
while (running)
{
Canvas canvas = null;
try
{
// подготовка Canvas-а
canvas = view.getHolder().lockCanvas();
synchronized (view.getHolder())
{
// собственно рисование
onDraw(canvas);
}
}
catch (Exception e) { }
finally
{
if (canvas != null)
{
view.getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
}
//-------------End of GameThread--------------------------------------------------\\
public GameView(Context context)
{
super(context);
mThread = new GameThread(this);
/*Рисуем все наши объекты и все все все*/
getHolder().addCallback(new SurfaceHolder.Callback()
{
/*** Уничтожение области рисования */
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
mThread.setRunning(false);
while (retry)
{
try
{
// ожидание завершение потока
mThread.join();
retry = false;
}
catch (InterruptedException e) { }
}
}
/** Создание области рисования */
public void surfaceCreated(SurfaceHolder holder)
{
mThread.setRunning(true);
mThread.start();
}
/** Изменение области рисования */
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
});
}
/**Функция рисующая все спрайты и фон*/
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
}
}
Из комментариев надеюсь понятно какая функция что делает. Этот класс является базовым по этому в нем мы будем производиться все действия (функции) которые будут происходить в игре, но для начало нам нужно сделать еще несколько классов Переходи к следующему пункту — создание спрайтов.
Создание спрайтов
Спрайты это маленькие картинки в 2D-играх, которые передвигаются. Это могут быть человечки, боеприпасы или даже облака. В этой игре мы будем иметь три различных типа спрайта: Нинзя , призрак , и снаряд .
Сейчас мы будем использовать не анимированные спрайты но в будущем я вставлю спрайты в проэкт, если тянет научиться делать спрайты прошу во второй урок по созданию игры под android.
Теперь загрузите эти картинки в папку res/drawable для того что бы Eclipse мог удивить эти картинки и вставить в Ваш проект.
Следующий рисунок должен визуально помочь понять как будет располагаться игрок на экране.
Скучная картинка… Давайте лучше создадим этого самого игрока.
Нам нужно разместить спрайт на экране, как это сделать? Создаем класс Player.java и записываем в него следующее:
Player.java
import android.graphics.Bitmap;
import android.graphics.Canvas;
public class Player
{
/**Объект главного класса*/
GameView gameView;
//спрайт
Bitmap bmp;
//х и у координаты рисунка
int x;
int y;
//конструктор
public Player(GameView gameView, Bitmap bmp)
{
this.gameView = gameView;
this.bmp = bmp; //возвращаем рисунок
this.x = 0; //отступ по х нет
this.y = gameView.getHeight() / 2; //делаем по центру
}
//рисуем наш спрайт
public void onDraw(Canvas c)
{
c.drawBitmap(bmp, x, y, null);
}
}
Все очень просто и понятно, наш игрок будет стоять на месте и ничего не делать, кроме как стрелять по врагу но стрельба будет реализована в классе пуля (снаряд), который будем делать дальше.
Создаем еще один файл классов и назовем его Bullet.java, этот класс будет определять координаты полета, скорость полета и другие параметры пули. И так, создали файл, и пишем в него следующее:
Bullet.java
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
public class Bullet
{
/**Картинка*/
private Bitmap bmp;
/**Позиция*/
public int x;
public int y;
/**Скорость по Х=15*/
private int mSpeed=25;
public double angle;
/**Ширина*/
public int width;
/**Ввыоста*/
public int height;
public GameView gameView;
/**Конструктор*/
public Bullet(GameView gameView, Bitmap bmp) {
this.gameView=gameView;
this.bmp=bmp;
this.x = 0; //позиция по Х
this.y = 120; //позиция по У
this.width = 27; //ширина снаряда
this.height = 40; //высота снаряда
//угол полета пули в зависипости от координаты косания к экрану
angle = Math.atan((double)(y - gameView.shotY) / (x - gameView.shotX));
}
/**Перемещение объекта, его направление*/
private void update() {
x += mSpeed * Math.cos(angle); //движение по Х со скоростью mSpeed и углу заданном координатой angle
y += mSpeed * Math.sin(angle); // движение по У -//-
}
/**Рисуем наши спрайты*/
public void onDraw(Canvas canvas) {
update(); //говорим что эту функцию нам нужно вызывать для работы класса
canvas.drawBitmap(bmp, x, y, null);
}
}
Из комментариев должно быть понятно что пуля выполняет только одно действие — она должна лететь по направлению указанному игроком.
Рисуем спрайты на сцене
Для того что бы нарисовать эти два класса которые мы создали, нам нужно отредактировать код в классе GameView.java, добавить несколько методов которые будут возвращать нам наши рисунки. Полностью весь код я писать не буду, буду приводить только код нужных мне методов.
Для начала нам нужно создать объекты классов Bullet и Player для того что бы отобразить их на экране, для этого создадим список пуль, что бы они у нас никогда не заканчивались, и обычный объект класса игрока.
Шапка GameView
private List<Bullet> ball = new ArrayList<Bullet>(); private Player player;
Bitmap players;
Дальше нам нужно присвоить картинки нашим классам, находим конструктор GameView и вставляем в самый конец две строчки:
GameView.java — Конструктор GameView
players= BitmapFactory.decodeResource(getResources(), R.drawable.player2);
player= new Player(this, guns);
И в методе onDraw(Canvas c); делаем видимыми эти спрайты. Проходим по всей коллекции наших элементов сгенерировавшихся в списке.
GameView,java
/**Функция рисующая все спрайты и фон*/
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Iterator<Bullet> j = ball.iterator();
while(j.hasNext()) {
Bullet b = j.next();
if(b.x >= 1000 || b.x <= 1000) {
b.onDraw(canvas);
} else {
j.remove();
}
}
canvas.drawBitmap(player, 5, 120, null);
}
А для того что бы пули начали вылетать при нажатии на экран, нужно создать метод createSprites(); который будет возвращать наш спрайт.
GameView.java
public Bullet createSprite(int resouce) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);
return new Bullet(this, bmp);
}
Ну и в конце концов создаем еще один метод — onTouch(); который собственно будет отлавливать все касания по экрану и устремлять пулю в ту точку где было нажатия на экран.
GameView.java
public boolean onTouchEvent(MotionEvent e)
{
shotX = (int) e.getX();
shotY = (int) e.getY();
if(e.getAction() == MotionEvent.ACTION_DOWN)
ball.add(createSprite(R.drawable.bullet));
return true;
}
Если хотите сделать что бы нажатие обрабатывалось не единоразово, т.е. 1 нажатие — 1 пуля, а 1 нажатие — и пока не отпустишь оно будет стрелять, нужно удалить if(e.getAction() == MotionEvent.ACTION_DOWN) { }
и оставить только ball.add(createSprite(R.drawable.bullet));.
Все, запускаем нашу игру и пробуем стрелять. Должно выйти вот такое:
Враги
Для того что бы нам не было скучно играться, нужно создать врагов. Для этого нам придется создать еще один класс который будет называться Enemy.java и который будет уметь отображать и направлять нашего врага на нашу базу. Класс довольно простой по этому смотрим код ниже:
Enemy.java
import java.util.Random;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
public class Enemy {
/**Х и У коорданаты*/
public int x;
public int y;
/**Скорость*/
public int speed;
/**Выосота и ширина спрайта*/
public int width;
public int height;
public GameView gameView;
public Bitmap bmp;
/**Конструктор класса*/
public Enemy(GameView gameView, Bitmap bmp){
this.gameView = gameView;
this.bmp = bmp;
Random rnd = new Random();
this.x = 900;
this.y = rnd.nextInt(300);
this.speed = rnd.nextInt(10);
this.width = 9;
this.height = 8;
}
public void update(){
x -= speed;
}
public void onDraw(Canvas c){
update();
c.drawBitmap(bmp, x, y, null);
}
}
И так что происходит в этом классе? Рассказываю: мы объявили жизненно важные переменные для нашего врага, высота ширина и координаты. Для размещения их на сцене я использовал класс Random() для того что бы когда они будут появляться на сцене, появлялись на все в одной точке, а в разных точках и на разных координатах. Скорость так же является у нас рандомной что бы каждый враг шел с разной скоростью, скорость у нас начинается с 0 и заканчивается 10, 10 — максимальная скорость которой может достигнуть враг. Двигаться они будут с права налево, для того что бы они не были сразу видны на сцене я закинул их на 900 пикселей за видимость экрана. Так что пока они дойдут можно уже будет подготовиться по полной к атаке.
Дальше нам нужно отобразить врага на сцене, для этого в классе GameView.java делаем следующее:
Создаем список врагов для того что бы они никогда не заканчивались и создаем битмап который будет содержать спрайт:
Шапка GameView
private List<Enemy> enemy = new ArrayList<Enemy>();
Bitmap enemies;
Далее создаем новый поток для задания скорости появления врагов на экране:
Шапка GameView
private Thread thred = new Thread(this);
И имплементируем класс Runuble, вот как должна выглядеть инициализация класса GameView:
public class GameView extends SurfaceView implements Runnable
Теперь у Вас еклипс требует создать метод run(), создайте его, он будет иметь следующий вид:
В самом низу класса GameView
public void run() {
while(true) {
Random rnd = new Random();
try {
Thread.sleep(rnd.nextInt(2000));
enemy.add(new Enemy(this, enemies));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Здесь мы создаем поток который будет создавать спрайт от 0 до 2000 милисекунд или каждые 0, 1 или 2 секунды.
Теперь в конструкторе в самом конце пишем инициализируем наш спрайт с классом для отображения на сцене:
Конструктор GameView
enemies = BitmapFactory.decodeResource(getResources(), R.drawable.target);
enemy.add(new Enemy(this, enemies));
Ну и конечно же нам нужно объявить эти методы в onDraw(); Вот значит и пишем в нем следующее:
Метод onDraw() в GameView
Iterator<Enemy> i = enemy.iterator();
while(i.hasNext()) {
Enemy e = i.next();
if(e.x >= 1000 || e.x <= 1000) {
e.onDraw(canvas);
} else {
i.remove();
}
}
Снова проходим по коллекции врагов с помощью итератора и проверяем — если враг зашел за предел в 1000 пикселей — удаляем его, так как если мы не будем удалять у нас пямять закакается и телефон зависнет, а нам такие проблемы не нужны. Все игра готова для запуска.
Запускаем нашу игру и что мы увидим? А вот что:
Но что я вижу? О нет!!! Пули никак не убивают наших призраков что же делать? А я Вам скажу что делать, нам нужно создать метод который будет образовывать вокруг каждого спрайта — прямоугольник и будет сравнивать их на коллизии. Следующая тема будет об этом.
Обнаружение столкновений
И так, у нас есть спрайт, у нас есть сцена, у нас все это даже движется красиво, но какая польза от всего этого когда у нас на сцене ничего не происходит кроме хождения туда сюда этих спрайтов? :)
С этой функцией я навозился по полной, даже как-то так выходило что психовал и уходил гулять по улице)) Самый трудный метод, хотя выглядеть совершенно безобидно…
Ладно, давайте уже создадим этот метод и не будем много разглагольствовать… Где то в конце класса GameView создаем метод testCollision() и пишем следующий код:
В самом низу класса GameView.java
/*Проверка на столкновения*/
private void testCollision() {
Iterator<Bullet> b = ball.iterator();
while(b.hasNext()) {
Bullet balls = b.next();
Iterator<Enemy> i = enemy.iterator();
while(i.hasNext()) {
Enemy enemies = i.next();
if ((Math.abs(balls.x - enemies.x) <= (balls.width + enemies.width) / 2f)
&& (Math.abs(balls.y - enemies.y) <= (balls.height + enemies.height) / 2f)) {
i.remove();
b.remove();
}
}
}
}
И так, что у нас происходит в этом методе? Мы создаем один итератор и запускаем цикл для просмотра всей коллекции спрайтов, и говорим что каждый следующий спрайт пули будет первым.
Дальше создаем еще один итератор с другим списком спрайтов и снова переопределяем и говорим что каждый следующий спрайт врага будет первым. И создаем оператор ветвления — if() который собственно и проверяет на столкновения наши спрайты. В нем я использовал математическую функцию модуль (abs) которая возвращает мне абсолютное целое от двух прямоугольников.
Внутри ифа происходит сравнения двух прямоугольников Модуль от (Пуля по координате Х минус координата врага по координате Х меньше либо равен ширина пули плюс ширина врага / 2 (делим на два для нахождения центра прямоугольника)) и (Модуль от (Пуля по координате У минус координата врага по координате У меньше либо равен ширина пули плюс ширина врага / 2 (делим на два для нахождения центра прямоугольника)));
И в конце всего, если пуля таки достала до врага — мы удаляем его со сцены с концами.
Ну и для того что бы эта функция стала работать записываем её в метод run() который находится в классе GameThread, ниже нашего метода рисования onDraw(). И в surfaceCreated() добавляем thred.start() для запуска отрисовки объектов.
Вот что у нас получается после запуска приложения:
не работает(((
ОтветитьУдалитьвыдает ошибку
The aplication Def(process Game.main)has stopped unexpectedly.Try again.
Def(process Game.main) у Вас - towe.def
ответил на имейл
УдалитьСпасибо за статью, на днях попробую все набрать и протестировать.
ОтветитьУдалитьЗаметил что что-то не так в столкновениях. Допустим возьмем координаты пули по х, левая = 20, правая = 30, и врага, левая = 31, правая = 51. То получится по формуле |20 - 31| <= (10 + 20)/2 выходит 11 <= 15, тоесть столкновение есть, хотя на самом деле его нету. В других примерах я видел используют класс Rect там есть функция intersects которая определяет пересекаются ли квадраты.
В расчетах все правильно, если и есть погрешность то очень маленькая, а по поводу intersects, этой функции в android SDK нет, по этмоу пришлось изгаляться.
Удалитьпочему нинзя такой толстый?)) а вообще спасибо тебе, добрый человек
ОтветитьУдалитьСильно много пива пьют) И на онлайн игры подсели. Пожалуйста :)
УдалитьЕсли свернуть нажав на хоме и потом опять на него , вылетает с ошибкой
ОтветитьУдалитьЭту проблему не учел, как будет время напишу костыль что бы не было ошибок. Ну или перепишу так что бы все запускалось :)
УдалитьБольше всего интересует колизия(столкновения итд) Даже на последнем видео видно, шарик пролетает под юпкой призрака. Но в любом случае гг , это замечательно.
ОтветитьУдалитьЭто из за того что размер я не отрегулиловал, если поставить чуть больше размер квадрата у врага то будет нормально убивать.
УдалитьУжас, руки отбивать таким программерам-костылеписателям
ОтветитьУдалитьПыСы написать функцию определяющую пересечение прямоугольников - дело 10 минут, причем сдесь SDK?
Отбивайте, мне не жалко.
УдалитьЧто графика и идея потырена с cocos2d-x.org можно было и упомянуть
ОтветитьУдалитьИзвините конечно но какая разница какую я идею реализовываю? Разве я на этом зарабатываю? Я написал пособие по разработке, а чья идея уже не имеет значения.
УдалитьМожет стоило бы "наплодить сущностей" и все же создать класс спрайтов, от которого уже наследовать пули, врагов и тд. ?
ОтветитьУдалитьСоздать базовый класс от которого будет наследоваться все спрайты? Там проблемы с столкновениями тогда были бы. Попробуй сделать так и посмотри что будет когда два объекта столкнуться.
УдалитьДа вообще при столкновении спрайтов их можно уничтожать, я думаю) Учитывая что игрок то у нас статичный, он вообще может быть элементом фона, который мы обновляем каждый раз) Но это уже не хорошо, есть такое дело
УдалитьИ еще, Глеб, как можно с Вами связаться по поводу вопросов регистрации на маркете и монетизации приложений?
ОтветитьУдалить398782127 icq
Удалитьdajver4 скайп
на базе твеой игры сделал игру. спасибо, Мужик))
ОтветитьУдалитьты, кстати, andEngine не используешь?
Пожалуйста (:
УдалитьandAngine хотел начать учить, но прыгнул на openGL и все, теперь юзаю эту платформу)
Очень нужна ваша помощь по поводу этой игры. Как можно с вами связатся?
ОтветитьУдалитьфункция intersects есть :) это же базовая функция)
ОтветитьУдалитьRect.intersects(Rect a, Rect b)
Rect - это android.graphics.Rect
А вообще, мой совет - это сразу брать какой-нибудь движок и делать игру на нем, потому что он как раз лишает тебя возни с низкоуровневым кодом и остается только реализовать саму логику.
Великолепные Angry Birds сделаны на простом движке Box2D. У него подробный мануал и кроме того есть библиотеки не только на Java, но и на других языках - и это очень удобно в том случае, если захочется вдруг поменять платформу (например, перелезть с андроида на айфон или виндоузфон)
Ну, Box2D это лишь пример.
Функция есть, но она мне не подходила, вот я свою и сделал) А по поводу движка, соглашусь (: Вещь очень удобная, все делается в раз пять быстрее, но я это как пример писал, в дальнейшем будет время напишу игру на боксе, или на опенГЛ, как карта ляжет так и сделаю)
Удалитьв упор не понимаю строчки (e.x >= 1000 || e.x <= 1000) в onDraw() это условие ведь должно всегда выполняться?
ОтветитьУдалитьТам ошибка, все вы правильно поняли, нада поменять местами условия.
Удалитьможете сказать какая у вас версия eclipse у меня стоит sdk eclipse с adt от сюда http://developer.android.com/sdk/index.html
ОтветитьУдалитьи у меня нет android proect а на эти строки показывает ошибки
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
подскажите пожалуйста что делать
Как понять - "и у меня нет android proect "?
УдалитьПо поводу строк сделайте Project - Clean, и если не помогло тогда впишите в манифест:
в настройках активити
android:screenOrientation="landscape"
android:configChanges="keyboardHidden|orientation|screenSize"
ну а в каком делать у меня есть seampelproject applicaionproject и testpoject и ещё
Удалитьчто значит Запускаем Eclipse — File — Android Project — Defens — Main.java.
Это значит что вам нужно запустить Eclipse, дальше нажать в меню вверху раздел File в нем выбрать Create android project и дальше создать проект с именем Defens и с файлом Main.java
Удалитьprivate List ball = new ArrayList(); private Player player;
ОтветитьУдалитьBitmap players;
GameView.java — Конструктор GameView
players= BitmapFactory.decodeResource(getResources(), R.drawable.player2);
player= new Player(this, guns);
GameView,java
/**Функция рисующая все спрайты и фон*/
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
Iterator j = ball.iterator();
while(j.hasNext()) {
Bullet b = j.next();
if(b.x >= 1000 || b.x <= 1000) {
b.onDraw(canvas);
} else {
j.remove();
}
}
canvas.drawBitmap(player, 5, 120, null);
}
GameView.java
public Bullet createSprite(int resouce) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);
return new Bullet(this, bmp);
}
GameView.java
public boolean onTouchEvent(MotionEvent e)
{
shotX = (int) e.getX();
shotY = (int) e.getY();
if(e.getAction() == MotionEvent.ACTION_DOWN)
ball.add(createSprite(R.drawable.bullet));
return true;
а куда это правильно вставлять
а то не очень понятно
там в самом конце статьи есть исходный код, скачайте и посмотрите, тут вам объяснить я никак не смогу, сильно много вопросов.
Удалитьа можете дать ссылку на то как открыть исходники а то по тому что я нашёл в интернете он у меня открывается с ошибкой
Удалитьhttp://dl.dropbox.com/u/61086119/TowerDefense.rar
Удалитьмне не ссылку на исходники мне как их открыть в eclipse
ОтветитьУдалитьа то у меня hellow world вылазит и ничего не могу с этим поделать
File->Import->General->Existing Projects into Workspace, Next
УдалитьSelect root directory: /path/to/project
Projects->Select All
UNCHECK both "Copy projects into workspace" and "Add project to working sets"
Finish
спасибо разобрался
Удалитьно есть ещё три вопроса
как поставить картинку на фон
как добавить графический элемент счёта и запоминания рекордов
и как добавить меню к приложению из исходников
если сможете пожалуйста ответьте на nikfominyh@mail.ru
спасибо большое вам без вас бы не разобрался и ещё а вы когда нибудь писали большие игры
ОтветитьУдалитьГлеб, здравствуйте! отличный урок! все понятно! попробовал по пунктам все прописать сам и в эмуляторе работает на ура, НО на устройстве устанавливается и при попытке запустить - вылетает ошибка. Я так подозреваю ошибка где-то в потоке - но хз где подскажите... ваш исходник работает - но различия кода исходника и то что написал не вижу. В чем может быть дело?
ОтветитьУдалитьЗдравствуйте. Скорей всего из за различий версий андроида на устройстве и в эмуляторе, других идей у меня нет.
УдалитьНе могли бы вы описать как будет выглядеть функция отслеживания столкновений, если бы нужно было отслеживать столкновение Player and Enemy?
ОтветитьУдалитьПримерно вот так вот http://pastebin.com/pzrtppp7
УдалитьСпасибо за метод, но он не помог, столкновения не происходят =\
УдалитьДолжно срабатывать так как я этот способ использовал при написании игры, там как раз все так же написано и работает на ура.
УдалитьВам нужно столкновение с одним енеми или с множеством?
Их множество, суть игры в том, что плеер должен ловить этих самых Enemy, но вот столкновения почему то не выходят.
Удалитьpublic void testCollision(){
Iterator i = drop.iterator(); //Это допустим Enemy
while(i.hasNext()){
Drops drops = i.next();
if(Math.abs(drops.x-player.x)<=(drops.width + player.width)/2f
&& Math.abs(drops.y - player.y) <= (drops.height +
drops.height)/2f){
i.remove();
}
}
}
Игра сама, что то наподобие "Волк и яйца".
А откуда бралась высота и ширина спрайтов?
ОтветитьУдалитьПросто размеры самих изображений и размеры в программе (width/height) отличаются.
Собственно сама высота и ширина картинки, размеры картинки всегда колеблятся от размера экрана, так что конечно же размеры всегда будут разные.
УдалитьЕсть ещё один вопрос, о том где и как был объявлен "p - player"
ОтветитьУдалитьдля этого метода http://pastebin.com/pzrtppp7
private final Player p; в самом верху кода.
УдалитьСпасибо за ответы!
ОтветитьУдалитьА чем именно вам не подошёл метод intersect(Rect) и что работало не так?
Просто по вашему методу у меня так и ничего не получилось, пытаюсь с помощью стандартных функций(intersect) реализовать столкновения, но здесь тоже "провал" .
Эта функция у меня не работала, и не хотела проверять на столкновения по этому я решил написать свою) Такой вариант оказался намного проще.
УдалитьНе могли бы вы посмотреть мой код, и может вы найдёте, почему не работают столкновения, собствено игру я писал по вашей статье, по этому код особо не отличается, но всё же никак не могу сделать столкновения, был бы очень признателен если бы помогли.
УдалитьУвы нет времени на просмотры и отлаживание чужого кода, но я могу посоветовать создать тестовый проект в котором создайте один объект типа плеер, а во втором спауните врагов, собственно скачайте проект в конце статьи и посмотрите как реализовано в нем, может тогда поймете как оно работает. Сорри что не могу посмотреть код но на самом деле проблемно с временем.
УдалитьИ на том большое спасибо, собственно по вашей статье пишу дипломный проект, и вы во многом помогли, огромное спасибо!
УдалитьПожалуйста, рад помочь (:
УдалитьНе могли бы вы подсказать как осуществить вывод очков на экран и возможно как сохранять их?
ОтветитьУдалитьНу или подкиньте какие то статьи по этому поводу.
Был бы очень признателен!)
А вот цикл в цикле - это разве правильное решение? Более оптимального нет? например если я хочу что бы снаряд при столкновении взрывался и задевал цели вокруг - надо делать еще один цикл проверки?
ОтветитьУдалитьМожно ли как то сделать, что бы например появлялось 3 разных вида духов?
ОтветитьУдалитьИ как приблизительно это реализовать?