diff options
author | davidovski <david@davidovski.xyz> | 2024-04-21 13:47:41 +0100 |
---|---|---|
committer | davidovski <david@davidovski.xyz> | 2024-04-21 13:47:41 +0100 |
commit | 1f1c464f84a169ea5146d360ae6b1211d8a7074f (patch) | |
tree | 5095a7c59deaa5fe26408efcee1774c8d811d7be |
Initial Commit
-rw-r--r-- | go.mod | 14 | ||||
-rw-r--r-- | main.go | 132 | ||||
-rw-r--r-- | player.go | 53 | ||||
-rw-r--r-- | tilemap.go | 118 |
4 files changed, 317 insertions, 0 deletions
@@ -0,0 +1,14 @@ +module git.davidovski.xyz/gamejam-2024 + +go 1.22.2 + +require github.com/hajimehoshi/ebiten/v2 v2.7.2 + +require ( + github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.7.0 // indirect + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.18.0 // indirect +) @@ -0,0 +1,132 @@ +package main + +import ( + "log" + "image" + _ "image/png" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" +) + +const ( + screenWidth = 240 + screenHeight = 240 + tileSize = 16 + playerSpeed = 0.2 + jumpHeight = 0.2 +) + +type Game struct { + tilemap *Tilemap + offsetX int + offsetY int + player *GameObject +} + +func (g * Game)InitPlayer() { + g.player = &GameObject{ + x: 5, + y: 5, + } + + 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) +} + +func (g *Game) Init() { + tilemap := NewTilemap([][]int{ + { + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 218, 243, 244, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 244, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 219, 243, 243, 243, 219, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 218, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 243, 243, 243, + 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, + 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 29, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + }, 15) + g.tilemap = &tilemap + + g.tilemap.UpdateSurface() + + g.InitPlayer() + + + ebiten.SetWindowSize(screenWidth*2, screenHeight*2) + ebiten.SetWindowTitle("Tiles (Ebitengine Demo)") + if err := ebiten.RunGame(g); err != nil { + log.Fatal(err) + } + +} + +func (g *Game) Update() error { + 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 g.player.onGround && (ebiten.IsKeyPressed(ebiten.KeySpace) || ebiten.IsKeyPressed(ebiten.KeyUp)) { + g.player.vy = -jumpHeight + } + + g.player.Update(*g.tilemap) + return g.tilemap.Update() +} + +func (g *Game) Draw(screen *ebiten.Image) { + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(float64(g.offsetX), float64(g.offsetY)) + + screen.DrawImage(g.tilemap.surface, op) + g.player.Draw(screen, *g.tilemap) + ebitenutil.DebugPrint(screen, "Hello, World!") +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int){ + return screenWidth, screenHeight +} + +func main() { + ebiten.SetWindowTitle("Hello, World!") + game := &Game{} + game.Init() + + if err := ebiten.RunGame(game); err != nil { + log.Fatal(err) + } +} diff --git a/player.go b/player.go new file mode 100644 index 0000000..0b313ed --- /dev/null +++ b/player.go @@ -0,0 +1,53 @@ +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/tilemap.go b/tilemap.go new file mode 100644 index 0000000..281e76a --- /dev/null +++ b/tilemap.go @@ -0,0 +1,118 @@ +package main + +import ( + "github.com/hajimehoshi/ebiten/v2" + "log" + "image" + _ "image/png" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + +) + +type Tilemap struct { + tilesImage *ebiten.Image + surface *ebiten.Image + mapWidth int + tileSize int + layers [][]int + collisions []image.Rectangle +} + + +// take in map coord and make it real life coord +func (t *Tilemap) Translate(x, y int) (int, int) { + return x * t.tileSize, y*t.tileSize +} + +func (* Tilemap) Update() error { + return nil +} + +func (tm * Tilemap) Draw(screen *ebiten.Image) { +} + +func (tm *Tilemap) UpdateSurface() { + w := tm.tilesImage.Bounds().Dx() + tileXCount := w / tileSize + + // Draw each tile with each DrawImage call. + // As the source images of all DrawImage calls are always same, + // this rendering is done very efficiently. + // For more detail, see https://pkg.go.dev/github.com/hajimehoshi/ebiten/v2#Image.DrawImage + for _, l := range tm.layers { + for i, t := range l { + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(float64((i%tm.mapWidth)*tm.tileSize), float64((i/tm.mapWidth)*tm.tileSize)) + + sx := (t % tileXCount) * tileSize + sy := (t / tileXCount) * tileSize + tm.surface.DrawImage(tm.tilesImage.SubImage(image.Rect(sx, sy, sx+tileSize, sy+tileSize)).(*ebiten.Image), op) + } + } + + //ebitenutil.DebugPrint(screen, fmt.Sprintf("TPS: %0.2f", ebiten.ActualTPS())) +} + +func NewTilemap(layers [][]int, mapWidth int) Tilemap { + tilemap := Tilemap{ + tileSize: 16, + mapWidth: 15, + } + + tilemap.layers = layers + + tilemap.surface = ebiten.NewImage(mapWidth*tilemap.tileSize, len(layers[0])/mapWidth*tilemap.tileSize) + + var err error + tilemap.tilesImage, _, err = ebitenutil.NewImageFromFile("Assets/Terrain/Terrain (16x16).png") + + if err != nil { + log.Fatal(err) + } + + tilemap.CalculateCollisions() + + return tilemap +} + +func (tm * Tilemap) CalculateCollisions() { + for i, t := range tm.layers[1] { + if t != 0 { + x := i%tm.mapWidth * tm.tileSize + y := i/tm.mapWidth * tm.tileSize + + w, h := tm.tileSize, tm.tileSize + rect := image.Rect(x, y, x+w, y+h) + tm.collisions = append(tm.collisions, rect) + + } + } +} + +func (t * Tilemap) Collide(x, y, width, height int) bool { + r1 := image.Rect( + x, + y, + x+width, + y+height, + ) + + for _, r2 := range t.collisions { + if ! ( r2.Min.X > r1.Max.X || r2.Max.X < r1.Min.X || r2.Min.Y > r1.Max.Y || r2.Max.Y < r1.Min.Y) { + return true + + } + } + return false + +} + + +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)) + return t.Collide(x, y, width, height) +} + |