Wie kann ich Aufzählungszeichenformen erweitern?

12

Ich möchte eine Reihe expandierender Aufzählungszeichenmuster erstellen, die Formen wie Quadrate, Dreiecke usw. bilden. Ein Beispiel dafür, wonach ich suche, ist im folgenden Video zu sehen, in dem die Aufzählungszeichen in der Form eines explodieren, wenn die Sterne gesammelt werden expandierender Stern:

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

Lepton
quelle
2
Oh, das ist eine gute Frage. Ich habe keine spezifischen Antworten, aber ich könnte mir vorstellen, dass Sie ein 2D-Objekt verwenden, entweder ein Sprite oder eine einfache Form, und die Kugeln entlang der Kante erzeugen. Der Trick wäre natürlich, ihnen die richtige Geschwindigkeit zu geben, sowohl in ihrer Form nach außen als auch, wenn Sie einen Scroller wie diesen bauen, um sie mit dem Bildschirm vorwärts zu bewegen. Sehr interessiert, hier irgendwelche Antworten zu sehen.
Jesse Williams
1
Ein beliebter Name für diesen Effekt ist "Partikeleffekte". Dieser Suchbegriff kann Ihnen helfen!
Cort Ammon
1
Vielen Dank, ich benutze Partikeleffekte in XNA und libGDX schon seit einiger Zeit, war mir aber nicht sicher, wie ich mit diesem speziellen Effektstil umgehen sollte.
Lepton
1
Darauf gibt es eine andere Antwort, die unglaublich leistungsfähig, aber sehr komplex zu programmieren ist. Und zum Tippen braucht man eine richtige Tastatur. Lesezeichen für eine spätere Erklärung.
Draco18s vertraut SE
Interessant - für so etwas wäre ich nie mit Partikeleffekten gefahren. Oder vielleicht ist es nur eine Abgrenzung in Unity. Während Partikeleffekte Kollider haben können (wodurch ein Objekt beschädigt wird), scheint dies viel mehr Aufwand zu verursachen, als nur Objektkopien zu instanziieren.
Jesse Williams

Antworten:

11

Am einfachsten ist es, zuerst die Form zu entwerfen und dann die Bewegung der Partikel zu berechnen. In dieser Antwort werde ich ein Quadrat konstruieren, aber das gilt für jede Form.

Beginnen Sie, indem Sie Ihre Form als relative Position um einen Ursprungspunkt herum entwerfen.

Platz

Nun müssen Sie berechnen, wie sich die Form ausdehnt. Dazu berechnen wir einfach den Vektor, der von dem originauf jeden zeigt, pointindem wir die originPosition von unserer pointPosition subtrahieren und dann den Vektor normalisieren . vector = normalize(point.x - origin.x, point.y - origin.y).

Vektor

Mit diesem Vektor können wir nun die Position der Punkte zu jedem Zeitpunkt berechnen. Sie berechnen damit die nächste Position der Punkte point.position += point.vector * point.velocity. Pseudocode-Beispiel mit unserem vorherigen Punkt:

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

Auf diese Weise werden alle Punkte um 3 Einheiten pro Frame nach außen verschoben.


Anmerkungen

  • Sie können hier einige einfache Vektor-Mathematik nachlesen .
  • Die Position kann beliebig sein, solange sich alle Positionen auf einen Ursprungspunkt beziehen.
  • Die Geschwindigkeit aller Punkte sollte gleich sein, um eine gleichmäßige Bewegung zu gewährleisten. Unterschiedliche Geschwindigkeiten können jedoch zu interessanten Ergebnissen führen.
  • Wenn die Bewegung abweicht, sollten Sie den Ursprungspunkt überprüfen. Wenn es nicht genau in der Mitte der Form ist, kann sich die Form auf seltsame Weise ausdehnen.
Charanor
quelle
9
Ich möchte nur darauf hinweisen, dass die Geschwindigkeit jedes Partikels proportional zum Abstand vom Ursprung des ersten Frames sein sollte (dh nur einmal berechnen, nicht pro Frame). Alternativ könnten Sie den Richtungsvektor einfach nicht normalisieren. Wenn Sie dies nicht tun, wird die Form nicht linear skaliert, sondern wird zu einem Kreis (wenn alle Geschwindigkeiten gleich sind).
Aaron
@Charanor Vielen Dank für die Erklärung. Eigentlich habe ich Diskrete Mathematik an der Universität studiert, aber es ist schon eine Weile her. Ich werde heute versuchen, etwas umzusetzen.
Lepton
2

Es gibt also ein Projekt namens BulletML , eine Auszeichnungssprache zum Erstellen komplexer Partikel- / Aufzählungsmuster. Sie müssen den Code mit ziemlicher Sicherheit in Ihre eigene Sprache portieren, aber er kann einige wirklich erstaunliche Dinge bewirken.

