Files
groovy/examples/splashmenu/scenes/parallax.go
2023-08-31 17:01:21 -04:00

255 lines
5.8 KiB
Go

package splashmenu
import (
"bytes"
"cosmos/diego/groovy"
"image"
"image/color"
"log"
"math"
"math/rand"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/vector"
_ "embed"
)
const (
GALAXY_SCALE_DIST = 50
DEBRIS_SCALE_DIST = 10
FLOAT_SCALE = 8
ASTEROID_COUNT = 14
MAX_ROTATION_SPEED = 3
MIN_ROTATION_SPEED = 1
OFF_SCREEN_SCALE = 2 // number of parallax dimension screens for the logical game display area
)
var (
//go:embed assets/galaxy4.jpg
galaxyImg_jpg []byte
//go:embed assets/debris_00.png
debrisImg_png []byte
//go:embed assets/roid1.png
roid1Img_png []byte
//go:embed assets/roid2.png
roid2Img_png []byte
//go:embed assets/blend.png
blendImg_png []byte
galaxyBackground *ebiten.Image
debrisImage *ebiten.Image
blendImage *ebiten.Image
blendTmp *ebiten.Image
roids []*ebiten.Image
)
type Asteroid struct {
cX int
cY int
rotationSpeed float64
}
func init() {
// Load up images
img, _, err := image.Decode(bytes.NewReader(galaxyImg_jpg))
if err != nil {
log.Fatal(err)
}
galaxyBackground = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(debrisImg_png))
if err != nil {
log.Fatal(err)
}
debrisImage = ebiten.NewImageFromImage(img)
//asteroids!
img, _, err = image.Decode(bytes.NewReader(roid1Img_png))
if err != nil {
log.Fatal(err)
}
roids = append(roids, ebiten.NewImageFromImage(img))
img, _, err = image.Decode(bytes.NewReader(roid2Img_png))
if err != nil {
log.Fatal(err)
}
roids = append(roids, ebiten.NewImageFromImage(img))
img, _, err = image.Decode(bytes.NewReader(blendImg_png))
if err != nil {
log.Fatal(err)
}
blendImage = ebiten.NewImageFromImage(img)
blendTmp = ebiten.NewImageFromImage(blendImage)
}
type Parallax struct {
events map[groovy.SceneEvent]func()
Dimensions groovy.Area
increment int
asteroids []Asteroid
}
func NewParallax() Parallax {
return Parallax{
events: make(map[groovy.SceneEvent]func()),
}
}
func (p *Parallax) Update() error {
p.increment++
return nil
}
func (p *Parallax) Draw(screen *ebiten.Image) {
p.DrawDynamicBackground(screen)
p.DrawObstacles(screen)
//p.DrawObstaclesWithShamLighting(screen)
p.RepositionTest(screen)
}
func (p *Parallax) SetDimensions(a groovy.Area) {
p.Dimensions = a
}
func (p *Parallax) SetEventHandler(event groovy.SceneEvent, f func()) {
p.events[event] = f
}
func (p *Parallax) InitializeParallax() {
if p.Dimensions.Area() <= 0 {
return
}
for i := 0; i < ASTEROID_COUNT; i++ {
newX := rand.Intn(p.Dimensions.Width) * OFF_SCREEN_SCALE
newY := rand.Intn(p.Dimensions.Height) * OFF_SCREEN_SCALE
rS := rand.Float64()*MAX_ROTATION_SPEED + MIN_ROTATION_SPEED
p.asteroids = append(p.asteroids, Asteroid{cX: newX, cY: newY, rotationSpeed: rS})
}
whiteImage.Fill(color.White)
}
func (p *Parallax) DrawDynamicBackground(screen *ebiten.Image) {
x, y := ebiten.CursorPosition()
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(-x-p.Dimensions.Width)/GALAXY_SCALE_DIST, float64(-y-p.Dimensions.Height)/GALAXY_SCALE_DIST)
screen.DrawImage(galaxyBackground, op)
//debris layers
const DEBRIS_QTY = 3
const DEBRIS_WIDTH = 640 / 3 * 2
const DEBRIS_HEIGHT = 480 / 3 * 2
const TOTAL_LAYERS = 2
for k := 0; k < TOTAL_LAYERS; k++ {
for i := 0; i < DEBRIS_QTY; i++ {
for j := 0; j < DEBRIS_QTY; j++ {
//compute object origins (pre-shifts)
x0 := float64(j*DEBRIS_WIDTH) + float64(p.increment/640)
y0 := float64(i*DEBRIS_HEIGHT) + float64(p.increment/480)
//now compute delta with parallax effect
xd := (x0-float64(x))/DEBRIS_SCALE_DIST*float64(k+1) + x0 - float64(p.increment*k)/FLOAT_SCALE
yd := (y0-float64(y))/DEBRIS_SCALE_DIST*float64(k+1) + y0 - float64(p.increment*k)/FLOAT_SCALE
op = &ebiten.DrawImageOptions{}
op.GeoM.Translate(xd, yd)
//op.GeoM.Rotate(math.Pi / 180 * float64(p.increment))
op.Blend = ebiten.BlendSourceOver
screen.DrawImage(debrisImage, op)
//log.Printf("%d, %d", xoffset, yoffset)
}
}
}
}
func (p *Parallax) DrawObstacles(screen *ebiten.Image) {
x, y := ebiten.CursorPosition()
for _, a := range p.asteroids {
cx := float64(a.cX)
cy := float64(a.cY)
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(-100, -66)
op.GeoM.Rotate(-float64(p.increment) * a.rotationSpeed / (math.Pi * 120))
op.GeoM.Translate(cx-float64(x), cy-float64(y))
screen.DrawImage(roids[0], op)
}
}
func (p *Parallax) DrawObstaclesWithShamLighting(screen *ebiten.Image) {
x, y := ebiten.CursorPosition()
for _, a := range p.asteroids {
cx := float64(a.cX)
cy := float64(a.cY)
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(-100, -66)
op.GeoM.Rotate(-float64(p.increment) * a.rotationSpeed / (math.Pi * 120))
op.GeoM.Translate(100, 66)
//draw roid to temporary location
blendTmp.DrawImage(roids[0], op)
op = &ebiten.DrawImageOptions{}
op.Blend = ebiten.BlendSourceAtop
blendTmp.DrawImage(blendImage, op)
op = &ebiten.DrawImageOptions{}
op.GeoM.Translate(cx-float64(x), cy-float64(y))
screen.DrawImage(blendTmp, op)
}
}
func (p *Parallax) RepositionTest(screen *ebiten.Image) {
var path vector.Path
//set up origin and boundaries
x0 := float32(p.Dimensions.Width / 2)
y0 := float32(p.Dimensions.Height / 2)
xd := float32(100.0)
yd := float32(100.0)
//connect the dots
path.MoveTo(x0-xd, y0-yd)
path.LineTo(x0+xd, y0-yd)
path.LineTo(x0+xd, y0+yd)
path.LineTo(x0-xd, y0+yd)
path.LineTo(x0-xd, y0+yd)
path.Close()
var vs []ebiten.Vertex
var is []uint16
vs, is = path.AppendVerticesAndIndicesForFilling(nil, nil)
for i := range vs {
vs[i].SrcX = 1
vs[i].SrcY = 1
vs[i].ColorR = float32(0xff) / 0xFF
vs[i].ColorG = float32(0x00) / 0xFF
vs[i].ColorB = float32(0x00) / 0xFF
vs[i].ColorA = 1
}
screen.DrawTriangles(vs, is, whiteSubImage, nil)
}