Created
July 22, 2017 11:08
-
-
Save peterhellberg/b63c0afa93cfbcc02cdab96be87f1de0 to your computer and use it in GitHub Desktop.
pixel-particles.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"image" | |
"image/color" | |
"image/draw" | |
"math" | |
"math/rand" | |
"time" | |
"github.com/faiface/pixel" | |
"github.com/faiface/pixel/pixelgl" | |
) | |
const ( | |
w, h = 512, 512 | |
fw, fh = float64(w), float64(h) | |
) | |
func flip() float64 { | |
if rand.Float64() > 0.5 { | |
return 1.0 | |
} | |
return -1.0 | |
} | |
func run() { | |
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{ | |
Bounds: pixel.R(0, 0, fw, fh), | |
Undecorated: true, | |
}) | |
if err != nil { | |
panic(err) | |
} | |
rand.Seed(time.Now().UnixNano()) | |
canvas := pixelgl.NewCanvas(win.Bounds()) | |
particles := []*particle{} | |
last := time.Now() | |
for !win.Closed() { | |
dt := time.Since(last).Seconds() | |
last = time.Now() | |
buffer := image.NewRGBA(image.Rect(0, 0, w, h)) | |
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ)) | |
if win.JustPressed(pixelgl.KeyC) { | |
particles = nil | |
draw.Draw(buffer, buffer.Bounds(), image.Transparent, image.ZP, draw.Src) | |
} | |
fx, fy := win.MousePosition().XY() | |
x, y := int(fx), int(fy) | |
fmt.Println("particles:", len(particles)) | |
if win.Pressed(pixelgl.MouseButtonLeft) { | |
c := color.RGBA{255, uint8(y % 255), uint8(x % 255), 255} | |
angle := 90.0 + rand.Float64()*80.0*flip() | |
speed := 30.0 + dt | |
life := 3.0 | |
particles = append(particles, | |
newParticle(fx, fy, angle, speed, life, c), | |
) | |
} | |
for _, p := range particles { | |
p.update(dt) | |
x, y := p.XY() | |
buffer.Set(x, y, p.color) | |
buffer.Set(x-1, y, p.color) | |
buffer.Set(x+1, y, p.color) | |
buffer.Set(x, y-1, p.color) | |
buffer.Set(x, y+1, p.color) | |
} | |
aliveParticles := []*particle{} | |
for _, p := range particles { | |
if p.life > 0 { | |
aliveParticles = append(aliveParticles, p) | |
} | |
} | |
particles = aliveParticles | |
win.Clear(color.RGBA{0, 0, 0, 255}) | |
canvas.SetPixels(buffer.Pix) | |
canvas.Draw(win, pixel.IM.Moved(win.Bounds().Center())) | |
win.Update() | |
} | |
} | |
func main() { | |
pixelgl.Run(run) | |
} | |
type particle struct { | |
position pixel.Vec | |
velocity pixel.Vec | |
color color.RGBA | |
life float64 | |
} | |
func newParticle(x, y, angle, speed, life float64, c color.RGBA) *particle { | |
angleInRadians := angle * math.Pi / 180 | |
return &particle{ | |
position: pixel.Vec{x, y}, | |
velocity: pixel.Vec{ | |
X: speed * math.Cos(angleInRadians), | |
Y: -speed * math.Sin(angleInRadians), | |
}, | |
life: life, | |
color: c, | |
} | |
} | |
func (p *particle) XY() (int, int) { | |
return int(p.position.X), int(p.position.Y) | |
} | |
func (p *particle) update(dt float64) { | |
p.life -= dt | |
if p.life > 0 { | |
p.position.X += p.velocity.X * dt | |
p.position.Y += p.velocity.Y * dt | |
if p.life > 1.5 { | |
if rand.Float64() < 0.4+dt { | |
p.color.R -= 1 | |
p.color.G -= 1 | |
p.color.B -= 1 | |
} | |
} else { | |
p.color.R -= 1 | |
p.color.G -= 1 | |
p.color.B -= 1 | |
} | |
} | |
} |
Author
peterhellberg
commented
Jul 22, 2017
A good article on the subject of particle systems: http://buildnewgames.com/particle-systems/
package main
import (
"image"
"image/color"
"image/draw"
"math"
"math/rand"
"time"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
)
const (
w, h = 512, 512
fw, fh = float64(w), float64(h)
)
func flip() float64 {
if rand.Float64() > 0.5 {
return 1.0
}
return -1.0
}
func run() {
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
Bounds: pixel.R(0, 0, fw, fh),
Undecorated: true,
})
if err != nil {
panic(err)
}
rand.Seed(time.Now().UnixNano())
canvas := pixelgl.NewCanvas(win.Bounds())
particles := []*particle{}
last := time.Now()
var total float64
explode := func(fx, fy float64) {
if len(particles) > 500 {
return
}
x := int(fx)
y := int(fy)
n := 30 + (6 * rand.Intn(10))
c := color.RGBA{255, uint8(int(total) + y%255), uint8((int(total) ^ x) % 255), 0}
for i := 0; i < n; i++ {
angle := 90.0 + rand.Float64()*180.0*flip()
speed := (2.0 * float64(n)) + (float64(i) * rand.Float64())
life := 0.1 + (1.3 * rand.Float64())
particles = append(particles,
newParticle(fx, fy, angle, speed, life, c),
)
}
}
go func() {
for {
time.Sleep(2700 * time.Millisecond)
explode(rand.Float64()*fw, (rand.Float64()*fh)/2+(fh/2))
}
}()
for !win.Closed() {
dt := time.Since(last).Seconds()
last = time.Now()
total += dt
buffer := image.NewRGBA(image.Rect(0, 0, w, h))
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
if win.JustPressed(pixelgl.KeyC) {
particles = nil
draw.Draw(buffer, buffer.Bounds(), image.Transparent, image.ZP, draw.Src)
}
if win.Pressed(pixelgl.MouseButtonLeft) {
explode(win.MousePosition().XY())
}
if win.JustPressed(pixelgl.KeyS) {
explode(rand.Float64()*fw, (rand.Float64()*fh)/2+(fh/2))
}
for _, p := range particles {
p.update(dt)
x := int(p.position.X)
y := int(p.position.Y)
buffer.Set(x, y, p.color)
buffer.Set(x-1, y, p.color)
buffer.Set(x+1, y, p.color)
buffer.Set(x, y-1, p.color)
buffer.Set(x, y+1, p.color)
}
aliveParticles := []*particle{}
for _, p := range particles {
if p.life > 0 {
aliveParticles = append(aliveParticles, p)
}
}
particles = aliveParticles
win.Clear(color.RGBA{0, 0, 0, 255})
canvas.SetPixels(buffer.Pix)
canvas.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
type particle struct {
angle float64
speed float64
position pixel.Vec
velocity pixel.Vec
color color.RGBA
life float64
}
func newParticle(x, y, angle, speed, life float64, c color.RGBA) *particle {
angleInRadians := angle * math.Pi / 180
return &particle{
angle: angle,
speed: speed,
position: pixel.Vec{x, y},
velocity: pixel.Vec{
X: speed * math.Cos(angleInRadians),
Y: -speed * math.Sin(angleInRadians),
},
life: life,
color: c,
}
}
func (p *particle) update(dt float64) {
p.life -= dt
if p.life > 0 {
p.position.X += p.velocity.X * dt
p.position.Y += p.velocity.Y * dt
if p.life > 1.0 {
if rand.Float64() < 0.6 {
p.color.G -= 1
p.color.B -= 1
}
} else {
p.color.R -= 2
p.color.G -= 1
p.color.B -= 1
}
}
}
package main
import (
"image"
"image/color"
"image/draw"
"math"
"math/rand"
"time"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
)
const (
w, h = 512, 256
fw, fh = float64(w), float64(h)
)
func run() {
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
Bounds: pixel.R(0, 0, fw, fh),
Undecorated: true,
})
if err != nil {
panic(err)
}
rand.Seed(time.Now().UnixNano())
canvas := pixelgl.NewCanvas(win.Bounds())
particles := []*particle{}
last := time.Now()
var total float64
angle := 90.0
white := color.RGBA{255, 255, 255, 255}
fall := func(fx, fy float64) {
speed := 32.0 + (32.0 * rand.Float64())
life := 16.0 + (1.6 * rand.Float64())
particles = append(particles,
newParticle(fx, fy, angle, speed, life, white),
)
}
go func() {
for {
time.Sleep(40 * time.Millisecond)
fall(rand.Float64()*fw, fh-(rand.Float64()*fh)/2+(fh/2))
}
}()
for !win.Closed() {
dt := time.Since(last).Seconds()
last = time.Now()
total += dt
buffer := image.NewRGBA(image.Rect(0, 0, w, h))
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
if win.JustPressed(pixelgl.KeyC) {
particles = nil
draw.Draw(buffer, buffer.Bounds(), image.Transparent, image.ZP, draw.Src)
}
if win.Pressed(pixelgl.KeyS) {
fall(rand.Float64()*fw, fh-(rand.Float64()*fh)/2+(fh/2))
}
if win.JustPressed(pixelgl.KeyLeft) {
angle += 4
}
if win.JustPressed(pixelgl.KeyRight) {
angle -= 4
}
for _, p := range particles {
p.update(dt)
x := int(p.position.X)
y := int(p.position.Y)
buffer.Set(x, y, p.color)
buffer.Set(x-1, y, p.color)
buffer.Set(x+1, y, p.color)
buffer.Set(x, y-1, p.color)
buffer.Set(x, y+1, p.color)
}
aliveParticles := []*particle{}
for _, p := range particles {
if p.life > 0 {
aliveParticles = append(aliveParticles, p)
}
}
particles = aliveParticles
win.Clear(color.RGBA{0, 0, 0, 255})
canvas.SetPixels(buffer.Pix)
canvas.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
type particle struct {
position pixel.Vec
velocity pixel.Vec
color color.RGBA
life float64
}
func newParticle(x, y, angle, speed, life float64, c color.RGBA) *particle {
angleInRadians := angle * math.Pi / 180
return &particle{
position: pixel.Vec{x, y},
velocity: pixel.Vec{
X: speed * math.Cos(angleInRadians),
Y: -speed * math.Sin(angleInRadians),
},
life: life,
color: c,
}
}
func (p *particle) update(dt float64) {
p.life -= dt * 3
if p.life > 0 {
p.position.X += p.velocity.X * dt
p.position.Y += p.velocity.Y * dt
if p.life < 10 && p.color.R > 0 {
if rand.Float64() < 0.3 {
p.color.R -= 1
p.color.G -= 1
p.color.B -= 1
}
}
}
}
package main
import (
"image"
"image/color"
"image/draw"
"math"
"math/rand"
"time"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
)
const (
w, h = 512, 512
fw, fh = float64(w), float64(h)
)
func run() {
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
Bounds: pixel.R(0, 0, fw, fh),
Undecorated: true,
})
if err != nil {
panic(err)
}
rand.Seed(time.Now().UnixNano())
canvas := pixelgl.NewCanvas(win.Bounds())
particles := []*particle{}
last := time.Now()
var total float64
angle := 90.0
white := color.RGBA{255, 255, 255, 255}
fall := func(fx, fy float64) {
speed := 25.0 + (25.0 * rand.Float64())
life := 10.1 + (1.3 * rand.Float64())
particles = append(particles,
newParticle(fx, fy, angle, speed, life, white),
)
}
go func() {
for {
time.Sleep(100 * time.Millisecond)
fall(rand.Float64()*fw, (rand.Float64()*fh)/2+(fh/2))
}
}()
for !win.Closed() {
dt := time.Since(last).Seconds()
last = time.Now()
total += dt
buffer := image.NewRGBA(image.Rect(0, 0, w, h))
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
if win.JustPressed(pixelgl.KeyC) {
particles = nil
draw.Draw(buffer, buffer.Bounds(), image.Transparent, image.ZP, draw.Src)
}
if win.Pressed(pixelgl.MouseButtonLeft) {
fall(win.MousePosition().XY())
}
if win.JustPressed(pixelgl.KeyS) {
fall(rand.Float64()*fw, (rand.Float64()*fh)/2+(fh/2))
}
for _, p := range particles {
p.update(dt)
x := int(p.position.X)
y := int(p.position.Y)
buffer.Set(x, y, p.color)
buffer.Set(x-1, y, p.color)
buffer.Set(x+1, y, p.color)
buffer.Set(x, y-1, p.color)
buffer.Set(x, y+1, p.color)
}
aliveParticles := []*particle{}
for _, p := range particles {
if p.life > 0 {
aliveParticles = append(aliveParticles, p)
}
}
particles = aliveParticles
win.Clear(color.RGBA{0, 0, 0, 255})
canvas.SetPixels(buffer.Pix)
canvas.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
type particle struct {
angle float64
speed float64
position pixel.Vec
velocity pixel.Vec
color color.RGBA
life float64
}
func newParticle(x, y, angle, speed, life float64, c color.RGBA) *particle {
angleInRadians := angle * math.Pi / 180
return &particle{
angle: angle,
speed: speed,
position: pixel.Vec{x, y},
velocity: pixel.Vec{
X: speed * math.Cos(angleInRadians),
Y: -speed * math.Sin(angleInRadians),
},
life: life,
color: c,
}
}
func (p *particle) update(dt float64) {
p.life -= dt
if p.life > 0 {
p.position.X += p.velocity.X * dt
p.position.Y += p.velocity.Y * dt
if p.life > 1.0 {
if rand.Float64() < 0.6 {
p.color.G -= 1
p.color.B -= 1
}
} else {
p.color.R -= 2
p.color.G -= 1
p.color.B -= 1
}
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment