Как я могу сделать расширяющиеся фигуры пули?

12

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

https://youtu.be/7JGcuTWYdvU?t=2m41s

лептон
источник
2
О, это хороший вопрос. У меня нет конкретных ответов, но я думаю, что вы могли бы использовать 2D-объект, либо спрайт, либо простую форму, и создавать пули вдоль края. Конечно, уловка будет давать им правильную скорость, как по форме, так и наружу, и если вы будете делать такой скроллер, чтобы они двигались вперед по экрану. Очень интересно увидеть какие-либо ответы здесь.
Джесси Уильямс
1
Популярное название для такого рода эффекта - «эффекты частиц». Этот поисковый термин может вам помочь!
Корт Аммон
1
Спасибо, я уже давно использую эффекты частиц в XNA и libGDX, но не был уверен, как справиться с этим конкретным стилем эффекта.
лептон
1
Есть еще один ответ, который невероятно мощный, но очень сложный для программирования. И нужна настоящая клавиатура, чтобы напечатать. Добавьте это в закладки для дальнейшего объяснения.
Draco18s больше не доверяет SE
Интересно - я бы никогда не пошел с эффектами частиц для чего-то подобного. Или, может быть, это просто разграничение в Unity. В то время как эффекты частиц могут иметь коллайдеры (таким образом повреждая объект), кажется, что это создаст намного больше накладных расходов, чем простое создание экземпляров объекта.
Джесси Уильямс

Ответы:

11

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

Начните с проектирования вашей фигуры как относительного положения вокруг некоторой исходной точки.

площадь

Теперь вам нужно рассчитать, как форма будет расширяться. Чтобы сделать это, мы просто вычисляем вектор, указывающий от на originк каждому point, вычитая originположение из нашего pointположения и затем нормализуя вектор. vector = normalize(point.x - origin.x, point.y - origin.y),

вектор

Теперь мы можем рассчитать положение точек в любой момент времени, используя этот вектор. Вы рассчитываете следующую позицию очков, делая point.position += point.vector * point.velocity. Пример псевдокода с использованием нашего предыдущего пункта:

// When you start your program you set these values.
point.position = (-3, 3); // Start position. Can be anything.
point.vector = normalize(-3, 3); // Normalized vector.
point.velocity = 3; // Can be anything.

// You do this calculation every frame.
point.position += point.vector * point.velocity;
// point.vector * point.velocity = (-3, 3)
// point.position is now (-6, 6) since (-3, 3) + (-3, 3) = (-6, 6)

При этом все точки будут перемещаться наружу на 3 единицы в каждом кадре.


Примечания

  • Вы можете прочитать некоторые простые векторные математики здесь .
  • Позиция может быть любой, пока все позиции относительно некоторой исходной точки.
  • Скорость всех точек должна быть одинаковой, чтобы обеспечить равномерное движение, но наличие разных скоростей может дать вам интересные результаты.
  • Если движение кажется выключенным, вы должны проверить исходную точку. Если он не находится точно в середине фигуры, форма может расширяться странным образом.
Charanor
источник
9
Я просто хочу отметить, что скорость каждой частицы должна быть пропорциональна расстоянию от начала координат в первом кадре (то есть рассчитывать только один раз, а не на кадр). В качестве альтернативы, вы можете просто не нормализовать вектор направления. Если вы этого не сделаете, форма не будет линейно масштабироваться, а будет двигаться в направлении круга (если все скорости одинаковы).
Аарон,
@Charanor Большое спасибо за объяснение. На самом деле я изучал дискретную математику в университете, но это было довольно давно. Я собираюсь попытаться реализовать что-то сегодня.
лептон
2

Итак, существует проект под названием BulletML, который является языком разметки для создания сложных паттернов частиц / пуль. Вам почти наверняка понадобится перенести код на свой собственный язык, но он может сделать действительно удивительные вещи.

Например, этот босс был сделан в (сильно модифицированном) расширении BulletML для Unity3D (автор этого паттерна загрузил это видео и Misery безумно, а также хорошо 1 ). Это самый сложный вариант этого врага, и он показывает, на что BulletML способен довольно хорошо (и, кроме того, посмотрите на других боссов Мизери, таких как Wallmaster ).

Или я могу показать этот пример, который является шаблоном, который я написал, работая над расширением для The Last Federation , используя более старую версию системы, которая менее дружественна к модам и использует только односимвольные переменные AZ:

Пример пули

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

Вот часть синтаксиса XML, которая создает эти пузыри:

<bullet_pattern name="Barrier">
    $WallShotAngle B=.3 A=90
    $WallShotAngle B=.3 A=-90
    $WallShotAngle B=.3 A=0
    $WallShotAngle B=.375 A=180
</bullet_pattern>

<var name="WallShotAngle">
    <bullet angle="[A]" speed="4000" interval_mult=".01" dumbfire="1" shot_type="GravityWavePurple">
        <wait time="[B]" />
        <change angle="0" speed="1000" time=".0001" />
        <spawn>
            <bullet_pattern>
                <bullet angle="[A]" speed="0" shot_type="CurveBarGreen" damage_mult="8">
                <wait time="12" />
                <die />
                </bullet>
            </bullet_pattern>
        </spawn>
        <die />
    </bullet>
</var>

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

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

С BulletML легко работать, и он может делать удивительные вещи. Пули могут изменять направление, изменять скорость, порождать другие шаблоны, рано умирать, повторять сбор команд в цикле, использовать задержки, менять изображение спрайта пули, следовать за своим родителем (или нет) ... И все, что оно не поддерживает, вы могли бы напиши в него.

