package splashmenu import ( "bytes" "cosmos/diego/groovy" "image" "image/color" "log" "math" "math/rand" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/inpututil" "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++ var keysPressed []ebiten.Key keysPressed = inpututil.AppendJustPressedKeys(keysPressed[:0]) //check for user input to transition scene if inpututil.IsKeyJustPressed(ebiten.KeyQ) { if p.events[groovy.COMPLETED] != nil { p.events[groovy.COMPLETED]() } } 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 adjustX := float64(p.Dimensions.Width) adjustY := float64(p.Dimensions.Height) intX := adjustX * math.Floor(float64(p.increment)/adjustX) intY := adjustY * math.Floor(float64(p.increment)/adjustY) xd = xd + intX yd = yd + intY 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) adjustment := float32(400.0) xd := adjustment / 4.0 yd := adjustment / 4.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) //minX := x0 - xd maxX := x0 + xd //minY := y0 - yd //maxY := y0 + yd //178x229 roX := float64(178 / 4) roY := float64(229 / 4) startX := float64(maxX) + roX*2 startY := float64(y0) + roY/2 currentX := startX - float64(p.increment) + float64(adjustment)*math.Floor(float64(p.increment)/float64(adjustment)) op := &ebiten.DrawImageOptions{} op.GeoM.Scale(.5, .5) op.GeoM.Translate(-roX, -roY) op.GeoM.Rotate(-float64(p.increment) / (math.Pi * 12.0)) op.GeoM.Translate(currentX, startY) screen.DrawImage(roids[1], op) }