Update tileset and game log for improved asset management and debugging

- Modified tileset configuration to include multiple tile images with updated dimensions.
- Enhanced game log to provide detailed startup information and entity loading for room1.
- Updated binary file for the game executable.
added climebol laders
This commit is contained in:
2026-04-26 12:13:55 +02:00
parent a6c8044e4a
commit ef550044ad
15 changed files with 2599 additions and 200354 deletions

View File

@@ -4,6 +4,7 @@
"id": 1, "id": 1,
"tiled_id": 1, "tiled_id": 1,
"solid": true, "solid": true,
"climbable": false,
"sprites": { "sprites": {
"top": "Tile_31", "top": "Tile_31",
"side": "Tile_02" "side": "Tile_02"
@@ -13,6 +14,7 @@
"id": 2, "id": 2,
"tiled_id": 2, "tiled_id": 2,
"solid": true, "solid": true,
"climbable": false,
"sprites": { "sprites": {
"top": "Tile_12", "top": "Tile_12",
"side": "Tile_12" "side": "Tile_12"
@@ -28,6 +30,34 @@
}, },
"target_map": "room2" "target_map": "room2"
}, },
"ladder_1": {
"id": 4,
"tiled_id": 4,
"solid": false,
"climbable": true,
"sprites": {
"top": "../Ladders/1",
"side": "../Ladders/1"
}
},
"stone_1": {
"id": 5,
"tiled_id": 5,
"solid": true,
"sprites": {
"top": "../Stones/1",
"side": "../Stones/1"
}
},
"tree_1": {
"id": 6,
"tiled_id": 6,
"solid": true,
"sprites": {
"top": "../Trees/1",
"side": "../Trees/1"
}
},
"player": { "player": {
"id": 100, "id": 100,
"solid": true, "solid": true,

46
assets/maps/tileset.tsj Normal file
View File

@@ -0,0 +1,46 @@
{
"name": "MainTileset",
"tilecount": 6,
"tileheight": 96,
"tilewidth": 96,
"type": "tileset",
"version": "1.9",
"tiles": [
{
"id": 0,
"image": "../assets/images/1 Tiles/Tile_02.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 1,
"image": "../assets/images/1 Tiles/Tile_12.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 2,
"image": "../assets/images/1 Tiles/Tile_31.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 3,
"image": "../assets/images/Ladders/1.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 4,
"image": "../assets/images/Stones/1.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 5,
"image": "../assets/images/Trees/1.png",
"imageheight": 96,
"imagewidth": 96
}
]
}

View File

@@ -8,6 +8,7 @@ Pos() mgl64.Vec3
SetPos(pos mgl64.Vec3) SetPos(pos mgl64.Vec3)
IsBlocking() bool IsBlocking() bool
IsMovable() bool IsMovable() bool
IsClimbable() bool
Move(dx, dy, dz float64) Move(dx, dy, dz float64)
Damage(amount int) Damage(amount int)
Pickup() bool Pickup() bool

View File

@@ -8,13 +8,15 @@ import (
type Tile struct { type Tile struct {
pos mgl64.Vec3 pos mgl64.Vec3
health int health int
Climbable bool
SideImg *ebiten.Image SideImg *ebiten.Image
TopImg *ebiten.Image TopImg *ebiten.Image
} }
func (t *Tile) Pos() mgl64.Vec3 { return t.pos } func (t *Tile) Pos() mgl64.Vec3 { return t.pos }
func (t *Tile) SetPos(pos mgl64.Vec3) { t.pos = pos } func (t *Tile) SetPos(pos mgl64.Vec3) { t.pos = pos }
func (t *Tile) IsBlocking() bool { return true } func (t *Tile) IsBlocking() bool { return !t.Climbable }
func (t *Tile) IsClimbable() bool { return t.Climbable }
func (t *Tile) IsMovable() bool { return false } func (t *Tile) IsMovable() bool { return false }
func (t *Tile) Move(dx, dy, dz float64) { func (t *Tile) Move(dx, dy, dz float64) {
// Not feasible for soil etc. // Not feasible for soil etc.

View File

@@ -71,25 +71,47 @@ type TiledMap struct {
type Portal struct { type Portal struct {
pos mgl64.Vec3 pos mgl64.Vec3
TargetMap string TargetMap string
SideImg *ebiten.Image
TopImg *ebiten.Image
} }
func (p *Portal) Pos() mgl64.Vec3 { return p.pos } func (p *Portal) Pos() mgl64.Vec3 { return p.pos }
func (p *Portal) SetPos(pos mgl64.Vec3) { p.pos = pos } func (p *Portal) SetPos(pos mgl64.Vec3) { p.pos = pos }
func (p *Portal) IsBlocking() bool { return false } func (p *Portal) IsBlocking() bool { return false }
func (p *Portal) IsMovable() bool { return false } func (p *Portal) IsMovable() bool { return false }
func (p *Portal) IsClimbable() bool { return false }
func (p *Portal) Move(dx, dy, dz float64) {} func (p *Portal) Move(dx, dy, dz float64) {}
func (p *Portal) Damage(amount int) {} func (p *Portal) Damage(amount int) {}
func (p *Portal) Pickup() bool { return false } func (p *Portal) Pickup() bool { return false }
func (p *Portal) GetHealth() int { return 100 } func (p *Portal) GetHealth() int { return 100 }
func (p *Portal) DrawSide(screen interface{}, x, y float64, tint, alpha float32) {} func (p *Portal) DrawSide(screen interface{}, x, y float64, tint, alpha float32) {
func (p *Portal) DrawTop(screen interface{}, x, y float64, tint, alpha float32) {} scr := screen.(*ebiten.Image)
if p.SideImg != nil {
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(x, y)
op.ColorScale.Scale(tint, tint, tint, alpha)
scr.DrawImage(p.SideImg, op)
}
}
func (p *Portal) DrawTop(screen interface{}, x, y float64, tint, alpha float32) {
scr := screen.(*ebiten.Image)
if p.TopImg != nil {
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(x, y)
op.ColorScale.Scale(tint, tint, tint, alpha)
scr.DrawImage(p.TopImg, op)
}
}
// EntityDef definierar egenskaperna för ett tile-id // EntityDef definierar egenskaperna för ett tile-id
type EntityDef struct { type EntityDef struct {
ID int `json:"id"` ID int `json:"id"`
TiledID int `json:"tiled_id"` TiledID int `json:"tiled_id"`
Solid bool `json:"solid"` Solid bool `json:"solid"`
TargetMap string `json:"target_map,omitempty"` Climbable bool `json:"climbable,omitempty"`
TargetMap string
SideImg *ebiten.Image
TopImg *ebiten.Image `json:"target_map,omitempty"`
Sprites struct { Sprites struct {
Top string `json:"top"` Top string `json:"top"`
Side string `json:"side"` Side string `json:"side"`
@@ -142,9 +164,14 @@ func LoadWorldFromTiled(tmjPath string) (*World, error) {
if img, ok := loadedImages[name]; ok { if img, ok := loadedImages[name]; ok {
return img return img
} }
img, _, err := ebitenutil.NewImageFromFile("assets/images/1 Tiles/" + name + ".png") imgPath := "assets/images/1 Tiles/" + name + ".png"
if len(name) > 3 && name[:3] == "../" {
imgPath = "assets/images/" + name[3:] + ".png"
}
img, _, err := ebitenutil.NewImageFromFile(imgPath)
if err != nil { if err != nil {
fmt.Println("Warning, missing image:", name) fmt.Println("Warning, missing image:", imgPath, err)
} }
loadedImages[name] = img loadedImages[name] = img
return img return img
@@ -190,9 +217,9 @@ func LoadWorldFromTiled(tmjPath string) (*World, error) {
sideImg := loadImage(def.Sprites.Side) sideImg := loadImage(def.Sprites.Side)
if def.TargetMap != "" { if def.TargetMap != "" {
ent = &Portal{TargetMap: def.TargetMap} ent = &Portal{TargetMap: def.TargetMap, SideImg: sideImg, TopImg: topImg}
} else { } else {
ent = &Tile{SideImg: sideImg, TopImg: topImg} ent = &Tile{SideImg: sideImg, TopImg: topImg, Climbable: def.Climbable}
} }
} else { } else {
// Fallback om def saknas // Fallback om def saknas

View File

@@ -30,6 +30,7 @@ type Player struct {
// Sprites // Sprites
SpriteRun *ebiten.Image SpriteRun *ebiten.Image
SpriteJump *ebiten.Image
SpriteIdle *ebiten.Image SpriteIdle *ebiten.Image
SpriteTop *ebiten.Image SpriteTop *ebiten.Image
AnimationFrame int AnimationFrame int
@@ -61,16 +62,21 @@ func NewPlayScene() *PlayScene {
if pIdle == nil { if pIdle == nil {
pIdle = pRun pIdle = pRun
} }
pJump, _, _ := ebitenutil.NewImageFromFile("assets/images/Warrior_1/Jump.png")
if pJump == nil {
pJump = pRun
}
pTop := ebiten.NewImage(96, 96) pTop := ebiten.NewImage(96, 96)
pTop.Fill(color.RGBA{255, 255, 0, 255}) pTop.Fill(color.RGBA{255, 255, 0, 255})
player := &Player{ player := &Player{
Pos: mgl64.Vec3{50.0 * entities.TileSize, 6.0 * entities.TileSize, 50.0 * entities.TileSize}, Pos: mgl64.Vec3{50.0 * entities.TileSize, 6.0 * entities.TileSize, 50.0 * entities.TileSize},
Width: 96, Width: 32,
Height: 96, Height: 32,
SpriteRun: pRun, SpriteRun: pRun,
SpriteIdle: pIdle, SpriteIdle: pIdle,
SpriteJump: pJump,
SpriteTop: pTop, SpriteTop: pTop,
FacingRight: true, FacingRight: true,
} }
@@ -86,26 +92,74 @@ func NewPlayScene() *PlayScene {
func (s *PlayScene) Update() error { func (s *PlayScene) Update() error {
p := s.player p := s.player
currentMoveSpeed := MoveSpeed
if ebiten.IsKeyPressed(ebiten.KeyControl) {
currentMoveSpeed = MoveSpeed * 1.8 // Lite snabbare när man springer
}
p.Vel[0] = 0 p.Vel[0] = 0
p.Vel[2] = 0 p.Vel[2] = 0
isMoving := false isMoving := false
// Kolla om spelaren är i ett klättringsbart objekt
currentGridX := int((p.Pos[0] + p.Width/2) / entities.TileSize)
currentGridY := int((p.Pos[1] + p.Height/2) / entities.TileSize)
currentGridZ := int((p.Pos[2] + p.Width/2) / entities.TileSize)
entIn := s.world.GetEntityAt(currentGridX, currentGridY, currentGridZ)
isClimbing := false
if entIn != nil && entIn.IsClimbable() && ebiten.IsKeyPressed(ebiten.KeyShift) {
isClimbing = true
}
if isClimbing {
p.Vel[1] = 0 // Ingen gravitation när man klättrar
// W, A, S, D styr X och Y axlar
if ebiten.IsKeyPressed(ebiten.KeyA) {
p.Vel[0] = -currentMoveSpeed
p.FacingRight = false
isMoving = true
}
if ebiten.IsKeyPressed(ebiten.KeyD) {
p.Vel[0] = currentMoveSpeed
p.FacingRight = true
isMoving = true
}
if ebiten.IsKeyPressed(ebiten.KeyW) {
p.Vel[1] = -currentMoveSpeed // Uppåt
isMoving = true
}
if ebiten.IsKeyPressed(ebiten.KeyS) {
p.Vel[1] = currentMoveSpeed // Nedåt
isMoving = true
}
// Upp/ner pilarna styr Z-axeln
if ebiten.IsKeyPressed(ebiten.KeyUp) {
p.Vel[2] = -currentMoveSpeed
isMoving = true
}
if ebiten.IsKeyPressed(ebiten.KeyDown) {
p.Vel[2] = currentMoveSpeed
isMoving = true
}
} else {
if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) { if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
p.Vel[0] = -MoveSpeed p.Vel[0] = -currentMoveSpeed
p.FacingRight = false p.FacingRight = false
isMoving = true isMoving = true
} }
if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) { if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) {
p.Vel[0] = MoveSpeed p.Vel[0] = currentMoveSpeed
p.FacingRight = true p.FacingRight = true
isMoving = true isMoving = true
} }
if ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) { if ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) {
p.Vel[2] = -MoveSpeed p.Vel[2] = -currentMoveSpeed
isMoving = true isMoving = true
} }
if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) { if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) {
p.Vel[2] = MoveSpeed p.Vel[2] = currentMoveSpeed
isMoving = true isMoving = true
} }
@@ -117,6 +171,7 @@ func (s *PlayScene) Update() error {
if !p.IsGrounded { if !p.IsGrounded {
p.Vel[1] += Gravity p.Vel[1] += Gravity
} }
}
nextX := p.Pos[0] + p.Vel[0] nextX := p.Pos[0] + p.Vel[0]
nextY := p.Pos[1] + p.Vel[1] nextY := p.Pos[1] + p.Vel[1]
@@ -295,8 +350,8 @@ func (s *PlayScene) drawPlayerSide(screen *ebiten.Image, camX, camY float64) {
op.GeoM.Translate(96, 0) op.GeoM.Translate(96, 0)
} }
drawX := p.Pos[0] - camX drawX := p.Pos[0] - camX - 32
drawY := p.Pos[1] - camY drawY := p.Pos[1] - camY - 64
op.GeoM.Translate(drawX, drawY) op.GeoM.Translate(drawX, drawY)
screen.DrawImage(subImg, op) screen.DrawImage(subImg, op)
@@ -306,8 +361,8 @@ func (s *PlayScene) drawPlayerTop(screen *ebiten.Image, camX, camZ float64) {
p := s.player p := s.player
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
drawX := p.Pos[0] - camX drawX := p.Pos[0] - camX - 32
drawZ := p.Pos[2] - camZ drawZ := p.Pos[2] - camZ - 64
op.GeoM.Translate(drawX, drawZ) op.GeoM.Translate(drawX, drawZ)
screen.DrawImage(p.SpriteTop, op) screen.DrawImage(p.SpriteTop, op)
} }

View File

@@ -8,15 +8,15 @@
"scale": 0.135375, "scale": 0.135375,
"selectedLayer": 4, "selectedLayer": 4,
"viewCenter": { "viewCenter": {
"x": 1676.8236380424748, "x": 1599.2613111726687,
"y": 2256.694367497692 "y": 1599.2613111726687
} }
}, },
"room2.tmj": { "room2.tmj": {
"scale": 0.135375, "scale": 0.135375,
"selectedLayer": 3, "selectedLayer": 3,
"viewCenter": { "viewCenter": {
"x": 1602.9547553093257, "x": 1599.2613111726687,
"y": 1599.2613111726687 "y": 1599.2613111726687
} }
}, },

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,46 @@
{ {
"columns": 1, "name": "MainTileset",
"tilecount": 6,
"tileheight": 96,
"tilewidth": 96,
"type": "tileset",
"version": "1.9",
"tiles": [
{
"id": 0,
"image": "../assets/images/1 Tiles/Tile_02.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 1,
"image": "../assets/images/1 Tiles/Tile_12.png", "image": "../assets/images/1 Tiles/Tile_12.png",
"imageheight": 32, "imageheight": 32,
"imagewidth": 32, "imagewidth": 32
"name": "MainTileset", },
"tilecount": 1, {
"tileheight": 32, "id": 2,
"tilewidth": 32, "image": "../assets/images/1 Tiles/Tile_31.png",
"type": "tileset", "imageheight": 32,
"version": "1.9" "imagewidth": 32
},
{
"id": 3,
"image": "../assets/images/Ladders/1.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 4,
"image": "../assets/images/Stones/1.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 5,
"image": "../assets/images/Trees/1.png",
"imageheight": 96,
"imagewidth": 96
}
]
} }

View File

@@ -18,7 +18,7 @@ Spelets arkitektur bygger på att helt separera 3D-spellogiken från 2D-renderin
- **+2 block upp**: Ännu ljusare och nästan helt transparent (alpha 0.2, tint 1.6). - **+2 block upp**: Ännu ljusare och nästan helt transparent (alpha 0.2, tint 1.6).
3. **Kartstruktur:** Världen sparas i grunden som en tre-dimensionell array (`[][][]Entity`), exempelvis motsvarande en 10x10x10 map. Varje block motsvarar 32x32 pixlar i world-space som sedan representeras av specifika sprites ur `assets/images/1 Tiles`. Kartans botten består av solida jordblock som förhindrar spelaren från att falla igenom. 3. **Kartstruktur:** Världen sparas i grunden som en tre-dimensionell array (`[][][]Entity`), exempelvis motsvarande en 10x10x10 map. Varje block motsvarar 32x32 pixlar i world-space som sedan representeras av specifika sprites ur `assets/images/1 Tiles`. Kartans botten består av solida jordblock som förhindrar spelaren från att falla igenom.
4. **Gemensamt Spelobjekt (Entity):** All logik byggs på ett generiskt `Entity`-interface som besvarar frågor som `IsBlocking()`, `IsMovable()`, `Move()`, `Damage()`, `Pickup()` och `GetHealth()`. Detta gör att karaktärer, pussel och fiender smidigt kan kalla varandras triggers och dela samma kollisionsmekanismer i 3D-kuben. 4. **Gemensamt Spelobjekt (Entity):** All logik byggs på ett generiskt `Entity`-interface som besvarar frågor som `IsBlocking()`, `IsMovable()`, `Move()`, `Damage()`, `Pickup()` och `GetHealth()`. Detta gör att karaktärer, pussel och fiender smidigt kan kalla varandras triggers och dela samma kollisionsmekanismer i 3D-kuben.
5. **Animation och Input:** Kontroller och inputs (Gå, Hoppa m.m) knyts till konfigurerbara actions. Karaktärerna använder sprite-sheets där varje "frame" är 96x96 pixlar som bläddras igenom i en specifik timer för att spela upp livliga animationer. Att byta håll innebär att man byter animation state och hastighetsvektor. 5. **Animation och Input:** Kontroller och inputs (Gå, Hoppa m.m) knyts till konfigurerbara actions. Karaktärerna använder sprite-sheets där varje "frame" är 96x96 pixlar som bläddras igenom i en specifik timer för att spela upp livliga animationer. Att byta håll innebär att man byter animation state och hastighetsvektor. För att springa håller spelaren nere `Ctrl`-knappen. För att klättra i klättringsbara objekt måste spelaren stå inuti objektet och hålla nere `Shift`-knappen; då används `A`, `S`, `D`, `W` för att klättra vänster, ner, höger och upp, och pil upp / pil ner för att klättra inåt eller utåt (Z-axeln).
## Map Pipeline ## Map Pipeline
* **Tiled JSON (`assets/maps/*.tmj`)**: Kartdata. * **Tiled JSON (`assets/maps/*.tmj`)**: Kartdata.

View File

@@ -4,6 +4,7 @@
"id": 1, "id": 1,
"tiled_id": 1, "tiled_id": 1,
"solid": true, "solid": true,
"climbable": false,
"sprites": { "sprites": {
"top": "Tile_31", "top": "Tile_31",
"side": "Tile_02" "side": "Tile_02"
@@ -13,6 +14,7 @@
"id": 2, "id": 2,
"tiled_id": 2, "tiled_id": 2,
"solid": true, "solid": true,
"climbable": false,
"sprites": { "sprites": {
"top": "Tile_12", "top": "Tile_12",
"side": "Tile_12" "side": "Tile_12"
@@ -28,6 +30,34 @@
}, },
"target_map": "room2" "target_map": "room2"
}, },
"ladder_1": {
"id": 4,
"tiled_id": 4,
"solid": false,
"climbable": true,
"sprites": {
"top": "../Ladders/1",
"side": "../Ladders/1"
}
},
"stone_1": {
"id": 5,
"tiled_id": 5,
"solid": true,
"sprites": {
"top": "../Stones/1",
"side": "../Stones/1"
}
},
"tree_1": {
"id": 6,
"tiled_id": 6,
"solid": true,
"sprites": {
"top": "../Trees/1",
"side": "../Trees/1"
}
},
"player": { "player": {
"id": 100, "id": 100,
"solid": true, "solid": true,

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,46 @@
{ {
"columns": 1, "name": "MainTileset",
"tilecount": 6,
"tileheight": 96,
"tilewidth": 96,
"type": "tileset",
"version": "1.9",
"tiles": [
{
"id": 0,
"image": "../assets/images/1 Tiles/Tile_02.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 1,
"image": "../assets/images/1 Tiles/Tile_12.png", "image": "../assets/images/1 Tiles/Tile_12.png",
"imageheight": 32, "imageheight": 32,
"imagewidth": 32, "imagewidth": 32
"name": "MainTileset", },
"tilecount": 1, {
"tileheight": 32, "id": 2,
"tilewidth": 32, "image": "../assets/images/1 Tiles/Tile_31.png",
"type": "tileset", "imageheight": 32,
"version": "1.9" "imagewidth": 32
},
{
"id": 3,
"image": "../assets/images/Ladders/1.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 4,
"image": "../assets/images/Stones/1.png",
"imageheight": 32,
"imagewidth": 32
},
{
"id": 5,
"image": "../assets/images/Trees/1.png",
"imageheight": 96,
"imagewidth": 96
}
]
} }

View File

@@ -31,3 +31,15 @@
2026/04/26 02:35:58 Byter rum till: room2 2026/04/26 02:35:58 Byter rum till: room2
2026/04/26 02:35:58 Laddar värld från 'assets/maps/room2.tmj'... 2026/04/26 02:35:58 Laddar värld från 'assets/maps/room2.tmj'...
2026/04/26 02:35:58 Map parsead med 10 lager. 2026/04/26 02:35:58 Map parsead med 10 lager.
2026/04/26 11:55:47 --- Spelet startar ---
2026/04/26 11:55:49 Laddar värld från 'assets/maps/room1.tmj'...
2026/04/26 11:55:49 Laddade 7 st entity definitions från JSON.
2026/04/26 11:55:49 Map parsead med 10 lager.
2026/04/26 12:00:56 --- Spelet startar ---
2026/04/26 12:00:57 Laddar värld från 'assets/maps/room1.tmj'...
2026/04/26 12:00:57 Laddade 7 st entity definitions från JSON.
2026/04/26 12:00:57 Map parsead med 10 lager.
2026/04/26 12:09:46 --- Spelet startar ---
2026/04/26 12:09:48 Laddar värld från 'assets/maps/room1.tmj'...
2026/04/26 12:09:48 Laddade 7 st entity definitions från JSON.
2026/04/26 12:09:48 Map parsead med 10 lager.

Binary file not shown.