Fireworks


単に見るだけのコンテンツですが、花火が打ち上げられる様子はそれなりに面白いと思います。三角関数を使って座標計算をするサンプルとして実装しました。Advanced(上級)クラスでこれらの内容をカバーする予定です。以下の書籍で実装の詳細について解説しています。

ソースコード


<!DOCTYPE html>
<html>
<head>
<style>
	#field {width:800px; height:600px;}
</style>
<script>
    var ctx, fires = [];
    var width = 800, height = 600;

    function random(limit) {
        return Math.floor(Math.random() * limit);
    }

    function Firework(radius, color) {
        this.color = color;
        this.radius = radius;

        this.initialize = function () {
            this.count = 0;
            this.scale = 0;
            this.x = random(width);
            this.y = height + random(20);
            this.xSpeed = -3 + random(6);
            this.ySpeed = -3 - random(8);
        }

        this.move = function () {
            this.x += this.xSpeed;
            this.y += this.ySpeed;

            this.ySpeed += 0.1;
            this.xSpeed /= 1.01;
        }

        this.draw = function () {
            if (this.ySpeed < -1) { // going up
                ctx.fillStyle = this.color;
                ctx.beginPath();
                ctx.arc(this.x, this.y, 4, 0, Math.PI * 2);
                ctx.fill();
            }
            else {                   // exploded
                this.count++;
                for (var t = 0 ; t < 4 ; t++) {
                    this.scale += 0.06 / this.count;
                    var rad = this.radius * this.scale;
                    for (var i = 0 ; i < Math.PI * 2 ; i += 0.6) {
                        var dx = Math.cos(i) * rad;
                        var dy = Math.sin(i) * rad;
                        ctx.fillStyle = this.color;
                        ctx.beginPath();
                        ctx.arc(this.x + dx, this.y + dy, 2, 0, Math.PI * 2);
                        ctx.fill();
                    }
                }
                if (this.count > 30) {
                    this.initialize();
                }
            }
        }

        this.initialize();
    }

    function init() {
        var canvas = document.getElementById("field");
        ctx = canvas.getContext("2d");
        ctx.globalAlpha = 0.3;
        var colors = ["#ff0000", "#ffff00", "#ffffff",
        "#ff00ff", "#00ff00", "#7F7FFF", "#00ffff"];

        for (var i = 0 ; i < 14 ; i++) {
            fires.push(new Firework(random(60) + 60, colors[i % 7]));
        }
        setInterval(tick, 100);
    }

    function tick() {
        fires.forEach(function (e) { e.move() })
        paint();
    }

    function paint() {
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, width, height);
        fires.forEach(function (e) { e.draw() })
    }
</script>
</head>
<body onload="init()">
<canvas width="800" height="600" id="field"></canvas>
</body>
</html>

""" trig_firework.py - Copyright 2016 Kenichiro Tanaka """
import sys
from math import sin, cos, radians
from random import randint
import pygame
from pygame.locals import QUIT

pygame.init()
SURFACE = pygame.display.set_mode((800, 600))
FPSCLOCK = pygame.time.Clock()

class Firework():
    """ Firework object """
    def __init__(self, radius, color):
        self.radius = radius
        self.color = color
        self.initialize()

    def initialize(self):
        """ initialize the firework """
        self.count = 0
        self.scale = 0
        self.pos = [randint(0, 800), randint(0, 20) + 600]
        self.speed = [randint(-3, 3), randint(-10, -2)]

    def move(self):
        """ move this firework """
        self.pos[0] += self.speed[0]
        self.pos[1] += self.speed[1]
        self.speed[0] /= 1.01
        self.speed[1] += 0.1

    def draw(self):
        """ draw this firework """
        if self.speed[1] < -1:
            posint = (int(self.pos[0]), int(self.pos[1]))
            pygame.draw.circle(SURFACE, self.color, posint, 1)
        else:
            self.count += 1
            for _ in range(4):
                self.scale += 0.06 / self.count
                rad = self.radius * self.scale
                for theta in range(0, 360, 36):
                    pos = (int(cos(radians(theta)) * rad \
                                + self.pos[0]),
                           int(sin(radians(theta)) * rad \
                                + self.pos[1]))
                    pygame.draw.circle(SURFACE, self.color, pos, 2)

            if self.count > 30:
                self.initialize()

def main():
    """ main routine """
    fires = []
    colors = ((255, 0, 0), (255, 255, 0), (225, 225, 225), \
        (255, 0, 255), (0, 255, 0), (128, 128, 255), (0, 255, 255))
    SURFACE.set_alpha(128)

    for index in range(14):
        fires.append(
            Firework(randint(0, 60) + 60, colors[index % 7]))

    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        for fire in fires:
            fire.move()

        surface = pygame.Surface((800, 600))
        surface.set_alpha(96)
        surface.fill((0, 0, 0))
        SURFACE.blit(surface, (0, 0))

        for fire in fires:
            fire.draw()

        pygame.display.update()
        FPSCLOCK.tick(10)

if __name__ == '__main__':
    main()