pixel-examples/community/amidakuji/glossary/starfield.go

167 lines
4.2 KiB
Go
Raw Normal View History

2018-08-26 07:45:01 -05:00
package glossary
import (
"image/color"
"math/rand"
"sync"
"github.com/faiface/pixel"
"github.com/faiface/pixel/imdraw"
)
// -------------------------------------------------------------------------
// Reusable modified starfiled
// - Original: "github.com/faiface/pixel-examples/community/starfield"
2018-08-26 07:45:01 -05:00
// - Encapsulated by nanitefactory
// -------------------------------------------------------------------------
// Galaxy
type star struct {
Pos pixel.Vec // x, y
Z float64 // z
P float64 // prev z
C color.RGBA // color
}
// Galaxy is an imd of stars.
type Galaxy struct {
imd *imdraw.IMDraw // shared variable
mutex sync.Mutex // synchronize
//
width float64
height float64
speed float64
stars [1024]*star
}
// NewGalaxy is a constructor.
func NewGalaxy(_width, _height, _speed float64) *Galaxy {
return &Galaxy{
width: _width,
height: _height,
speed: _speed,
}
}
// Speed is a getter.
// Pass lock by value warning from (galaxy Galaxy) should be ignored,
// because a galaxy here is just passed as a read only argument.
func (galaxy Galaxy) Speed() float64 {
return galaxy.speed
}
// SetSpeed is a setter.
func (galaxy *Galaxy) SetSpeed(_speed float64) {
galaxy.speed = _speed
}
// Draw guarantees the thread safety, though it's not a necessary condition.
// It is quite dangerous to access this struct's member (imdraw) directly from outside these methods.
func (galaxy *Galaxy) Draw(t pixel.Target) {
galaxy.mutex.Lock()
defer galaxy.mutex.Unlock()
if galaxy.imd == nil { // isInvisible set to true.
return // An empty image is drawn.
}
galaxy.imd.Draw(t)
}
// Update animates a galaxy.
func (galaxy *Galaxy) Update(dt float64) {
// random()
random := func(min, max float64) float64 {
return rand.Float64()*(max-min) + min
}
// newStar()
newStar := func() *star {
starColors := []color.RGBA{
color.RGBA{157, 180, 255, 255},
color.RGBA{162, 185, 255, 255},
color.RGBA{167, 188, 255, 255},
color.RGBA{170, 191, 255, 255},
color.RGBA{175, 195, 255, 255},
color.RGBA{186, 204, 255, 255},
color.RGBA{192, 209, 255, 255},
color.RGBA{202, 216, 255, 255},
color.RGBA{228, 232, 255, 255},
color.RGBA{237, 238, 255, 255},
color.RGBA{251, 248, 255, 255},
color.RGBA{255, 249, 249, 255},
color.RGBA{255, 245, 236, 255},
color.RGBA{255, 244, 232, 255},
color.RGBA{255, 241, 223, 255},
color.RGBA{255, 235, 209, 255},
color.RGBA{255, 215, 174, 255},
color.RGBA{255, 198, 144, 255},
color.RGBA{255, 190, 127, 255},
color.RGBA{255, 187, 123, 255},
color.RGBA{255, 187, 123, 255},
} // Colors based on stellar types listed at // http://www.vendian.org/mncharity/dir3/starcolor/
return &star{
Pos: pixel.V(random(-galaxy.width, galaxy.width), random(-galaxy.height, galaxy.height)),
Z: random(0, galaxy.width),
P: 0,
C: starColors[rand.Intn(len(starColors))],
}
}
// lock before imdraw update
galaxy.mutex.Lock()
defer galaxy.mutex.Unlock()
// imdraw (a state machine)
if galaxy.imd == nil { // lazy creation
galaxy.imd = imdraw.New(nil)
galaxy.imd.SetMatrix(pixel.IM.Moved(pixel.V(galaxy.width/2, galaxy.height/2)))
}
imd := galaxy.imd
imd.Clear()
imd.Precision = 7
// now update all stars in this galaxy
for i, s := range galaxy.stars {
if s == nil {
galaxy.stars[i] = newStar()
s = galaxy.stars[i]
}
scale := func(unscaledNum, min, max, minAllowed, maxAllowed float64) float64 {
return (maxAllowed-minAllowed)*(unscaledNum-min)/(max-min) + minAllowed
}
s.P = s.Z
s.Z -= dt * galaxy.speed
if s.Z < 0 {
s.Pos.X = random(-galaxy.width, galaxy.width)
s.Pos.Y = random(-galaxy.height, galaxy.height)
s.Z = galaxy.width
s.P = s.Z
}
p := pixel.V(
scale(s.Pos.X/s.Z, 0, 1, 0, galaxy.width),
scale(s.Pos.Y/s.Z, 0, 1, 0, galaxy.height),
)
o := pixel.V(
scale(s.Pos.X/s.P, 0, 1, 0, galaxy.width),
scale(s.Pos.Y/s.P, 0, 1, 0, galaxy.height),
)
r := scale(s.Z, 0, galaxy.width, 11, 0)
galaxy.imd.Color = s.C
if p.Sub(o).Len() > 6 {
galaxy.imd.Push(p, o)
galaxy.imd.Line(r)
}
galaxy.imd.Push(p)
galaxy.imd.Circle(r, 0)
}
}