diff options
-rw-r--r-- | main.go | 146 | ||||
-rw-r--r-- | player.go | 52 | ||||
-rw-r--r-- | shaders/none.kage | 12 | ||||
-rw-r--r-- | shaders/vcr.kage | 44 | ||||
-rw-r--r-- | tilemap.go | 12 |
5 files changed, 149 insertions, 117 deletions
@@ -1,24 +1,27 @@ package main import ( + _ "embed" + "fmt" + "image/color" + _ "image/png" "log" - "image" - "image/color" - _ "embed" - _ "image/png" + "math" - "github.com/hajimehoshi/ebiten/v2/inpututil" "github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/hajimehoshi/ebiten/v2/inpututil" ) const ( screenWidth = 400 screenHeight = 240 tileSize = 16 - playerSpeed = 0.2 - jumpHeight = 0.2 + playerSpeed = 2.1 + jumpHeight = 3.2 rewindSpeed = 2 + gravity = 0.16 + friction = 0.9 ) var ( @@ -26,13 +29,11 @@ var ( noneShader_src []byte //go:embed shaders/vcr.kage vcrShader_src []byte - - //go:embed shaders/reverse.kage - rewindShader_src []byte ) var ( shaders map[string]*ebiten.Shader + tilesImage *ebiten.Image ) type State int @@ -40,6 +41,7 @@ type State int const ( IN_GAME State = iota END + PLACING REVERSING ) @@ -64,6 +66,7 @@ type Game struct { shaderName string recording [][]RecPoint state State + toPlace []*GameObject } func (g * Game)RecordPoint() { @@ -94,42 +97,20 @@ func (g * Game)ReplayPoint() { } } -func (g * Game)InitPlayer() { - g.player = &GameObject{} - - playerImage, _, err := ebitenutil.NewImageFromFile("Assets/Main Characters/Ninja Frog/Idle (32x32).png") - if err != nil { - log.Fatal(err) - } - - g.player.image = playerImage.SubImage(image.Rect(0, 0, 32, 32)).(*ebiten.Image) - g.objects = append(g.objects, g.player) - - g.ResetPlayer() -} - -func (g * Game)ResetPlayer() { - g.player.x = g.startPosition.x - g.player.y = g.startPosition.y - 0.1 -} - -func (g * Game)InitExit() { - g.exit = &GameObject{ - x: 21, - y: 8, +func (g * Game)ResetAll() { + for _, obj := range g.objects { + obj.x = obj.startx + obj.y = obj.starty } - - exitImage, _, err := ebitenutil.NewImageFromFile("tiles.png") - if err != nil { - log.Fatal(err) - } - - g.exit.image = exitImage.SubImage(image.Rect(0, 16, 32, 48)).(*ebiten.Image) - g.objects = append(g.objects, g.exit) } func (g *Game) Init() { - g.state = IN_GAME + g.state = PLACING + + g.toPlace = append(g.toPlace, NewBox(0, 0)) + g.toPlace = append(g.toPlace, NewBox(0, 0)) + g.toPlace = append(g.toPlace, NewBox(0, 0)) + g.toPlace = append(g.toPlace, NewSpring(0, 0)) g.surface = ebiten.NewImage(screenWidth, screenHeight) g.shaderName = "none" @@ -154,17 +135,16 @@ func (g *Game) Init() { }, }, 25) - g.startPosition = &GameObject{ - x: 4, - y: 8, - } - g.tilemap = &tilemap g.tilemap.UpdateSurface() - g.InitPlayer() - g.InitExit() + g.player = NewPlayer(4 * tileSize, 8 * tileSize) + g.objects = append(g.objects, g.player) + g.exit = NewExit(21 * tileSize, 8 * tileSize) + g.objects = append(g.objects, g.exit) + + g.ResetAll() ebiten.SetWindowSize(screenWidth*2, screenHeight*2) @@ -182,28 +162,30 @@ func (g *Game) Update() error { if g.state == REVERSING { g.state = IN_GAME g.shaderName = "none" - } else { + } + + if g.state == IN_GAME { g.state = REVERSING g.shaderName = "vcr" } } - if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) { - g.player.vx = -playerSpeed - } + if g.state == IN_GAME { + if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) { + g.player.vx = -playerSpeed + } - if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) { - g.player.vx = playerSpeed - } + if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) { + g.player.vx = playerSpeed + } - if g.player.onGround && (ebiten.IsKeyPressed(ebiten.KeySpace) || ebiten.IsKeyPressed(ebiten.KeyUp)) { - g.player.vy = -jumpHeight - } + if g.player.onGround && (ebiten.IsKeyPressed(ebiten.KeySpace) || ebiten.IsKeyPressed(ebiten.KeyUp)) { + g.player.vy += -jumpHeight + } - if g.state == IN_GAME { for _, obj := range g.objects { - obj.Update(*g.tilemap) + obj.Update(*g.tilemap, g.objects) } g.tilemap.Update() @@ -216,9 +198,38 @@ func (g *Game) Update() error { } } + if g.state == PLACING { + if len(g.toPlace) > 0 { + cx, cy := ebiten.CursorPosition() + 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 + + g.toPlace[0].x = float32(cx) + g.toPlace[0].y = float32(cy) + + if inpututil.IsMouseButtonJustPressed(ebiten.MouseButton0) { + g.PlaceObject(cx, cy) + } + } + } + return nil } +func (g *Game)PlaceObject(cx, cy int) { + g.toPlace[0].startx = float32(cx) + g.toPlace[0].starty = float32(cy) + + g.objects = append(g.objects, g.toPlace[0]) + + g.toPlace = g.toPlace[1:len(g.toPlace)] + + if len(g.toPlace) == 0 { + g.ResetAll() + g.state = IN_GAME + } +} + func (g *Game) Draw(screen *ebiten.Image) { g.surface.Fill(color.Alpha16{0x9ccf}) @@ -233,6 +244,12 @@ func (g *Game) Draw(screen *ebiten.Image) { obj.Draw(g.surface, *g.tilemap) } + if g.state == PLACING { + if len(g.toPlace) > 0 { + g.toPlace[0].Draw(g.surface, *g.tilemap) + } + } + cx, cy := ebiten.CursorPosition() shop := &ebiten.DrawRectShaderOptions{} @@ -246,6 +263,7 @@ func (g *Game) Draw(screen *ebiten.Image) { shop.Images[3] = g.surface screen.DrawRectShader(screenWidth, screenHeight, shaders[g.shaderName], shop) + ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.ActualTPS())) //screen.DrawImage(surface, &ebiten.DrawImageOptions{}) } @@ -275,6 +293,12 @@ func LoadShaders() error { func main() { LoadShaders() + var err error + tilesImage, _, err = ebitenutil.NewImageFromFile("tiles.png") + if err != nil { + log.Fatal(err) + } + ebiten.SetWindowTitle("Hello, World!") game := &Game{} game.Init() diff --git a/player.go b/player.go deleted file mode 100644 index 4fe7aa8..0000000 --- a/player.go +++ /dev/null @@ -1,52 +0,0 @@ -package main - -import ( - "github.com/hajimehoshi/ebiten/v2" -) - -const ( - gravity = 0.01 - friction = 0.6 -) - -type GameObject struct { - x, y float32 - vx, vy float32 - image *ebiten.Image - onGround bool -} - - -type Player struct { - GameObject -} - -func (o * GameObject) Update(tilemap Tilemap) { - o.vy += gravity - - o.x += o.vx - if tilemap.CollideObject(o) { - o.x -= o.vx - o.vx = 0 - } - - o.y += o.vy - if (tilemap.CollideObject(o)) { - - o.onGround = true; - o.vx *= friction - - o.y -= o.vy - o.vy = 0 - } else { - o.onGround = false; - } -} - -func (o * GameObject) Draw(screen *ebiten.Image, tilemap Tilemap) { - op := &ebiten.DrawImageOptions{} - op.GeoM.Translate(float64(o.x * float32(tilemap.tileSize)), float64(o.y * float32(tilemap.tileSize))) - screen.DrawImage(o.image, op) -} - - diff --git a/shaders/none.kage b/shaders/none.kage new file mode 100644 index 0000000..3eccc50 --- /dev/null +++ b/shaders/none.kage @@ -0,0 +1,12 @@ +//go:build ignore + +//kage:unit pixels + +package main + +var Time float +var Cursor vec2 + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + return imageSrc2UnsafeAt(srcPos); +} diff --git a/shaders/vcr.kage b/shaders/vcr.kage new file mode 100644 index 0000000..3bee5cd --- /dev/null +++ b/shaders/vcr.kage @@ -0,0 +1,44 @@ +//go:build ignore + +//kage:unit pixels + +package main + +var Time float +var Cursor vec2 + +const noiseX = 80.0 +const noiseY = 100.0 + +const colorOffsetIntensity = 1.2 + +func rand(co vec2) float { + return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); +} + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + noiseOffset := Time / 7 + col := vec4(0) + uv := srcPos / imageSrc0Size() + + uv.x = uv.x + (rand(vec2(Time,srcPos.y)) - 0.5) / (noiseX); + uv.y = uv.y + (rand(vec2(Time))-0.5) / (noiseY); + + whiteNoise := rand(vec2(floor(uv.y*80.0),floor(uv.x*50.0))+vec2(Time,0)) + off := 1.0 - mod(uv.y - noiseOffset, 1) + + if (whiteNoise > 11.5-30.0*(off)) || whiteNoise < 1.5-2.0*(off) { + // Sample the texture. + + offsetR := vec2(0.006 * sin(Time), 0.0) * colorOffsetIntensity; + offsetG := vec2(0.0073 * (cos(Time * 0.97)), 0.0) * colorOffsetIntensity; + r := imageSrc0UnsafeAt((uv+offsetR) * imageSrc0Size()).r + g := imageSrc0UnsafeAt((uv+offsetG) * imageSrc0Size()).g + b := imageSrc0UnsafeAt(uv * imageSrc0Size()).b + return vec4(r, g, b, 1.0) + } else { + col = imageSrc2UnsafeAt(uv * imageSrc0Size()); + return col + vec4(0.8); + } + +} @@ -5,7 +5,6 @@ import ( "log" "image" _ "image/png" - "github.com/hajimehoshi/ebiten/v2/ebitenutil" ) @@ -64,7 +63,7 @@ func NewTilemap(layers [][]int, mapWidth int) Tilemap { tilemap.surface = ebiten.NewImage(mapWidth*tilemap.tileSize, len(layers[0])/mapWidth*tilemap.tileSize) var err error - tilemap.tilesImage, _, err = ebitenutil.NewImageFromFile("tiles.png") + tilemap.tilesImage = tilesImage if err != nil { log.Fatal(err) @@ -89,6 +88,11 @@ func (tm * Tilemap) CalculateCollisions() { } } + +func Collide(r1 image.Rectangle, r2 image.Rectangle) bool { + return ! ( r2.Min.X > r1.Max.X || r2.Max.X < r1.Min.X || r2.Min.Y > r1.Max.Y || r2.Max.Y < r1.Min.Y) +} + func (t * Tilemap) Collide(x, y, width, height int) bool { r1 := image.Rect( x, @@ -111,8 +115,8 @@ func (t * Tilemap) Collide(x, y, width, height int) bool { func (t * Tilemap) CollideObject(object *GameObject) bool { width := object.image.Bounds().Dx() height := object.image.Bounds().Dy() - x := int(object.x*float32(t.tileSize)) - y := int(object.y*float32(t.tileSize)) + x := int(object.x) + y := int(object.y) return t.Collide(x, y, width, height) } |