Я определенно рекомендую это, если вы делаете серьезную игру в жанре «стреляй» Вам все равно придется потренироваться в математической системе координат, чтобы получить желаемые фигуры, о чем Чаранор говорит в своем ответе, но пули, такие как BulletML, дадут вам гораздо больше гибкости, что вы потратите больше времени на разработку новых шаблонов, чем на выяснение как их кодировать

  1. Чтобы объяснить, насколько хорош Misery, эти ролики направлены против половых боссов со стартовым оборудованием : без модулей, без расходных материалов и базового стрелка из гороха. И хе получает только один удар, несмотря на удлиненный характер боя. Хорошо, 9 ударов по центрифуге (который не появится до третьего этажа после того, как игрок определенно получит улучшения, в результате чего будет сравнительно увеличен как минимум двойной урон).
Draco18s больше не доверяет SE
источник
Спасибо, я смутно знал о BulletML, так как он существует уже некоторое время, но это определенно излишне для моей простой игры, которая лишь изредка балуется пулями и не является стрелком из пули.
лептон
@lepton Совершенно понятно. Это решение для вас, но ответ может быть «лучшим» для кого-то еще. Я знаю, что после работы над TLF и начала создавать свой собственный шутер, я хотел использовать его только из-за того, насколько мощным и простым он был для работы. :)
Draco18s больше не доверяет SE
1

Как указывает Charanor, вы можете использовать массив точек, чтобы определить свою форму, а затем обновить их положение с течением времени. Ниже приведен рабочий пример реализации формы звезды или пользовательской формы с использованием точек:

package com.mygdx.gtest;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;

public class Test extends ApplicationAdapter{

    public SpriteBatch sb;
    private StarShape ss, ssBig;

    @Override
    public void create() {
        sb = new SpriteBatch();
        Pixmap pmap = new Pixmap(2, 2,Format.RGBA8888);
        pmap.setColor(Color.WHITE);
        pmap.fill();
        ss = new StarShape(50,50,new Texture(pmap), 10, true);
        ssBig = new StarShape(250,250,new Texture(pmap), 50, false);
        pmap.dispose();

    }


    @Override
    public void render() {
        super.render();

        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        ss.update(Gdx.graphics.getDeltaTime());
        ssBig.update(Gdx.graphics.getDeltaTime());

        sb.begin();
            ss.draw(sb);
            ssBig.draw(sb);
        sb.end();

    }


    @Override
    public void dispose() {
        super.dispose();
    }

    private class StarShape{
        public float progress = 1f;
        public Texture bulletTex;
        public Array<Vector2> points = new Array<Vector2>();
        public Vector2 center;

        public StarShape(float x, float y, Texture tex, float initialSize, boolean mathWay){
            center = new Vector2(x,y);
            bulletTex = tex;

            if(mathWay){
                // define star shape with maths
                float alpha = (float)(2 * Math.PI) / 10; 
                float radius = initialSize;

                for(int i = 11; i != 0; i--){
                    float r = radius*(i % 2 + 1)/2;
                    float omega = alpha * i;
                    points.add(
                            new Vector2(
                                    (float)(r * Math.sin(omega)), 
                                    (float)(r * Math.cos(omega)) 
                                )
                            );
                }
            }else{
            // or define star shape manually (better for non geometric shapes etc

                //define circle
                points.add(new Vector2(-3f,0f));
                points.add(new Vector2(-2.8f,1f));
                points.add(new Vector2(-2.2f,2.2f));
                points.add(new Vector2(-1f,2.8f));
                points.add(new Vector2(0f,3f));
                points.add(new Vector2(1f,2.8f));
                points.add(new Vector2(2.2f,2.2f));
                points.add(new Vector2(2.8f,1f));
                points.add(new Vector2(3f,0f));
                points.add(new Vector2(2.8f,-1f));
                points.add(new Vector2(2.2f,-2.2f));
                points.add(new Vector2(1f,-2.8f));
                points.add(new Vector2(0f,-3f));
                points.add(new Vector2(-1f,-2.8f));
                points.add(new Vector2(-2.2f,-2.2f));
                points.add(new Vector2(-2.8f,-1f));

                // mouth
                points.add(new Vector2(-2,-1));
                points.add(new Vector2(-1,-1));
                points.add(new Vector2(0,-1));
                points.add(new Vector2(1,-1));
                points.add(new Vector2(2,-1));
                points.add(new Vector2(-1.5f,-1.1f));
                points.add(new Vector2(-1,-2));
                points.add(new Vector2(0,-2.2f));
                points.add(new Vector2(1,-2));
                points.add(new Vector2(1.5f,-1.1f));

                points.add(new Vector2(-1.5f,1.5f));
                points.add(new Vector2(1.5f,1.5f));

            }

        }

        public void update(float deltaTime){
            this.progress+= deltaTime;
        }

        public void draw(SpriteBatch sb){
            Vector2 temp = new Vector2(0,0);
            for(Vector2 point: points){
                temp.x = (point.x);
                temp.y = (point.y);
                temp.scl(progress);
                sb.draw(bulletTex,temp.x + center.x,temp.y +center.y);
            }
        }
    }
}
dfour
источник
Большое спасибо за пример, я собираюсь проверить это сегодня днем, чтобы посмотреть, смогу ли я его запустить и запустить.
лептон