如何制作扩展的子弹图案形状?


12

我想制作一系列可扩展的子弹图案,以形成正方形,三角形等形状。在下面的视频中可以看到我所追求的一个例子,当收集星星时,子弹会以膨胀星:

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


2
哦,这是一个好问题。我没有具体的答案,但我想您可以使用2D对象(精灵或简单形状)并沿边缘生成项目符号。当然,诀窍是给它们适当的速度,既可以使其形状向外移动,又可以使它们随着屏幕向前移动(如果您要制作这样的滚动条)。非常有兴趣在这里看到任何答案。
杰西·威廉姆斯

1
这种效果的俗称是“粒子效果”。该搜索词可以帮助您!
Cort Ammon

1
谢谢,我已经在XNA和libGDX中使用粒子效果一段时间了,但是不确定如何处理这种特殊的效果。
lepton

1
还有另一个答案,它功能强大,但编程非常复杂。并且需要一个真正的键盘才能输入。将其添加为书签以供以后解释。
Draco18s不再信任SE SE

有趣的是,我永远都不会为这样的东西带来粒子效果。也许这只是Unity中的描述。尽管粒子效果可能会产生碰撞(因此会损坏对象),但与仅实例化对象副本相比,这似乎会产生更多的开销。
杰西·威廉姆斯

Answers:


11

最简单的方法是首先设计形状,然后计算粒子的运动。在这个答案中,我将构建一个正方形,但这适用于任何形状。

首先将形状设计为围绕某个原点的相对位置。

广场

现在,您需要计算形状将如何扩展。为此,我们只需从的位置减去位置,然后对向量进行归一化,即可计算从指向origin每个向量的向量。。pointoriginpointvector = 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
我只想指出,每个粒子的速度应与第一帧上距原点的距离成比例(这意味着每帧仅计算一次)。或者,您根本无法对方向向量进行标准化。如果不这样做,形状将不会线性缩放,而是会变成一个圆形(如果所有速度都相同)
Aaron

@Charanor非常感谢您的解释。我实际上是在大学学习离散数学的,但距今已经有一段时间了。我今天将尝试实施一些东西。
lepton

2

因此,有一个名为BulletML的项目,它是用于创建复杂粒子/项目符号模式的标记语言。几乎可以肯定,您需要将代码移植到您自己的语言,但是它可以做一些非常神奇的事情。

例如,这个老板是在BulletML for Unity3D的(经过大量修改的)扩展中完成的(该模式的作者上传了该视频,而Misery则是疯了,以及good 1)。这是那个敌人最困难的变种,它展示了BulletML的能力很强(还可以检查Misery的其他一些老板,例如Wallmaster)。

或者,我可以显示此示例,该示例是我在为The Last Federation进行扩展时使用的模式,使用的系统较旧版本对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秒产卵。我已经省略了蓝色和黄色的镜头,因为它们要复杂得多。

扩展中的其他模式之一(炮弹外壳)实际上是由Misery编写的,尽管我对此做了一些修改。最初,这是一种低损伤,穿透力强的射击,可以飞到很远的地方,然后爆炸成巨大的烟火表演,造成数吨的伤害。它的最大射程远高于玩家所能达到的范围,从根本上迫使玩家在短距离内进行战斗,由于the弹枪的效果(更多子弹聚集在一个小区域内),这对于其他类型的NPC单位是有利的。

通常,BulletML易于使用,并且可以完成出色的工作。项目符号可以更改方向,更改速度,生成其他模式,尽早死亡,在循环中重复收集命令,使用延迟,更改项目符号精灵图像,跟随其父代(或不跟随)……而任何不支持的功能都可以写进去。

如果您正在认真进行射击游戏,我绝对会推荐它。正如Charanor在他的回答中所提到的,您仍然需要计算坐标数学以获得所需的形状,但是BulletML之类的子弹引擎将为您提供更多的灵活性,您将花费更多的时间设计新的模式而不是弄清楚如何编码。

  1. 为了说明“苦难”到底有多好,这些视频都是针对带有启动设备的地上老板:没有模块,没有消耗品以及基本的豌豆射手。尽管战斗时间很长,但xe只命中一击。好吧,对离心机有9次命中(直到玩家肯定升级后,直到三楼才会出现,相对而言,至少造成两倍的伤害)。

谢谢,我已经模糊地意识到BulletML了,因为它已经存在了一段时间了,但是对于我的简单游戏来说绝对是过分的了,因为它只是偶尔涉足子弹地狱,本身并不是子弹地狱的射手。
lepton

@lepton完全可以理解。这是您要做出的决定,但答案可能是其他人“最佳”的答案。我知道在从事TLF工作并继续构建自己的射击游戏之后,我想使用它只是因为它的功能强大且容易。:)
Draco18s不再信任SE 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);
            }
        }
    }
}

非常感谢您的示例,今天下午我将进行检查,看看是否可以启动并运行它。
lepton
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.