dep -> govendor
16
vendor/github.com/faiface/pixel/examples/community/bouncing/README.md
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# bouncing
|
||||
|
||||
Bouncing particles using the [imdraw](https://godoc.org/github.com/faiface/pixel/imdraw) package.
|
||||
|
||||
Made by [Peter Hellberg](https://github.com/peterhellberg/) as part of his [pixel-experiments](https://github.com/peterhellberg/pixel-experiments)
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Links
|
||||
|
||||
- https://github.com/peterhellberg/pixel-experiments/tree/master/bouncing
|
||||
- https://gist.github.com/peterhellberg/674f32a15a7d2d249e634ce781f333e8
|
303
vendor/github.com/faiface/pixel/examples/community/bouncing/bouncing.go
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
var (
|
||||
w, h, s, scale = float64(640), float64(360), float64(2.3), float64(32)
|
||||
|
||||
p, bg = newPalette(Colors), color.RGBA{32, p.color().G, 32, 255}
|
||||
|
||||
balls = []*ball{
|
||||
newRandomBall(scale),
|
||||
newRandomBall(scale),
|
||||
}
|
||||
)
|
||||
|
||||
func run() {
|
||||
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
|
||||
Bounds: pixel.R(0, 0, w, h),
|
||||
VSync: true,
|
||||
Undecorated: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
imd := imdraw.New(nil)
|
||||
|
||||
imd.EndShape = imdraw.RoundEndShape
|
||||
imd.Precision = 3
|
||||
|
||||
go func() {
|
||||
start := time.Now()
|
||||
|
||||
for range time.Tick(16 * time.Millisecond) {
|
||||
bg = color.RGBA{32 + (p.color().R/128)*4, 32 + (p.color().G/128)*4, 32 + (p.color().B/128)*4, 255}
|
||||
s = pixel.V(math.Sin(time.Since(start).Seconds())*0.8, 0).Len()*2 - 1
|
||||
scale = 64 + 15*s
|
||||
imd.Intensity = 1.2 * s
|
||||
}
|
||||
}()
|
||||
|
||||
for !win.Closed() {
|
||||
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
|
||||
|
||||
if win.JustPressed(pixelgl.KeySpace) {
|
||||
for _, ball := range balls {
|
||||
ball.color = ball.palette.next()
|
||||
}
|
||||
}
|
||||
|
||||
if win.JustPressed(pixelgl.KeyEnter) {
|
||||
for _, ball := range balls {
|
||||
ball.pos = center()
|
||||
ball.vel = randomVelocity()
|
||||
}
|
||||
}
|
||||
|
||||
imd.Clear()
|
||||
|
||||
for _, ball := range balls {
|
||||
imd.Color = ball.color
|
||||
imd.Push(ball.pos)
|
||||
}
|
||||
|
||||
imd.Polygon(scale)
|
||||
|
||||
for _, ball := range balls {
|
||||
imd.Color = color.RGBA{ball.color.R, ball.color.G, ball.color.B, 128 - uint8(128*s)}
|
||||
imd.Push(ball.pos)
|
||||
}
|
||||
|
||||
imd.Polygon(scale * s)
|
||||
|
||||
for _, ball := range balls {
|
||||
aliveParticles := []*particle{}
|
||||
|
||||
for _, particle := range ball.particles {
|
||||
if particle.life > 0 {
|
||||
aliveParticles = append(aliveParticles, particle)
|
||||
}
|
||||
}
|
||||
|
||||
for _, particle := range aliveParticles {
|
||||
imd.Color = particle.color
|
||||
imd.Push(particle.pos)
|
||||
imd.Circle(16*particle.life, 0)
|
||||
}
|
||||
}
|
||||
|
||||
win.Clear(bg)
|
||||
imd.Draw(win)
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(4)
|
||||
|
||||
go func() {
|
||||
for range time.Tick(32 * time.Millisecond) {
|
||||
for _, ball := range balls {
|
||||
go ball.update()
|
||||
|
||||
for _, particle := range ball.particles {
|
||||
go particle.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
pixelgl.Run(run)
|
||||
}
|
||||
|
||||
func newParticleAt(pos, vel pixel.Vec) *particle {
|
||||
c := p.color()
|
||||
c.A = 5
|
||||
|
||||
return &particle{pos, vel, c, rand.Float64() * 1.5}
|
||||
}
|
||||
|
||||
func newRandomBall(radius float64) *ball {
|
||||
return &ball{
|
||||
center(), randomVelocity(),
|
||||
math.Pi * (radius * radius),
|
||||
radius, p.random(), p, []*particle{},
|
||||
}
|
||||
}
|
||||
|
||||
func center() pixel.Vec {
|
||||
return pixel.V(w/2, h/2)
|
||||
}
|
||||
|
||||
func randomVelocity() pixel.Vec {
|
||||
return pixel.V((rand.Float64()*2)-1, (rand.Float64()*2)-1).Scaled(scale / 4)
|
||||
}
|
||||
|
||||
type particle struct {
|
||||
pos pixel.Vec
|
||||
vel pixel.Vec
|
||||
color color.RGBA
|
||||
life float64
|
||||
}
|
||||
|
||||
func (p *particle) update() {
|
||||
p.pos = p.pos.Add(p.vel)
|
||||
p.life -= 0.03
|
||||
|
||||
switch {
|
||||
case p.pos.Y < 0 || p.pos.Y >= h:
|
||||
p.vel.Y *= -1.0
|
||||
case p.pos.X < 0 || p.pos.X >= w:
|
||||
p.vel.X *= -1.0
|
||||
}
|
||||
}
|
||||
|
||||
type ball struct {
|
||||
pos pixel.Vec
|
||||
vel pixel.Vec
|
||||
mass float64
|
||||
radius float64
|
||||
color color.RGBA
|
||||
palette *Palette
|
||||
particles []*particle
|
||||
}
|
||||
|
||||
func (b *ball) update() {
|
||||
b.pos = b.pos.Add(b.vel)
|
||||
|
||||
var bounced bool
|
||||
|
||||
switch {
|
||||
case b.pos.Y <= b.radius || b.pos.Y >= h-b.radius:
|
||||
b.vel.Y *= -1.0
|
||||
bounced = true
|
||||
|
||||
if b.pos.Y < b.radius {
|
||||
b.pos.Y = b.radius
|
||||
} else {
|
||||
b.pos.Y = h - b.radius
|
||||
}
|
||||
case b.pos.X <= b.radius || b.pos.X >= w-b.radius:
|
||||
b.vel.X *= -1.0
|
||||
bounced = true
|
||||
|
||||
if b.pos.X < b.radius {
|
||||
b.pos.X = b.radius
|
||||
} else {
|
||||
b.pos.X = w - b.radius
|
||||
}
|
||||
}
|
||||
|
||||
for _, a := range balls {
|
||||
if a != b {
|
||||
d := a.pos.Sub(b.pos)
|
||||
|
||||
if d.Len() > a.radius+b.radius {
|
||||
continue
|
||||
}
|
||||
|
||||
pen := d.Unit().Scaled(a.radius + b.radius - d.Len())
|
||||
|
||||
a.pos = a.pos.Add(pen.Scaled(b.mass / (a.mass + b.mass)))
|
||||
b.pos = b.pos.Sub(pen.Scaled(a.mass / (a.mass + b.mass)))
|
||||
|
||||
u := d.Unit()
|
||||
v := 2 * (a.vel.Dot(u) - b.vel.Dot(u)) / (a.mass + b.mass)
|
||||
|
||||
a.vel = a.vel.Sub(u.Scaled(v * b.mass))
|
||||
b.vel = b.vel.Add(u.Scaled(v * a.mass))
|
||||
|
||||
bounced = true
|
||||
}
|
||||
}
|
||||
|
||||
if bounced {
|
||||
b.color = p.next()
|
||||
b.particles = append(b.particles,
|
||||
newParticleAt(b.pos, b.vel.Rotated(1).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(2).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(3).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(4).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(5).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(6).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(7).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(8).Scaled(rand.Float64())),
|
||||
newParticleAt(b.pos, b.vel.Rotated(9).Scaled(rand.Float64())),
|
||||
|
||||
newParticleAt(b.pos, b.vel.Rotated(10).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(20).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(30).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(40).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(50).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(60).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(70).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(80).Scaled(rand.Float64()+1)),
|
||||
newParticleAt(b.pos, b.vel.Rotated(90).Scaled(rand.Float64()+1)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func newPalette(cc []color.Color) *Palette {
|
||||
colors := []color.RGBA{}
|
||||
|
||||
for _, v := range cc {
|
||||
if c, ok := v.(color.RGBA); ok {
|
||||
colors = append(colors, c)
|
||||
}
|
||||
}
|
||||
|
||||
return &Palette{colors, len(colors), 0}
|
||||
}
|
||||
|
||||
type Palette struct {
|
||||
colors []color.RGBA
|
||||
size int
|
||||
index int
|
||||
}
|
||||
|
||||
func (p *Palette) clone() *Palette {
|
||||
return &Palette{p.colors, p.size, p.index}
|
||||
}
|
||||
|
||||
func (p *Palette) next() color.RGBA {
|
||||
if p.index++; p.index >= p.size {
|
||||
p.index = 0
|
||||
}
|
||||
|
||||
return p.colors[p.index]
|
||||
}
|
||||
|
||||
func (p *Palette) color() color.RGBA {
|
||||
return p.colors[p.index]
|
||||
}
|
||||
|
||||
func (p *Palette) random() color.RGBA {
|
||||
p.index = rand.Intn(p.size)
|
||||
|
||||
return p.colors[p.index]
|
||||
}
|
||||
|
||||
var Colors = []color.Color{
|
||||
color.RGBA{190, 38, 51, 255},
|
||||
color.RGBA{224, 111, 139, 255},
|
||||
color.RGBA{73, 60, 43, 255},
|
||||
color.RGBA{164, 100, 34, 255},
|
||||
color.RGBA{235, 137, 49, 255},
|
||||
color.RGBA{247, 226, 107, 255},
|
||||
color.RGBA{47, 72, 78, 255},
|
||||
color.RGBA{68, 137, 26, 255},
|
||||
color.RGBA{163, 206, 39, 255},
|
||||
color.RGBA{0, 87, 132, 255},
|
||||
color.RGBA{49, 162, 242, 255},
|
||||
color.RGBA{178, 220, 239, 255},
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/community/bouncing/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 6.9 KiB |
20
vendor/github.com/faiface/pixel/examples/community/game_of_life/README.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# Conway's Game of Lfe
|
||||
|
||||
Created by [Nathan Leniz](https://github.com/terakilobyte).
|
||||
Inspired by and heavily uses [the doc](https://golang.org/doc/play/life.go)
|
||||
|
||||
> The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970. The "game" is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input. One interacts with the Game of Life by creating an initial configuration and observing how it evolves, or, for advanced "players", by creating patterns with particular properties. The Game has been reprogrammed multiple times in various coding languages.
|
||||
|
||||
For more information, please see the [wikipedia](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life) article.
|
||||
|
||||
## Use
|
||||
|
||||
go run main.go -h
|
||||
-frameRate duration
|
||||
The framerate in milliseconds (default 33ms)
|
||||
-size int
|
||||
The size of each cell (default 5)
|
||||
-windowSize float
|
||||
The pixel size of one side of the grid (default 800)
|
||||
|
||||

|
BIN
vendor/github.com/faiface/pixel/examples/community/game_of_life/life.png
generated
vendored
Normal file
After Width: | Height: | Size: 86 KiB |
74
vendor/github.com/faiface/pixel/examples/community/game_of_life/life/grid.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package life
|
||||
|
||||
import (
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
// Shamelessly taken/inspired by https://golang.org/doc/play/life.go
|
||||
// Grid is the structure in which the cellular automota live
|
||||
type Grid struct {
|
||||
h int
|
||||
cellSize int
|
||||
Cells [][]bool
|
||||
}
|
||||
|
||||
// NewGrid constructs a new Grid
|
||||
func NewGrid(h, size int) *Grid {
|
||||
cells := make([][]bool, h)
|
||||
for i := 0; i < h; i++ {
|
||||
cells[i] = make([]bool, h)
|
||||
}
|
||||
return &Grid{h: h, cellSize: size, Cells: cells}
|
||||
}
|
||||
|
||||
// Alive returns whether the specified position is alive
|
||||
func (g *Grid) Alive(x, y int) bool {
|
||||
x += g.h
|
||||
x %= g.h
|
||||
y += g.h
|
||||
y %= g.h
|
||||
return g.Cells[y][x]
|
||||
}
|
||||
|
||||
// Set sets the state of a specific location
|
||||
func (g *Grid) Set(x, y int, state bool) {
|
||||
g.Cells[y][x] = state
|
||||
}
|
||||
|
||||
// Draw draws the grid
|
||||
func (g *Grid) Draw(imd *imdraw.IMDraw) {
|
||||
for i := 0; i < g.h; i++ {
|
||||
for j := 0; j < g.h; j++ {
|
||||
if g.Alive(i, j) {
|
||||
imd.Color = colornames.Black
|
||||
} else {
|
||||
imd.Color = colornames.White
|
||||
}
|
||||
imd.Push(
|
||||
pixel.V(float64(i*g.cellSize), float64(j*g.cellSize)),
|
||||
pixel.V(float64(i*g.cellSize+g.cellSize), float64(j*g.cellSize+g.cellSize)),
|
||||
)
|
||||
imd.Rectangle(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next state
|
||||
func (g *Grid) Next(x, y int) bool {
|
||||
// Count the adjacent cells that are alive.
|
||||
alive := 0
|
||||
for i := -1; i <= 1; i++ {
|
||||
for j := -1; j <= 1; j++ {
|
||||
if (j != 0 || i != 0) && g.Alive(x+i, y+j) {
|
||||
alive++
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return next state according to the game rules:
|
||||
// exactly 3 neighbors: on,
|
||||
// exactly 2 neighbors: maintain current state,
|
||||
// otherwise: off.
|
||||
return alive == 3 || alive == 2 && g.Alive(x, y)
|
||||
}
|
35
vendor/github.com/faiface/pixel/examples/community/game_of_life/life/life.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Package life manages the "game" state
|
||||
// Shamelessly taken from https://golang.org/doc/play/life.go
|
||||
package life
|
||||
|
||||
import "math/rand"
|
||||
|
||||
// Life stores the state of a round of Conway's Game of Life.
|
||||
type Life struct {
|
||||
A, b *Grid
|
||||
h int
|
||||
}
|
||||
|
||||
// NewLife returns a new Life game state with a random initial state.
|
||||
func NewLife(h, size int) *Life {
|
||||
a := NewGrid(h, size)
|
||||
for i := 0; i < (h * h / 2); i++ {
|
||||
a.Set(rand.Intn(h), rand.Intn(h), true)
|
||||
}
|
||||
return &Life{
|
||||
A: a, b: NewGrid(h, size),
|
||||
h: h,
|
||||
}
|
||||
}
|
||||
|
||||
// Step advances the game by one instant, recomputing and updating all cells.
|
||||
func (l *Life) Step() {
|
||||
// Update the state of the next field (b) from the current field (a).
|
||||
for y := 0; y < l.h; y++ {
|
||||
for x := 0; x < l.h; x++ {
|
||||
l.b.Set(x, y, l.A.Next(x, y))
|
||||
}
|
||||
}
|
||||
// Swap fields a and b.
|
||||
l.A, l.b = l.b, l.A
|
||||
}
|
64
vendor/github.com/faiface/pixel/examples/community/game_of_life/main.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"golang.org/x/image/colornames"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/examples/community/game_of_life/life"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
var (
|
||||
size *int
|
||||
windowSize *float64
|
||||
frameRate *time.Duration
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
size = flag.Int("size", 5, "The size of each cell")
|
||||
windowSize = flag.Float64("windowSize", 800, "The pixel size of one side of the grid")
|
||||
frameRate = flag.Duration("frameRate", 33*time.Millisecond, "The framerate in milliseconds")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
||||
|
||||
func run() {
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, *windowSize, *windowSize),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
win.Clear(colornames.White)
|
||||
|
||||
// since the game board is square, rows and cols will be the same
|
||||
rows := int(*windowSize) / *size
|
||||
|
||||
gridDraw := imdraw.New(nil)
|
||||
game := life.NewLife(rows, *size)
|
||||
tick := time.Tick(*frameRate)
|
||||
for !win.Closed() {
|
||||
// game loop
|
||||
select {
|
||||
case <-tick:
|
||||
gridDraw.Clear()
|
||||
game.A.Draw(gridDraw)
|
||||
gridDraw.Draw(win)
|
||||
game.Step()
|
||||
}
|
||||
win.Update()
|
||||
}
|
||||
}
|
13
vendor/github.com/faiface/pixel/examples/community/isometric-basics/README.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Isometric view basics
|
||||
|
||||
Created by [Sergio Vera](https://github.com/svera).
|
||||
|
||||
Isometric view is a display method used to create an illusion of 3D for an otherwise 2D game - sometimes referred to as pseudo 3D or 2.5D.
|
||||
|
||||
Implementing an isometric view can be done in many ways, but for the sake of simplicity we'll implement a tile-based approach, which is the most efficient and widely used method.
|
||||
|
||||
In the tile-based approach, each visual element is broken down into smaller pieces, called tiles, of a standard size. These tiles will be arranged to form the game world according to pre-determined level data - usually a 2D array.
|
||||
|
||||
For a detailed explanation about the maths behind this, read [http://clintbellanger.net/articles/isometric_math/](http://clintbellanger.net/articles/isometric_math/).
|
||||
|
||||

|
BIN
vendor/github.com/faiface/pixel/examples/community/isometric-basics/castle.png
generated
vendored
Normal file
After Width: | Height: | Size: 26 KiB |
102
vendor/github.com/faiface/pixel/examples/community/isometric-basics/main.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"os"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
|
||||
_ "image/png"
|
||||
)
|
||||
|
||||
const (
|
||||
windowWidth = 800
|
||||
windowHeight = 800
|
||||
// sprite tiles are squared, 64x64 size
|
||||
tileSize = 64
|
||||
f = 0 // floor identifier
|
||||
w = 1 // wall identifier
|
||||
)
|
||||
|
||||
var levelData = [][]uint{
|
||||
{f, f, f, f, f, f}, // This row will be rendered in the lower left part of the screen (closer to the viewer)
|
||||
{w, f, f, f, f, w},
|
||||
{w, f, f, f, f, w},
|
||||
{w, f, f, f, f, w},
|
||||
{w, f, f, f, f, w},
|
||||
{w, w, w, w, w, w}, // And this in the upper right
|
||||
}
|
||||
var win *pixelgl.Window
|
||||
var offset = pixel.V(400, 325)
|
||||
var floorTile, wallTile *pixel.Sprite
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
var err error
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Isometric demo",
|
||||
Bounds: pixel.R(0, 0, windowWidth, windowHeight),
|
||||
VSync: true,
|
||||
}
|
||||
win, err = pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pic, err := loadPicture("castle.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
wallTile = pixel.NewSprite(pic, pixel.R(0, 448, tileSize, 512))
|
||||
floorTile = pixel.NewSprite(pic, pixel.R(0, 128, tileSize, 192))
|
||||
|
||||
depthSort()
|
||||
|
||||
for !win.Closed() {
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
// Draw level data tiles to window, from farthest to closest.
|
||||
// In order to achieve the depth effect, we need to render tiles up to down, being lower
|
||||
// closer to the viewer (see painter's algorithm). To do that, we need to process levelData in reverse order,
|
||||
// so its first row is rendered last, as OpenGL considers its origin to be in the lower left corner of the display.
|
||||
func depthSort() {
|
||||
for x := len(levelData) - 1; x >= 0; x-- {
|
||||
for y := len(levelData[x]) - 1; y >= 0; y-- {
|
||||
isoCoords := cartesianToIso(pixel.V(float64(x), float64(y)))
|
||||
mat := pixel.IM.Moved(offset.Add(isoCoords))
|
||||
// Not really needed, just put to show bigger blocks
|
||||
mat = mat.ScaledXY(win.Bounds().Center(), pixel.V(2, 2))
|
||||
tileType := levelData[x][y]
|
||||
if tileType == f {
|
||||
floorTile.Draw(win, mat)
|
||||
} else {
|
||||
wallTile.Draw(win, mat)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cartesianToIso(pt pixel.Vec) pixel.Vec {
|
||||
return pixel.V((pt.X-pt.Y)*(tileSize/2), (pt.X+pt.Y)*(tileSize/4))
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/community/isometric-basics/result.png
generated
vendored
Normal file
After Width: | Height: | Size: 126 KiB |
19
vendor/github.com/faiface/pixel/examples/community/maze/README.md
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Maze generator in Go
|
||||
|
||||
Created by [Stephen Chavez](https://github.com/redragonx)
|
||||
|
||||
This uses the game engine: Pixel. Install it here: https://github.com/faiface/pixel
|
||||
|
||||
I made this to improve my understanding of Go and some game concepts with some basic maze generating algorithms.
|
||||
|
||||
Controls: Press 'R' to restart the maze.
|
||||
|
||||
Optional command-line arguments: `go run ./maze-generator.go`
|
||||
- `-w` sets the maze's width in pixels.
|
||||
- `-h` sets the maze's height in pixels.
|
||||
- `-c` sets the maze cell's size in pixels.
|
||||
|
||||
Code based on the Recursive backtracker algorithm.
|
||||
- https://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker
|
||||
|
||||

|
317
vendor/github.com/faiface/pixel/examples/community/maze/maze-generator.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
package main
|
||||
|
||||
// Code based on the Recursive backtracker algorithm.
|
||||
// https://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_backtracker
|
||||
// See https://youtu.be/HyK_Q5rrcr4 as an example
|
||||
// YouTube example ported to Go for the Pixel library.
|
||||
|
||||
// Created by Stephen Chavez
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/examples/community/maze/stack"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
|
||||
"github.com/pkg/profile"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
var visitedColor = pixel.RGB(0.5, 0, 1).Mul(pixel.Alpha(0.35))
|
||||
var hightlightColor = pixel.RGB(0.3, 0, 0).Mul(pixel.Alpha(0.45))
|
||||
var debug = false
|
||||
|
||||
type cell struct {
|
||||
walls [4]bool // Wall order: top, right, bottom, left
|
||||
|
||||
row int
|
||||
col int
|
||||
visited bool
|
||||
}
|
||||
|
||||
func (c *cell) Draw(imd *imdraw.IMDraw, wallSize int) {
|
||||
drawCol := c.col * wallSize // x
|
||||
drawRow := c.row * wallSize // y
|
||||
|
||||
imd.Color = colornames.White
|
||||
if c.walls[0] {
|
||||
// top line
|
||||
imd.Push(pixel.V(float64(drawCol), float64(drawRow)), pixel.V(float64(drawCol+wallSize), float64(drawRow)))
|
||||
imd.Line(3)
|
||||
}
|
||||
if c.walls[1] {
|
||||
// right Line
|
||||
imd.Push(pixel.V(float64(drawCol+wallSize), float64(drawRow)), pixel.V(float64(drawCol+wallSize), float64(drawRow+wallSize)))
|
||||
imd.Line(3)
|
||||
}
|
||||
if c.walls[2] {
|
||||
// bottom line
|
||||
imd.Push(pixel.V(float64(drawCol+wallSize), float64(drawRow+wallSize)), pixel.V(float64(drawCol), float64(drawRow+wallSize)))
|
||||
imd.Line(3)
|
||||
}
|
||||
if c.walls[3] {
|
||||
// left line
|
||||
imd.Push(pixel.V(float64(drawCol), float64(drawRow+wallSize)), pixel.V(float64(drawCol), float64(drawRow)))
|
||||
imd.Line(3)
|
||||
}
|
||||
imd.EndShape = imdraw.SharpEndShape
|
||||
|
||||
if c.visited {
|
||||
imd.Color = visitedColor
|
||||
imd.Push(pixel.V(float64(drawCol), (float64(drawRow))), pixel.V(float64(drawCol+wallSize), float64(drawRow+wallSize)))
|
||||
imd.Rectangle(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cell) GetNeighbors(grid []*cell, cols int, rows int) ([]*cell, error) {
|
||||
neighbors := []*cell{}
|
||||
j := c.row
|
||||
i := c.col
|
||||
|
||||
top, _ := getCellAt(i, j-1, cols, rows, grid)
|
||||
right, _ := getCellAt(i+1, j, cols, rows, grid)
|
||||
bottom, _ := getCellAt(i, j+1, cols, rows, grid)
|
||||
left, _ := getCellAt(i-1, j, cols, rows, grid)
|
||||
|
||||
if top != nil && !top.visited {
|
||||
neighbors = append(neighbors, top)
|
||||
}
|
||||
if right != nil && !right.visited {
|
||||
neighbors = append(neighbors, right)
|
||||
}
|
||||
if bottom != nil && !bottom.visited {
|
||||
neighbors = append(neighbors, bottom)
|
||||
}
|
||||
if left != nil && !left.visited {
|
||||
neighbors = append(neighbors, left)
|
||||
}
|
||||
|
||||
if len(neighbors) == 0 {
|
||||
return nil, errors.New("We checked all cells...")
|
||||
}
|
||||
return neighbors, nil
|
||||
}
|
||||
|
||||
func (c *cell) GetRandomNeighbor(grid []*cell, cols int, rows int) (*cell, error) {
|
||||
neighbors, err := c.GetNeighbors(grid, cols, rows)
|
||||
if neighbors == nil {
|
||||
return nil, err
|
||||
}
|
||||
nBig, err := rand.Int(rand.Reader, big.NewInt(int64(len(neighbors))))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
randomIndex := nBig.Int64()
|
||||
return neighbors[randomIndex], nil
|
||||
}
|
||||
|
||||
func (c *cell) hightlight(imd *imdraw.IMDraw, wallSize int) {
|
||||
x := c.col * wallSize
|
||||
y := c.row * wallSize
|
||||
|
||||
imd.Color = hightlightColor
|
||||
imd.Push(pixel.V(float64(x), float64(y)), pixel.V(float64(x+wallSize), float64(y+wallSize)))
|
||||
imd.Rectangle(0)
|
||||
}
|
||||
|
||||
func newCell(col int, row int) *cell {
|
||||
newCell := new(cell)
|
||||
newCell.row = row
|
||||
newCell.col = col
|
||||
|
||||
for i := range newCell.walls {
|
||||
newCell.walls[i] = true
|
||||
}
|
||||
return newCell
|
||||
}
|
||||
|
||||
// Creates the inital maze slice for use.
|
||||
func initGrid(cols, rows int) []*cell {
|
||||
grid := []*cell{}
|
||||
for j := 0; j < rows; j++ {
|
||||
for i := 0; i < cols; i++ {
|
||||
newCell := newCell(i, j)
|
||||
grid = append(grid, newCell)
|
||||
}
|
||||
}
|
||||
return grid
|
||||
}
|
||||
|
||||
func setupMaze(cols, rows int) ([]*cell, *stack.Stack, *cell) {
|
||||
// Make an empty grid
|
||||
grid := initGrid(cols, rows)
|
||||
backTrackStack := stack.NewStack(len(grid))
|
||||
currentCell := grid[0]
|
||||
|
||||
return grid, backTrackStack, currentCell
|
||||
}
|
||||
|
||||
func cellIndex(i, j, cols, rows int) int {
|
||||
if i < 0 || j < 0 || i > cols-1 || j > rows-1 {
|
||||
return -1
|
||||
}
|
||||
return i + j*cols
|
||||
}
|
||||
|
||||
func getCellAt(i int, j int, cols int, rows int, grid []*cell) (*cell, error) {
|
||||
possibleIndex := cellIndex(i, j, cols, rows)
|
||||
|
||||
if possibleIndex == -1 {
|
||||
return nil, fmt.Errorf("cellIndex: CellIndex is a negative number %d", possibleIndex)
|
||||
}
|
||||
return grid[possibleIndex], nil
|
||||
}
|
||||
|
||||
func removeWalls(a *cell, b *cell) {
|
||||
x := a.col - b.col
|
||||
|
||||
if x == 1 {
|
||||
a.walls[3] = false
|
||||
b.walls[1] = false
|
||||
} else if x == -1 {
|
||||
a.walls[1] = false
|
||||
b.walls[3] = false
|
||||
}
|
||||
|
||||
y := a.row - b.row
|
||||
|
||||
if y == 1 {
|
||||
a.walls[0] = false
|
||||
b.walls[2] = false
|
||||
} else if y == -1 {
|
||||
a.walls[2] = false
|
||||
b.walls[0] = false
|
||||
}
|
||||
}
|
||||
|
||||
func run() {
|
||||
// unsiged integers, because easier parsing error checks.
|
||||
// We must convert these to intergers, as done below...
|
||||
uScreenWidth, uScreenHeight, uWallSize := parseArgs()
|
||||
|
||||
var (
|
||||
// In pixels
|
||||
// Defualt is 800x800x40 = 20x20 wallgrid
|
||||
screenWidth = int(uScreenWidth)
|
||||
screenHeight = int(uScreenHeight)
|
||||
wallSize = int(uWallSize)
|
||||
|
||||
frames = 0
|
||||
second = time.Tick(time.Second)
|
||||
|
||||
grid = []*cell{}
|
||||
cols = screenWidth / wallSize
|
||||
rows = screenHeight / wallSize
|
||||
currentCell = new(cell)
|
||||
backTrackStack = stack.NewStack(1)
|
||||
)
|
||||
|
||||
// Set game FPS manually
|
||||
fps := time.Tick(time.Second / 60)
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks! - Maze example",
|
||||
Bounds: pixel.R(0, 0, float64(screenHeight), float64(screenWidth)),
|
||||
}
|
||||
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
grid, backTrackStack, currentCell = setupMaze(cols, rows)
|
||||
|
||||
gridIMDraw := imdraw.New(nil)
|
||||
|
||||
for !win.Closed() {
|
||||
if win.JustReleased(pixelgl.KeyR) {
|
||||
fmt.Println("R pressed")
|
||||
grid, backTrackStack, currentCell = setupMaze(cols, rows)
|
||||
}
|
||||
|
||||
win.Clear(colornames.Gray)
|
||||
gridIMDraw.Clear()
|
||||
|
||||
for i := range grid {
|
||||
grid[i].Draw(gridIMDraw, wallSize)
|
||||
}
|
||||
|
||||
// step 1
|
||||
// Make the initial cell the current cell and mark it as visited
|
||||
currentCell.visited = true
|
||||
currentCell.hightlight(gridIMDraw, wallSize)
|
||||
|
||||
// step 2.1
|
||||
// If the current cell has any neighbours which have not been visited
|
||||
// Choose a random unvisited cell
|
||||
nextCell, _ := currentCell.GetRandomNeighbor(grid, cols, rows)
|
||||
if nextCell != nil && !nextCell.visited {
|
||||
// step 2.2
|
||||
// Push the current cell to the stack
|
||||
backTrackStack.Push(currentCell)
|
||||
|
||||
// step 2.3
|
||||
// Remove the wall between the current cell and the chosen cell
|
||||
|
||||
removeWalls(currentCell, nextCell)
|
||||
|
||||
// step 2.4
|
||||
// Make the chosen cell the current cell and mark it as visited
|
||||
nextCell.visited = true
|
||||
currentCell = nextCell
|
||||
} else if backTrackStack.Len() > 0 {
|
||||
currentCell = backTrackStack.Pop().(*cell)
|
||||
}
|
||||
|
||||
gridIMDraw.Draw(win)
|
||||
win.Update()
|
||||
<-fps
|
||||
updateFPSDisplay(win, &cfg, &frames, grid, second)
|
||||
}
|
||||
}
|
||||
|
||||
// Parses the maze arguments, all of them are optional.
|
||||
// Uses uint as implicit error checking :)
|
||||
func parseArgs() (uint, uint, uint) {
|
||||
var mazeWidthPtr = flag.Uint("w", 800, "w sets the maze's width in pixels.")
|
||||
var mazeHeightPtr = flag.Uint("h", 800, "h sets the maze's height in pixels.")
|
||||
var wallSizePtr = flag.Uint("c", 40, "c sets the maze cell's size in pixels.")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// If these aren't default values AND if they're not the same values.
|
||||
// We should warn the user that the maze will look funny.
|
||||
if *mazeWidthPtr != 800 || *mazeHeightPtr != 800 {
|
||||
if *mazeWidthPtr != *mazeHeightPtr {
|
||||
fmt.Printf("WARNING: maze width: %d and maze height: %d don't match. \n", *mazeWidthPtr, *mazeHeightPtr)
|
||||
fmt.Println("Maze will look funny because the maze size is bond to the window size!")
|
||||
}
|
||||
}
|
||||
|
||||
return *mazeWidthPtr, *mazeHeightPtr, *wallSizePtr
|
||||
}
|
||||
|
||||
func updateFPSDisplay(win *pixelgl.Window, cfg *pixelgl.WindowConfig, frames *int, grid []*cell, second <-chan time.Time) {
|
||||
*frames++
|
||||
select {
|
||||
case <-second:
|
||||
win.SetTitle(fmt.Sprintf("%s | FPS: %d with %d Cells", cfg.Title, *frames, len(grid)))
|
||||
*frames = 0
|
||||
default:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
if debug {
|
||||
defer profile.Start().Stop()
|
||||
}
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/community/maze/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 14 KiB |
86
vendor/github.com/faiface/pixel/examples/community/maze/stack/stack.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package stack
|
||||
|
||||
type Stack struct {
|
||||
top *Element
|
||||
size int
|
||||
max int
|
||||
}
|
||||
|
||||
type Element struct {
|
||||
value interface{}
|
||||
next *Element
|
||||
}
|
||||
|
||||
func NewStack(max int) *Stack {
|
||||
return &Stack{max: max}
|
||||
}
|
||||
|
||||
// Return the stack's length
|
||||
func (s *Stack) Len() int {
|
||||
return s.size
|
||||
}
|
||||
|
||||
// Return the stack's max
|
||||
func (s *Stack) Max() int {
|
||||
return s.max
|
||||
}
|
||||
|
||||
// Push a new element onto the stack
|
||||
func (s *Stack) Push(value interface{}) {
|
||||
if s.size+1 > s.max {
|
||||
if last := s.PopLast(); last == nil {
|
||||
panic("Unexpected nil in stack")
|
||||
}
|
||||
}
|
||||
s.top = &Element{value, s.top}
|
||||
s.size++
|
||||
}
|
||||
|
||||
// Remove the top element from the stack and return it's value
|
||||
// If the stack is empty, return nil
|
||||
func (s *Stack) Pop() (value interface{}) {
|
||||
if s.size > 0 {
|
||||
value, s.top = s.top.value, s.top.next
|
||||
s.size--
|
||||
return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) PopLast() (value interface{}) {
|
||||
if lastElem := s.popLast(s.top); lastElem != nil {
|
||||
return lastElem.value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//Peek returns a top without removing it from list
|
||||
func (s *Stack) Peek() (value interface{}, exists bool) {
|
||||
exists = false
|
||||
if s.size > 0 {
|
||||
value = s.top.value
|
||||
exists = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Stack) popLast(elem *Element) *Element {
|
||||
if elem == nil {
|
||||
return nil
|
||||
}
|
||||
// not last because it has next and a grandchild
|
||||
if elem.next != nil && elem.next.next != nil {
|
||||
return s.popLast(elem.next)
|
||||
}
|
||||
|
||||
// current elem is second from bottom, as next elem has no child
|
||||
if elem.next != nil && elem.next.next == nil {
|
||||
last := elem.next
|
||||
// make current elem bottom of stack by removing its next element
|
||||
elem.next = nil
|
||||
s.size--
|
||||
return last
|
||||
}
|
||||
return nil
|
||||
}
|
9
vendor/github.com/faiface/pixel/examples/community/parallax-scrolling-background/README.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# Parallax scrolling demo
|
||||
|
||||
Created by [Sergio Vera](https://github.com/svera)
|
||||
|
||||
This example shows how to implement an infinite side scrolling background with a depth effect, using [parallax scrolling](https://en.wikipedia.org/wiki/Parallax_scrolling). Code is based in the [infinite scrolling background](https://github.com/faiface/pixel/tree/master/examples/community/scrolling-background) demo.
|
||||
|
||||
Credits to [Peter Hellberg](https://github.com/peterhellberg) for the improved background images.
|
||||
|
||||

|
BIN
vendor/github.com/faiface/pixel/examples/community/parallax-scrolling-background/background.png
generated
vendored
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
vendor/github.com/faiface/pixel/examples/community/parallax-scrolling-background/foreground.png
generated
vendored
Normal file
After Width: | Height: | Size: 25 KiB |
74
vendor/github.com/faiface/pixel/examples/community/parallax-scrolling-background/main.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
const (
|
||||
windowWidth = 600
|
||||
windowHeight = 450
|
||||
foregroundHeight = 149
|
||||
// This is the scrolling speed (pixels per second)
|
||||
// Negative values will make background to scroll to the left,
|
||||
// positive to the right.
|
||||
backgroundSpeed = -60
|
||||
foregroundSpeed = -120
|
||||
)
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Parallax scrolling demo",
|
||||
Bounds: pixel.R(0, 0, windowWidth, windowHeight),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pic must have double the width of the window, as it will scroll to the left or right
|
||||
picBackground, err := loadPicture("background.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
picForeground, err := loadPicture("foreground.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
background := NewScrollingBackground(picBackground, windowWidth, windowHeight, backgroundSpeed)
|
||||
foreground := NewScrollingBackground(picForeground, windowWidth, foregroundHeight, foregroundSpeed)
|
||||
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
background.Update(win, dt)
|
||||
foreground.Update(win, dt)
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/community/parallax-scrolling-background/result.png
generated
vendored
Normal file
After Width: | Height: | Size: 91 KiB |
64
vendor/github.com/faiface/pixel/examples/community/parallax-scrolling-background/scrolling_background.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
// ScrollingBackground stores all needed information to scroll a background
|
||||
// to the left or right
|
||||
type ScrollingBackground struct {
|
||||
width float64
|
||||
height float64
|
||||
displacement float64
|
||||
speed float64
|
||||
backgrounds [2]*pixel.Sprite
|
||||
positions [2]pixel.Vec
|
||||
}
|
||||
|
||||
// NewScrollingBackground construct and returns a new instance of scrollingBackground,
|
||||
// positioning the background images according to the speed value
|
||||
func NewScrollingBackground(pic pixel.Picture, width, height, speed float64) *ScrollingBackground {
|
||||
sb := &ScrollingBackground{
|
||||
width: width,
|
||||
height: height,
|
||||
speed: speed,
|
||||
backgrounds: [2]*pixel.Sprite{
|
||||
pixel.NewSprite(pic, pixel.R(0, 0, width, height)),
|
||||
pixel.NewSprite(pic, pixel.R(width, 0, width*2, height)),
|
||||
},
|
||||
}
|
||||
|
||||
sb.positionImages()
|
||||
return sb
|
||||
}
|
||||
|
||||
// If scrolling speed > 0, put second background image ouside the screen,
|
||||
// at the left side, otherwise put it at the right side.
|
||||
func (sb *ScrollingBackground) positionImages() {
|
||||
if sb.speed > 0 {
|
||||
sb.positions = [2]pixel.Vec{
|
||||
pixel.V(sb.width/2, sb.height/2),
|
||||
pixel.V((sb.width/2)-sb.width, sb.height/2),
|
||||
}
|
||||
} else {
|
||||
sb.positions = [2]pixel.Vec{
|
||||
pixel.V(sb.width/2, sb.height/2),
|
||||
pixel.V(sb.width+(sb.width/2), sb.height/2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update will move backgrounds certain pixels, depending of the amount of time passed
|
||||
func (sb *ScrollingBackground) Update(win *pixelgl.Window, dt float64) {
|
||||
if math.Abs(sb.displacement) >= sb.width {
|
||||
sb.displacement = 0
|
||||
sb.positions[0], sb.positions[1] = sb.positions[1], sb.positions[0]
|
||||
}
|
||||
d := pixel.V(sb.displacement, 0)
|
||||
sb.backgrounds[0].Draw(win, pixel.IM.Moved(sb.positions[0].Add(d)))
|
||||
sb.backgrounds[1].Draw(win, pixel.IM.Moved(sb.positions[1].Add(d)))
|
||||
sb.displacement += sb.speed * dt
|
||||
}
|
12
vendor/github.com/faiface/pixel/examples/community/procedural-terrain-1d/README.md
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Procedural 1D terrain generator
|
||||
|
||||
Created by [Sergio Vera](https://github.com/svera).
|
||||
|
||||
This is a demo of a 1D terrain generator using [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) algorithm.
|
||||
Press *space* to generate a random terrain.
|
||||
|
||||
Uses [Go-Perlin](https://github.com/aquilax/go-perlin).
|
||||
|
||||
Texture by [hh316](https://hhh316.deviantart.com/art/Seamless-stone-cliff-face-mountain-texture-377076626).
|
||||
|
||||

|
104
vendor/github.com/faiface/pixel/examples/community/procedural-terrain-1d/main.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/jpeg"
|
||||
|
||||
perlin "github.com/aquilax/go-perlin"
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 800
|
||||
height = 600
|
||||
// Top of the mountain must be around the half of the screen height
|
||||
verticalOffset = height / 2
|
||||
// Perlin noise provides variations in values between -1 and 1,
|
||||
// we multiply those so they're visible on screen
|
||||
scale = 100
|
||||
waveLength = 100
|
||||
alpha = 2.
|
||||
beta = 2.
|
||||
n = 3
|
||||
maximumSeedValue = 100
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Procedural terrain 1D",
|
||||
Bounds: pixel.R(0, 0, width, height),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pic, err := loadPicture("stone.jpg")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
imd := imdraw.New(pic)
|
||||
|
||||
drawTerrain(win, imd)
|
||||
|
||||
for !win.Closed() {
|
||||
if win.JustPressed(pixelgl.KeySpace) {
|
||||
drawTerrain(win, imd)
|
||||
}
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
func drawTerrain(win *pixelgl.Window, imd *imdraw.IMDraw) {
|
||||
var seed = rand.Int63n(maximumSeedValue)
|
||||
p := perlin.NewPerlin(alpha, beta, n, seed)
|
||||
|
||||
imd.Clear()
|
||||
win.Clear(colornames.Skyblue)
|
||||
for x := 0.; x < width; x++ {
|
||||
y := p.Noise1D(x/waveLength)*scale + verticalOffset
|
||||
renderTexturedLine(x, y, imd)
|
||||
}
|
||||
imd.Draw(win)
|
||||
}
|
||||
|
||||
// Render a textured line in position x with a height y.
|
||||
// Note that the textured line is just a 1 px width rectangle.
|
||||
// We push the opposite vertices of that rectangle and specify the points of the
|
||||
// texture we want to apply to them. Pixel will fill the rest of the rectangle interpolating the texture.
|
||||
func renderTexturedLine(x, y float64, imd *imdraw.IMDraw) {
|
||||
imd.Intensity = 1.
|
||||
imd.Picture = pixel.V(x, 0)
|
||||
imd.Push(pixel.V(x, 0))
|
||||
imd.Picture = pixel.V(x+1, y)
|
||||
imd.Push(pixel.V(x+1, y))
|
||||
imd.Rectangle(0)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/community/procedural-terrain-1d/result.png
generated
vendored
Normal file
After Width: | Height: | Size: 759 KiB |
BIN
vendor/github.com/faiface/pixel/examples/community/procedural-terrain-1d/stone.jpg
generated
vendored
Normal file
After Width: | Height: | Size: 368 KiB |
22
vendor/github.com/faiface/pixel/examples/community/raycaster/README.md
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# raycaster
|
||||
|
||||
A raycaster made by [Peter Hellberg](https://github.com/peterhellberg/) as part of his [pixel-experiments](https://github.com/peterhellberg/pixel-experiments).
|
||||
|
||||
Based on Lode’s article on [raycasting](http://lodev.org/cgtutor/raycasting.html).
|
||||
|
||||
## Controls
|
||||
|
||||
WASD for strafing and arrow keys for rotation.
|
||||
|
||||
Place blocks using the number keys.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Links
|
||||
|
||||
- https://github.com/peterhellberg/pixel-experiments/tree/master/raycaster
|
||||
- https://gist.github.com/peterhellberg/835eccabf95800555120cc8f0c9e16c2
|
550
vendor/github.com/faiface/pixel/examples/community/raycaster/raycaster.go
generated
vendored
Normal file
BIN
vendor/github.com/faiface/pixel/examples/community/raycaster/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 47 KiB |
7
vendor/github.com/faiface/pixel/examples/community/scrolling-background/README.md
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Infinite scrolling background demo
|
||||
|
||||
Created by [Sergio Vera](https://github.com/svera)
|
||||
|
||||
This example shows how to implement an infinite side scrolling background.
|
||||
|
||||
Credits to [Peter Hellberg](https://github.com/peterhellberg) for the improved background image.
|
BIN
vendor/github.com/faiface/pixel/examples/community/scrolling-background/gamebackground.png
generated
vendored
Normal file
After Width: | Height: | Size: 56 KiB |
83
vendor/github.com/faiface/pixel/examples/community/scrolling-background/main.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
const (
|
||||
windowWidth = 600
|
||||
windowHeight = 450
|
||||
// This is the scrolling speed
|
||||
linesPerSecond = 60
|
||||
)
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Scrolling background demo",
|
||||
Bounds: pixel.R(0, 0, windowWidth, windowHeight),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Pic must have double the width of the window, as it will scroll to the left
|
||||
pic, err := loadPicture("gamebackground.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Backgrounds are made taking the left and right halves of the image
|
||||
background1 := pixel.NewSprite(pic, pixel.R(0, 0, windowWidth, windowHeight))
|
||||
background2 := pixel.NewSprite(pic, pixel.R(windowWidth, 0, windowWidth*2, windowHeight))
|
||||
|
||||
// In the beginning, vector1 will put background1 filling the whole window, while vector2 will
|
||||
// put background2 just at the right side of the window, out of view
|
||||
vector1 := pixel.V(windowWidth/2, windowHeight/2)
|
||||
vector2 := pixel.V(windowWidth+(windowWidth/2), windowHeight/2)
|
||||
|
||||
i := float64(0)
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
// When one of the backgrounds has completely scrolled, we swap displacement vectors,
|
||||
// so the backgrounds will swap positions too regarding the previous iteration,
|
||||
// thus making the background endless.
|
||||
if i <= -windowWidth {
|
||||
i = 0
|
||||
vector1, vector2 = vector2, vector1
|
||||
}
|
||||
// This delta vector will move the backgrounds to the left
|
||||
d := pixel.V(-i, 0)
|
||||
background1.Draw(win, pixel.IM.Moved(vector1.Sub(d)))
|
||||
background2.Draw(win, pixel.IM.Moved(vector2.Sub(d)))
|
||||
i -= linesPerSecond * dt
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
20
vendor/github.com/faiface/pixel/examples/community/starfield/README.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# starfield
|
||||
|
||||
Classic starfield… with [supposedly accurate stellar colors](http://www.vendian.org/mncharity/dir3/starcolor/)
|
||||
|
||||
Made by [Peter Hellberg](https://github.com/peterhellberg/) as part of his [pixel-experiments](https://github.com/peterhellberg/pixel-experiments)
|
||||
|
||||
## Controls
|
||||
|
||||
Arrow up and down to change speed. Space bar to almost stop.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Links
|
||||
|
||||
- https://github.com/peterhellberg/pixel-experiments/tree/master/starfield
|
||||
- https://gist.github.com/peterhellberg/4018e228cced61a0bb26991e49299c96
|
BIN
vendor/github.com/faiface/pixel/examples/community/starfield/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 6.0 KiB |
165
vendor/github.com/faiface/pixel/examples/community/starfield/starfield.go
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
const w, h = float64(1024), float64(512)
|
||||
|
||||
var speed = float64(200)
|
||||
|
||||
var stars [1024]*star
|
||||
|
||||
func init() {
|
||||
rand.Seed(4)
|
||||
|
||||
for i := 0; i < len(stars); i++ {
|
||||
stars[i] = newStar()
|
||||
}
|
||||
}
|
||||
|
||||
type star struct {
|
||||
pixel.Vec
|
||||
Z float64
|
||||
P float64
|
||||
C color.RGBA
|
||||
}
|
||||
|
||||
func newStar() *star {
|
||||
return &star{
|
||||
pixel.V(random(-w, w), random(-h, h)),
|
||||
random(0, w), 0, Colors[rand.Intn(len(Colors))],
|
||||
}
|
||||
}
|
||||
|
||||
func (s *star) update(d float64) {
|
||||
s.P = s.Z
|
||||
s.Z -= d * speed
|
||||
|
||||
if s.Z < 0 {
|
||||
s.X = random(-w, w)
|
||||
s.Y = random(-h, h)
|
||||
s.Z = w
|
||||
s.P = s.Z
|
||||
}
|
||||
}
|
||||
|
||||
func (s *star) draw(imd *imdraw.IMDraw) {
|
||||
p := pixel.V(
|
||||
scale(s.X/s.Z, 0, 1, 0, w),
|
||||
scale(s.Y/s.Z, 0, 1, 0, h),
|
||||
)
|
||||
|
||||
o := pixel.V(
|
||||
scale(s.X/s.P, 0, 1, 0, w),
|
||||
scale(s.Y/s.P, 0, 1, 0, h),
|
||||
)
|
||||
|
||||
r := scale(s.Z, 0, w, 11, 0)
|
||||
|
||||
imd.Color = s.C
|
||||
|
||||
if p.Sub(o).Len() > 6 {
|
||||
imd.Push(p, o)
|
||||
imd.Line(r)
|
||||
}
|
||||
|
||||
imd.Push(p)
|
||||
imd.Circle(r, 0)
|
||||
}
|
||||
|
||||
func run() {
|
||||
win, err := pixelgl.NewWindow(pixelgl.WindowConfig{
|
||||
Bounds: pixel.R(0, 0, w, h),
|
||||
VSync: true,
|
||||
Undecorated: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
imd := imdraw.New(nil)
|
||||
|
||||
imd.Precision = 7
|
||||
|
||||
imd.SetMatrix(pixel.IM.Moved(win.Bounds().Center()))
|
||||
|
||||
last := time.Now()
|
||||
|
||||
for !win.Closed() {
|
||||
win.SetClosed(win.JustPressed(pixelgl.KeyEscape) || win.JustPressed(pixelgl.KeyQ))
|
||||
|
||||
if win.Pressed(pixelgl.KeyUp) {
|
||||
speed += 10
|
||||
}
|
||||
|
||||
if win.Pressed(pixelgl.KeyDown) {
|
||||
if speed > 10 {
|
||||
speed -= 10
|
||||
}
|
||||
}
|
||||
|
||||
if win.Pressed(pixelgl.KeySpace) {
|
||||
speed = 100
|
||||
}
|
||||
|
||||
d := time.Since(last).Seconds()
|
||||
|
||||
last = time.Now()
|
||||
|
||||
imd.Clear()
|
||||
|
||||
for _, s := range stars {
|
||||
s.update(d)
|
||||
s.draw(imd)
|
||||
}
|
||||
|
||||
win.Clear(color.Black)
|
||||
imd.Draw(win)
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
||||
|
||||
func random(min, max float64) float64 {
|
||||
return rand.Float64()*(max-min) + min
|
||||
}
|
||||
|
||||
func scale(unscaledNum, min, max, minAllowed, maxAllowed float64) float64 {
|
||||
return (maxAllowed-minAllowed)*(unscaledNum-min)/(max-min) + minAllowed
|
||||
}
|
||||
|
||||
// Colors based on stellar types listed at
|
||||
// http://www.vendian.org/mncharity/dir3/starcolor/
|
||||
var Colors = []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},
|
||||
}
|
29
vendor/github.com/faiface/pixel/examples/guide/01_creating_a_window/main.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
win.Clear(colornames.Skyblue)
|
||||
|
||||
for !win.Closed() {
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/guide/02_drawing_a_sprite/hiking.png
generated
vendored
Normal file
After Width: | Height: | Size: 68 KiB |
56
vendor/github.com/faiface/pixel/examples/guide/02_drawing_a_sprite/main.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"os"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pic, err := loadPicture("hiking.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sprite := pixel.NewSprite(pic, pic.Bounds())
|
||||
|
||||
win.Clear(colornames.Greenyellow)
|
||||
|
||||
sprite.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
|
||||
|
||||
for !win.Closed() {
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/guide/03_moving_scaling_and_rotating_with_matrix/hiking.png
generated
vendored
Normal file
After Width: | Height: | Size: 68 KiB |
70
vendor/github.com/faiface/pixel/examples/guide/03_moving_scaling_and_rotating_with_matrix/main.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
win.SetSmooth(true)
|
||||
|
||||
pic, err := loadPicture("hiking.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sprite := pixel.NewSprite(pic, pic.Bounds())
|
||||
|
||||
angle := 0.0
|
||||
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
|
||||
angle += 3 * dt
|
||||
|
||||
win.Clear(colornames.Firebrick)
|
||||
|
||||
mat := pixel.IM
|
||||
mat = mat.Rotated(pixel.ZV, angle)
|
||||
mat = mat.Moved(win.Bounds().Center())
|
||||
sprite.Draw(win, mat)
|
||||
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
102
vendor/github.com/faiface/pixel/examples/guide/04_pressing_keys_and_clicking_mouse/main.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
spritesheet, err := loadPicture("trees.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var treesFrames []pixel.Rect
|
||||
for x := spritesheet.Bounds().Min.X; x < spritesheet.Bounds().Max.X; x += 32 {
|
||||
for y := spritesheet.Bounds().Min.Y; y < spritesheet.Bounds().Max.Y; y += 32 {
|
||||
treesFrames = append(treesFrames, pixel.R(x, y, x+32, y+32))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
camPos = pixel.ZV
|
||||
camSpeed = 500.0
|
||||
camZoom = 1.0
|
||||
camZoomSpeed = 1.2
|
||||
trees []*pixel.Sprite
|
||||
matrices []pixel.Matrix
|
||||
)
|
||||
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
|
||||
cam := pixel.IM.Scaled(camPos, camZoom).Moved(win.Bounds().Center().Sub(camPos))
|
||||
win.SetMatrix(cam)
|
||||
|
||||
if win.JustPressed(pixelgl.MouseButtonLeft) {
|
||||
tree := pixel.NewSprite(spritesheet, treesFrames[rand.Intn(len(treesFrames))])
|
||||
trees = append(trees, tree)
|
||||
mouse := cam.Unproject(win.MousePosition())
|
||||
matrices = append(matrices, pixel.IM.Scaled(pixel.ZV, 4).Moved(mouse))
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyLeft) {
|
||||
camPos.X -= camSpeed * dt
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyRight) {
|
||||
camPos.X += camSpeed * dt
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyDown) {
|
||||
camPos.Y -= camSpeed * dt
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyUp) {
|
||||
camPos.Y += camSpeed * dt
|
||||
}
|
||||
camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)
|
||||
|
||||
win.Clear(colornames.Forestgreen)
|
||||
|
||||
for i, tree := range trees {
|
||||
tree.Draw(win, matrices[i])
|
||||
}
|
||||
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/guide/04_pressing_keys_and_clicking_mouse/trees.png
generated
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
110
vendor/github.com/faiface/pixel/examples/guide/05_drawing_efficiently_with_batch/main.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
spritesheet, err := loadPicture("trees.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
batch := pixel.NewBatch(&pixel.TrianglesData{}, spritesheet)
|
||||
|
||||
var treesFrames []pixel.Rect
|
||||
for x := spritesheet.Bounds().Min.X; x < spritesheet.Bounds().Max.X; x += 32 {
|
||||
for y := spritesheet.Bounds().Min.Y; y < spritesheet.Bounds().Max.Y; y += 32 {
|
||||
treesFrames = append(treesFrames, pixel.R(x, y, x+32, y+32))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
camPos = pixel.ZV
|
||||
camSpeed = 500.0
|
||||
camZoom = 1.0
|
||||
camZoomSpeed = 1.2
|
||||
)
|
||||
|
||||
var (
|
||||
frames = 0
|
||||
second = time.Tick(time.Second)
|
||||
)
|
||||
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
|
||||
cam := pixel.IM.Scaled(camPos, camZoom).Moved(win.Bounds().Center().Sub(camPos))
|
||||
win.SetMatrix(cam)
|
||||
|
||||
if win.Pressed(pixelgl.MouseButtonLeft) {
|
||||
tree := pixel.NewSprite(spritesheet, treesFrames[rand.Intn(len(treesFrames))])
|
||||
mouse := cam.Unproject(win.MousePosition())
|
||||
tree.Draw(batch, pixel.IM.Scaled(pixel.ZV, 4).Moved(mouse))
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyLeft) {
|
||||
camPos.X -= camSpeed * dt
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyRight) {
|
||||
camPos.X += camSpeed * dt
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyDown) {
|
||||
camPos.Y -= camSpeed * dt
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyUp) {
|
||||
camPos.Y += camSpeed * dt
|
||||
}
|
||||
camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)
|
||||
|
||||
win.Clear(colornames.Forestgreen)
|
||||
batch.Draw(win)
|
||||
win.Update()
|
||||
|
||||
frames++
|
||||
select {
|
||||
case <-second:
|
||||
win.SetTitle(fmt.Sprintf("%s | FPS: %d", cfg.Title, frames))
|
||||
frames = 0
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/guide/05_drawing_efficiently_with_batch/trees.png
generated
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
53
vendor/github.com/faiface/pixel/examples/guide/06_drawing_shapes_with_imdraw/main.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
imd := imdraw.New(nil)
|
||||
|
||||
imd.Color = colornames.Blueviolet
|
||||
imd.EndShape = imdraw.RoundEndShape
|
||||
imd.Push(pixel.V(100, 100), pixel.V(700, 100))
|
||||
imd.EndShape = imdraw.SharpEndShape
|
||||
imd.Push(pixel.V(100, 500), pixel.V(700, 500))
|
||||
imd.Line(30)
|
||||
|
||||
imd.Color = colornames.Limegreen
|
||||
imd.Push(pixel.V(500, 500))
|
||||
imd.Circle(300, 50)
|
||||
imd.Color = colornames.Navy
|
||||
imd.Push(pixel.V(200, 500), pixel.V(800, 500))
|
||||
imd.Ellipse(pixel.V(120, 80), 0)
|
||||
|
||||
imd.Color = colornames.Red
|
||||
imd.EndShape = imdraw.RoundEndShape
|
||||
imd.Push(pixel.V(500, 350))
|
||||
imd.CircleArc(150, -math.Pi, 0, 30)
|
||||
|
||||
for !win.Closed() {
|
||||
win.Clear(colornames.Aliceblue)
|
||||
imd.Draw(win)
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/guide/07_typing_text_on_the_screen/intuitive.ttf
generated
vendored
Normal file
78
vendor/github.com/faiface/pixel/examples/guide/07_typing_text_on_the_screen/main.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"github.com/faiface/pixel/text"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"golang.org/x/image/colornames"
|
||||
"golang.org/x/image/font"
|
||||
)
|
||||
|
||||
func loadTTF(path string, size float64) (font.Face, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
bytes, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
font, err := truetype.Parse(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return truetype.NewFace(font, &truetype.Options{
|
||||
Size: size,
|
||||
GlyphCacheEntries: 1,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Pixel Rocks!",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
win.SetSmooth(true)
|
||||
|
||||
face, err := loadTTF("intuitive.ttf", 80)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
atlas := text.NewAtlas(face, text.ASCII)
|
||||
txt := text.New(pixel.V(50, 500), atlas)
|
||||
|
||||
txt.Color = colornames.Lightgrey
|
||||
|
||||
fps := time.Tick(time.Second / 120)
|
||||
|
||||
for !win.Closed() {
|
||||
txt.WriteString(win.Typed())
|
||||
if win.JustPressed(pixelgl.KeyEnter) || win.Repeated(pixelgl.KeyEnter) {
|
||||
txt.WriteRune('\n')
|
||||
}
|
||||
|
||||
win.Clear(colornames.Darkcyan)
|
||||
txt.Draw(win, pixel.IM.Moved(win.Bounds().Center().Sub(txt.Bounds().Center())))
|
||||
win.Update()
|
||||
|
||||
<-fps
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
13
vendor/github.com/faiface/pixel/examples/lights/README.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Lights
|
||||
|
||||
This example demonstrates powerful Porter-Duff composition used to create a nice noisy light effect.
|
||||
|
||||
**Use W and S keys** to adjust the level of "dust".
|
||||
|
||||
The FPS is limited to 30, because the effect is a little expensive and my computer couldn't handle
|
||||
60 FPS. If you have a more powerful computer (which is quite likely), peek into the code and disable
|
||||
the limit.
|
||||
|
||||
Credit for the panda art goes to [Ján Štrba](https://www.artstation.com/artist/janstrba).
|
||||
|
||||

|
195
vendor/github.com/faiface/pixel/examples/lights/main.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
)
|
||||
|
||||
func loadPicture(path string) (pixel.Picture, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pixel.PictureDataFromImage(img), nil
|
||||
}
|
||||
|
||||
type colorlight struct {
|
||||
color pixel.RGBA
|
||||
point pixel.Vec
|
||||
angle float64
|
||||
radius float64
|
||||
dust float64
|
||||
|
||||
spread float64
|
||||
|
||||
imd *imdraw.IMDraw
|
||||
}
|
||||
|
||||
func (cl *colorlight) apply(dst pixel.ComposeTarget, center pixel.Vec, src, noise *pixel.Sprite) {
|
||||
// create the light arc if not created already
|
||||
if cl.imd == nil {
|
||||
imd := imdraw.New(nil)
|
||||
imd.Color = pixel.Alpha(1)
|
||||
imd.Push(pixel.ZV)
|
||||
imd.Color = pixel.Alpha(0)
|
||||
for angle := -cl.spread / 2; angle <= cl.spread/2; angle += cl.spread / 64 {
|
||||
imd.Push(pixel.V(1, 0).Rotated(angle))
|
||||
}
|
||||
imd.Polygon(0)
|
||||
cl.imd = imd
|
||||
}
|
||||
|
||||
// draw the light arc
|
||||
dst.SetMatrix(pixel.IM.Scaled(pixel.ZV, cl.radius).Rotated(pixel.ZV, cl.angle).Moved(cl.point))
|
||||
dst.SetColorMask(pixel.Alpha(1))
|
||||
dst.SetComposeMethod(pixel.ComposePlus)
|
||||
cl.imd.Draw(dst)
|
||||
|
||||
// draw the noise inside the light
|
||||
dst.SetMatrix(pixel.IM)
|
||||
dst.SetComposeMethod(pixel.ComposeIn)
|
||||
noise.Draw(dst, pixel.IM.Moved(center))
|
||||
|
||||
// draw an image inside the noisy light
|
||||
dst.SetColorMask(cl.color)
|
||||
dst.SetComposeMethod(pixel.ComposeIn)
|
||||
src.Draw(dst, pixel.IM.Moved(center))
|
||||
|
||||
// draw the light reflected from the dust
|
||||
dst.SetMatrix(pixel.IM.Scaled(pixel.ZV, cl.radius).Rotated(pixel.ZV, cl.angle).Moved(cl.point))
|
||||
dst.SetColorMask(cl.color.Mul(pixel.Alpha(cl.dust)))
|
||||
dst.SetComposeMethod(pixel.ComposePlus)
|
||||
cl.imd.Draw(dst)
|
||||
}
|
||||
|
||||
func run() {
|
||||
pandaPic, err := loadPicture("panda.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
noisePic, err := loadPicture("noise.png")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Lights",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
panda := pixel.NewSprite(pandaPic, pandaPic.Bounds())
|
||||
noise := pixel.NewSprite(noisePic, noisePic.Bounds())
|
||||
|
||||
colors := []pixel.RGBA{
|
||||
pixel.RGB(1, 0, 0),
|
||||
pixel.RGB(0, 1, 0),
|
||||
pixel.RGB(0, 0, 1),
|
||||
pixel.RGB(1/math.Sqrt2, 1/math.Sqrt2, 0),
|
||||
}
|
||||
|
||||
points := []pixel.Vec{
|
||||
{X: win.Bounds().Min.X, Y: win.Bounds().Min.Y},
|
||||
{X: win.Bounds().Max.X, Y: win.Bounds().Min.Y},
|
||||
{X: win.Bounds().Max.X, Y: win.Bounds().Max.Y},
|
||||
{X: win.Bounds().Min.X, Y: win.Bounds().Max.Y},
|
||||
}
|
||||
|
||||
angles := []float64{
|
||||
math.Pi / 4,
|
||||
math.Pi/4 + math.Pi/2,
|
||||
math.Pi/4 + 2*math.Pi/2,
|
||||
math.Pi/4 + 3*math.Pi/2,
|
||||
}
|
||||
|
||||
lights := make([]colorlight, 4)
|
||||
for i := range lights {
|
||||
lights[i] = colorlight{
|
||||
color: colors[i],
|
||||
point: points[i],
|
||||
angle: angles[i],
|
||||
radius: 800,
|
||||
dust: 0.3,
|
||||
spread: math.Pi / math.E,
|
||||
}
|
||||
}
|
||||
|
||||
speed := []float64{11.0 / 23, 13.0 / 23, 17.0 / 23, 19.0 / 23}
|
||||
|
||||
oneLight := pixelgl.NewCanvas(win.Bounds())
|
||||
allLight := pixelgl.NewCanvas(win.Bounds())
|
||||
|
||||
fps30 := time.Tick(time.Second / 30)
|
||||
|
||||
start := time.Now()
|
||||
for !win.Closed() {
|
||||
if win.Pressed(pixelgl.KeyW) {
|
||||
for i := range lights {
|
||||
lights[i].dust += 0.05
|
||||
if lights[i].dust > 1 {
|
||||
lights[i].dust = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyS) {
|
||||
for i := range lights {
|
||||
lights[i].dust -= 0.05
|
||||
if lights[i].dust < 0 {
|
||||
lights[i].dust = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
since := time.Since(start).Seconds()
|
||||
for i := range lights {
|
||||
lights[i].angle = angles[i] + math.Sin(since*speed[i])*math.Pi/8
|
||||
}
|
||||
|
||||
win.Clear(pixel.RGB(0, 0, 0))
|
||||
|
||||
// draw the panda visible outside the light
|
||||
win.SetColorMask(pixel.Alpha(0.4))
|
||||
win.SetComposeMethod(pixel.ComposePlus)
|
||||
panda.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
|
||||
|
||||
allLight.Clear(pixel.Alpha(0))
|
||||
allLight.SetComposeMethod(pixel.ComposePlus)
|
||||
|
||||
// accumulate all the lights
|
||||
for i := range lights {
|
||||
oneLight.Clear(pixel.Alpha(0))
|
||||
lights[i].apply(oneLight, oneLight.Bounds().Center(), panda, noise)
|
||||
oneLight.Draw(allLight, pixel.IM.Moved(allLight.Bounds().Center()))
|
||||
}
|
||||
|
||||
// compose the final result
|
||||
win.SetColorMask(pixel.Alpha(1))
|
||||
allLight.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
|
||||
|
||||
win.Update()
|
||||
|
||||
<-fps30 // maintain 30 fps, because my computer couldn't handle 60 here
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/lights/noise.png
generated
vendored
Normal file
After Width: | Height: | Size: 838 KiB |
BIN
vendor/github.com/faiface/pixel/examples/lights/panda.png
generated
vendored
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
vendor/github.com/faiface/pixel/examples/lights/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 1.1 MiB |
14
vendor/github.com/faiface/pixel/examples/platformer/README.md
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Platformer
|
||||
|
||||
This example demostrates a way to put things together and create a simple platformer game with a
|
||||
Gopher!
|
||||
|
||||
Use **arrow keys** to run and jump around. Press **ENTER** to restart. (And hush, hush, secret.
|
||||
Press TAB for slo-mo!)
|
||||
|
||||
The retro feel is, other than from the pixel art spritesheet, achieved by using a 160x120px large
|
||||
off-screen canvas, drawing everything to it and then stretching it to fit the window.
|
||||
|
||||
The Gopher spritesheet comes from excellent [Egon Elbre](https://github.com/egonelbre/gophers).
|
||||
|
||||

|
394
vendor/github.com/faiface/pixel/examples/platformer/main.go
generated
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"image"
|
||||
"image/color"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func loadAnimationSheet(sheetPath, descPath string, frameWidth float64) (sheet pixel.Picture, anims map[string][]pixel.Rect, err error) {
|
||||
// total hack, nicely format the error at the end, so I don't have to type it every time
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error loading animation sheet")
|
||||
}
|
||||
}()
|
||||
|
||||
// open and load the spritesheet
|
||||
sheetFile, err := os.Open(sheetPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer sheetFile.Close()
|
||||
sheetImg, _, err := image.Decode(sheetFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sheet = pixel.PictureDataFromImage(sheetImg)
|
||||
|
||||
// create a slice of frames inside the spritesheet
|
||||
var frames []pixel.Rect
|
||||
for x := 0.0; x+frameWidth <= sheet.Bounds().Max.X; x += frameWidth {
|
||||
frames = append(frames, pixel.R(
|
||||
x,
|
||||
0,
|
||||
x+frameWidth,
|
||||
sheet.Bounds().H(),
|
||||
))
|
||||
}
|
||||
|
||||
descFile, err := os.Open(descPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer descFile.Close()
|
||||
|
||||
anims = make(map[string][]pixel.Rect)
|
||||
|
||||
// load the animation information, name and interval inside the spritesheet
|
||||
desc := csv.NewReader(descFile)
|
||||
for {
|
||||
anim, err := desc.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
name := anim[0]
|
||||
start, _ := strconv.Atoi(anim[1])
|
||||
end, _ := strconv.Atoi(anim[2])
|
||||
|
||||
anims[name] = frames[start : end+1]
|
||||
}
|
||||
|
||||
return sheet, anims, nil
|
||||
}
|
||||
|
||||
type platform struct {
|
||||
rect pixel.Rect
|
||||
color color.Color
|
||||
}
|
||||
|
||||
func (p *platform) draw(imd *imdraw.IMDraw) {
|
||||
imd.Color = p.color
|
||||
imd.Push(p.rect.Min, p.rect.Max)
|
||||
imd.Rectangle(0)
|
||||
}
|
||||
|
||||
type gopherPhys struct {
|
||||
gravity float64
|
||||
runSpeed float64
|
||||
jumpSpeed float64
|
||||
|
||||
rect pixel.Rect
|
||||
vel pixel.Vec
|
||||
ground bool
|
||||
}
|
||||
|
||||
func (gp *gopherPhys) update(dt float64, ctrl pixel.Vec, platforms []platform) {
|
||||
// apply controls
|
||||
switch {
|
||||
case ctrl.X < 0:
|
||||
gp.vel.X = -gp.runSpeed
|
||||
case ctrl.X > 0:
|
||||
gp.vel.X = +gp.runSpeed
|
||||
default:
|
||||
gp.vel.X = 0
|
||||
}
|
||||
|
||||
// apply gravity and velocity
|
||||
gp.vel.Y += gp.gravity * dt
|
||||
gp.rect = gp.rect.Moved(gp.vel.Scaled(dt))
|
||||
|
||||
// check collisions against each platform
|
||||
gp.ground = false
|
||||
if gp.vel.Y <= 0 {
|
||||
for _, p := range platforms {
|
||||
if gp.rect.Max.X <= p.rect.Min.X || gp.rect.Min.X >= p.rect.Max.X {
|
||||
continue
|
||||
}
|
||||
if gp.rect.Min.Y > p.rect.Max.Y || gp.rect.Min.Y < p.rect.Max.Y+gp.vel.Y*dt {
|
||||
continue
|
||||
}
|
||||
gp.vel.Y = 0
|
||||
gp.rect = gp.rect.Moved(pixel.V(0, p.rect.Max.Y-gp.rect.Min.Y))
|
||||
gp.ground = true
|
||||
}
|
||||
}
|
||||
|
||||
// jump if on the ground and the player wants to jump
|
||||
if gp.ground && ctrl.Y > 0 {
|
||||
gp.vel.Y = gp.jumpSpeed
|
||||
}
|
||||
}
|
||||
|
||||
type animState int
|
||||
|
||||
const (
|
||||
idle animState = iota
|
||||
running
|
||||
jumping
|
||||
)
|
||||
|
||||
type gopherAnim struct {
|
||||
sheet pixel.Picture
|
||||
anims map[string][]pixel.Rect
|
||||
rate float64
|
||||
|
||||
state animState
|
||||
counter float64
|
||||
dir float64
|
||||
|
||||
frame pixel.Rect
|
||||
|
||||
sprite *pixel.Sprite
|
||||
}
|
||||
|
||||
func (ga *gopherAnim) update(dt float64, phys *gopherPhys) {
|
||||
ga.counter += dt
|
||||
|
||||
// determine the new animation state
|
||||
var newState animState
|
||||
switch {
|
||||
case !phys.ground:
|
||||
newState = jumping
|
||||
case phys.vel.Len() == 0:
|
||||
newState = idle
|
||||
case phys.vel.Len() > 0:
|
||||
newState = running
|
||||
}
|
||||
|
||||
// reset the time counter if the state changed
|
||||
if ga.state != newState {
|
||||
ga.state = newState
|
||||
ga.counter = 0
|
||||
}
|
||||
|
||||
// determine the correct animation frame
|
||||
switch ga.state {
|
||||
case idle:
|
||||
ga.frame = ga.anims["Front"][0]
|
||||
case running:
|
||||
i := int(math.Floor(ga.counter / ga.rate))
|
||||
ga.frame = ga.anims["Run"][i%len(ga.anims["Run"])]
|
||||
case jumping:
|
||||
speed := phys.vel.Y
|
||||
i := int((-speed/phys.jumpSpeed + 1) / 2 * float64(len(ga.anims["Jump"])))
|
||||
if i < 0 {
|
||||
i = 0
|
||||
}
|
||||
if i >= len(ga.anims["Jump"]) {
|
||||
i = len(ga.anims["Jump"]) - 1
|
||||
}
|
||||
ga.frame = ga.anims["Jump"][i]
|
||||
}
|
||||
|
||||
// set the facing direction of the gopher
|
||||
if phys.vel.X != 0 {
|
||||
if phys.vel.X > 0 {
|
||||
ga.dir = +1
|
||||
} else {
|
||||
ga.dir = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ga *gopherAnim) draw(t pixel.Target, phys *gopherPhys) {
|
||||
if ga.sprite == nil {
|
||||
ga.sprite = pixel.NewSprite(nil, pixel.Rect{})
|
||||
}
|
||||
// draw the correct frame with the correct position and direction
|
||||
ga.sprite.Set(ga.sheet, ga.frame)
|
||||
ga.sprite.Draw(t, pixel.IM.
|
||||
ScaledXY(pixel.ZV, pixel.V(
|
||||
phys.rect.W()/ga.sprite.Frame().W(),
|
||||
phys.rect.H()/ga.sprite.Frame().H(),
|
||||
)).
|
||||
ScaledXY(pixel.ZV, pixel.V(-ga.dir, 1)).
|
||||
Moved(phys.rect.Center()),
|
||||
)
|
||||
}
|
||||
|
||||
type goal struct {
|
||||
pos pixel.Vec
|
||||
radius float64
|
||||
step float64
|
||||
|
||||
counter float64
|
||||
cols [5]pixel.RGBA
|
||||
}
|
||||
|
||||
func (g *goal) update(dt float64) {
|
||||
g.counter += dt
|
||||
for g.counter > g.step {
|
||||
g.counter -= g.step
|
||||
for i := len(g.cols) - 2; i >= 0; i-- {
|
||||
g.cols[i+1] = g.cols[i]
|
||||
}
|
||||
g.cols[0] = randomNiceColor()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *goal) draw(imd *imdraw.IMDraw) {
|
||||
for i := len(g.cols) - 1; i >= 0; i-- {
|
||||
imd.Color = g.cols[i]
|
||||
imd.Push(g.pos)
|
||||
imd.Circle(float64(i+1)*g.radius/float64(len(g.cols)), 0)
|
||||
}
|
||||
}
|
||||
|
||||
func randomNiceColor() pixel.RGBA {
|
||||
again:
|
||||
r := rand.Float64()
|
||||
g := rand.Float64()
|
||||
b := rand.Float64()
|
||||
len := math.Sqrt(r*r + g*g + b*b)
|
||||
if len == 0 {
|
||||
goto again
|
||||
}
|
||||
return pixel.RGB(r/len, g/len, b/len)
|
||||
}
|
||||
|
||||
func run() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
sheet, anims, err := loadAnimationSheet("sheet.png", "sheet.csv", 12)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Platformer",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
phys := &gopherPhys{
|
||||
gravity: -512,
|
||||
runSpeed: 64,
|
||||
jumpSpeed: 192,
|
||||
rect: pixel.R(-6, -7, 6, 7),
|
||||
}
|
||||
|
||||
anim := &gopherAnim{
|
||||
sheet: sheet,
|
||||
anims: anims,
|
||||
rate: 1.0 / 10,
|
||||
dir: +1,
|
||||
}
|
||||
|
||||
// hardcoded level
|
||||
platforms := []platform{
|
||||
{rect: pixel.R(-50, -34, 50, -32)},
|
||||
{rect: pixel.R(20, 0, 70, 2)},
|
||||
{rect: pixel.R(-100, 10, -50, 12)},
|
||||
{rect: pixel.R(120, -22, 140, -20)},
|
||||
{rect: pixel.R(120, -72, 140, -70)},
|
||||
{rect: pixel.R(120, -122, 140, -120)},
|
||||
{rect: pixel.R(-100, -152, 100, -150)},
|
||||
{rect: pixel.R(-150, -127, -140, -125)},
|
||||
{rect: pixel.R(-180, -97, -170, -95)},
|
||||
{rect: pixel.R(-150, -67, -140, -65)},
|
||||
{rect: pixel.R(-180, -37, -170, -35)},
|
||||
{rect: pixel.R(-150, -7, -140, -5)},
|
||||
}
|
||||
for i := range platforms {
|
||||
platforms[i].color = randomNiceColor()
|
||||
}
|
||||
|
||||
gol := &goal{
|
||||
pos: pixel.V(-75, 40),
|
||||
radius: 18,
|
||||
step: 1.0 / 7,
|
||||
}
|
||||
|
||||
canvas := pixelgl.NewCanvas(pixel.R(-160/2, -120/2, 160/2, 120/2))
|
||||
imd := imdraw.New(sheet)
|
||||
imd.Precision = 32
|
||||
|
||||
camPos := pixel.ZV
|
||||
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
|
||||
// lerp the camera position towards the gopher
|
||||
camPos = pixel.Lerp(camPos, phys.rect.Center(), 1-math.Pow(1.0/128, dt))
|
||||
cam := pixel.IM.Moved(camPos.Scaled(-1))
|
||||
canvas.SetMatrix(cam)
|
||||
|
||||
// slow motion with tab
|
||||
if win.Pressed(pixelgl.KeyTab) {
|
||||
dt /= 8
|
||||
}
|
||||
|
||||
// restart the level on pressing enter
|
||||
if win.JustPressed(pixelgl.KeyEnter) {
|
||||
phys.rect = phys.rect.Moved(phys.rect.Center().Scaled(-1))
|
||||
phys.vel = pixel.ZV
|
||||
}
|
||||
|
||||
// control the gopher with keys
|
||||
ctrl := pixel.ZV
|
||||
if win.Pressed(pixelgl.KeyLeft) {
|
||||
ctrl.X--
|
||||
}
|
||||
if win.Pressed(pixelgl.KeyRight) {
|
||||
ctrl.X++
|
||||
}
|
||||
if win.JustPressed(pixelgl.KeyUp) {
|
||||
ctrl.Y = 1
|
||||
}
|
||||
|
||||
// update the physics and animation
|
||||
phys.update(dt, ctrl, platforms)
|
||||
gol.update(dt)
|
||||
anim.update(dt, phys)
|
||||
|
||||
// draw the scene to the canvas using IMDraw
|
||||
canvas.Clear(colornames.Black)
|
||||
imd.Clear()
|
||||
for _, p := range platforms {
|
||||
p.draw(imd)
|
||||
}
|
||||
gol.draw(imd)
|
||||
anim.draw(imd, phys)
|
||||
imd.Draw(canvas)
|
||||
|
||||
// stretch the canvas to the window
|
||||
win.Clear(colornames.White)
|
||||
win.SetMatrix(pixel.IM.Scaled(pixel.ZV,
|
||||
math.Min(
|
||||
win.Bounds().W()/canvas.Bounds().W(),
|
||||
win.Bounds().H()/canvas.Bounds().H(),
|
||||
),
|
||||
).Moved(win.Bounds().Center()))
|
||||
canvas.Draw(win, pixel.IM.Moved(canvas.Bounds().Center()))
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/platformer/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 8.3 KiB |
9
vendor/github.com/faiface/pixel/examples/platformer/sheet.csv
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Front,0,0
|
||||
FrontBlink,1,1
|
||||
LookUp,2,2
|
||||
Left,3,7
|
||||
LeftRight,4,6
|
||||
LeftBlink,7,7
|
||||
Walk,8,15
|
||||
Run,16,23
|
||||
Jump,24,26
|
|
BIN
vendor/github.com/faiface/pixel/examples/platformer/sheet.png
generated
vendored
Normal file
After Width: | Height: | Size: 530 B |
8
vendor/github.com/faiface/pixel/examples/smoke/README.md
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Smoke
|
||||
|
||||
This example implements a smoke particle effect using sprites. It uses a spritesheet with a CSV
|
||||
description.
|
||||
|
||||
The art in the spritesheet comes from [Kenney](https://kenney.nl/).
|
||||
|
||||

|
25
vendor/github.com/faiface/pixel/examples/smoke/blackSmoke.csv
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
1543,1146,362,336
|
||||
396,0,398,364
|
||||
761,1535,386,342
|
||||
795,794,351,367
|
||||
394,1163,386,364
|
||||
1120,1163,377,348
|
||||
795,0,368,407
|
||||
0,0,395,397
|
||||
1164,0,378,415
|
||||
781,1163,338,360
|
||||
1543,0,372,370
|
||||
1148,1535,393,327
|
||||
387,1535,373,364
|
||||
396,365,371,388
|
||||
0,758,378,404
|
||||
379,758,378,371
|
||||
1543,774,360,371
|
||||
1543,1483,350,398
|
||||
0,398,382,359
|
||||
1164,416,356,382
|
||||
1164,799,369,350
|
||||
0,1535,386,394
|
||||
795,408,366,385
|
||||
1543,371,367,402
|
||||
0,1163,393,371
|
|
BIN
vendor/github.com/faiface/pixel/examples/smoke/blackSmoke.png
generated
vendored
Normal file
After Width: | Height: | Size: 3.0 MiB |
230
vendor/github.com/faiface/pixel/examples/smoke/main.go
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/csv"
|
||||
"image"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
_ "image/png"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
type particle struct {
|
||||
Sprite *pixel.Sprite
|
||||
Pos pixel.Vec
|
||||
Rot, Scale float64
|
||||
Mask pixel.RGBA
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type particles struct {
|
||||
Generate func() *particle
|
||||
Update func(dt float64, p *particle) bool
|
||||
SpawnAvg, SpawnDist float64
|
||||
|
||||
parts list.List
|
||||
spawnTime float64
|
||||
}
|
||||
|
||||
func (p *particles) UpdateAll(dt float64) {
|
||||
p.spawnTime -= dt
|
||||
for p.spawnTime <= 0 {
|
||||
p.parts.PushFront(p.Generate())
|
||||
p.spawnTime += math.Max(0, p.SpawnAvg+rand.NormFloat64()*p.SpawnDist)
|
||||
}
|
||||
|
||||
for e := p.parts.Front(); e != nil; e = e.Next() {
|
||||
part := e.Value.(*particle)
|
||||
if !p.Update(dt, part) {
|
||||
defer p.parts.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *particles) DrawAll(t pixel.Target) {
|
||||
for e := p.parts.Front(); e != nil; e = e.Next() {
|
||||
part := e.Value.(*particle)
|
||||
|
||||
part.Sprite.DrawColorMask(
|
||||
t,
|
||||
pixel.IM.
|
||||
Scaled(pixel.ZV, part.Scale).
|
||||
Rotated(pixel.ZV, part.Rot).
|
||||
Moved(part.Pos),
|
||||
part.Mask,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type smokeData struct {
|
||||
Vel pixel.Vec
|
||||
Time float64
|
||||
Life float64
|
||||
}
|
||||
|
||||
type smokeSystem struct {
|
||||
Sheet pixel.Picture
|
||||
Rects []pixel.Rect
|
||||
Orig pixel.Vec
|
||||
|
||||
VelBasis []pixel.Vec
|
||||
VelDist float64
|
||||
|
||||
LifeAvg, LifeDist float64
|
||||
}
|
||||
|
||||
func (ss *smokeSystem) Generate() *particle {
|
||||
sd := new(smokeData)
|
||||
for _, base := range ss.VelBasis {
|
||||
c := math.Max(0, 1+rand.NormFloat64()*ss.VelDist)
|
||||
sd.Vel = sd.Vel.Add(base.Scaled(c))
|
||||
}
|
||||
sd.Vel = sd.Vel.Scaled(1 / float64(len(ss.VelBasis)))
|
||||
sd.Life = math.Max(0, ss.LifeAvg+rand.NormFloat64()*ss.LifeDist)
|
||||
|
||||
p := new(particle)
|
||||
p.Data = sd
|
||||
|
||||
p.Pos = ss.Orig
|
||||
p.Scale = 1
|
||||
p.Mask = pixel.Alpha(1)
|
||||
p.Sprite = pixel.NewSprite(ss.Sheet, ss.Rects[rand.Intn(len(ss.Rects))])
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (ss *smokeSystem) Update(dt float64, p *particle) bool {
|
||||
sd := p.Data.(*smokeData)
|
||||
sd.Time += dt
|
||||
|
||||
frac := sd.Time / sd.Life
|
||||
|
||||
p.Pos = p.Pos.Add(sd.Vel.Scaled(dt))
|
||||
p.Scale = 0.5 + frac*1.5
|
||||
|
||||
const (
|
||||
fadeIn = 0.2
|
||||
fadeOut = 0.4
|
||||
)
|
||||
if frac < fadeIn {
|
||||
p.Mask = pixel.Alpha(math.Pow(frac/fadeIn, 0.75))
|
||||
} else if frac >= fadeOut {
|
||||
p.Mask = pixel.Alpha(math.Pow(1-(frac-fadeOut)/(1-fadeOut), 1.5))
|
||||
} else {
|
||||
p.Mask = pixel.Alpha(1)
|
||||
}
|
||||
|
||||
return sd.Time < sd.Life
|
||||
}
|
||||
|
||||
func loadSpriteSheet(sheetPath, descriptionPath string) (sheet pixel.Picture, rects []pixel.Rect, err error) {
|
||||
sheetFile, err := os.Open(sheetPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer sheetFile.Close()
|
||||
|
||||
sheetImg, _, err := image.Decode(sheetFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sheet = pixel.PictureDataFromImage(sheetImg)
|
||||
|
||||
descriptionFile, err := os.Open(descriptionPath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer descriptionFile.Close()
|
||||
|
||||
description := csv.NewReader(descriptionFile)
|
||||
for {
|
||||
record, err := description.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
x, _ := strconv.ParseFloat(record[0], 64)
|
||||
y, _ := strconv.ParseFloat(record[1], 64)
|
||||
w, _ := strconv.ParseFloat(record[2], 64)
|
||||
h, _ := strconv.ParseFloat(record[3], 64)
|
||||
|
||||
y = sheet.Bounds().H() - y - h
|
||||
|
||||
rects = append(rects, pixel.R(x, y, x+w, y+h))
|
||||
}
|
||||
|
||||
return sheet, rects, nil
|
||||
}
|
||||
|
||||
func run() {
|
||||
sheet, rects, err := loadSpriteSheet("blackSmoke.png", "blackSmoke.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Smoke",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
Resizable: true,
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ss := &smokeSystem{
|
||||
Rects: rects,
|
||||
Orig: pixel.ZV,
|
||||
VelBasis: []pixel.Vec{pixel.V(-100, 100), pixel.V(100, 100), pixel.V(0, 100)},
|
||||
VelDist: 0.1,
|
||||
LifeAvg: 7,
|
||||
LifeDist: 0.5,
|
||||
}
|
||||
|
||||
p := &particles{
|
||||
Generate: ss.Generate,
|
||||
Update: ss.Update,
|
||||
SpawnAvg: 0.3,
|
||||
SpawnDist: 0.1,
|
||||
}
|
||||
|
||||
batch := pixel.NewBatch(&pixel.TrianglesData{}, sheet)
|
||||
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
|
||||
p.UpdateAll(dt)
|
||||
|
||||
win.Clear(colornames.Aliceblue)
|
||||
|
||||
orig := win.Bounds().Center()
|
||||
orig.Y -= win.Bounds().H() / 2
|
||||
win.SetMatrix(pixel.IM.Moved(orig))
|
||||
|
||||
batch.Clear()
|
||||
p.DrawAll(batch)
|
||||
batch.Draw(win)
|
||||
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/smoke/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 381 KiB |
13
vendor/github.com/faiface/pixel/examples/typewriter/README.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Typewriter
|
||||
|
||||
This example demonstrates text drawing and text input facilities by implementing a fancy typewriter
|
||||
with a red laser cursor. Screen shakes a bit when typing and some letters turn bold or italic
|
||||
randomly.
|
||||
|
||||
ASCII and Latin characters are supported here. Feel free to add support for more characters in the
|
||||
code (it's easy, but increases load time).
|
||||
|
||||
The seemingly buggy letters (one over another) in the screenshot are not bugs, but a result of using
|
||||
the typewriter backspace functionality.
|
||||
|
||||

|
317
vendor/github.com/faiface/pixel/examples/typewriter/main.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"github.com/faiface/pixel/text"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"golang.org/x/image/colornames"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/gofont/gobold"
|
||||
"golang.org/x/image/font/gofont/goitalic"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
)
|
||||
|
||||
func ttfFromBytesMust(b []byte, size float64) font.Face {
|
||||
ttf, err := truetype.Parse(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return truetype.NewFace(ttf, &truetype.Options{
|
||||
Size: size,
|
||||
GlyphCacheEntries: 1,
|
||||
})
|
||||
}
|
||||
|
||||
type typewriter struct {
|
||||
mu sync.Mutex
|
||||
|
||||
regular *text.Text
|
||||
bold *text.Text
|
||||
italic *text.Text
|
||||
|
||||
offset pixel.Vec
|
||||
position pixel.Vec
|
||||
move pixel.Vec
|
||||
}
|
||||
|
||||
func newTypewriter(c color.Color, regular, bold, italic *text.Atlas) *typewriter {
|
||||
tw := &typewriter{
|
||||
regular: text.New(pixel.ZV, regular),
|
||||
bold: text.New(pixel.ZV, bold),
|
||||
italic: text.New(pixel.ZV, italic),
|
||||
}
|
||||
tw.regular.Color = c
|
||||
tw.bold.Color = c
|
||||
tw.italic.Color = c
|
||||
return tw
|
||||
}
|
||||
|
||||
func (tw *typewriter) Ribbon(r rune) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
dice := rand.Intn(21)
|
||||
switch {
|
||||
case 0 <= dice && dice <= 18:
|
||||
tw.regular.WriteRune(r)
|
||||
case dice == 19:
|
||||
tw.bold.Dot = tw.regular.Dot
|
||||
tw.bold.WriteRune(r)
|
||||
tw.regular.Dot = tw.bold.Dot
|
||||
case dice == 20:
|
||||
tw.italic.Dot = tw.regular.Dot
|
||||
tw.italic.WriteRune(r)
|
||||
tw.regular.Dot = tw.italic.Dot
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *typewriter) Back() {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
tw.regular.Dot = tw.regular.Dot.Sub(pixel.V(tw.regular.Atlas().Glyph(' ').Advance, 0))
|
||||
}
|
||||
|
||||
func (tw *typewriter) Offset(off pixel.Vec) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
tw.offset = tw.offset.Add(off)
|
||||
}
|
||||
|
||||
func (tw *typewriter) Position() pixel.Vec {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
return tw.position
|
||||
}
|
||||
|
||||
func (tw *typewriter) Move(vel pixel.Vec) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
tw.move = vel
|
||||
}
|
||||
|
||||
func (tw *typewriter) Dot() pixel.Vec {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
return tw.regular.Dot
|
||||
}
|
||||
|
||||
func (tw *typewriter) Update(dt float64) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
tw.position = tw.position.Add(tw.move.Scaled(dt))
|
||||
}
|
||||
|
||||
func (tw *typewriter) Draw(t pixel.Target, m pixel.Matrix) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
m = pixel.IM.Moved(tw.position.Add(tw.offset)).Chained(m)
|
||||
tw.regular.Draw(t, m)
|
||||
tw.bold.Draw(t, m)
|
||||
tw.italic.Draw(t, m)
|
||||
}
|
||||
|
||||
func typeRune(tw *typewriter, r rune) {
|
||||
tw.Ribbon(r)
|
||||
if !unicode.IsSpace(r) {
|
||||
go shake(tw, 3, 17)
|
||||
}
|
||||
}
|
||||
|
||||
func back(tw *typewriter) {
|
||||
tw.Back()
|
||||
}
|
||||
|
||||
func shake(tw *typewriter, intensity, friction float64) {
|
||||
const (
|
||||
freq = 24
|
||||
dt = 1.0 / freq
|
||||
)
|
||||
ticker := time.NewTicker(time.Second / freq)
|
||||
defer ticker.Stop()
|
||||
|
||||
off := pixel.ZV
|
||||
|
||||
for range ticker.C {
|
||||
tw.Offset(off.Scaled(-1))
|
||||
|
||||
if intensity < 0.01*dt {
|
||||
break
|
||||
}
|
||||
|
||||
off = pixel.V((rand.Float64()-0.5)*intensity*2, (rand.Float64()-0.5)*intensity*2)
|
||||
intensity -= friction * dt
|
||||
|
||||
tw.Offset(off)
|
||||
}
|
||||
}
|
||||
|
||||
func scroll(tw *typewriter, intensity, speedUp float64) {
|
||||
const (
|
||||
freq = 120
|
||||
dt = 1.0 / freq
|
||||
)
|
||||
ticker := time.NewTicker(time.Second / freq)
|
||||
defer ticker.Stop()
|
||||
|
||||
speed := 0.0
|
||||
|
||||
for range ticker.C {
|
||||
if math.Abs(tw.Dot().Y+tw.Position().Y) < 0.01 {
|
||||
break
|
||||
}
|
||||
|
||||
targetSpeed := -(tw.Dot().Y + tw.Position().Y) * intensity
|
||||
if speed < targetSpeed {
|
||||
speed += speedUp * dt
|
||||
} else {
|
||||
speed = targetSpeed
|
||||
}
|
||||
|
||||
tw.Move(pixel.V(0, speed))
|
||||
}
|
||||
}
|
||||
|
||||
type dotlight struct {
|
||||
tw *typewriter
|
||||
color color.Color
|
||||
radius float64
|
||||
intensity float64
|
||||
acceleration float64
|
||||
maxSpeed float64
|
||||
|
||||
pos pixel.Vec
|
||||
vel pixel.Vec
|
||||
|
||||
imd *imdraw.IMDraw
|
||||
}
|
||||
|
||||
func newDotlight(tw *typewriter, c color.Color, radius, intensity, acceleration, maxSpeed float64) *dotlight {
|
||||
return &dotlight{
|
||||
tw: tw,
|
||||
color: c,
|
||||
radius: radius,
|
||||
intensity: intensity,
|
||||
acceleration: acceleration,
|
||||
maxSpeed: maxSpeed,
|
||||
pos: tw.Dot(),
|
||||
vel: pixel.ZV,
|
||||
imd: imdraw.New(nil),
|
||||
}
|
||||
}
|
||||
|
||||
func (dl *dotlight) Update(dt float64) {
|
||||
targetVel := dl.tw.Dot().Add(dl.tw.Position()).Sub(dl.pos).Scaled(dl.intensity)
|
||||
acc := targetVel.Sub(dl.vel).Scaled(dl.acceleration)
|
||||
dl.vel = dl.vel.Add(acc.Scaled(dt))
|
||||
if dl.vel.Len() > dl.maxSpeed {
|
||||
dl.vel = dl.vel.Unit().Scaled(dl.maxSpeed)
|
||||
}
|
||||
dl.pos = dl.pos.Add(dl.vel.Scaled(dt))
|
||||
}
|
||||
|
||||
func (dl *dotlight) Draw(t pixel.Target, m pixel.Matrix) {
|
||||
dl.imd.Clear()
|
||||
dl.imd.SetMatrix(m)
|
||||
dl.imd.Color = dl.color
|
||||
dl.imd.Push(dl.pos)
|
||||
dl.imd.Color = pixel.Alpha(0)
|
||||
for i := 0.0; i <= 32; i++ {
|
||||
angle := i * 2 * math.Pi / 32
|
||||
dl.imd.Push(dl.pos.Add(pixel.V(dl.radius, 0).Rotated(angle)))
|
||||
}
|
||||
dl.imd.Polygon(0)
|
||||
dl.imd.Draw(t)
|
||||
}
|
||||
|
||||
func run() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Typewriter",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
Resizable: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
win.SetSmooth(true)
|
||||
|
||||
var (
|
||||
regular = text.NewAtlas(
|
||||
ttfFromBytesMust(goregular.TTF, 42),
|
||||
text.ASCII, text.RangeTable(unicode.Latin),
|
||||
)
|
||||
bold = text.NewAtlas(
|
||||
ttfFromBytesMust(gobold.TTF, 42),
|
||||
text.ASCII, text.RangeTable(unicode.Latin),
|
||||
)
|
||||
italic = text.NewAtlas(
|
||||
ttfFromBytesMust(goitalic.TTF, 42),
|
||||
text.ASCII, text.RangeTable(unicode.Latin),
|
||||
)
|
||||
|
||||
bgColor = color.RGBA{
|
||||
R: 241,
|
||||
G: 241,
|
||||
B: 212,
|
||||
A: 255,
|
||||
}
|
||||
fgColor = color.RGBA{
|
||||
R: 0,
|
||||
G: 15,
|
||||
B: 85,
|
||||
A: 255,
|
||||
}
|
||||
|
||||
tw = newTypewriter(pixel.ToRGBA(fgColor).Scaled(0.9), regular, bold, italic)
|
||||
dl = newDotlight(tw, colornames.Red, 6, 30, 20, 1600)
|
||||
)
|
||||
|
||||
fps := time.Tick(time.Second / 120)
|
||||
last := time.Now()
|
||||
for !win.Closed() {
|
||||
for _, r := range win.Typed() {
|
||||
go typeRune(tw, r)
|
||||
}
|
||||
if win.JustPressed(pixelgl.KeyTab) || win.Repeated(pixelgl.KeyTab) {
|
||||
go typeRune(tw, '\t')
|
||||
}
|
||||
if win.JustPressed(pixelgl.KeyEnter) || win.Repeated(pixelgl.KeyEnter) {
|
||||
go typeRune(tw, '\n')
|
||||
go scroll(tw, 20, 6400)
|
||||
}
|
||||
if win.JustPressed(pixelgl.KeyBackspace) || win.Repeated(pixelgl.KeyBackspace) {
|
||||
go back(tw)
|
||||
}
|
||||
|
||||
dt := time.Since(last).Seconds()
|
||||
last = time.Now()
|
||||
|
||||
tw.Update(dt)
|
||||
dl.Update(dt)
|
||||
|
||||
win.Clear(bgColor)
|
||||
|
||||
m := pixel.IM.Moved(pixel.V(32, 32))
|
||||
tw.Draw(win, m)
|
||||
dl.Draw(win, m)
|
||||
|
||||
win.Update()
|
||||
<-fps
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/typewriter/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 156 KiB |
8
vendor/github.com/faiface/pixel/examples/xor/README.md
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Xor
|
||||
|
||||
This example demonstrates an unusual Porter-Duff composition method: Xor. (And the capability of
|
||||
drawing circles.)
|
||||
|
||||
Just thought it was cool.
|
||||
|
||||

|
76
vendor/github.com/faiface/pixel/examples/xor/main.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/faiface/pixel"
|
||||
"github.com/faiface/pixel/imdraw"
|
||||
"github.com/faiface/pixel/pixelgl"
|
||||
"golang.org/x/image/colornames"
|
||||
)
|
||||
|
||||
func run() {
|
||||
cfg := pixelgl.WindowConfig{
|
||||
Title: "Xor",
|
||||
Bounds: pixel.R(0, 0, 1024, 768),
|
||||
Resizable: true,
|
||||
VSync: true,
|
||||
}
|
||||
win, err := pixelgl.NewWindow(cfg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
imd := imdraw.New(nil)
|
||||
|
||||
canvas := pixelgl.NewCanvas(win.Bounds())
|
||||
|
||||
start := time.Now()
|
||||
for !win.Closed() {
|
||||
// in case window got resized, we also need to resize our canvas
|
||||
canvas.SetBounds(win.Bounds())
|
||||
|
||||
offset := math.Sin(time.Since(start).Seconds()) * 300
|
||||
|
||||
// clear the canvas to be totally transparent and set the xor compose method
|
||||
canvas.Clear(pixel.Alpha(0))
|
||||
canvas.SetComposeMethod(pixel.ComposeXor)
|
||||
|
||||
// red circle
|
||||
imd.Clear()
|
||||
imd.Color = pixel.RGB(1, 0, 0)
|
||||
imd.Push(win.Bounds().Center().Add(pixel.V(-offset, 0)))
|
||||
imd.Circle(200, 0)
|
||||
imd.Draw(canvas)
|
||||
|
||||
// blue circle
|
||||
imd.Clear()
|
||||
imd.Color = pixel.RGB(0, 0, 1)
|
||||
imd.Push(win.Bounds().Center().Add(pixel.V(offset, 0)))
|
||||
imd.Circle(150, 0)
|
||||
imd.Draw(canvas)
|
||||
|
||||
// yellow circle
|
||||
imd.Clear()
|
||||
imd.Color = pixel.RGB(1, 1, 0)
|
||||
imd.Push(win.Bounds().Center().Add(pixel.V(0, -offset)))
|
||||
imd.Circle(100, 0)
|
||||
imd.Draw(canvas)
|
||||
|
||||
// magenta circle
|
||||
imd.Clear()
|
||||
imd.Color = pixel.RGB(1, 0, 1)
|
||||
imd.Push(win.Bounds().Center().Add(pixel.V(0, offset)))
|
||||
imd.Circle(50, 0)
|
||||
imd.Draw(canvas)
|
||||
|
||||
win.Clear(colornames.Green)
|
||||
canvas.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
|
||||
win.Update()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
pixelgl.Run(run)
|
||||
}
|
BIN
vendor/github.com/faiface/pixel/examples/xor/screenshot.png
generated
vendored
Normal file
After Width: | Height: | Size: 10 KiB |