Как создать графический эффект частиц фейерверка на Android

Кто-нибудь знает, как сделать эффект фейерверка, рисуя на холсте? Есть один хороший пример в живых обоях wireworks (бесплатное приложение).

Там многие точки движутся и оставляют следы в звезде, подобные взрыву, и постепенно исчезают. Я думаю, что при движении каждой точки есть какое-то размытие движения, которое я не знаю, как создать.

Любые идеи или ссылки на соответствующие примеры приветствуются.


person Lumis    schedule 23.03.2011    source источник


Ответы (3)


Это очень абстрактный (и интересный) вопрос, но слишком сложный, чтобы на него можно было подробно ответить.

То, что вы действительно ищете, вероятно, является своего рода модифицированной системой частиц. Система частиц — это, по сути, движок для частиц, который включает в себя физический движок.

Тем не менее, вы должны сосредоточиться на следующем:

После того, как вы его построили (или получили представление о нем), вы можете легко преобразовать его в систему Canvas, хотя OpenGL является предпочтительной альтернативой.

person Wroclai    schedule 23.03.2011
comment
Спасибо Виктор за полезный ответ (в отличие от другого). Я пытаюсь создать его на SurfaceView. Если вы проверите эти живые обои market.android.com/details?id= com.aoi.livewallpaper.Fireworks вы увидите, что точка образует размытый хвост. Все, что мне нужно, это сделать движение точки, похожее на комету, тогда все остальное будет математикой. Я думаю, что фильтры для краски могли бы помочь. - person Lumis; 24.03.2011
comment
Учебник по системе частиц, резервная копия из Internet Wayback Machine. - person Rodia; 09.06.2018

Я только что выпустил v1.1 библиотеки, которая создает систему частиц для обычных представлений Android: https://github.com/plattysoft/Leonids

Я знаю, что вопрос довольно старый, и вы, вероятно, реализовали его самостоятельно, но если вы все еще заинтересованы в использовании библиотеки, проверьте ее.

person shalafi    schedule 30.05.2014
comment
Спасибо за вашу усердную работу, просто быстрый вопрос - я хотел бы, чтобы частицы появлялись в случайных местах, а затем исчезали через 2 секунды. Я искал, как я могу добиться случайного размещения частиц с помощью вашей библиотеки. - person Alex; 21.08.2016
comment
Теоретически вы могли бы иметь представление размером с экран и убедиться, что гравитация не установлена ​​​​в ЦЕНТР, это должно сработать. - person shalafi; 22.08.2016
comment
Я попробовал твой способ. Но он генерирует одну звезду за раз. Чего я действительно хотел, так это. Скажем, 20/30 маленьких частиц (звезд) генерируются случайного размера в случайном месте, а затем начинают исчезать в случайное время, и по мере того, как каждая из них исчезает, другая звезда появляется в случайном месте. - person Alex; 23.08.2016
comment
Тогда вы можете сделать это с 2 системами частиц. один с 20 и один с 10. Я попробовал Gravity.NO_GRAVITY и работает как положено. Однако мы должны перенести это обсуждение в тикет на github. - person shalafi; 21.09.2016

import java.util.Random;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

enum AnimateState
{
    asReady, asRunning, asPause;
}

class Rocket
{
    public boolean sleep = true;

    private float energy, length, mx, my, gravity, ox, oy, x, y, t;
    private float vx[], vy[];
    private int patch, red, green, blue;
    private Random random;

    public Rocket( int a, int b, int g )
    {
        mx = a;
        my = b;
        gravity = g;
    }

    public void init( int e, int p, int l, long seed )
    {
        energy = e;
        patch = p + 20;
        length = l;

        random = new Random( seed );

        vx = new float[patch];
        vy = new float[patch];

        red = ( int )( random.nextFloat() * 128 ) + 128;
        blue = ( int )( random.nextFloat() * 128 ) + 128;
        green = ( int )( random.nextFloat() * 128 ) + 128;

        ox = ( random.nextFloat() * mx / 2 ) + mx / 4;
        oy = ( random.nextFloat() * my / 2 ) + my / 4;

        for( int i = 0; i < patch; ++i )
        {
            vx[i] = ( ( random.nextFloat() + random.nextFloat() / 2 ) * energy ) - energy / ( random.nextInt( 2 ) + 1 );
            vy[i] = ( ( random.nextFloat() + random.nextFloat() / 2 ) * energy * 7 / 8 ) - energy / ( random.nextInt( 5 ) + 4 );
        }
    }

    public void start()
    {
        t = 0;
        sleep = false;
    }

    public void doDraw( Canvas canvas, Paint paint )
    {
        if ( ! sleep )
        {
            if ( t < length )
            {
                int i, cr, cg, cb;
                double s;

                cr = ( int )( random.nextDouble() * 64 ) - 32 + red;
                cg = ( int )( random.nextDouble() * 64 ) - 32 + green;
                cb = ( int )( random.nextDouble() * 64 ) - 32 + blue;

                if ( cr >= 0 && cr <= 256 )
                    red = cr;
                if ( cg >= 0 && cg <= 256 )
                    green = cg;
                if ( cb >= 0 && cb <= 256 )
                    blue = cb;

                int _red = red == 256 ? 255 : red;
                int _green = green == 256 ? 255 : green;
                int _blue = blue == 256 ? 255 : blue;

                int color = Color.rgb( _red, _green, _blue );

                paint.setColor( color );

                for ( i = 0; i < patch; ++i )
                {
                    s = ( double )t / 100;
                    x = ( int )( vx[i] * s );
                    y = ( int )( vy[i] * s - gravity * s * s );

                    canvas.drawCircle( ox + x, oy - y, 2f, paint );
                }

                paint.setColor( Color.BLACK );

                for ( i = 0; i < patch; ++i )
                {
                    if ( t >= length / 2 )
                    {
                        for ( int j = 0; j < 2; ++j )
                        {
                            s = ( double ) ( ( t - length / 2 ) * 2 + j ) / 100;
                            x = ( int )( vx[i] * s );
                            y = ( int )( vy[i] * s - gravity * s * s );

                            canvas.drawCircle( ox + x, oy - y, 2f, paint );
                        }
                    }
                }

                ++t;
            }
            else
                sleep = true;
        }
    }
}

class Fireworks
{
    /**
     * Maximum number of rockets.
     */
    public int MaxRocketNumber = 9;
    /**
     * Controls "energy" of firwork explosion. Default value 850.
     */
    public int MaxRocketExplosionEnergy = 950;
    /**
     * Controls the density of the firework burst. Larger numbers give higher density.
     * Default value 90.
     */
    public int MaxRocketPatchNumber = 90;
    /**
     * Controls the radius of the firework burst. Larger numbers give larger radius.
     * Default value 68.
     */
    public int MaxRocketPatchLength = 68;

    /**
     * Controls gravity of the firework simulation.
     * Default value 400.
     */
    public int Gravity = 400;

    transient private Rocket rocket[];
    transient private boolean rocketsCreated = false;

    private int width;
    private int height;

    Fireworks( int width, int height )
    {
        this.width = width;
        this.height = height;
    }

    void createRockets()
    {
        rocketsCreated = true;

        Rocket tempRocket[] = new Rocket[MaxRocketNumber];

        for ( int i = 0; i < MaxRocketNumber; i++ )
            tempRocket[i] = new Rocket( width, height, Gravity );

        rocket = tempRocket;
    }

    public synchronized void reshape( int width, int height )
    {
        this.width = width;
        this.height = height;

        rocketsCreated = false;
    }

    public void doDraw( Canvas canvas, Paint paint )
    {
        canvas.drawColor( Color.BLACK );

        int i, e, p, l;
        long s;

        boolean sleep;

        if ( ! rocketsCreated )
        {
            createRockets();
        }

        if ( rocketsCreated )
        {
            sleep = true;

            for ( i = 0; i < MaxRocketNumber; i++ )
                sleep = sleep && rocket[i].sleep;

            for ( i = 0; i < MaxRocketNumber; ++i )
            {
                e = ( int )( Math.random() * MaxRocketExplosionEnergy * 3 / 4 ) + MaxRocketExplosionEnergy / 4 + 1;
                p = ( int )( Math.random() * MaxRocketPatchNumber * 3 / 4 ) + MaxRocketPatchNumber / 4 + 1;
                l = ( int )( Math.random() * MaxRocketPatchLength * 3 / 4 ) + MaxRocketPatchLength / 4 + 1;
                s = ( long )( Math.random() * 10000 );

                Rocket r = rocket[i];
                if ( r.sleep && Math.random() * MaxRocketNumber * l < 2 ) 
                {
                    r.init( e, p, l, s );
                    r.start();
                }

                if ( rocketsCreated )
                    r.doDraw( canvas, paint );
            }
        }
    }
}

public class FireworkLayout extends SurfaceView implements SurfaceHolder.Callback
{

    class GameThread extends Thread
    {
        private boolean mRun = false;

        private SurfaceHolder surfaceHolder;
        private AnimateState state;
        private Context context;
        private Handler handler;
        private Paint paint;
        Fireworks fireworks;

        GameThread( SurfaceHolder surfaceHolder, Context context, Handler handler )
        {
            this.surfaceHolder = surfaceHolder;
            this.context = context;
            this.handler = handler;

            fireworks = new Fireworks( getWidth(), getHeight() );

            paint = new Paint();
            paint.setStrokeWidth( 2 / getResources().getDisplayMetrics().density );
            paint.setColor( Color.BLACK );
            paint.setAntiAlias( true );
        }

        public void doStart()
        {
            synchronized ( surfaceHolder )
            {
                setState( AnimateState.asRunning );
            }
        }

        public void pause()
        {
            synchronized ( surfaceHolder )
            {
                if ( state == AnimateState.asRunning )
                    setState( AnimateState.asPause );
            }
        }

        public void unpause()
        {
            setState( AnimateState.asRunning );
        }

        @Override
        public void run()
        {
            while ( mRun )
            {
                Canvas c = null;
                try
                {
                    c = surfaceHolder.lockCanvas( null );

                    synchronized ( surfaceHolder )
                    {
                        if ( state == AnimateState.asRunning )
                            doDraw( c );
                    }
                }
                finally
                {
                    if ( c != null )
                    {
                        surfaceHolder.unlockCanvasAndPost( c );
                    }
                }
            }
        }

        public void setRunning( boolean b )
        {
            mRun = b;
        }

        public void setState( AnimateState state )
        {
            synchronized ( surfaceHolder )
            {
                this.state = state;
            }
        }

        public void doDraw( Canvas canvas )
        {
            fireworks.doDraw( canvas, paint );
        }

        public void setSurfaceSize( int width, int height )
        {
            synchronized ( surfaceHolder )
            {
                fireworks.reshape( width, height );
            }
        }
    }

    private GameThread thread;

    @SuppressLint( "HandlerLeak" )
    public FireworkLayout( Context context )
    {
        super( context );

        SurfaceHolder holder = getHolder();
        holder.addCallback( this );

        getHolder().addCallback( this );

        thread = new GameThread( holder, context, new Handler() {
            @Override
            public void handleMessage( Message m ) {

            }} );

        setFocusable( true );
    }

    @Override
    public void surfaceChanged( SurfaceHolder holder, int format, int width, int height )
    {
        thread.setSurfaceSize( width, height );
    }

    @Override
    public void surfaceCreated( SurfaceHolder holder )
    {
        thread.setRunning( true );
        thread.doStart();
        thread.start();
    }

    @Override
    public void surfaceDestroyed( SurfaceHolder holder )
    {
        boolean retry = true;
        thread.setRunning( false );

        while ( retry )
        {
            try
            {
                thread.join();
                retry = false;
            }
            catch ( InterruptedException e )
            {
            }
        }
    }
}

использовать:

    firework = new FireworkView( this );
    LinearLayout surface = (LinearLayout) findViewById( R.id.surface );
    surface.addView( firework );
person Community    schedule 07.02.2013
comment
Спасибо, думаю, следующий шаг — заставить его оставлять следы, а не исчезать так быстро. - person Lumis; 07.02.2013
comment
Может ли кто-нибудь помочь отредактировать этот ответ, чтобы предоставить пример кода для вызывающего абонента/использования? Я даже не знаю, какой метод является отправной точкой. - person Fruit; 09.06.2016