Як я можу зробити фігури, що розширюються кулями?


12

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

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


2
О, це гарне питання. У мене немає конкретних відповідей, але я б уявив, що ви можете використовувати 2D-об'єкт, спрайт або просту форму, і покласти кулі по краю. Звичайно, хитрість полягає в наданні їм належної швидкості, як зовні, так і за формою, і якщо ви робите такий скроллер, щоб змусити їх рухатися вперед екраном. Дуже цікаво побачити будь-які відповіді тут.
Джессі Вільямс

1
Популярна назва такого ефекту - "ефекти частинок". Цей пошуковий термін може вам допомогти!
Корт Аммон

1
Дякую, я використовую ефекти частинок у XNA та libGDX вже досить давно, але не знаю, як впоратися з цим стилем ефекту.
лептон

1
На це є ще одна відповідь, яка неймовірно потужна, але дуже складна для програмування. І потрібна справжня клавіатура для набору. Закладка цього для подальшого пояснення.
Draco18s більше не довіряє SE

Цікаво - я ніколи не пішов би з ефектами частинок для чогось подібного. А може, це просто розмежування в Єдності. У той час як ефекти частинок можуть мати сутички (тим самим пошкоджуючи об’єкт), схоже, це створило б набагато більше накладних витрат, ніж просто копіювання об'єктів.
Джессі Вільямс

Відповіді:


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 одиниці кожного кадру.


Примітки

  • Ви можете прочитати деякі прості векторні математики тут .
  • Позиція може бути будь-якою, доки всі позиції відносно якоїсь точки початку.
  • Швидкість всіх точок повинна бути однаковою для забезпечення рівномірного руху, але наявність різної швидкості може дати вам цікаві результати.
  • Якщо рух здається вимкненим, слід перевірити точку початку. Якщо вона не знаходиться в точній середині форми, форма може розширитися дивним чином.

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 секунд до відпадання. Сині та жовті кадри я опустив, оскільки вони набагато складніші.

Один з інших шаблонів ( артилерійський снаряд ) у розширенні насправді був написаний Мізері, хоча я вносив до нього деякі модифікації. Спочатку це малопошкоджений проникаючий постріл, який вилітає на далекі відстані, а потім вибухає у величезний феєрверк, наносячи тонни збитків. Максимальний діапазон був набагато більшим, ніж гравцеві вдалося досягти, по суті змушуючи гравця брати участь на невеликій відстані, що було вигідно для інших типів одиниць NPC завдяки ефекту дробовика (більше куль купилося в невеликій зоні).

Працювати з BulletML, як правило, легко і можна робити дивовижні речі. Кулі можуть змінювати напрямок, змінювати швидкість, нерестувати інші шаблони, рано вмирати, повторювати збір команд у циклі, використовувати затримки, змінювати спрайтове зображення кулі, слідувати за своїм батьківським (чи ні) ... І все, що це не підтримує, ви могли б запишіть його.

Я б точно рекомендував це, якщо ви робите серйозну гру в стрілянину. Вам все одно знадобиться опрацювати математику координат, щоб отримати бажані форми, про що Харанор говорить у своїй відповіді, але кульовий двигун, як BulletML, дасть вам набагато більше гнучкості, що ви витратите більше часу на розробку нових шаблонів, ніж з'ясування як їх кодувати.

  1. Щоб пояснити, наскільки хороша місія, ці відео проти босів підлоги зі стартовим обладнанням : ні модулів, ні витратних матеріалів, ні основної шуруповерти. І Xe приймає лише один удар, незважаючи на подовжений характер поєдинку. Гаразд, 9 ударів по Centrifuge (який не з'явиться до третього поверху після того, як гравець, безумовно, матиме оновлення, що призведе до принаймні подвійного ураження порівняно).

Дякую, я розпливчасто усвідомлював 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);
            }
        }
    }
}

Велике спасибі за приклад, я збираюся перевірити це сьогодні вдень, щоб побачити, чи зможу я його встати та працювати.
лептон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.