Come posso creare forme di pattern bullet in espansione?


12

Voglio creare una serie di modelli di proiettili in espansione che formano forme come quadrati, triangoli, ecc. Un esempio di ciò che sto cercando può essere visto nel seguente video in cui quando le stelle vengono raccolte i proiettili esplodono nella forma di un stella in espansione:

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


2
Oh, questa è una buona domanda. Non ho risposte specifiche, ma immagino che potresti usare un oggetto 2D, uno sprite o una forma semplice, e generare i proiettili lungo il bordo. Naturalmente, il trucco sarebbe quello di dare loro una velocità adeguata, sia nella loro forma esteriore che se si sta facendo uno scroller come questo, per farli avanzare con lo schermo. Molto interessato a vedere qualsiasi risposta qui.
Jesse Williams,

1
Un nome popolare per quell'effetto gentile è "effetti particellari". Quel termine di ricerca potrebbe aiutarti!
Cort Ammon,

1
Grazie, ho usato effetti particellari in XNA e libGDX da un po 'di tempo, ma non ero sicuro di come gestire questo particolare stile di effetto.
lept

1
C'è un'altra risposta a questa che è incredibilmente potente, ma molto complessa da programmare. Ed è necessaria una vera tastiera per scrivere. Aggiungi questo segnalibro per una spiegazione successiva.
Draco18s non si fida più di SE

Interessante: non avrei mai avuto effetti particellari per qualcosa del genere. O forse è solo una delineazione in Unity. Mentre gli effetti particellari possono avere collider (danneggiando così un oggetto), sembra che ciò creerebbe molto più overhead che semplicemente istanziare copie di oggetti.
Jesse Williams,

Risposte:


11

Il modo più semplice per farlo sarebbe prima progettare la forma, quindi calcolare il movimento delle particelle. In questa risposta costruirò un quadrato, ma questo vale per qualsiasi forma.

Inizia progettando la forma come posizioni relative attorno a un punto di origine.

piazza

Ora devi calcolare come si espanderà la forma. Per fare ciò calcoliamo semplicemente il vettore che punta da origina ogni pointsottraendo la originposizione dalla nostra pointposizione e quindi normalizzando il vettore. vector = normalize(point.x - origin.x, point.y - origin.y).

vettore

Ora possiamo calcolare la posizione dei punti in qualsiasi momento usando questo vettore. Calcola la posizione successiva dei punti facendo point.position += point.vector * point.velocity. Esempio di pseudocodice usando il nostro punto precedente:

// 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)

In questo modo tutti i punti verranno spostati verso l'esterno di 3 unità per ogni fotogramma.


Appunti

  • Puoi leggere alcuni semplici calcoli vettoriali qui .
  • La posizione può essere qualsiasi, purché tutte le posizioni siano relative a un certo punto di origine.
  • La velocità di tutti i punti dovrebbe essere la stessa per garantire un movimento uniforme, ma avere velocità diverse potrebbe darti risultati interessanti.
  • Se il movimento sembra spento, dovresti controllare il punto di origine. Se non si trova esattamente al centro della forma, la forma potrebbe espandersi in un modo strano.

9
Voglio solo sottolineare che la velocità di ogni particella dovrebbe essere proporzionale alla distanza dall'origine sul primo fotogramma (che significa calcolare solo una volta non per fotogramma). In alternativa, non è possibile semplicemente normalizzare il vettore di direzione. Se non lo fai, la forma non si ridimensionerà linearmente, ma piuttosto si muoverà verso un cerchio (se tutte le velocità sono uguali).
Aaron,

@Charanor Mille grazie per la spiegazione. In realtà ho studiato matematica discreta all'università, ma è passato un po 'di tempo fa. Proverò a implementare qualcosa oggi.
lepton,

2

Quindi, c'è questo progetto là fuori chiamato BulletML che è un linguaggio di markup per la creazione di schemi complessi di particelle / proiettili. Avrai quasi sicuramente bisogno di trasferire il codice nella tua lingua, ma può fare cose davvero sorprendenti.

