Compare commits
29 Commits
5da470937e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 67241b9b37 | |||
| afab7697bb | |||
|
|
220e0ed712 | ||
|
|
09de5bf455 | ||
|
|
296f56df72 | ||
|
|
10d6cad4a3 | ||
|
|
6cd2b8186c | ||
|
|
8341fa9933 | ||
|
|
05dde984e9 | ||
|
|
c5f5c01ab0 | ||
|
|
f4299e15a5 | ||
| 55fcb205ee | |||
| 4c19ce7f87 | |||
| 8138fb6329 | |||
| 5075196e72 | |||
| b6ea6abad6 | |||
| ffb02b2169 | |||
| d5094c699b | |||
| 4b701e3ff3 | |||
| 43c2b35e30 | |||
| 80f0ecf279 | |||
| 4930557ba3 | |||
| 403a81d0fb | |||
| 49a8f2c768 | |||
| 8c7c9c0b8c | |||
| fbfe87137e | |||
| 67e9399ad2 | |||
| 30734a21ea | |||
| 896e7eeb2a |
82
examples/splashmenu/fonts/splashfonts.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package splashmenu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||||
|
"golang.org/x/image/font"
|
||||||
|
"golang.org/x/image/font/opentype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FontBase struct {
|
||||||
|
Title font.Face
|
||||||
|
Menu font.Face
|
||||||
|
Splash font.Face
|
||||||
|
BigTitle font.Face
|
||||||
|
MegaTitle font.Face
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
SplashFont *FontBase
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
const dpi = 72
|
||||||
|
SplashFont = &FontBase{}
|
||||||
|
|
||||||
|
tt, err := opentype.Parse(fonts.PressStart2P_ttf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashFont.Title, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||||
|
Size: 16,
|
||||||
|
DPI: dpi,
|
||||||
|
Hinting: font.HintingVertical,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashFont.Menu, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||||
|
Size: 10,
|
||||||
|
DPI: dpi,
|
||||||
|
Hinting: font.HintingVertical,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
tt, err = opentype.Parse(fonts.PressStart2P_ttf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashFont.Splash, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||||
|
Size: 12,
|
||||||
|
DPI: dpi,
|
||||||
|
Hinting: font.HintingVertical,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashFont.BigTitle, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||||
|
Size: 40,
|
||||||
|
DPI: dpi,
|
||||||
|
Hinting: font.HintingVertical,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SplashFont.MegaTitle, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||||
|
Size: 70,
|
||||||
|
DPI: dpi,
|
||||||
|
Hinting: font.HintingVertical,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ func main() {
|
|||||||
//setup manager
|
//setup manager
|
||||||
manager := groovy.NewManager()
|
manager := groovy.NewManager()
|
||||||
manager.SetDimensions(groovy.Area{Width: 1280, Height: 720})
|
manager.SetDimensions(groovy.Area{Width: 1280, Height: 720})
|
||||||
ebiten.SetWindowSize(manager.Info.Dimension.Width, manager.Info.Dimension.Height)
|
ebiten.SetWindowSize(manager.Info.Dimensions.Width, manager.Info.Dimensions.Height)
|
||||||
ebiten.SetWindowTitle(manager.Info.Name)
|
ebiten.SetWindowTitle(manager.Info.Name)
|
||||||
|
|
||||||
loadScenes(&manager)
|
loadScenes(&manager)
|
||||||
@@ -32,8 +32,13 @@ func main() {
|
|||||||
// Example loading of two scenes
|
// Example loading of two scenes
|
||||||
func loadScenes(m *groovy.Manager) {
|
func loadScenes(m *groovy.Manager) {
|
||||||
//call the loaders for each scene
|
//call the loaders for each scene
|
||||||
loadSplash(m)
|
//loadSplash(m)
|
||||||
loadMenu(m)
|
//loadBsoft(m)
|
||||||
|
//loadMenu(m)
|
||||||
|
//loadNoise(m)
|
||||||
|
//loadSplashpad(m)
|
||||||
|
//loadParallax(m)
|
||||||
|
loadRays(m)
|
||||||
|
|
||||||
//reset the manager to start scene 1
|
//reset the manager to start scene 1
|
||||||
m.ResetScenes()
|
m.ResetScenes()
|
||||||
@@ -52,10 +57,47 @@ func loadMenu(m *groovy.Manager) {
|
|||||||
sceneMenu := splashmenu.NewMenu()
|
sceneMenu := splashmenu.NewMenu()
|
||||||
sceneMenu.SetOptions(map[int]splashmenu.MenuOption{
|
sceneMenu.SetOptions(map[int]splashmenu.MenuOption{
|
||||||
1: {Description: "splash", SelectionEvent: groovy.RESET, Mapping: ebiten.Key1},
|
1: {Description: "splash", SelectionEvent: groovy.RESET, Mapping: ebiten.Key1},
|
||||||
2: {Description: "menu"},
|
2: {Description: "bsoft", SelectionEvent: groovy.COMPLETED, Mapping: ebiten.Key2},
|
||||||
3: {Description: "swing"},
|
3: {Description: "menu"},
|
||||||
4: {Description: "exit", SelectionEvent: groovy.ENDGAME, Mapping: ebiten.Key4},
|
4: {Description: "noise", SelectionEvent: groovy.COMPLETED, Mapping: ebiten.Key4},
|
||||||
|
5: {Description: "guy", SelectionEvent: groovy.COMPLETED, Mapping: ebiten.Key5},
|
||||||
|
6: {Description: "parallax", SelectionEvent: groovy.COMPLETED, Mapping: ebiten.Key6},
|
||||||
|
7: {Description: "exit", SelectionEvent: groovy.ENDGAME, Mapping: ebiten.Key7},
|
||||||
})
|
})
|
||||||
|
|
||||||
m.AddScene(&sceneMenu)
|
m.AddScene(&sceneMenu)
|
||||||
|
|
||||||
|
//set up a custom event handler for the menu completion >:]
|
||||||
|
scene := m.GetScene(m.SceneCount() - 1)
|
||||||
|
scene.SetEventHandler(groovy.COMPLETED, func() {
|
||||||
|
//direct manager to update current scene by retrieving menu selection
|
||||||
|
sceneSelected := sceneMenu.GetMenuSelection() - 1
|
||||||
|
m.SetCurrentScene(sceneSelected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBsoft(m *groovy.Manager) {
|
||||||
|
sceneBsoft := splashmenu.NewBsoft()
|
||||||
|
m.AddScene(&sceneBsoft)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadNoise(m *groovy.Manager) {
|
||||||
|
sceneNoisy := splashmenu.NewNoisy()
|
||||||
|
m.AddScene(&sceneNoisy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSplashpad(m *groovy.Manager) {
|
||||||
|
sceneSplashpad := splashmenu.NewSplashPad()
|
||||||
|
m.AddScene(&sceneSplashpad)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadParallax(m *groovy.Manager) {
|
||||||
|
sceneParallax := splashmenu.NewParallax()
|
||||||
|
m.AddScene(&sceneParallax)
|
||||||
|
sceneParallax.InitializeParallax()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRays(m *groovy.Manager) {
|
||||||
|
sceneRays := splashmenu.NewRays()
|
||||||
|
m.AddScene(&sceneRays)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
examples/splashmenu/scenes/assets/blend.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
examples/splashmenu/scenes/assets/bsoft.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
examples/splashmenu/scenes/assets/debris_00.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
examples/splashmenu/scenes/assets/forest.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
examples/splashmenu/scenes/assets/galaxy.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
examples/splashmenu/scenes/assets/galaxy2.jpg
Normal file
|
After Width: | Height: | Size: 518 KiB |
BIN
examples/splashmenu/scenes/assets/galaxy3.jpg
Normal file
|
After Width: | Height: | Size: 19 MiB |
BIN
examples/splashmenu/scenes/assets/galaxy4.jpg
Normal file
|
After Width: | Height: | Size: 6.6 MiB |
BIN
examples/splashmenu/scenes/assets/galaxy5.jpg
Normal file
|
After Width: | Height: | Size: 540 KiB |
BIN
examples/splashmenu/scenes/assets/galaxy6.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
examples/splashmenu/scenes/assets/roid1.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
examples/splashmenu/scenes/assets/roid1_normal.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
examples/splashmenu/scenes/assets/roid2.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
examples/splashmenu/scenes/assets/slash.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
examples/splashmenu/scenes/assets/slashright.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
examples/splashmenu/scenes/assets/spotlight.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
examples/splashmenu/scenes/assets/title.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
233
examples/splashmenu/scenes/bsoft.go
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
package splashmenu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cosmos/diego/groovy"
|
||||||
|
_ "embed"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
_ "image/jpeg"
|
||||||
|
_ "image/png"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
magicRatio = 12
|
||||||
|
maxGlitchOffset = 25
|
||||||
|
minGlitchInterval = 60
|
||||||
|
maxGlitchTimeOffset = 15
|
||||||
|
//in seconds
|
||||||
|
fadeTimer = 1.0
|
||||||
|
fadeOffset = 2.0
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed assets/bsoft.png
|
||||||
|
mImage_png []byte
|
||||||
|
imgDimensions = groovy.Area{Width: 827, Height: 628}
|
||||||
|
|
||||||
|
//go:embed assets/title.png
|
||||||
|
bImage_png []byte
|
||||||
|
titlePosition = groovy.Area{Width: 580, Height: 490}
|
||||||
|
|
||||||
|
imgToBlur *ebiten.Image
|
||||||
|
bImage *ebiten.Image
|
||||||
|
|
||||||
|
glitchTimer uint
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Decode an image from the image file's byte slice.
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(mImage_png))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
imgToBlur = ebiten.NewImageFromImage(img)
|
||||||
|
|
||||||
|
// Decode an image from the image file's byte slice.
|
||||||
|
img, _, err = image.Decode(bytes.NewReader(bImage_png))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
bImage = ebiten.NewImageFromImage(img)
|
||||||
|
|
||||||
|
glitchTimer = 15
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fader struct {
|
||||||
|
Curtain *ebiten.Image
|
||||||
|
counter int
|
||||||
|
timer float32
|
||||||
|
offset float32
|
||||||
|
alpha float32
|
||||||
|
fps float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFader(x, y int, t, offset float32) Fader {
|
||||||
|
f := Fader{
|
||||||
|
Curtain: ebiten.NewImage(x, y),
|
||||||
|
timer: t,
|
||||||
|
counter: 0,
|
||||||
|
alpha: 0.0,
|
||||||
|
fps: 60,
|
||||||
|
offset: offset,
|
||||||
|
}
|
||||||
|
f.Curtain.Fill(color.RGBA{0x00, 0x00, 0x00, 0xFF})
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
func (f *Fader) Update() {
|
||||||
|
f.counter++
|
||||||
|
|
||||||
|
f.alpha = (float32(f.counter) - (f.offset * f.fps)) / (f.timer * f.fps)
|
||||||
|
if f.alpha < 0 {
|
||||||
|
f.alpha = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fader) Draw(img *ebiten.Image) {
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.ColorScale.Scale(1, 1, 1, f.alpha)
|
||||||
|
img.DrawImage(f.Curtain, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fader) IsComplete() bool {
|
||||||
|
return f.alpha > 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bsoft struct {
|
||||||
|
Dimensions groovy.Area
|
||||||
|
events map[groovy.SceneEvent]func()
|
||||||
|
bgcolor color.RGBA
|
||||||
|
increment int
|
||||||
|
renderTarget *ebiten.Image
|
||||||
|
mosaicRatio uint
|
||||||
|
countDown uint
|
||||||
|
curtain Fader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBsoft() Bsoft {
|
||||||
|
return Bsoft{
|
||||||
|
bgcolor: backgroundBaseColor,
|
||||||
|
events: make(map[groovy.SceneEvent]func()),
|
||||||
|
mosaicRatio: 16,
|
||||||
|
increment: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bsoft) Update() error {
|
||||||
|
b.increment++
|
||||||
|
|
||||||
|
var keysPressed []ebiten.Key
|
||||||
|
keysPressed = inpututil.AppendPressedKeys(keysPressed[:0])
|
||||||
|
|
||||||
|
for _, k := range keysPressed {
|
||||||
|
switch k {
|
||||||
|
case ebiten.KeyUp:
|
||||||
|
b.mosaicRatio = b.mosaicRatio + 1
|
||||||
|
case ebiten.KeyDown:
|
||||||
|
if b.mosaicRatio > 1 {
|
||||||
|
b.mosaicRatio = b.mosaicRatio - 1
|
||||||
|
}
|
||||||
|
case ebiten.KeyQ:
|
||||||
|
if b.events[groovy.COMPLETED] != nil {
|
||||||
|
b.events[groovy.COMPLETED]()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.curtain.IsComplete() {
|
||||||
|
if b.events[groovy.COMPLETED] != nil {
|
||||||
|
b.events[groovy.COMPLETED]()
|
||||||
|
b.curtain.Clear()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.curtain.Update()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fader) Clear() {
|
||||||
|
f.alpha = 0 //reset fader
|
||||||
|
f.counter = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bsoft) Draw(screen *ebiten.Image) {
|
||||||
|
b.DrawGlitchLogo(screen)
|
||||||
|
b.DrawGlitchLogoText(screen)
|
||||||
|
b.curtain.Draw(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bsoft) SetEventHandler(event groovy.SceneEvent, f func()) {
|
||||||
|
b.events[event] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets sene dimensions
|
||||||
|
func (b *Bsoft) SetDimensions(a groovy.Area) {
|
||||||
|
b.Dimensions = a
|
||||||
|
b.renderTarget = ebiten.NewImage(imgDimensions.Width, imgDimensions.Height)
|
||||||
|
b.curtain = NewFader(a.Width, a.Height, fadeTimer, fadeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bsoft) DrawGlitchLogo(screen *ebiten.Image) {
|
||||||
|
//summation of sinusoids with different frequencies for 'step' response on pixel-glithcy-ness
|
||||||
|
var sum float64 = 0
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
w := math.Sin(float64(b.increment) / (math.Pi * float64(i+1)))
|
||||||
|
sum = sum + w
|
||||||
|
}
|
||||||
|
ratio := sum + magicRatio
|
||||||
|
|
||||||
|
// Shrink the image once.
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Scale(1/ratio, 1/ratio)
|
||||||
|
b.renderTarget.DrawImage(imgToBlur, op)
|
||||||
|
|
||||||
|
// Enlarge the shrunk image.
|
||||||
|
// The filter is the nearest filter, so the result will be mosaic.
|
||||||
|
op = &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Scale(ratio, ratio)
|
||||||
|
|
||||||
|
op.GeoM.Translate(float64(b.Dimensions.Width)/2-float64(imgDimensions.Width)/2, float64(b.Dimensions.Height)/2-float64(imgDimensions.Height)/2-35)
|
||||||
|
screen.DrawImage(b.renderTarget, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bsoft) DrawGlitchLogoText(screen *ebiten.Image) {
|
||||||
|
//glitchy twitch title behaviour
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
var glitchX int = 0
|
||||||
|
var glitchY int = 0
|
||||||
|
X := minGlitchInterval
|
||||||
|
Y := maxGlitchTimeOffset
|
||||||
|
|
||||||
|
//glithiness active until countdown resolved, randomize the delta offset
|
||||||
|
//creates temporary glitch effect on the logo text, this is achieved as follows:
|
||||||
|
// 1. if the glitch frame counter is active, glitch offset is in effect and computed
|
||||||
|
// 2. otherwise, we engage the glitch after X frames have passed with a random Y frame offset
|
||||||
|
if b.countDown > 0 {
|
||||||
|
glitchX = rand.Intn(maxGlitchOffset) * getRandomDirection()
|
||||||
|
glitchY = rand.Intn(maxGlitchOffset) * getRandomDirection()
|
||||||
|
b.countDown--
|
||||||
|
|
||||||
|
} else if b.increment%(X+rand.Intn(Y)) == 0 {
|
||||||
|
b.countDown = glitchTimer //set glitch timer, in frames
|
||||||
|
}
|
||||||
|
op.GeoM.Translate(float64(titlePosition.Width+glitchX), float64(titlePosition.Height+glitchY))
|
||||||
|
screen.DrawImage(bImage, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns +1 or -1 as an integer
|
||||||
|
func getRandomDirection() int {
|
||||||
|
if rand.Intn(2) == 1 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
580
examples/splashmenu/scenes/controller.go
Normal file
@@ -0,0 +1,580 @@
|
|||||||
|
package splashmenu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cosmos/diego/groovy"
|
||||||
|
splashmenu "cosmos/diego/groovy/examples/splashmenu/fonts"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/text"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
|
|
||||||
|
_ "embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FORCE_GRAVITY = 0
|
||||||
|
FORCE_ATTACK = 1
|
||||||
|
FORCE_ENVIRONMENT = 2
|
||||||
|
MAX_FLOOR_POS = 720 - 50
|
||||||
|
HERO_WIDTH = 20
|
||||||
|
HERO_HEIGHT = HERO_WIDTH
|
||||||
|
|
||||||
|
//initial thrust
|
||||||
|
MAX_JUMP_HEIGHT = -150 //pixels
|
||||||
|
TIME_TO_APEX = 20.0 // in frame counts (60fps means this value is 1/60 seconds)
|
||||||
|
JUMP_DURATION = TIME_TO_APEX * 2.0
|
||||||
|
GRAVITY_VECTOR = -2.0 * MAX_JUMP_HEIGHT / (TIME_TO_APEX * TIME_TO_APEX)
|
||||||
|
INITIAL_JUMP_VELOCITY = -GRAVITY_VECTOR * TIME_TO_APEX
|
||||||
|
|
||||||
|
//weighted drop
|
||||||
|
TIME_TO_APEX2 = 17.0 // in frame counts (60fps means this value is 1/60 seconds)
|
||||||
|
GRAVITY_VECTOR2 = -2.0 * MAX_JUMP_HEIGHT / (TIME_TO_APEX2 * TIME_TO_APEX2)
|
||||||
|
INITIAL_DROP_VELOCITY = -GRAVITY_VECTOR2 * TIME_TO_APEX2
|
||||||
|
|
||||||
|
//run animation ramp up
|
||||||
|
TOP_SPEED = 20
|
||||||
|
TIME_TO_TOP_SPEED = 6 //in frames
|
||||||
|
VELOCITY_VECTOR = 2 * TOP_SPEED / (TIME_TO_TOP_SPEED * TIME_TO_TOP_SPEED)
|
||||||
|
|
||||||
|
//decel animation ramp down
|
||||||
|
TIME_TO_STOP = 5 //in frames
|
||||||
|
|
||||||
|
//attacking stats
|
||||||
|
MAX_HERO_ATTACK_DURATION = 22 //in frames
|
||||||
|
TOTAL_ATTACK_ANIMATION_IMAGES = 8
|
||||||
|
FRAMES_PER_ATTACK_SLIDE = MAX_HERO_ATTACK_DURATION / TOTAL_ATTACK_ANIMATION_IMAGES
|
||||||
|
|
||||||
|
SENSITIVITY = 0.15
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
whiteImage = ebiten.NewImage(3, 3)
|
||||||
|
whiteSubImage = whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
|
||||||
|
|
||||||
|
//go:embed assets/slash.png
|
||||||
|
slashImg_png []byte
|
||||||
|
//go:embed assets/slashright.png
|
||||||
|
slashRightImg_png []byte
|
||||||
|
|
||||||
|
//go:embed assets/forest.png
|
||||||
|
forestImg_png []byte
|
||||||
|
|
||||||
|
slashImgHeight = 60
|
||||||
|
slashImgCellsWidth = 72
|
||||||
|
//slashImgCells = 8
|
||||||
|
//slashImgDimensions = groovy.Area{Width: 576, Height: slashImgHeight}
|
||||||
|
slashImage = ebiten.NewImage(576, slashImgHeight)
|
||||||
|
slashRightImage = ebiten.NewImage(576, slashImgHeight)
|
||||||
|
forestImage = ebiten.NewImage(1280, 720)
|
||||||
|
//slashRenderTarget = ebiten.NewImage(slashImgCellsWidth, slashImgHeight)
|
||||||
|
|
||||||
|
slowmo = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Decode an image from the image file's byte slice.
|
||||||
|
img, _, err := image.Decode(bytes.NewReader(slashImg_png))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
slashImage = ebiten.NewImageFromImage(img)
|
||||||
|
|
||||||
|
img, _, err = image.Decode(bytes.NewReader(slashRightImg_png))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
slashRightImage = ebiten.NewImageFromImage(img)
|
||||||
|
|
||||||
|
img, _, err = image.Decode(bytes.NewReader(forestImg_png))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
forestImage = ebiten.NewImageFromImage(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
type vec struct {
|
||||||
|
x float32
|
||||||
|
y float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type impulse struct {
|
||||||
|
f vec
|
||||||
|
timeleft int
|
||||||
|
}
|
||||||
|
|
||||||
|
type forceType uint32
|
||||||
|
|
||||||
|
type MyChar struct {
|
||||||
|
posX int16
|
||||||
|
posY int16
|
||||||
|
mass float32 //g
|
||||||
|
|
||||||
|
//acceleration
|
||||||
|
aX float32
|
||||||
|
aY float32
|
||||||
|
jumptime int
|
||||||
|
jumping bool
|
||||||
|
leftRamptime int
|
||||||
|
runningLeft bool
|
||||||
|
|
||||||
|
rightRamptime int
|
||||||
|
runningRight bool
|
||||||
|
runningStopFrame int
|
||||||
|
|
||||||
|
attacking bool
|
||||||
|
attacktime int
|
||||||
|
|
||||||
|
facingLeft bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type force struct {
|
||||||
|
vector vec
|
||||||
|
ftype forceType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *vec) Size() float32 {
|
||||||
|
return float32(math.Sqrt(float64(v.x*v.x + v.y*v.y)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplashPad struct {
|
||||||
|
increment int
|
||||||
|
Dimensions groovy.Area
|
||||||
|
events map[groovy.SceneEvent]func()
|
||||||
|
gamepadIDs map[ebiten.GamepadID]struct{}
|
||||||
|
|
||||||
|
globalForces []force
|
||||||
|
|
||||||
|
hero MyChar
|
||||||
|
heroImpulse impulse
|
||||||
|
jumping bool
|
||||||
|
jumptimer int
|
||||||
|
|
||||||
|
controller bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSplashPad() SplashPad {
|
||||||
|
h := MyChar{
|
||||||
|
posX: 640,
|
||||||
|
posY: MAX_FLOOR_POS,
|
||||||
|
mass: 80000,
|
||||||
|
aX: 0,
|
||||||
|
aY: 0,
|
||||||
|
jumptime: 0,
|
||||||
|
jumping: false,
|
||||||
|
leftRamptime: 0,
|
||||||
|
runningLeft: false,
|
||||||
|
rightRamptime: 0,
|
||||||
|
runningRight: false,
|
||||||
|
runningStopFrame: 0,
|
||||||
|
attacking: false,
|
||||||
|
attacktime: 0,
|
||||||
|
facingLeft: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
s := SplashPad{
|
||||||
|
increment: 0,
|
||||||
|
events: make(map[groovy.SceneEvent]func()),
|
||||||
|
gamepadIDs: make(map[ebiten.GamepadID]struct{}),
|
||||||
|
hero: h,
|
||||||
|
jumping: false,
|
||||||
|
jumptimer: 0,
|
||||||
|
controller: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
gravity := &force{}
|
||||||
|
gravity.vector = vec{x: 0, y: 9.8}
|
||||||
|
gravity.ftype = FORCE_GRAVITY
|
||||||
|
s.globalForces = append(s.globalForces, *gravity)
|
||||||
|
|
||||||
|
s.heroImpulse = impulse{f: vec{x: 0, y: 0}, timeleft: 0}
|
||||||
|
|
||||||
|
whiteImage.Fill(color.White)
|
||||||
|
log.Println("hero.PosY, prev.impulse.Y, impulse.Y, inc, netFy, timer")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) Draw(screen *ebiten.Image) {
|
||||||
|
|
||||||
|
s.DrawBackground(screen)
|
||||||
|
s.DrawController(screen)
|
||||||
|
s.DrawHero(screen)
|
||||||
|
|
||||||
|
if slowmo && s.increment/60%2 == 0 {
|
||||||
|
text.Draw(screen, " slow mo ", splashmenu.SplashFont.BigTitle, s.Dimensions.Width/2-140, 80, color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) Update() error {
|
||||||
|
|
||||||
|
var keys []ebiten.Key
|
||||||
|
keys = inpututil.AppendJustPressedKeys(keys[:0])
|
||||||
|
for _, k := range keys {
|
||||||
|
if k == ebiten.Key9 {
|
||||||
|
slowmo = !slowmo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.increment++
|
||||||
|
keys = inpututil.AppendJustPressedKeys(keys[:0])
|
||||||
|
for _, k := range keys {
|
||||||
|
if k == ebiten.KeyB {
|
||||||
|
s.hero.posX = int16(s.Dimensions.Width) / 2
|
||||||
|
s.hero.posY = MAX_FLOOR_POS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.doThePhysics()
|
||||||
|
|
||||||
|
if s.hero.jumping && s.hero.jumptime < JUMP_DURATION {
|
||||||
|
s.hero.jumptime++
|
||||||
|
} else if s.hero.posY >= MAX_FLOOR_POS {
|
||||||
|
s.hero.posY = MAX_FLOOR_POS
|
||||||
|
s.heroImpulse.f.y = 0.0
|
||||||
|
s.hero.jumptime = 0
|
||||||
|
s.hero.jumping = false
|
||||||
|
}
|
||||||
|
|
||||||
|
//check for user input to transition scene
|
||||||
|
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
|
||||||
|
if s.events[groovy.COMPLETED] != nil {
|
||||||
|
s.events[groovy.COMPLETED]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) SetEventHandler(event groovy.SceneEvent, f func()) {
|
||||||
|
s.events[event] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) SetDimensions(a groovy.Area) {
|
||||||
|
s.Dimensions = a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) DrawController(screen *ebiten.Image) {
|
||||||
|
|
||||||
|
const radInner = 20
|
||||||
|
const radOuter = 25
|
||||||
|
const maxOffset = radOuter - radInner
|
||||||
|
const padding = 20
|
||||||
|
|
||||||
|
//leftOriginX := float32(s.Dimensions.Width/2.0) - 1.75*radOuter
|
||||||
|
//leftOriginY := float32(s.Dimensions.Width / 2.0)
|
||||||
|
|
||||||
|
leftOriginX := float32(s.Dimensions.Width - 3*(2*radOuter) - padding)
|
||||||
|
leftOriginY := float32((2 * radOuter) - padding)
|
||||||
|
|
||||||
|
leftXOffset := float32(ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisLeftStickHorizontal) * maxOffset)
|
||||||
|
leftYOffset := float32(ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisLeftStickVertical) * maxOffset)
|
||||||
|
|
||||||
|
vector.DrawFilledCircle(screen, leftOriginX, leftOriginY, radOuter, color.White, true)
|
||||||
|
vector.DrawFilledCircle(screen, leftOriginX+leftXOffset, leftOriginY+leftYOffset, radInner, color.Black, true)
|
||||||
|
vector.DrawFilledCircle(screen, leftOriginX+leftXOffset, leftOriginY+leftYOffset, radInner-5, color.White, true)
|
||||||
|
vector.DrawFilledCircle(screen, leftOriginX+leftXOffset, leftOriginY+leftYOffset, radInner-8, color.RGBA{0xff, 0x5c, 0x5c, 0xff}, true)
|
||||||
|
|
||||||
|
//rightOriginX := float32(s.Dimensions.Width/2.0) + 1.75*radOuter
|
||||||
|
//rightOriginY := float32(s.Dimensions.Width / 2.0)
|
||||||
|
|
||||||
|
rightOriginX := float32(s.Dimensions.Width - 2*(2*radOuter))
|
||||||
|
rightOriginY := float32((2 * radOuter) - padding)
|
||||||
|
|
||||||
|
rightXOffset := float32(ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickHorizontal) * maxOffset)
|
||||||
|
rightYOffset := float32(ebiten.StandardGamepadAxisValue(0, ebiten.StandardGamepadAxisRightStickVertical) * maxOffset)
|
||||||
|
|
||||||
|
vector.DrawFilledCircle(screen, rightOriginX, rightOriginY, radOuter, color.White, true)
|
||||||
|
vector.DrawFilledCircle(screen, rightOriginX+rightXOffset, rightOriginY+rightYOffset, radInner, color.Black, true)
|
||||||
|
vector.DrawFilledCircle(screen, rightOriginX+rightXOffset, rightOriginY+rightYOffset, radInner-5, color.White, true)
|
||||||
|
vector.DrawFilledCircle(screen, rightOriginX+rightXOffset, rightOriginY+rightYOffset, radInner-8, color.RGBA{0xff, 0x5c, 0x5c, 0xff}, true)
|
||||||
|
|
||||||
|
gpInput := ebiten.GamepadAxisValue(0, 0)
|
||||||
|
text.Draw(screen, fmt.Sprintf("%1.02f", gpInput), splashmenu.SplashFont.Menu, 20, 20, color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) DrawBackground(screen *ebiten.Image) {
|
||||||
|
screen.DrawImage(forestImage, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) DrawHero(screen *ebiten.Image) {
|
||||||
|
|
||||||
|
var path vector.Path
|
||||||
|
|
||||||
|
//set up origin and boundaries
|
||||||
|
x0 := float32(s.hero.posX)
|
||||||
|
y0 := float32(s.hero.posY)
|
||||||
|
|
||||||
|
xd := float32(HERO_WIDTH / 2.0)
|
||||||
|
yd := float32(HERO_WIDTH / 2.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()
|
||||||
|
|
||||||
|
//fill time
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
//op := &ebiten.DrawTrianglesOptions{}
|
||||||
|
screen.DrawTriangles(vs, is, whiteSubImage, nil)
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
//handle attacking animation
|
||||||
|
if s.hero.attacking {
|
||||||
|
|
||||||
|
i := (s.hero.attacktime / FRAMES_PER_ATTACK_SLIDE)
|
||||||
|
|
||||||
|
if s.hero.facingLeft {
|
||||||
|
op.GeoM.Translate(float64(s.hero.posX-int16(slashImgCellsWidth)/2-13), float64(s.hero.posY-HERO_HEIGHT/2-5))
|
||||||
|
dx := i * slashImgCellsWidth
|
||||||
|
screen.DrawImage(slashImage.SubImage(image.Rect(dx, 0, dx+slashImgCellsWidth, slashImgHeight)).(*ebiten.Image), op)
|
||||||
|
} else {
|
||||||
|
op.GeoM.Translate(float64(s.hero.posX-24), float64(s.hero.posY-HERO_HEIGHT/2-5))
|
||||||
|
dx := slashImgCellsWidth*TOTAL_ATTACK_ANIMATION_IMAGES - i*slashImgCellsWidth
|
||||||
|
screen.DrawImage(slashRightImage.SubImage(image.Rect(dx, 0, dx+slashImgCellsWidth, slashImgHeight)).(*ebiten.Image), op)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hero.jumping {
|
||||||
|
text.Draw(screen, "hup!", splashmenu.SplashFont.Splash, int(s.hero.posX), int(s.hero.posY)-HERO_HEIGHT, color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) doThePhysics() {
|
||||||
|
var netFx float32 = 0
|
||||||
|
var netFy float32 = 0
|
||||||
|
|
||||||
|
for _, i := range s.globalForces {
|
||||||
|
netFx = netFx + i.vector.x
|
||||||
|
netFy = netFy + i.vector.y
|
||||||
|
}
|
||||||
|
|
||||||
|
//HANDLE JUMPING
|
||||||
|
s.handleJumping()
|
||||||
|
|
||||||
|
//HANDLE LEFT/RIGHT MOVEMENT
|
||||||
|
s.handleLeftMovement()
|
||||||
|
s.handleRightMovement()
|
||||||
|
|
||||||
|
//HANDLE ATTACK
|
||||||
|
s.handleAttack()
|
||||||
|
|
||||||
|
//if s.hero.ramptime < TIME_TO_STOP {
|
||||||
|
// s.hero.posX = s.hero.posX - (VELOCITY_VECTOR * s.hero.ramptime)
|
||||||
|
//} else if s.hero.ramptime >= TIME_TO_TOP_SPEED {
|
||||||
|
// s.hero.posX = s.hero.posX - TOP_SPEED
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//HANDLE FLOOR COLLISION CHECK
|
||||||
|
if s.hero.posY > MAX_FLOOR_POS {
|
||||||
|
s.hero.posY = MAX_FLOOR_POS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) handleAttack() {
|
||||||
|
maxButton := ebiten.GamepadButton(ebiten.GamepadButtonCount(0))
|
||||||
|
for b := ebiten.GamepadButton(0); b < maxButton; b++ {
|
||||||
|
if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton0) {
|
||||||
|
s.hero.attacking = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||||
|
s.hero.attacking = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hero.attacking {
|
||||||
|
s.hero.attacktime++
|
||||||
|
|
||||||
|
if s.hero.attacktime < MAX_HERO_ATTACK_DURATION {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
s.hero.attacking = false
|
||||||
|
s.hero.attacktime = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) handleJumping() {
|
||||||
|
maxButton := ebiten.GamepadButton(ebiten.GamepadButtonCount(0))
|
||||||
|
for b := ebiten.GamepadButton(0); b < maxButton; b++ {
|
||||||
|
if ebiten.IsGamepadButtonPressed(0, ebiten.GamepadButton1) {
|
||||||
|
if !s.hero.jumping {
|
||||||
|
s.hero.jumptime = 0
|
||||||
|
s.hero.jumping = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if inpututil.IsKeyJustPressed(ebiten.KeySpace) {
|
||||||
|
if !s.hero.jumping {
|
||||||
|
s.hero.jumptime = 0
|
||||||
|
s.hero.jumping = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.hero.jumping {
|
||||||
|
//asymmetric jump to simulate additional character weight
|
||||||
|
if s.hero.jumptime < TIME_TO_APEX {
|
||||||
|
t2 := float32((s.hero.jumptime * s.hero.jumptime))
|
||||||
|
s.hero.posY = int16(0.5*GRAVITY_VECTOR*t2 + INITIAL_JUMP_VELOCITY*float32(s.hero.jumptime) + MAX_FLOOR_POS)
|
||||||
|
} else {
|
||||||
|
t2 := float32((s.hero.jumptime * s.hero.jumptime))
|
||||||
|
s.hero.posY = int16(0.5*GRAVITY_VECTOR2*t2 + INITIAL_DROP_VELOCITY*float32(s.hero.jumptime) + MAX_FLOOR_POS)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) handleLeftMovement() {
|
||||||
|
|
||||||
|
var keys []ebiten.Key
|
||||||
|
keys = inpututil.AppendPressedKeys(keys[:0])
|
||||||
|
for _, pressed := range keys {
|
||||||
|
if pressed == ebiten.KeyA {
|
||||||
|
s.hero.leftRamptime++
|
||||||
|
s.hero.runningLeft = true
|
||||||
|
log.Println(" a engaged")
|
||||||
|
s.hero.facingLeft = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = inpututil.AppendJustReleasedKeys(keys[:0])
|
||||||
|
for _, pressed := range keys {
|
||||||
|
if pressed == ebiten.KeyA {
|
||||||
|
s.hero.runningLeft = false
|
||||||
|
s.hero.runningStopFrame = s.increment
|
||||||
|
log.Println(" a released")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ebiten.GamepadAxisValue(0, 0) <= -SENSITIVITY {
|
||||||
|
s.hero.leftRamptime++
|
||||||
|
s.hero.runningLeft = true
|
||||||
|
log.Println(" left gamepad engaged")
|
||||||
|
s.hero.facingLeft = true
|
||||||
|
s.controller = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.controller {
|
||||||
|
if -SENSITIVITY < ebiten.GamepadAxisValue(0, 0) && ebiten.GamepadAxisValue(0, 0) < SENSITIVITY {
|
||||||
|
s.hero.runningLeft = false
|
||||||
|
if s.hero.runningStopFrame > 0 {
|
||||||
|
s.hero.runningStopFrame = s.increment
|
||||||
|
log.Println(" left gamepad released")
|
||||||
|
s.controller = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if running, need to update their position
|
||||||
|
var offset = 0
|
||||||
|
if s.hero.runningLeft {
|
||||||
|
|
||||||
|
if s.hero.leftRamptime < TIME_TO_TOP_SPEED {
|
||||||
|
offset = s.hero.leftRamptime * s.hero.leftRamptime
|
||||||
|
} else {
|
||||||
|
offset = TOP_SPEED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////we've let go, time to decel
|
||||||
|
//if !s.hero.runningLeft && s.hero.leftRamptime < {
|
||||||
|
// s.hero.posX = s.hero.posX
|
||||||
|
//}
|
||||||
|
|
||||||
|
if s.hero.runningStopFrame > 0 {
|
||||||
|
if s.increment-s.hero.runningStopFrame < TIME_TO_STOP {
|
||||||
|
scaler := float32(TOP_SPEED) / float32(TIME_TO_STOP*TIME_TO_STOP)
|
||||||
|
deltaT := float32(s.increment - s.hero.runningStopFrame)
|
||||||
|
|
||||||
|
offset = TOP_SPEED - int(scaler*deltaT)
|
||||||
|
offset = int(math.Max(0, float64(offset))) //should not be less than zero, otherwise player would move wrong way ??
|
||||||
|
log.Printf(" stopping (%d,%d)...", s.hero.posX, s.hero.posY)
|
||||||
|
} else {
|
||||||
|
s.hero.runningStopFrame = 0
|
||||||
|
s.hero.leftRamptime = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.hero.posX = s.hero.posX - int16(offset)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SplashPad) handleRightMovement() {
|
||||||
|
var keys []ebiten.Key
|
||||||
|
keys = inpututil.AppendPressedKeys(keys[:0])
|
||||||
|
for _, pressed := range keys {
|
||||||
|
if pressed == ebiten.KeyD {
|
||||||
|
s.hero.rightRamptime++
|
||||||
|
s.hero.runningRight = true
|
||||||
|
log.Println(" d engaged")
|
||||||
|
s.hero.facingLeft = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = inpututil.AppendJustReleasedKeys(keys[:0])
|
||||||
|
for _, pressed := range keys {
|
||||||
|
if pressed == ebiten.KeyD {
|
||||||
|
s.hero.runningRight = false
|
||||||
|
log.Println(" d released")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ebiten.GamepadAxisValue(0, 0) >= SENSITIVITY {
|
||||||
|
s.hero.rightRamptime++
|
||||||
|
s.hero.runningRight = true
|
||||||
|
log.Println(" right gamepad engaged")
|
||||||
|
s.hero.facingLeft = false
|
||||||
|
s.controller = true
|
||||||
|
}
|
||||||
|
if s.controller {
|
||||||
|
if -SENSITIVITY < ebiten.GamepadAxisValue(0, 0) && ebiten.GamepadAxisValue(0, 0) < SENSITIVITY {
|
||||||
|
s.hero.runningRight = false
|
||||||
|
if s.hero.runningStopFrame > 0 {
|
||||||
|
s.hero.runningStopFrame = s.increment
|
||||||
|
log.Println(" right gamepad released")
|
||||||
|
s.controller = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if running, need to update their position
|
||||||
|
var offset = 0
|
||||||
|
if s.hero.runningRight {
|
||||||
|
|
||||||
|
if s.hero.rightRamptime < TIME_TO_TOP_SPEED {
|
||||||
|
offset = s.hero.rightRamptime * s.hero.rightRamptime
|
||||||
|
} else {
|
||||||
|
offset = TOP_SPEED
|
||||||
|
}
|
||||||
|
s.hero.posX = s.hero.posX + int16(offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,73 +2,89 @@ package splashmenu
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cosmos/diego/groovy"
|
"cosmos/diego/groovy"
|
||||||
|
splashmenu "cosmos/diego/groovy/examples/splashmenu/fonts"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
"github.com/hajimehoshi/ebiten/v2/text"
|
"github.com/hajimehoshi/ebiten/v2/text"
|
||||||
"golang.org/x/image/font"
|
|
||||||
"golang.org/x/image/font/opentype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
titleFont font.Face
|
backgroundBaseColor color.RGBA
|
||||||
menuFont font.Face
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tt, err := opentype.Parse(fonts.PressStart2P_ttf)
|
backgroundBaseColor = color.RGBA{0x33, 0x33, 0x99, 0xFF}
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dpi = 72
|
|
||||||
titleFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
|
||||||
Size: 16,
|
|
||||||
DPI: dpi,
|
|
||||||
Hinting: font.HintingVertical,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
menuFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
|
||||||
Size: 10,
|
|
||||||
DPI: dpi,
|
|
||||||
Hinting: font.HintingVertical,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Menu struct {
|
type Menu struct {
|
||||||
bgcolor color.RGBA
|
Dimensions groovy.Area
|
||||||
options map[int]MenuOption
|
bgcolor color.RGBA
|
||||||
events map[groovy.SceneEvent]func()
|
increment int
|
||||||
|
options map[int]MenuOption
|
||||||
|
events map[groovy.SceneEvent]func()
|
||||||
|
menuSelection uint
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMenu() Menu {
|
func NewMenu() Menu {
|
||||||
return Menu{
|
return Menu{
|
||||||
bgcolor: color.RGBA{0x33, 0x33, 0x99, 0xFF},
|
bgcolor: backgroundBaseColor,
|
||||||
events: make(map[groovy.SceneEvent]func()),
|
events: make(map[groovy.SceneEvent]func()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Draw(screen *ebiten.Image) {
|
func (m *Menu) Update() error {
|
||||||
screen.Fill(m.bgcolor)
|
m.increment++
|
||||||
text.Draw(screen, "menu", titleFont, 40, 40, color.White)
|
|
||||||
|
|
||||||
m.RenderOptions(screen)
|
var keysPressed []ebiten.Key
|
||||||
|
keysPressed = inpututil.AppendPressedKeys(keysPressed[:0])
|
||||||
|
|
||||||
|
for idx, o := range m.options {
|
||||||
|
if m.events[o.SelectionEvent] != nil {
|
||||||
|
for _, k := range keysPressed {
|
||||||
|
if k == o.Mapping {
|
||||||
|
m.menuSelection = uint(idx)
|
||||||
|
m.events[o.SelectionEvent]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//m.UpdateNoise()
|
||||||
|
colorOffset := uint8(int(math.Sin(float64(m.increment)/60) * 25))
|
||||||
|
|
||||||
|
m.bgcolor.R = (colorOffset + backgroundBaseColor.R) % 0xFF
|
||||||
|
m.bgcolor.G = (colorOffset + backgroundBaseColor.G) % 0xFF
|
||||||
|
m.bgcolor.B = (colorOffset + backgroundBaseColor.B) % 0xFF
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Menu) SetEventHandler(event groovy.SceneEvent, f func()) {
|
func (m *Menu) Draw(screen *ebiten.Image) {
|
||||||
|
screen.Fill(m.bgcolor)
|
||||||
|
text.Draw(screen, "menu", splashmenu.SplashFont.Title, 40, 40, color.White)
|
||||||
|
|
||||||
|
m.RenderMetadata(screen)
|
||||||
|
m.RenderMenu(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Menu) SetEventHandler(event groovy.SceneEvent, f func()) {
|
||||||
m.events[event] = f
|
m.events[event] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sets sene dimensions
|
||||||
|
func (m *Menu) SetDimensions(a groovy.Area) {
|
||||||
|
m.Dimensions = a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Menu) RenderMetadata(screen *ebiten.Image) {
|
||||||
|
bgcolor := " 0x" + strconv.FormatUint(uint64(m.bgcolor.R), 16) + " 0x" + strconv.FormatUint(uint64(m.bgcolor.G), 16) + " 0x" + strconv.FormatUint(uint64(m.bgcolor.B), 16)
|
||||||
|
text.Draw(screen, bgcolor, splashmenu.SplashFont.Menu, 40, 700, color.White)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Menu) SetOptions(options map[int]MenuOption) {
|
func (m *Menu) SetOptions(options map[int]MenuOption) {
|
||||||
m.options = make(map[int]MenuOption)
|
m.options = make(map[int]MenuOption)
|
||||||
|
|
||||||
@@ -77,31 +93,19 @@ func (m *Menu) SetOptions(options map[int]MenuOption) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) RenderOptions(screen *ebiten.Image) {
|
func (m *Menu) RenderMenu(screen *ebiten.Image) {
|
||||||
|
|
||||||
var offset int = 20
|
var offset int = 20
|
||||||
|
|
||||||
|
screen.Set(m.Dimensions.Width/2, m.Dimensions.Height/2, color.RGBA{0xFF, 0x00, 0x00, 0xFF})
|
||||||
|
|
||||||
for k, v := range m.options {
|
for k, v := range m.options {
|
||||||
m.options[k] = v
|
m.options[k] = v
|
||||||
text.Draw(screen, strconv.Itoa(k)+": "+v.Description, menuFont, 40, 60+offset*k, color.White)
|
text.Draw(screen, strconv.Itoa(k)+": "+v.Description, splashmenu.SplashFont.Menu, 40, 60+offset*k, color.White)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) Update() error {
|
func (m *Menu) GetMenuSelection() uint {
|
||||||
|
return m.menuSelection
|
||||||
var keysPressed []ebiten.Key
|
|
||||||
keysPressed = inpututil.AppendPressedKeys(keysPressed[:0])
|
|
||||||
|
|
||||||
for _, o := range m.options {
|
|
||||||
if m.events[o.SelectionEvent] != nil {
|
|
||||||
for _, k := range keysPressed {
|
|
||||||
if k == o.Mapping {
|
|
||||||
m.events[o.SelectionEvent]()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
158
examples/splashmenu/scenes/noise.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package splashmenu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cosmos/diego/groovy"
|
||||||
|
splashmenu "cosmos/diego/groovy/examples/splashmenu/fonts"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/text"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// theRand = &randy{12345678, 4185243, 776511, 45411}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
scaleRatio = 12.0
|
||||||
|
maxMagWidth = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
//type randy struct {
|
||||||
|
// x, y, z, w uint32
|
||||||
|
//}
|
||||||
|
|
||||||
|
type Noisy struct {
|
||||||
|
increment int
|
||||||
|
Dimensions groovy.Area
|
||||||
|
magnifier groovy.Area
|
||||||
|
sX int
|
||||||
|
sY int
|
||||||
|
events map[groovy.SceneEvent]func()
|
||||||
|
pixImage *image.RGBA
|
||||||
|
pixSubImage *image.RGBA
|
||||||
|
subTarget *ebiten.Image
|
||||||
|
renderTarget *ebiten.Image
|
||||||
|
fader *ebiten.Image
|
||||||
|
//alpha float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoisy() Noisy {
|
||||||
|
return Noisy{
|
||||||
|
increment: 0,
|
||||||
|
events: make(map[groovy.SceneEvent]func()),
|
||||||
|
magnifier: groovy.Area{Width: maxMagWidth, Height: maxMagWidth},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Noisy) Update() error {
|
||||||
|
n.increment++
|
||||||
|
|
||||||
|
//n.alpha = float32(n.increment) / (60.0 * 5.0) //60fps * 5 seconds = 300frames
|
||||||
|
//fmt.Printf("%f, ", n.alpha)
|
||||||
|
|
||||||
|
n.UpdateNoise()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Noisy) Draw(screen *ebiten.Image) {
|
||||||
|
n.subTarget.WritePixels(n.pixSubImage.Pix)
|
||||||
|
|
||||||
|
screen.WritePixels(n.pixImage.Pix)
|
||||||
|
//screen.WritePixels(n.pixSubImage.Pix)
|
||||||
|
|
||||||
|
op := &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Scale(1/scaleRatio, 1/scaleRatio)
|
||||||
|
n.renderTarget.DrawImage(n.subTarget, op)
|
||||||
|
op = &ebiten.DrawImageOptions{}
|
||||||
|
op.GeoM.Scale(scaleRatio, scaleRatio)
|
||||||
|
op.GeoM.Translate(float64(n.sX)-maxMagWidth/2, float64(n.sY)-maxMagWidth/2)
|
||||||
|
|
||||||
|
screen.DrawImage(n.renderTarget, op)
|
||||||
|
|
||||||
|
//screen.DrawImage(n.subTarget, nil)
|
||||||
|
|
||||||
|
text.Draw(screen, fmt.Sprintf("%04d, ", n.sX), splashmenu.SplashFont.Menu, n.Dimensions.Width/2-58, n.Dimensions.Height/2+18, color.White)
|
||||||
|
text.Draw(screen, fmt.Sprintf("%04d ", n.sY), splashmenu.SplashFont.Menu, n.Dimensions.Width/2, n.Dimensions.Height/2+18, color.White)
|
||||||
|
|
||||||
|
//op = &ebiten.DrawImageOptions{}
|
||||||
|
//op.ColorScale.Scale(1, 1, 1, n.alpha)
|
||||||
|
//screen.DrawImage(n.fader, op)
|
||||||
|
|
||||||
|
text.Draw(screen, "Q TO EXIT", splashmenu.SplashFont.Title, n.Dimensions.Width/2-77, n.Dimensions.Height/2, color.RGBA{0xff, 0xff, 0xff, uint8(n.increment / (60 * 2) % 0xff)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// register scenevent handler
|
||||||
|
func (n *Noisy) SetEventHandler(event groovy.SceneEvent, f func()) {
|
||||||
|
n.events[event] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the current scene dimensions
|
||||||
|
func (n *Noisy) SetDimensions(a groovy.Area) {
|
||||||
|
n.Dimensions = a
|
||||||
|
n.pixImage = image.NewRGBA(image.Rect(0, 0, a.Width, a.Height))
|
||||||
|
n.fader = ebiten.NewImage(a.Width, a.Height)
|
||||||
|
n.fader.Fill(color.RGBA{0x00, 0x00, 0x00, 0xff})
|
||||||
|
|
||||||
|
n.pixSubImage = image.NewRGBA(image.Rect(0, 0, n.magnifier.Width, n.magnifier.Height))
|
||||||
|
n.subTarget = ebiten.NewImage(n.magnifier.Width, n.magnifier.Height)
|
||||||
|
n.renderTarget = ebiten.NewImage(n.magnifier.Width, n.magnifier.Height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Noisy) UpdateNoise() {
|
||||||
|
//update cursor position and lock it into the game region bounds
|
||||||
|
n.sX, n.sY = ebiten.CursorPosition()
|
||||||
|
n.sX = int(math.Min(float64(n.sX), float64(n.Dimensions.Width-1)))
|
||||||
|
n.sX = int(math.Max(float64(n.sX), 0))
|
||||||
|
n.sY = int(math.Min(float64(n.sY), float64(n.Dimensions.Height-1)))
|
||||||
|
n.sY = int(math.Max(float64(n.sY), 0))
|
||||||
|
|
||||||
|
l := n.Dimensions.Width * n.Dimensions.Height
|
||||||
|
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
//x := theRand.next()
|
||||||
|
//n.pixImage.Pix[4*i] = uint8(x >> 24)
|
||||||
|
//n.pixImage.Pix[4*i+1] = uint8(x >> 16)
|
||||||
|
//n.pixImage.Pix[4*i+2] = uint8(x >> 8)
|
||||||
|
//n.pixImage.Pix[4*i+3] = 0xff
|
||||||
|
|
||||||
|
ballsMode := int(float32(i) * (float32(n.increment) / 60.0))
|
||||||
|
|
||||||
|
n.pixImage.Pix[4*i] = uint8(n.increment)
|
||||||
|
n.pixImage.Pix[4*i] = uint8(i % 0xff)
|
||||||
|
n.pixImage.Pix[4*i+1] = uint8(i >> 16 % 0xff)
|
||||||
|
n.pixImage.Pix[4*i+2] = uint8(i >> 24 % 0xff)
|
||||||
|
n.pixImage.Pix[4*i+3] = uint8(ballsMode) % 0xFF
|
||||||
|
}
|
||||||
|
|
||||||
|
startIdx := n.sX * n.sY * 4
|
||||||
|
endIdx := startIdx + n.magnifier.Width*n.magnifier.Height*4
|
||||||
|
|
||||||
|
if endIdx >= l*4 {
|
||||||
|
delta := endIdx - l*4
|
||||||
|
startIdx = startIdx - delta
|
||||||
|
endIdx = endIdx - delta
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(n.pixSubImage.Pix[:], n.pixImage.Pix[startIdx:endIdx])
|
||||||
|
|
||||||
|
//check for user input to transition scene
|
||||||
|
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
|
||||||
|
if n.events[groovy.COMPLETED] != nil {
|
||||||
|
n.events[groovy.COMPLETED]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (r *randy) next() uint32 {
|
||||||
|
// // math/rand is too slow to keep 60 FPS on web browsers.
|
||||||
|
// // Use Xorshift instead: http://en.wikipedia.org/wiki/Xorshift
|
||||||
|
// t := r.x ^ (r.x << 11)
|
||||||
|
// r.x, r.y, r.z = r.y, r.z, r.w
|
||||||
|
// r.w = (r.w ^ (r.w >> 19)) ^ (t ^ (t >> 8))
|
||||||
|
// return r.w
|
||||||
|
//}
|
||||||
294
examples/splashmenu/scenes/parallax.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
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++
|
||||||
|
|
||||||
|
//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)
|
||||||
|
|
||||||
|
}
|
||||||
294
examples/splashmenu/scenes/rays.go
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
package splashmenu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cosmos/diego/groovy"
|
||||||
|
splashmenu "cosmos/diego/groovy/examples/splashmenu/fonts"
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/text"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BGCOLOR = 0xCCCCCC
|
||||||
|
LINECOLOR = 0x8a8a8a
|
||||||
|
SQUARECOLOR = 0x199a19
|
||||||
|
RADIUSCOLOR = 0xFF0000
|
||||||
|
AXISCOLOR = 0xf100c1
|
||||||
|
SEMIAXISCOLOR = 0x178fc5 //0x1e1e1e //0xdccdFF
|
||||||
|
)
|
||||||
|
|
||||||
|
type ColorData struct {
|
||||||
|
selectionColor color.RGBA
|
||||||
|
squareColor color.RGBA
|
||||||
|
radiusColor color.RGBA
|
||||||
|
axisColor color.RGBA
|
||||||
|
semiAxisColor color.RGBA
|
||||||
|
}
|
||||||
|
|
||||||
|
type Coords struct {
|
||||||
|
X float32
|
||||||
|
Y float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Coords) DistanceFrom(pos Coords) float32 {
|
||||||
|
return float32(math.Sqrt(float64((pos.X-c.X)*(pos.X-c.X) + (pos.Y-c.Y)*(pos.Y-c.Y))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Coords) SignedDistanceFrom(pos Coords) Coords {
|
||||||
|
return Coords{X: pos.X - c.X, Y: pos.Y - c.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Coords) Magnitude() float32 {
|
||||||
|
return float32(math.Sqrt(float64(c.X*c.X + c.Y*c.Y)))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rays struct {
|
||||||
|
events map[groovy.SceneEvent]func()
|
||||||
|
increment int
|
||||||
|
Dimensions groovy.Area
|
||||||
|
|
||||||
|
coordinates Coords
|
||||||
|
selection Coords
|
||||||
|
interacting bool
|
||||||
|
|
||||||
|
bgcolor color.RGBA
|
||||||
|
colorData ColorData
|
||||||
|
}
|
||||||
|
|
||||||
|
func hexToRGBA(hexcolor int) color.RGBA {
|
||||||
|
return color.RGBA{
|
||||||
|
R: uint8(hexcolor >> 0x10 & 0xff),
|
||||||
|
G: uint8(hexcolor >> 0x08 & 0xff),
|
||||||
|
B: uint8(hexcolor >> 0x00 & 0xff),
|
||||||
|
A: 0xff,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRays() Rays {
|
||||||
|
r := Rays{
|
||||||
|
events: make(map[groovy.SceneEvent]func()),
|
||||||
|
increment: 0,
|
||||||
|
bgcolor: hexToRGBA(BGCOLOR),
|
||||||
|
coordinates: Coords{X: 0, Y: 0},
|
||||||
|
selection: Coords{X: 0, Y: 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
r.colorData.selectionColor = hexToRGBA(LINECOLOR)
|
||||||
|
r.colorData.squareColor = hexToRGBA(SQUARECOLOR)
|
||||||
|
r.colorData.radiusColor = hexToRGBA(RADIUSCOLOR)
|
||||||
|
r.colorData.axisColor = hexToRGBA(AXISCOLOR)
|
||||||
|
r.colorData.semiAxisColor = hexToRGBA(SEMIAXISCOLOR)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func gimmeFloatCursor() (float32, float32) {
|
||||||
|
x, y := ebiten.CursorPosition()
|
||||||
|
|
||||||
|
return float32(x), float32(y)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rays) Update() error {
|
||||||
|
r.increment++
|
||||||
|
|
||||||
|
x, y := gimmeFloatCursor()
|
||||||
|
mouseCoords := Coords{X: x, Y: y}
|
||||||
|
newRad := mouseCoords.DistanceFrom(r.selection)
|
||||||
|
|
||||||
|
xMin := r.selection.X - newRad
|
||||||
|
xMax := r.selection.X + newRad
|
||||||
|
yMin := r.selection.Y - newRad
|
||||||
|
yMax := r.selection.Y + newRad
|
||||||
|
|
||||||
|
if 0 <= xMin && xMax < float32(r.Dimensions.Width) &&
|
||||||
|
0 <= yMin && yMax < float32(r.Dimensions.Height) {
|
||||||
|
r.coordinates = mouseCoords
|
||||||
|
}
|
||||||
|
|
||||||
|
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
||||||
|
r.selection = mouseCoords
|
||||||
|
r.interacting = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if inpututil.IsMouseButtonJustReleased(ebiten.MouseButtonLeft) {
|
||||||
|
//r.selection = r.coordinates
|
||||||
|
r.interacting = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !r.interacting {
|
||||||
|
r.selection = r.coordinates
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rays) Draw(screen *ebiten.Image) {
|
||||||
|
screen.Fill(r.bgcolor)
|
||||||
|
r.DrawTheAngleBits(screen)
|
||||||
|
|
||||||
|
x, y := gimmeFloatCursor()
|
||||||
|
|
||||||
|
text.Draw(screen, fmt.Sprintf("Mouse x, y: (%.1f, %.1f)", x, y), splashmenu.SplashFont.Menu, 20, 20, color.White)
|
||||||
|
text.Draw(screen, fmt.Sprintf("Selection x, y: (%.1f, %.1f)", r.selection.X, r.selection.Y), splashmenu.SplashFont.Menu, 20, 40, color.White)
|
||||||
|
text.Draw(screen, fmt.Sprintf("Radius: %.1f", r.GetPrimaryRadius()), splashmenu.SplashFont.Menu, 20, 60, color.White)
|
||||||
|
text.Draw(screen, fmt.Sprintf("Interaction: %s", strconv.FormatBool(r.interacting)), splashmenu.SplashFont.Menu, 20, 80, color.White)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rays) SetDimensions(a groovy.Area) {
|
||||||
|
r.Dimensions = a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rays) SetEventHandler(event groovy.SceneEvent, f func()) {
|
||||||
|
r.events[event] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rays) DrawTheAngleBits(screen *ebiten.Image) {
|
||||||
|
|
||||||
|
if r.interacting {
|
||||||
|
|
||||||
|
r.DrawTrigElementAxis(screen, r.selection, r.coordinates, r.colorData)
|
||||||
|
r.DrawTrigElement(screen, r.selection, r.coordinates, r.colorData)
|
||||||
|
r.DrawCircleElement(screen, r.selection, r.coordinates, r.colorData)
|
||||||
|
|
||||||
|
dist := r.coordinates.SignedDistanceFrom(r.selection)
|
||||||
|
angle := math.Atan2(float64(dist.Y), float64(dist.X))
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
var xSpot Coords
|
||||||
|
xSpot.X = r.selection.X + float32(math.Cos(angle))*dist.Magnitude()*float32(i)
|
||||||
|
xSpot.Y = r.selection.Y + float32(math.Sin(angle))*dist.Magnitude()*float32(i)
|
||||||
|
|
||||||
|
var nSpot Coords
|
||||||
|
|
||||||
|
//our limit is pi/2, but we want to stretch this out over 30 frames
|
||||||
|
u := (math.Pi / 2) * 30
|
||||||
|
|
||||||
|
v := float64(r.increment)
|
||||||
|
|
||||||
|
//our angular frequency is simply the remainder of our current frame count / our limit
|
||||||
|
w := (v/u - math.Floor(v/u))
|
||||||
|
|
||||||
|
//now scale the circles
|
||||||
|
nSpot.X = xSpot.X + float32(math.Cos(angle))*dist.Magnitude()*float32(math.Sin(w))
|
||||||
|
nSpot.Y = xSpot.Y + float32(math.Sin(angle))*dist.Magnitude()*float32(math.Sin(w))
|
||||||
|
r.DrawCircleElement(screen, xSpot, nSpot, r.colorData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the primary circle radius value of the current scene
|
||||||
|
func (r *Rays) GetPrimaryRadius() float32 {
|
||||||
|
return r.coordinates.DistanceFrom(r.selection)
|
||||||
|
}
|
||||||
|
|
||||||
|
// draws circle
|
||||||
|
func (r *Rays) DrawCircleElement(img *ebiten.Image, origin Coords, dest Coords, colorData ColorData) {
|
||||||
|
dist := dest.SignedDistanceFrom(origin)
|
||||||
|
vector.StrokeCircle(img, origin.X, origin.Y, dist.Magnitude(), 2, colorData.selectionColor, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// draws circle trig relationships with the x/y axis of the circles origin and a radius indicator
|
||||||
|
func (r *Rays) DrawTrigElement(img *ebiten.Image, origin Coords, dest Coords, colorData ColorData) {
|
||||||
|
dist := dest.SignedDistanceFrom(origin)
|
||||||
|
|
||||||
|
//circle and radius marker
|
||||||
|
vector.StrokeCircle(img, origin.X, origin.Y, dist.Magnitude(), 2, colorData.selectionColor, true)
|
||||||
|
vector.StrokeLine(img, origin.X, origin.Y, dest.X, dest.Y, 1, colorData.radiusColor, true)
|
||||||
|
|
||||||
|
//draw rectangle from origin to dest
|
||||||
|
vector.StrokeRect(img, origin.X, origin.Y, dest.X-origin.X, dest.Y-origin.Y, 1, colorData.squareColor, true)
|
||||||
|
|
||||||
|
//compute directionality (1)
|
||||||
|
dirX := dist.X / float32(math.Abs(float64(dist.X)))
|
||||||
|
dirY := dist.Y / float32(math.Abs(float64(dist.Y)))
|
||||||
|
|
||||||
|
//semi-axis within circle
|
||||||
|
vector.StrokeLine(img, origin.X, origin.Y, origin.X-dirX*dist.Magnitude(), origin.Y, 1, colorData.axisColor, true)
|
||||||
|
vector.StrokeLine(img, origin.X, origin.Y, origin.X, origin.Y-dirY*dist.Magnitude(), 1, colorData.axisColor, true)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Rays) DrawTrigElementAxis(img *ebiten.Image, origin Coords, dest Coords, colorData ColorData) {
|
||||||
|
dist := dest.SignedDistanceFrom(origin)
|
||||||
|
|
||||||
|
//origin to BORDER
|
||||||
|
// case 1. origin.x = destination.x, means intercept should be vertical line
|
||||||
|
// case 2. we have a slope
|
||||||
|
if dist.X == 0 {
|
||||||
|
vector.StrokeLine(img, origin.X, 0, origin.X, float32(r.Dimensions.Height), 1, colorData.semiAxisColor, true)
|
||||||
|
} else {
|
||||||
|
m := dist.Y / dist.X
|
||||||
|
|
||||||
|
/*
|
||||||
|
y - y1 = m(x - x1)
|
||||||
|
y = m(x - x1) + y1
|
||||||
|
x = (y - y1)/m + x1
|
||||||
|
find the intercepts:
|
||||||
|
x = Dimensions.Width
|
||||||
|
x = 0
|
||||||
|
|
||||||
|
y = 0
|
||||||
|
y = Dimensions.Height
|
||||||
|
*/
|
||||||
|
|
||||||
|
//find our y intercept for the start of our segment
|
||||||
|
var xZero Coords
|
||||||
|
xZero.X = 0
|
||||||
|
xZero.Y = m*(xZero.X-origin.X) + origin.Y
|
||||||
|
|
||||||
|
//in the even our point lies outside the visible screen boundaries,
|
||||||
|
//we can save some pixel operations by restricting to the bounds
|
||||||
|
if xZero.Y > float32(r.Dimensions.Height) {
|
||||||
|
xZero.Y = float32(r.Dimensions.Height)
|
||||||
|
xZero.X = (xZero.Y-origin.Y)/m + origin.X
|
||||||
|
}
|
||||||
|
|
||||||
|
if xZero.Y < 0 {
|
||||||
|
xZero.Y = 0
|
||||||
|
xZero.X = (xZero.Y-origin.Y)/m + origin.X
|
||||||
|
}
|
||||||
|
|
||||||
|
var xMax Coords
|
||||||
|
xMax.X = float32(r.Dimensions.Width)
|
||||||
|
xMax.Y = m*(xMax.X-origin.X) + origin.Y
|
||||||
|
|
||||||
|
if xMax.Y > float32(r.Dimensions.Height) {
|
||||||
|
xMax.Y = float32(r.Dimensions.Height)
|
||||||
|
xMax.X = (xMax.Y-origin.Y)/m + origin.X
|
||||||
|
}
|
||||||
|
|
||||||
|
if xMax.Y < 0 {
|
||||||
|
xMax.Y = 0
|
||||||
|
xMax.X = (xMax.Y-origin.Y)/m + origin.X
|
||||||
|
}
|
||||||
|
|
||||||
|
var slashLineOrigin Coords
|
||||||
|
var slashLineDest Coords
|
||||||
|
|
||||||
|
slashLineOrigin = xZero
|
||||||
|
slashLineDest = xMax
|
||||||
|
|
||||||
|
var xSpot Coords
|
||||||
|
angle := math.Atan2(float64(dist.Y), float64(dist.X))
|
||||||
|
xSpot.X = origin.X + float32(math.Cos(angle))*dist.Magnitude()
|
||||||
|
xSpot.Y = origin.Y + float32(math.Sin(angle))*dist.Magnitude()
|
||||||
|
|
||||||
|
vector.StrokeLine(img, slashLineOrigin.X, slashLineOrigin.Y, slashLineDest.X, slashLineDest.Y, 1, colorData.semiAxisColor, true)
|
||||||
|
text.Draw(img, fmt.Sprintf("%0.1f : (%0.1f, %0.1f) (%0.1f, %0.1f)", m, slashLineOrigin.X, slashLineOrigin.Y, slashLineDest.X, slashLineDest.Y), splashmenu.SplashFont.Menu, 20, 100, color.White)
|
||||||
|
|
||||||
|
//text.Draw(img, "x", splashmenu.SplashFont.Title, int(xSpot.X), int(xSpot.Y), color.Black)
|
||||||
|
vector.DrawFilledCircle(img, xSpot.X, xSpot.Y, 5, color.Black, true)
|
||||||
|
vector.DrawFilledCircle(img, origin.X, origin.Y, 5, colorData.radiusColor, true)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,46 +2,23 @@ package splashmenu
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cosmos/diego/groovy"
|
"cosmos/diego/groovy"
|
||||||
|
splashmenu "cosmos/diego/groovy/examples/splashmenu/fonts"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/text"
|
"github.com/hajimehoshi/ebiten/v2/text"
|
||||||
"golang.org/x/image/font"
|
|
||||||
"golang.org/x/image/font/opentype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
splashFont font.Face
|
splashText = "schmoopysoft®"
|
||||||
|
splashSubtext = "\"how was the gravy\"\n- christorpher hollick"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
tt, err := opentype.Parse(fonts.PressStart2P_ttf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const dpi = 72
|
|
||||||
splashFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
|
||||||
Size: 12,
|
|
||||||
DPI: dpi,
|
|
||||||
Hinting: font.HintingVertical,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Splash struct {
|
type Splash struct {
|
||||||
bgcolor color.RGBA
|
Dimensions groovy.Area
|
||||||
increment int
|
bgcolor color.RGBA
|
||||||
events map[groovy.SceneEvent]func()
|
increment int
|
||||||
}
|
events map[groovy.SceneEvent]func()
|
||||||
|
|
||||||
// GetSceneEvents implements groovy.Scene.
|
|
||||||
func (*Splash) GetSceneEvents() []groovy.SceneEvent {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSplash() Splash {
|
func NewSplash() Splash {
|
||||||
@@ -51,25 +28,40 @@ func NewSplash() Splash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Splash) Draw(screen *ebiten.Image) {
|
|
||||||
screen.Fill(s.bgcolor)
|
|
||||||
text.Draw(screen, "splash", splashFont, 40, 40, color.White)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Splash) Update() error {
|
func (s *Splash) Update() error {
|
||||||
s.increment++
|
s.increment++
|
||||||
|
|
||||||
s.bgcolor.R = (s.bgcolor.R - 2) % 0xFF
|
s.bgcolor.R = (s.bgcolor.R - 1) % 0xFF
|
||||||
s.bgcolor.G = (s.bgcolor.G - 2) % 0xFF
|
s.bgcolor.G = (s.bgcolor.G - 1) % 0xFF
|
||||||
s.bgcolor.B = (s.bgcolor.B - 2) % 0xFF
|
s.bgcolor.B = (s.bgcolor.B - 1) % 0xFF
|
||||||
|
|
||||||
if s.bgcolor.R == 0x00 {
|
if s.bgcolor.R == 0x00 {
|
||||||
s.events[groovy.COMPLETED]()
|
s.events[groovy.COMPLETED]()
|
||||||
|
|
||||||
|
//scene cleanup (for next time)
|
||||||
|
s.increment = 0
|
||||||
|
s.bgcolor.R = 0xFF
|
||||||
|
s.bgcolor.G = 0xFF
|
||||||
|
s.bgcolor.B = 0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Splash) SetEventHandler(event groovy.SceneEvent, f func()) {
|
func (s *Splash) Draw(screen *ebiten.Image) {
|
||||||
|
screen.Fill(s.bgcolor)
|
||||||
|
x := s.Dimensions.Width/2 - 400
|
||||||
|
y := s.Dimensions.Height / 2
|
||||||
|
text.Draw(screen, splashText, splashmenu.SplashFont.MegaTitle, x, y, color.White)
|
||||||
|
|
||||||
|
text.Draw(screen, splashSubtext, splashmenu.SplashFont.Title, x, y+40, color.White)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Splash) SetEventHandler(event groovy.SceneEvent, f func()) {
|
||||||
s.events[event] = f
|
s.events[event] = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sets sene dimensions
|
||||||
|
func (s *Splash) SetDimensions(a groovy.Area) {
|
||||||
|
s.Dimensions = a
|
||||||
|
}
|
||||||
|
|||||||
30
manager.go
@@ -17,9 +17,9 @@ type Area struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GameInfo struct {
|
type GameInfo struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Dimension Area
|
Dimensions Area
|
||||||
}
|
}
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
@@ -37,7 +37,7 @@ func NewManager() Manager {
|
|||||||
Info: GameInfo{
|
Info: GameInfo{
|
||||||
Name: "groovy",
|
Name: "groovy",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Dimension: Area{
|
Dimensions: Area{
|
||||||
Width: defaultWidth,
|
Width: defaultWidth,
|
||||||
Height: defaultHeight,
|
Height: defaultHeight,
|
||||||
},
|
},
|
||||||
@@ -47,18 +47,16 @@ func NewManager() Manager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ebitengine update proxy
|
// ebitengine update proxy on behalf of current scene
|
||||||
// manages scene transition and exists, then calls scene's update method
|
|
||||||
func (m *Manager) Update() error {
|
func (m *Manager) Update() error {
|
||||||
if m.currentScene == nil {
|
if m.currentScene == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//call the current scene's update method
|
|
||||||
return m.currentScene.Update()
|
return m.currentScene.Update()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shutdown application
|
||||||
func (m *Manager) Quit() {
|
func (m *Manager) Quit() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
@@ -72,12 +70,13 @@ func (m *Manager) Draw(screen *ebiten.Image) {
|
|||||||
|
|
||||||
// ebitengine proxy for layout
|
// ebitengine proxy for layout
|
||||||
func (m *Manager) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
func (m *Manager) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
||||||
return m.Info.Dimension.Width, m.Info.Dimension.Height
|
return m.Info.Dimensions.Width, m.Info.Dimensions.Height
|
||||||
}
|
}
|
||||||
|
|
||||||
// appends scene to the managed scenes
|
// appends scene to the managed scenes
|
||||||
func (m *Manager) AddScene(s Scene) {
|
func (m *Manager) AddScene(s Scene) {
|
||||||
setDefaultHandlers(m, s)
|
setDefaultHandlers(m, s)
|
||||||
|
s.SetDimensions(m.Info.Dimensions)
|
||||||
m.scenes = append(m.scenes, s)
|
m.scenes = append(m.scenes, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,22 +111,33 @@ func (m *Manager) SetCurrentScene(sceneId uint) {
|
|||||||
}
|
}
|
||||||
m.currentSceneId = sceneId
|
m.currentSceneId = sceneId
|
||||||
m.currentScene = m.scenes[sceneId]
|
m.currentScene = m.scenes[sceneId]
|
||||||
|
m.nextSceneId = m.currentSceneId + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle scene transition
|
||||||
func (m *Manager) TransitionScene() {
|
func (m *Manager) TransitionScene() {
|
||||||
m.SetCurrentScene(m.nextSceneId)
|
m.SetCurrentScene(m.nextSceneId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set new sceneId as the successor
|
||||||
func (m *Manager) SetNextScene(sceneId uint) {
|
func (m *Manager) SetNextScene(sceneId uint) {
|
||||||
m.nextSceneId = sceneId
|
m.nextSceneId = sceneId
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets sene dimensions
|
// sets sene dimensions
|
||||||
func (m *Manager) SetDimensions(a Area) {
|
func (m *Manager) SetDimensions(a Area) {
|
||||||
m.Info.Dimension = a
|
m.Info.Dimensions = a
|
||||||
}
|
}
|
||||||
|
|
||||||
// report number of total scenes
|
// report number of total scenes
|
||||||
func (m *Manager) SceneCount() uint {
|
func (m *Manager) SceneCount() uint {
|
||||||
return uint(len(m.scenes))
|
return uint(len(m.scenes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetScene(sceneId uint) Scene {
|
||||||
|
return m.scenes[sceneId]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Area) Area() int {
|
||||||
|
return a.Height * a.Width
|
||||||
|
}
|
||||||
|
|||||||
7
scene.go
@@ -7,6 +7,7 @@ type SceneEvent int64
|
|||||||
const (
|
const (
|
||||||
NOOP SceneEvent = 0
|
NOOP SceneEvent = 0
|
||||||
RESET SceneEvent = 1 // reset to initial scene
|
RESET SceneEvent = 1 // reset to initial scene
|
||||||
|
LOAD SceneEvent = 2
|
||||||
RELOAD SceneEvent = 3 // reload current scene
|
RELOAD SceneEvent = 3 // reload current scene
|
||||||
COMPLETED SceneEvent = 4 // current scene has completed
|
COMPLETED SceneEvent = 4 // current scene has completed
|
||||||
ENDGAME SceneEvent = 5 // shutdown all scenes
|
ENDGAME SceneEvent = 5 // shutdown all scenes
|
||||||
@@ -16,9 +17,5 @@ type Scene interface {
|
|||||||
Update() error
|
Update() error
|
||||||
Draw(screen *ebiten.Image)
|
Draw(screen *ebiten.Image)
|
||||||
SetEventHandler(e SceneEvent, f func())
|
SetEventHandler(e SceneEvent, f func())
|
||||||
}
|
SetDimensions(a Area)
|
||||||
|
|
||||||
type ArgoScale interface {
|
|
||||||
Draw(screen *ebiten.Image)
|
|
||||||
GetSceneEvents() []SceneEvent
|
|
||||||
}
|
}
|
||||||
|
|||||||