拡大する弾丸パターンの形状を作成するにはどうすればよいですか?


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単位で外側に移動します。


ノート

  • ここでいくつかの単純なベクトル数学を読むことができます
  • 位置は、すべての位置が何らかの原点に関連している限り、何でもかまいません。
  • すべてのポイントの速度は、均一な動きを確保するために同じである必要がありますが、速度が異なると面白い結果が得られる場合があります。
  • 動きがずれているようであれば、原点を確認する必要があります。シェイプの正確な中央にない場合、シェイプは奇妙な方法で拡大する可能性があります。

9
私が指摘したいのは、各粒子の速度は、最初のフレームの原点からの距離に比例する必要があることです(つまり、フレームごとではなく一度だけ計算することを意味します)。または、方向ベクトルを単純に正規化できません。これを行わないと、シェイプは直線的に拡大縮小されず、円になるように移動します(すべての速度が同じ場合)
アーロン

@Charanor説明に感謝します。私は実際に大学で離散数学を勉強しましたが、今はかなり前のことです。今日は何かを試して実装するつもりです。
レプトン

2

そのため、BulletMLと呼ばれるこのプロジェクトがあります。これは、複雑な粒子/弾丸パターンを作成するためのマークアップ言語です。ほぼ確実にコードを自分の言語に移植する必要がありますが、いくつかの本当に驚くべきことができます。

たとえば、このボスは、Unity3D用のBulletMLの(大幅に変更された)拡張で行われました(そのパターンの作成者はそのビデオをアップロードし、Miseryは正気であり1)。それはその敵の最も難しいバリエーションであり、BulletMLが非常に優れていることを示しています(そして、WallmasterのようなMiseryの他のボスもチェックしてください)。

または、この例を示すことができます。これは、最後のフェデレーションの拡張に取り組んでいるときに書いたパターンで、MODの友好性が低く、単一文字の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秒間待機する緑色の「曲線バー」ショットを生成します。スポーン。青と黄色のショットは、はるかに複雑なので省略しました。

拡張のその他のパタ​​ーン(砲弾)の1つは実際にMiseryによって作成されましたが、私はそれにいくつかの修正を加えました。当初は、長距離に飛び出してから巨大な花火大会に爆発し、大量のダメージを与える低ダメージの貫通ショットです。最大射程はプレイヤーが達成できる範囲よりもはるかに高く、基本的にプレイヤーは短距離で交戦することを余儀なくされました。これは、ショットガン効果により小さな他のタイプのNPCユニットに有利でした(小さなゾーンに多くの弾丸が集まっています)。

BulletMLは、一般的に操作が簡単で、驚くべきことを実行できます。弾丸は方向を変えたり、速度を変えたり、他のパターンを生成したり、早く死んだり、ループ内でコマンドの収集を繰り返したり、遅延を使用したり、弾丸のスプライト画像を変更したり、親を追ったり(追わなかったり)... それに書きます。

あなたが本格的なシュートエムアップゲームをしているなら、私は間違いなくそれをお勧めします。Charanorが答えで説明しているように、座標計算を行って必要な形状を取得する必要がありますが、BulletMLのような弾丸エンジンは柔軟性が非常に高いため、考え出すよりも新しいパターンの設計に多くの時間費やすことになりますそれらをどのようにコーディングする

  1. Miseryがどれだけ優れているかを説明するために、これらのビデオは、モジュール、消耗品、および基本的なエンドウ豆のシューティングゲームを備えたフロアボスに対するものです。そして、xeは、戦いの細長い性質にもかかわらず、1回の攻撃しか受けません。わかりました、Centrifugeに9ヒット(プレイヤーは間違いなく、少なくとも2倍のダメージをもたらすアップグレードを確実に行った後、3階まで現れません)。

おかげで、しばらくの間、BulletMLを漠然と認識していましたが、単純なゲームでは間違いなくやり過ぎです。
レプトン

@lepton完全に理解可能。それはあなたが下す決定ですが、答えは誰かにとって「最良の」ものかもしれません。TLFに取り組み、独自のシューティングゲームの構築に着手した後、それが強力で簡単であるという理由だけで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);
            }
        }
    }
}

例に大いに感謝します。今日の午後、それを実行して実行できるかどうかを確認します。
レプトン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.