Ad esempio, questo boss è stato realizzato in un'estensione (fortemente modificata) di BulletML per Unity3D (l'autore di quel modello ha caricato quel video e Misery è pazzo, oltre che buono 1 ). È la variante più difficile di quel nemico e mostra ciò di cui BulletML è abbastanza capace (e controlla anche alcuni degli altri boss di Misery, come Wallmaster ).

Oppure posso mostrare questo esempio, che è uno schema che ho scritto mentre lavoravo ad un'espansione per The Last Federation , usando una versione precedente del sistema che è meno adatta alle mod e usa solo variabili AZ a carattere singolo:

Esempio di modello di proiettile

I proiettili verdi che formano quegli anelli lì sono generati da un proiettile genitore che ruota ad alta velocità, ma essi stessi non hanno alcun movimento. Infliggono danni ingenti, mantenendo il giocatore a lungo raggio, limitandoli a ridurre le armi di danno e permettendo ai difensori mobili di molestare il giocatore (il giocatore ha vinto se la struttura immobile nel mezzo è stata distrutta).

Ecco una parte della sintassi XML che crea quelle bolle:

<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>

Puoi vedere alcuni dei colpi viola "dell'onda di gravità" nello screenshot, che viaggiano quasi istantaneamente dalla sorgente (che ruota) al bordo della bolla, dopodiché genera il colpo verde della "barra curva", che rimane lì per 12 secondi prima despawning. Gli scatti blu e gialli che ho omesso, in quanto sono molto più complicati.

Uno degli altri modelli (un proiettile di artiglieria ) nell'espansione è stato effettivamente scritto da Misery, anche se ho apportato alcune modifiche ad esso. Inizialmente è un colpo penetrante a basso danno che vola a grande distanza e poi esplode in un enorme spettacolo pirotecnico, infliggendo tonnellate di danni. Il raggio massimo era molto più alto di quello che il giocatore poteva raggiungere, costringendo essenzialmente il giocatore a impegnarsi a corto raggio, il che era vantaggioso per gli altri tipi di unità NPC a causa dell'effetto del fucile da caccia (più proiettili raggruppati in una piccola zona).

BulletML è facile da lavorare, in generale, e può fare cose incredibili. I proiettili possono cambiare direzione, cambiare velocità, generare altri schemi, morire presto, ripetere la raccolta di comandi in un ciclo, usare ritardi, cambiare l'immagine di sprite dei proiettili, seguire i loro genitori (o meno) ... E tutto ciò che non ti supporta potrebbe scrivici dentro.

Lo consiglio vivamente se stai facendo un serio gioco sparatutto. Avresti ancora bisogno di elaborare la matematica delle coordinate per ottenere le forme desiderate, come Charanor parla nella sua risposta, ma un motore di proiettile come BulletML ti darà molta più flessibilità che passerai più tempo a progettare nuovi modelli che a capire come codificarli.

  1. Per spiegare quanto sia buona la miseria, quei video sono contro i boss del pavimento con l' attrezzatura di partenza : nessun modulo, nessun materiale di consumo e lo sparatutto di base. E xe subisce solo un colpo nonostante la natura allungata della lotta. Ok, 9 colpi contro Centrifuge (che non si presenta fino al terzo piano dopo che il giocatore avrà sicuramente degli aggiornamenti con almeno il doppio danno in confronto).

Grazie, ero vagamente a conoscenza di BulletML, dato che è in circolazione da un po ', ma è sicuramente eccessivo per il mio gioco semplice, che occasionalmente si diletta nell'inferno di proiettili e non è uno sparatutto di per sé.
lept

@lepton Totalmente comprensibile. Questa è una decisione che devi prendere, ma la risposta potrebbe essere la "migliore" per qualcun altro. So che dopo aver lavorato su TLF e aver iniziato a costruire il mio sparatutto, volevo usarlo solo per quanto fosse potente e facile lavorare. :)
Draco18s non si fida più di SE

1

Come sottolineato da Charanor è possibile utilizzare una serie di punti per definire la forma e quindi aggiornarne la posizione nel tempo. Di seguito è riportato un esempio funzionante di come implementare una forma a stella o una forma personalizzata utilizzando i punti:

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);
            }
        }
    }
}

Grazie mille per l'esempio, vado a dare un'occhiata questo pomeriggio per vedere se riesco a farlo funzionare.
lept
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.