Статьи

Разработка игр для Android — Particle Explosion

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

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

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

Для простоты мы создадим несколько частиц, поместим их в одно место (начало координат) и дадим им случайные силы. Сила — это векторная величина. Это означает, что оно имеет величину и направление. Величина будет определять его скорость, а направление будет указывать частице, в какую сторону двигаться.

Частица

Файл класса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class Particle {
 
    public static final int STATE_ALIVE = 0;    // particle is alive
    public static final int STATE_DEAD = 1;     // particle is dead
 
    public static final int DEFAULT_LIFETIME    = 200// play with this
    public static final int MAX_DIMENSION       = 5;    // the maximum width or height
    public static final int MAX_SPEED           = 10;   // maximum speed (per update)
 
    private int state;          // particle is alive or dead
    private float width;        // width of the particle
    private float height;       // height of the particle
    private float x, y;         // horizontal and vertical position
    private double xv, yv;      // vertical and horizontal velocity
    private int age;            // current age of the particle
    private int lifetime;       // particle dies when it reaches this value
    private int color;          // the color of the particle
    private Paint paint;        // internal use to avoid instantiation
}

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

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

У него есть позиция . Его положение в 2D системе координат представлено двумя точками: x и y .

У этого также есть скорость и направление. Как вы помните, скорость — это вектор, поэтому она имеет 2 компонента в 2D. В 3D он также будет иметь компонент z, но пока мы остаемся в 2D. Для простоты теперь добавим два свойства для этого. VX и VY

Возраст частицы в начале равен 0 и увеличивается с каждым обновлением.

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

Остальные цвета и краски . Это только для рисования.

Если вы помните предыдущие записи, обновление игры — это не что иное, как вызов методов обновления всех сущностей в игре и их отображение. Метод обновления частицы довольно прост.

Но сначала нам нужно создать частицу:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public Particle(int x, int y) {
    this.x = x;
    this.y = y;
    this.state = Particle.STATE_ALIVE;
    this.widht = rndInt(1, MAX_DIMENSION);
    this.height = this.widht;
    this.lifetime = DEFAULT_LIFETIME;
    this.age = 0;
    this.xv = (rndDbl(0, MAX_SPEED * 2) - MAX_SPEED);
    this.yv = (rndDbl(0, MAX_SPEED * 2) - MAX_SPEED);
    // smoothing out the diagonal speed
    if (xv * xv + yv * yv > MAX_SPEED * MAX_SPEED) {
        xv *= 0.7;
        yv *= 0.7;
    }
    this.color = Color.argb(255, rndInt(0, 255), rndInt(0, 255), rndInt(0, 255));
    this.paint = new Paint(this.color);
}

Проверьте создание частицы, и она должна быть прямой.

Вы заметите, что частица создана в позиции x , y .

Государство установлено живым .
Мы хотим рандомизировать размер прямоугольников, потому что взрыв создает частицы разных размеров и форм, но мы просто рандомизируем размер и цвет.
Я написал несколько вспомогательных методов, которые дают мне случайные числа, для этого проверьте полный исходный код.

Далее устанавливается время жизни . Каждая частица будет иметь одинаковое время жизни.

Конечно, возраст равен 0, так как частица только что родилась.

Далее интересный момент. Это очень любительское. Чтобы установить скорость, я использовал 2 случайных числа для 2 компонентов вектора скорости ( vx и vy ). Сглаживание необходимо, потому что, если оба компонента близки к максимальному значению, результирующая величина будет выше максимальной скорости. Вместо этого вы можете использовать простые тригонометрические функции со случайной степенью.

Последнее, что нужно установить, это цвет, который снова является случайным.

Там у вас есть это.

Метод update () для частицы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public void update() {
    if (this.state != STATE_DEAD) {
        this.x += this.xv;
        this.y += this.yv;
 
        // extract alpha
        int a = this.color >>> 24;
        a -= 2; // fade by 2
        if (a <= 0) { // if reached transparency kill the particle
            this.state = STATE_DEAD;
        } else {
            this.color = (this.color & 0x00ffffff) + (a << 24);       // set the new alpha
            this.paint.setAlpha(a);
            this.age++; // increase the age of the particle
        }
        if (this.age >= this.lifetime) { // reached the end if its life
            this.state = STATE_DEAD;
        }
    }
}

Это довольно просто. При каждом обновлении позиция устанавливается в соответствии со скоростью, а альфа-компонент цвета частицы уменьшается. Другими словами, частица исчезает.

Если возраст превысил время жизни или непрозрачность равна 0 (это означает, что она полностью прозрачна), частица объявляется мертвой.

