summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod14
-rw-r--r--main.go132
-rw-r--r--player.go53
-rw-r--r--tilemap.go118
4 files changed, 317 insertions, 0 deletions
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..d3a2ecb
--- /dev/null
+++ b/go.mod
@@ -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
+)
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..f0448b3
--- /dev/null
+++ b/main.go
@@ -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)
+}
+