summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/rewind.wavbin0 -> 913832 bytes
-rw-r--r--level.go91
-rw-r--r--main.go163
-rw-r--r--objects.go58
4 files changed, 219 insertions, 93 deletions
diff --git a/assets/rewind.wav b/assets/rewind.wav
new file mode 100644
index 0000000..b7e7b67
--- /dev/null
+++ b/assets/rewind.wav
Binary files differ
diff --git a/level.go b/level.go
index 4c97f9d..2199a37 100644
--- a/level.go
+++ b/level.go
@@ -6,8 +6,7 @@ import (
func ReverseLevel(g *Game) {
fmt.Printf("pframe %d/%d\n", 0, len(g.playerAi))
- g.state = REVERSING
- g.shaderName = "vcr"
+ g.SetReversing()
}
func playTheEnd(g *Game) {
@@ -23,14 +22,40 @@ func afterReversed(g *Game) {
}
func levelStart(g *Game) {
-
+ g.ResetAll()
+ g.playerAiIdx = 0
+ g.SetInGame()
for _, o := range g.objects {
o.movable = false
}
}
func StartLevel1(g *Game ) {
- g.state = IN_GAME
+ g.SetInGame()
+
+ tilemap := NewTilemap([][]int{
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 0,
+ 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 0,
+ 0, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ }, 25)
+
+ g.tilemap = &tilemap
+ g.tilemap.UpdateSurface()
// when hit end
g.QueueState(func (g *Game){
@@ -47,7 +72,7 @@ func StartLevel1(g *Game ) {
}
func StartLevel2(g *Game) {
- g.state = PLACING
+ g.SetPlacing()
g.toPlace = append(g.toPlace, NewLeftSpike(g, 0, 0))
@@ -59,7 +84,7 @@ func StartLevel2(g *Game) {
}
func StartLevel3(g *Game) {
- g.state = PLACING
+ g.SetPlacing()
g.toPlace = append(g.toPlace, NewSpike(g, 0, 0))
@@ -67,6 +92,58 @@ func StartLevel3(g *Game) {
g.QueueState(ReverseLevel)
// after reversed
g.QueueState(afterReversed)
- g.QueueState(StartLevel3)
+ g.QueueState(StartLevel4)
+}
+
+func StartLevel4(g *Game) {
+ g.SetPlacing()
+ g.toPlace = append(g.toPlace, NewSpring(g, 0, 0))
+
+ g.ClearAll()
+ tilemap := NewTilemap([][]int{
+ {
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 5, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ },
+ }, 25)
+
+ g.tilemap = &tilemap
+ g.tilemap.UpdateSurface()
+ g.exit.startx = 20 * tileSize
+ g.exit.starty = 5 * tileSize
+
+ g.ResetAll()
+ g.playerAi = g.playerAi[:0]
+
+ g.QueueState(levelStart)
+ // after end
+ g.QueueState(ReverseLevel)
+ // after reversed
+ g.QueueState(afterReversed)
+ g.QueueState(StartLevel5)
}
+func StartLevel5(g *Game) {
+ g.SetPlacing()
+ g.toPlace = append(g.toPlace, NewSpike(g, 0, 0))
+
+ // after end
+ g.QueueState(ReverseLevel)
+ // after reversed
+ g.QueueState(afterReversed)
+ g.QueueState(StartLevel5)
+}
diff --git a/main.go b/main.go
index 04828a7..1134e94 100644
--- a/main.go
+++ b/main.go
@@ -26,6 +26,10 @@ const (
gravity = 0.16
friction = 0.75
airResistance = 0.98
+
+ exitTransitionWeight = 0.9
+ ghostAlpha = 0.5
+ hightlightBorder = 2
)
var (
@@ -54,6 +58,7 @@ type RecPoint struct {
y float32
vx float32
vy float32
+ alpha float32
}
@@ -86,17 +91,36 @@ func (g * Game)RecordPoint() {
y: object.y,
vx: object.vx,
vy: object.vy,
+ alpha: object.alpha,
})
}
g.recording = append(g.recording, points)
}
+func (g * Game)ReplayPoint() {
+ if len(g.recording) == 0 {
+ return
+ }
+
+ var points []RecPoint
+ points, g.recording = g.recording[len(g.recording)-1], g.recording[:len(g.recording)-1]
+ for i, point := range points {
+ obj := g.objects[i]
+ obj.x = point.x
+ obj.y = point.y
+ obj.vx = point.vx
+ obj.vy = point.vy
+ obj.alpha = point.alpha
+ }
+}
+
func (g * Game)ResetPlayerAi() {
g.playerAiIdx = 0
g.player.x = g.player.startx
g.player.y = g.player.starty
g.player.vx = 0
g.player.vy = 0
+ g.player.alpha = ghostAlpha
}
func (g * Game)ReplayPlayerAi() {
@@ -129,20 +153,10 @@ func (g * Game)ReplayPlayerAi() {
}
}
-
-func (g * Game)ReplayPoint() {
- if len(g.recording) == 0 {
- return
- }
-
- var points []RecPoint
- points, g.recording = g.recording[len(g.recording)-1], g.recording[:len(g.recording)-1]
- for i, point := range points {
- g.objects[i].x = point.x
- g.objects[i].y = point.y
- g.objects[i].vx = point.vx
- g.objects[i].vy = point.vy
- }
+func (g * Game) ClearAll() {
+ g.objects = g.objects[:0]
+ g.objects = append(g.objects, g.player)
+ g.objects = append(g.objects, g.exit)
}
func (g * Game) ResetAll() {
@@ -158,30 +172,6 @@ func (g * Game) ResetAll() {
func (g *Game) Init() {
g.surface = ebiten.NewImage(screenWidth, screenHeight)
g.shaderName = "none"
- tilemap := NewTilemap([][]int{
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 0,
- 0, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 0,
- 0, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- },
- }, 25)
-
- g.tilemap = &tilemap
-
- g.tilemap.UpdateSurface()
g.player = NewPlayer(g, 4 * tileSize, 9 * tileSize)
g.objects = append(g.objects, g.player)
@@ -203,7 +193,7 @@ func (g *Game) Init() {
func (g *Game) Update() error {
g.time += 1
- //if ebiten.IsKeyPressed(ebiten.KeyR) {
+ //if ebiten.IsKeyJustPressed(ebiten.KeyR) {
// if g.state == IN_GAME {
// g.state = REVERSING
// g.shaderName = "vcr"
@@ -216,6 +206,16 @@ func (g *Game) Update() error {
//}
if g.state == IN_GAME {
+ if inpututil.IsKeyJustPressed(ebiten.KeyR) {
+ g.SetReversing()
+ next := func (g *Game){
+ g.SetInGame()
+ g.recording = g.recording[:0]
+ g.playerAi = g.playerAi[:0]
+ }
+ g.whenStateFinished = append([]func(*Game){next}, g.whenStateFinished...)
+ }
+
var currentState [3]bool
if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
g.player.MoveLeft()
@@ -259,29 +259,7 @@ func (g *Game) Update() error {
}
if g.state == PLACING {
-
- for _, obj := range g.objects {
- obj.Update(*g.tilemap, g.objects)
- }
-
- g.tilemap.Update()
- g.ReplayPlayerAi()
-
- cx, cy := ebiten.CursorPosition()
-
- if len(g.toPlace) > 0 {
- placeable := g.toPlace[0]
- cx = int(math.Floor(float64(cx)/float64(g.tilemap.tileSize)))*g.tilemap.tileSize
- cy = int(math.Floor(float64(cy)/float64(g.tilemap.tileSize)))*g.tilemap.tileSize
-
- cx += placeable.offsetX
- cy += placeable.offsetY
- placeable.x = float32(cx)
- placeable.y = float32(cy)
- }
- if inpututil.IsMouseButtonJustPressed(ebiten.MouseButton0) {
- g.PlaceObject(cx, cy)
- }
+ g.UpdatePlacing()
}
if g.player.y > screenHeight {
g.KillPlayer()
@@ -291,6 +269,37 @@ func (g *Game) Update() error {
return nil
}
+func (g *Game) UpdatePlacing() {
+ for _, obj := range g.objects {
+ obj.Update(*g.tilemap, g.objects)
+ }
+
+ g.tilemap.Update()
+ g.ReplayPlayerAi()
+
+ cx, cy := ebiten.CursorPosition()
+
+ for _, object := range g.objects {
+ object.highlight = object.CollidePoint(float32(cx), float32(cy)) && object.movable && len(g.toPlace) == 0
+ }
+
+ if len(g.toPlace) > 0 {
+ placeable := g.toPlace[0]
+ cx = int(math.Floor(float64(cx)/float64(g.tilemap.tileSize)))*g.tilemap.tileSize
+ cy = int(math.Floor(float64(cy)/float64(g.tilemap.tileSize)))*g.tilemap.tileSize
+
+ cx += placeable.offsetX
+ cy += placeable.offsetY
+ placeable.x = float32(cx)
+ placeable.y = float32(cy)
+ placeable.alpha = float32(math.Abs(math.Sin(float64(g.time) / 30.0)))
+ }
+
+ if inpututil.IsMouseButtonJustPressed(ebiten.MouseButton0) {
+ g.PlaceObject(cx, cy)
+ }
+}
+
func (g *Game) PlaceObject(cx, cy int) {
if len(g.toPlace) == 0 {
object := GetObjectAt(g.objects, float32(cx), float32(cy))
@@ -310,11 +319,17 @@ func (g *Game) PlaceObject(cx, cy int) {
placeable.startx = float32(cx)
placeable.starty = float32(cy)
+ placeable.highlight = false
+ placeable.alpha = 1.0
g.objects = append(g.objects, placeable)
g.toPlace = g.toPlace[1:len(g.toPlace)]
+ if len(g.toPlace) == 0 && len(g.playerAi) == 0 {
+ g.TransitionState()
+ }
+
}
func (g *Game) Draw(screen *ebiten.Image) {
@@ -338,6 +353,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
}
if g.state == END {
+
// draw THE END
ebitenutil.DebugPrint(screen, fmt.Sprintf("THE END %d", g.time - g.animStart))
@@ -398,7 +414,7 @@ func (g *Game) KillPlayer() {
if len(g.toPlace) == 0 {
g.ResetAll()
g.playerAi = g.playerAi[:0]
- g.state = IN_GAME
+ g.SetInGame()
for _, o := range g.objects {
o.movable = false
@@ -411,7 +427,8 @@ func (g *Game) EndLevel() {
if g.state == IN_GAME {
g.state = END
g.TransitionState()
- } else {
+ }
+ if g.state == PLACING {
g.ResetPlayerAi()
}
}
@@ -434,6 +451,24 @@ func (g *Game)RemoveObject(obj *GameObject) {
g.objects = g.objects[:i]
}
+func (g *Game) SetReversing() {
+ g.state = REVERSING
+ g.shaderName = "vcr"
+ g.player.alpha = 1.0
+}
+
+func (g *Game) SetInGame() {
+ g.state = IN_GAME
+ g.shaderName = "none"
+ g.player.alpha = 1.0
+}
+
+func (g *Game) SetPlacing() {
+ g.state = PLACING
+ g.shaderName = "none"
+ g.player.alpha = ghostAlpha
+}
+
func main() {
LoadShaders()
diff --git a/objects.go b/objects.go
index b301495..3c44458 100644
--- a/objects.go
+++ b/objects.go
@@ -1,16 +1,17 @@
package main
import (
+ "image"
+ "image/color"
"log"
- "image"
- "image/color"
- "github.com/hajimehoshi/ebiten/v2"
- "github.com/hajimehoshi/ebiten/v2/vector"
+
+ "github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
+ "github.com/hajimehoshi/ebiten/v2/vector"
)
const (
- SPRING_FORCE = 8
+ SPRING_FORCE = 10
)
type Direction int
@@ -30,6 +31,8 @@ type GameObject struct {
vx, vy float32
offsetX, offsetY int
+ alpha float32
+ highlight bool
friction float32
resistance float32
@@ -88,17 +91,21 @@ func (o * GameObject) Update(tilemap Tilemap, others []*GameObject) {
func GetObjectAt(objects []*GameObject, x, y float32) *GameObject {
for _, object := range objects {
- maxX := object.x + float32(object.image.Bounds().Dx())
- maxY := object.y + float32(object.image.Bounds().Dy())
- minX := object.x
- minY := object.y
- if x >= minX && x < maxX && y >= minY && y < maxY {
+ if object.CollidePoint(x, y) {
return object
}
}
return nil
}
+func (object * GameObject) CollidePoint(x, y float32) bool {
+ maxX := object.x + float32(object.image.Bounds().Dx())
+ maxY := object.y + float32(object.image.Bounds().Dy())
+ minX := object.x
+ minY := object.y
+ return x >= minX && x < maxX && y >= minY && y < maxY
+ }
+
func (o * GameObject) HasCollision(tilemap Tilemap, others []*GameObject, dir Direction) bool {
if tilemap.CollideObject(o) {
return true
@@ -128,12 +135,12 @@ func (o * GameObject) HasCollision(tilemap Tilemap, others []*GameObject, dir Di
func (o * GameObject) Draw(screen *ebiten.Image, tilemap Tilemap) {
op := &ebiten.DrawImageOptions{}
+ op.ColorScale.ScaleAlpha(o.alpha)
op.GeoM.Translate(float64(o.x), float64(o.y))
screen.DrawImage(o.image, op)
-
- if o.movable {
- vector.StrokeRect(screen, o.x, o.y, float32(o.image.Bounds().Dx()), float32(o.image.Bounds().Dy()), 2, color.RGBA{255, 100, 100, 255}, false)
+ if o.highlight {
+ vector.StrokeRect(screen, o.x, o.y, float32(o.image.Bounds().Dx()), float32(o.image.Bounds().Dy()), hightlightBorder, color.RGBA{255, 100, 100, 255}, false)
}
@@ -184,18 +191,18 @@ func OnCollideGeneric(this, other *GameObject) bool {
}
func NewObject(game *Game, x, y float32) *GameObject{
- obj := &GameObject{
+ return &GameObject{
game: game,
startx: x,
starty: y,
+ alpha: 1.0,
+ highlight: false,
+ friction: friction,
+ resistance: airResistance,
+ x: x,
+ y: y,
+ movable: true,
}
-
- obj.friction = friction
- obj.resistance = airResistance
- obj.x = obj.startx
- obj.y = obj.starty
- obj.movable = true
- return obj
}
func NewPlayer(game *Game, x, y float32) *GameObject{
@@ -264,7 +271,14 @@ func NewLeftSpike(game *Game, x, y float32) *GameObject{
func OnCollideExit(this, other *GameObject) bool {
if other == this.game.player {
- this.game.EndLevel()
+ g := other.game
+ g.player.x = (exitTransitionWeight)*g.player.x + (1-exitTransitionWeight)*g.exit.x
+ g.player.y = (exitTransitionWeight)*g.player.y + (1-exitTransitionWeight)*g.exit.y
+ g.player.alpha *= exitTransitionWeight
+
+ if g.player.alpha < 0.001 {
+ g.EndLevel()
+ }
}
return false
}