Zum Beispiel wurde dieser Boss in einer (stark modifizierten) Erweiterung von BulletML für Unity3D (der Autor dieses Musters hat hochgeladen, dass Video und Misery verrückt sind, sowie gut 1 ). Es ist die schwierigste Variante dieses Feindes und zeigt, wozu BulletML ziemlich gut in der Lage ist (und schaut euch auch einige der anderen Bosse von Misery an, wie Wallmaster ).

Oder ich zeige dieses Beispiel, das ich während der Arbeit an einer Erweiterung für The Last Federation mit einer älteren Version des Systems geschrieben habe, die weniger modifizierbar ist und nur Einzelzeichen-AZ-Variablen verwendet:

Beispiel für ein Aufzählungsmuster

Die grünen Kugeln, die diese Ringe dort herstellen, werden von einer übergeordneten Kugel erzeugt, die sich mit hoher Geschwindigkeit dreht, aber selbst keine Bewegung hat. Sie verursachen massiven Schaden, halten den Spieler in größerer Reichweite, beschränken ihn auf Waffen mit geringerem Schaden und ermöglichen mobilen Verteidigern, den Spieler zu belästigen (der Spieler hat gewonnen, wenn die unbewegliche Struktur in der Mitte zerstört wurde).

Hier ist ein Teil der XML-Syntax, mit der diese Blasen erstellt werden:

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

Sie können einige der lila "Gravitationswellen" -Schüsse auf dem Screenshot sehen, die sich fast sofort von der Quelle (die sich dreht) zum Rand der Blase bewegen, woraufhin der grüne "gekrümmte Balken" -Schuss erzeugt wird, der dort 12 Sekunden vorher sitzt entlasten. Die blauen und gelben Aufnahmen habe ich weggelassen, da sie viel komplizierter sind.

Eines der anderen Muster (ein Artillerie-Granate ) in der Erweiterung wurde tatsächlich von Misery geschrieben, obwohl ich einige Änderungen daran vorgenommen habe. Anfänglich handelt es sich um einen schadensarmen, durchdringenden Schuss, der auf große Entfernung abgefeuert wird und dann in ein riesiges Feuerwerk explodiert, das Tonnen von Schaden verursacht. Die maximale Reichweite war viel höher als der Spieler erreichen konnte, was den Spieler zwang, sich auf kurze Distanz zu bewegen, was für die anderen Arten von NPC-Einheiten aufgrund des Schrotflinteneffekts von Vorteil war (mehr Kugeln in einer kleinen Zone).

BulletML ist im Allgemeinen einfach zu handhaben und kann erstaunliche Dinge bewirken. Aufzählungszeichen können die Richtung ändern, die Geschwindigkeit ändern, andere Muster erzeugen, frühzeitig absterben, das Sammeln von Befehlen in einer Schleife wiederholen, Verzögerungen verwenden, das Sprite-Bild des Aufzählungszeichens ändern, dem übergeordneten Element folgen (oder nicht) schreibe hinein.

Ich würde es auf jeden Fall empfehlen, wenn Sie ein ernstes Shoot-Em-Up-Spiel machen. Sie müssten immer noch die Koordinaten berechnen, um die gewünschten Formen zu erhalten, wie Charanor in seiner Antwort ausführt, aber eine Bullet-Engine wie BulletML bietet Ihnen so viel mehr Flexibilität, dass Sie mehr Zeit mit dem Entwerfen neuer Muster verbringen müssen als mit dem Herausfinden wie codieren sie.

  1. Um zu erklären, wie gut Misery ist, sind diese Videos gegen Floor Bosses mit Startausrüstung : keine Module, keine Verbrauchsmaterialien und das grundlegende Erbsen-Shooter. Und xe nimmt nur einen Schlag trotz der verlängerten Natur des Kampfes. Ok, 9 Treffer gegen Centrifuge (wer erst im dritten Stock nach dem Spieler auftaucht, wird definitiv Upgrades haben, die vergleichsweise mindestens den doppelten Schaden verursachen).
Draco18s vertraut SE nicht mehr
quelle
Danke, ich war mir von BulletML vage bewusst, da es schon eine Weile her ist, aber es ist definitiv übertrieben für mein einfaches Spiel, das sich nur gelegentlich in der Bullet-Hell versucht und per se kein Bullet-Hell-Shooter ist.
Lepton
@lepton Völlig verständlich. Das ist eine Entscheidung, die Sie treffen müssen, aber die Antwort ist möglicherweise die "beste" für jemanden anderen. Ich weiß, dass ich nach der Arbeit an TLF meinen eigenen Shooter bauen wollte, nur weil es so mächtig und einfach war, damit zu arbeiten. :)
Draco18s vertraut SE nicht mehr
1

Wie von Charanor hervorgehoben, können Sie eine Reihe von Punkten verwenden, um Ihre Form zu definieren und ihre Position im Laufe der Zeit zu aktualisieren. Im Folgenden finden Sie ein Arbeitsbeispiel für die Implementierung einer Sternform oder einer benutzerdefinierten Form mithilfe von Punkten:

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
quelle
Vielen Dank für das Beispiel, ich werde es heute Nachmittag überprüfen, um zu sehen, ob ich es zum Laufen bringen kann.
Lepton