Если вас интересует магия с цветами, это довольно просто, если вы получите побитовые операторы. Не волнуйтесь, я тоже мусор, просто убедитесь, что вы знаете, где искать. Вот хорошее объяснение компонентов цвета и как использовать побитовые операторы для управления ими: http://lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math/ . Это быстрее, чем использование объектов, но вы также можете безопасно использовать методы Android.

Просто как примечание к цветам
Вы можете указать цвета в Android как int. Если вы знакомы с rgb и argb, это здорово.
rgb — это 24-битный цвет, а argb — 32-битный. У этого также есть альфа-компонент, который является прозрачностью / непрозрачностью.

Значения непрозрачности: 0 = прозрачный, 255 = полностью непрозрачный.

Для представления целого числа в шестнадцатеричном виде вы просто ставите перед ним префикс 0x . Цвет в шестнадцатеричном формате прост: например, 0x00FF00 — зеленый. Шаблон: 0xRRGGBB (красный, зеленый, синий). Теперь, чтобы добавить альфа, вы добавите его в начало. 0xAARRGGBB .
Поскольку это в шестнадцатеричном, значения между 00 и FF. 0 — 0, а FF — 255 в десятичном виде.
Когда вы создаете цвет из таких компонентов, как цвет (a, r, g, b) (например, новый цвет (125, 255, 0, 0) создает полупрозрачный красный), вы можете просто создать его с целым числом, выраженным в шестнадцатеричном виде вот так: новый цвет (0x80FF0000);

Вот как вы должны извлечь компоненты цвета argb.

1
2
3
4
5
int color = 0xff336699;
int alpha = color >>> 24;
int red   = color >>> 16 & 0xFF;
int green = color >>>  8 & 0xFF;
int blue  = color & 0xFF;

Метод draw () снова прост.

1
2
3
4
public void draw(Canvas canvas) {
    paint.setColor(this.color);
    canvas.drawRect(this.x, this.y, this.x + this.widht, this.y + this.height, paint);
}

На этом этапе попробуйте создать несколько частиц на игровой панели и посмотреть, что произойдет.

Взрыв

Взрыв — это не что иное, как сотни частиц, происходящих из одного места, источника.

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

Основными свойствами взрыва являются:

01
02
03
04
05
06
07
08
09
10
public class Explosion {
 
    public static final int STATE_ALIVE     = 0;    // at least 1 particle is alive
    public static final int STATE_DEAD      = 1;    // all particles are dead
 
    private Particle[] particles;           // particles in the explosion
    private int x, y;                       // the explosion's origin
    private int size;                       // number of particles
    private int state;                      // whether it's still active or not
}

Он содержит массив частиц. Размер — это количество частиц. Взрыв жив, если в нем есть хотя бы одна частица.

Обновление предельно просто. Он перебирает все частицы и вызывает метод update () для каждой частицы. Ничья () тоже самое.

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

01
02
03
04
05
06
07
08
09
10
public Explosion(int particleNr, int x, int y) {
    Log.d(TAG, "Explosion created at " + x + "," + y);
    this.state = STATE_ALIVE;
    this.particles = new Particle[particleNr];
    for (int i = 0; i < this.particles.length; i++) {
        Particle p = new Particle(x, y);
        this.particles[i] = p;
    }
    this.size = particleNr;
}

Массив частиц заполняется в позиции касания.

В нашем приложении мы допустим до 10 взрывов. Поэтому в MainGamePanel мы объявляем массив взрывов.

1
private Explosion[] explosions;

В методе surfaceCreated мы создаем экземпляр массива и заполняем его нулем .

1
2
3
4
explosions = new Explosion[10];
for (int i = 0; i < explosions.length; i++) {
    explosions[i] = null;
}

На onTouchEvent мы создаем взрывы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        // check if explosion is null or if it is still active
        int currentExplosion = 0;
        Explosion explosion = explosions[currentExplosion];
        while (explosion != null && explosion.isAlive() && currentExplosion < explosions.length) {
            currentExplosion++;
            explosion = explosions[currentExplosion];
        }
        if (explosion == null || explosion.isDead()) {
            explosion = new Explosion(EXPLOSION_SIZE, (int)event.getX(), (int)event.getY());
            explosions[currentExplosion] = explosion;
        }
    }
    return true;
}

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

Методы обновления и рендеринга просты. Выполните итерацию по взрывам, и если они не равны NULL и живы, то вызовите их методы update и draw соответственно

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

Это должно выглядеть так:

Изучите код и получайте удовольствие.

Загрузите его здесь ( android.particles.tgz ).

Ссылка: Particle Explosion с Android от нашего партнера JCG Тамаса Яно из блога « Против зерна ».

Не забудьте проверить нашу новую Android игру ArkDroid (скриншоты ниже) . Ваш отзыв будет более чем полезным!
Статьи по Теме: