LoginSignup
3
3

More than 3 years have passed since last update.

PixiJSでパーティクルを作ってみる

Last updated at Posted at 2020-06-04

パーティクルに興味をもったので色々調べて作ってみた。
PixiJSのParticle-Containerを試してみたが、パーティクルの操作にかなり縛りがあったので使用せず。

また、この記事のコードはICSさんの下記のURLを参考にPixiJSを使ってコーディングしました(要PixiJSだったため)

JavaScriptとWebGLで取り組む
クリエイティブコーディング
https://www.slideshare.net/clockmaker_jp/javascriptwebgl-107429033

テスト的にやってみただけ&パーティクルを出したり引っ込めたかったのでオブジェクトプーリングはしてません。
追々対応予定。

作ったばかりでコードを整理していないので、微妙なとこがあるかもしれません(特に命名規則

import * as PIXI from 'pixi.js'

export class ParticleStage extends Dispatcher {
    private app
    private container
    private particles = []

    // 1フレームごとに生成するパーティクルの数
    private totalSpritesPerTick = 4
    // パーティクルのライフ
    private MAX_LIFE = 100

    public constructor(){
        super()

        this.app = new PIXI.Application({
            resolution: devicePixelRatio,
            transparent: true,
        })

        // パーティクルを追加していくコンテナインスタンスを作成
        this.container = new PIXI.Container()
        // ステージに追加
        this.app.stage.addChild(this.container)
        return this.app
    }

    // パーティクルを開始するメソッド
    public startTick() {
        //パーティクル作成tickを登録
        this.app.ticker.add(this.createTick, this)
        this.app.ticker.start()
    }
    // パーティクルを停止するメソッド
    public stopTick() {
        this.app.ticker.remove(this.createTick, this)
    }

    // 1フレーム毎に呼び出されるパーティクルを生成するメソッド
    private createTick() {
        for (let i = 0; i < this.totalSpritesPerTick; i++) {

            // PIXI.Spriteを拡張したパーティクルインスタンスを作成
            const particle = new Particle()

            // なんとなくぼかす
            particle.filters = [new PIXI.filters.BlurFilter(5)]
            // パーティクルの透明度をランダムで設定
            particle.alpha = Math.random() * 0.2 + 0.5
            // パーティクルの中心を設定
            particle.anchor.set(0.5)
            // 浮き上がる速度を設定
            particle.vy = 20 * (Math.random() - 0.5)
            // パーティクルのライフを設定
            particle.life = this.MAX_LIFE

            // パーティクルのシェイプを決定
            particle.type = Math.floor(Math.random() * 2).toString()

            // パーティクルのシェイプをtypeごとに生成
            const g = new PIXI.Graphics()
            switch (particle.type) {
                case '0':
                    g.beginFill(0xFFFFFF)
                    g.drawCircle(0, 0, 15)
                    g.endFill()
                    break
                case '1':
                    g.lineStyle(3, 0xFFFFFF)
                    g.drawCircle(0, 0, 15)

                    break
            }
            g.endFill()
            // GraphicsをTextureに変換
            const t = this.app.renderer.generateTexture(g)
            particle.texture = t

            // パーティクルの出現位置を設定(今回はnormal)
            const randomValue = this.createRandom('normal')
            particle.x = window.innerWidth * (randomValue)
            particle.y = window.innerHeight + 10

            // パーティクルを配列に保存
            this.particles.push(particle)
            // コンテナにaddChild
            this.container.addChild(particle)
        }
    }

    // パーティクルを動かすメソッド
    private tick() {
        for (let i = 0; i < this.particles.length; i++) {
            // オブジェクトの作成
            const particle = this.particles[i]
            // 重力
            particle.vy -= 2
            // 摩擦
            particle.vy *= 0.92
            // 速度を位置に適用
            particle.y += particle.vy

            // パーティクルのサイズをライフ依存にする
            const scale = particle.life / this.MAX_LIFE;
            particle.scale.x = scale
            particle.scale.y = scale

            // パーティクルの透過度をライフ依存にする(0.6が下限)
            particle.alpha = (1-scale) + 0.6

            // 寿命を減らす
            particle.life -= 1;
            // 寿命の判定
            if (particle.life <= 0) {
                // 変数を初期化
                particle.reset()
                // コンテナから削除
                this.container.removeChild(particle)
                // 配列からも削除
                this.particles.splice(i, 1)
                i -= 1
            }
        }
    }
    private createRandom(randomType) {
        switch (randomType) {
            case 'center':
                // 中央に配置
                return (Math.random() + Math.random()) / 2
            case 'center-strong':
                // さらに中央に配置
                const valueA = (Math.random() + Math.random()) / 2
                const valueB = (Math.random() + Math.random()) / 2
                return (valueA + valueB) / 2
            case 'edge':
                // 乗算の乱数
                const base = Math.random() * Math.random() * Math.random()
                const inverse = 1.0 - base
                return Math.random() < 0.5 ? base : inverse
            case 'normal':
            default:
                // 一様分布
                return Math.random()
        }
    }
}
export class Particle extends PIXI.Sprite {
    public vx = 0
    public vy = 0
    public life = 0
    public size = 0
    public type
    constructor() {
        super()
    }

    public reset() {
        this.vx = 0
        this.vy = 0
        this.life = 0
        this.size = 0
        this.type = 0
        this.texture = null
        this.destroy()
    }
}

試してみる

import { ParticleStage } from './ParticleStage '

const particles = new ParticleStage()
// 適当なdivのクラス名
const div = document.getElementsByClassName('canvas-stage')

// canvasを追加
div.appendChild(particles.view)

//パーティクルを開始
particles.startTick()

// 1秒後に停止
setTimeout(particles.stopTick(), 1000)

実際のものはHSLでランダムに色を変更したりしています。
パーティクルおもしろいなぁ。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3