New scene examples added.
This commit is contained in:
563
examples/splashmenu/scenes/controller.go
Normal file
563
examples/splashmenu/scenes/controller.go
Normal file
@@ -0,0 +1,563 @@
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//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 = max(0, 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
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//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)
|
||||
}
|
||||
}
|
||||
157
examples/splashmenu/scenes/noise.go
Normal file
157
examples/splashmenu/scenes/noise.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package splashmenu
|
||||
|
||||
import (
|
||||
"cosmos/diego/groovy"
|
||||
splashmenu "cosmos/diego/groovy/examples/splashmenu/fonts"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"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 = min(n.sX, n.Dimensions.Width-1)
|
||||
n.sX = max(n.sX, 0)
|
||||
n.sY = min(n.sY, n.Dimensions.Height-1)
|
||||
n.sY = max(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
|
||||
//}
|
||||
254
examples/splashmenu/scenes/parallax.go
Normal file
254
examples/splashmenu/scenes/parallax.go
Normal file
@@ -0,0 +1,254 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user