gamemap.go (5161B)
1 package main 2 3 import ( 4 "encoding/json" 5 "errors" 6 "github.com/hajimehoshi/ebiten/v2" 7 "github.com/hajimehoshi/ebiten/v2/ebitenutil" 8 "image" 9 _ "image/png" 10 "os" 11 ) 12 13 // to see them bits position with own eyes 14 const ( 15 flipHoriz uint32 = 0b10000000000000000000000000000000 16 flipVert uint32 = 0b01000000000000000000000000000000 17 flipDiag uint32 = 0b00100000000000000000000000000000 18 ) 19 20 type Tileset struct { 21 FirstGid uint32 `json:firstgid` 22 Image string `json:image` 23 Name string `json:name` 24 TileWidth uint32 `json:tilewidth` 25 TileHeight uint32 `json:tileheight` 26 Columns uint32 `json:columns` 27 28 Atlas *ebiten.Image 29 } 30 31 type Tile struct { 32 FlipHor, FlipVer, FlipDiag bool 33 Options *ebiten.DrawImageOptions 34 Image *ebiten.Image 35 Gid uint32 36 x, y float64 // absolute position coordinates 37 LayerId int32 38 } 39 40 type Layer struct { 41 Type string `json:type` 42 Id int32 `json:id` 43 Data []uint32 `json:data` 44 Height int32 `json:height` 45 Width int32 `json:width` 46 X int32 `json:x` 47 Y int32 `json:y` 48 } 49 50 type Gamemap struct { 51 Width uint32 `json:width` 52 Height uint32 `json:height` 53 TileWidth uint32 `json:tilewidth` 54 TileHeight uint32 `json:tileheight` 55 Layers []*Layer `json:layers` 56 Tilesets []*Tileset `json:tilesets` 57 58 RealWidth, RealHeight float64 59 60 Tiles map[uint64][]*Tile 61 } 62 63 func (l *Layer) Init(gamemap *Gamemap) error { 64 for i, t := range l.Data { 65 if t > 0 { 66 var tile *Tile = &Tile{} 67 // first 3 bits in that uint value are flags 68 tile.FlipHor = ((t & flipHoriz) >> 31) == 1 69 tile.FlipVer = ((t & flipVert) >> 30) == 1 70 tile.FlipDiag = ((t & flipDiag) >> 29) == 1 71 t &= ^(flipHoriz | flipVert | flipDiag) // cleanup flags 72 tileset := gamemap.findTileset(t) 73 if tileset == nil { 74 return errors.New("Invalid tile gid: " + string(t)) 75 } 76 t_width := tileset.TileWidth 77 t_height := tileset.TileHeight 78 tile.Gid = t - tileset.FirstGid 79 tile.Options = &ebiten.DrawImageOptions{} 80 // to rotate picture around its center we shift it 81 tile.Options.GeoM.Translate(-float64(t_width)/2, -float64(t_height)/2) 82 if tile.FlipDiag { 83 ret := ebiten.GeoM{} 84 ret.SetElement(0, 0, tile.Options.GeoM.Element(1, 0)) 85 ret.SetElement(0, 1, tile.Options.GeoM.Element(1, 1)) 86 ret.SetElement(0, 2, tile.Options.GeoM.Element(1, 2)) 87 ret.SetElement(1, 0, tile.Options.GeoM.Element(0, 0)) 88 ret.SetElement(1, 1, tile.Options.GeoM.Element(0, 1)) 89 ret.SetElement(1, 2, tile.Options.GeoM.Element(0, 2)) 90 tile.Options.GeoM = ret 91 } 92 if tile.FlipHor { 93 tile.Options.GeoM.Scale(-1, 1) 94 } 95 if tile.FlipVer { 96 tile.Options.GeoM.Scale(1, -1) 97 } 98 // shifting picture back after rotating the matrix 99 tile.Options.GeoM.Translate(float64(t_width)/2, float64(t_height)/2) 100 // setting tile coordinates on the map 101 tile_x := uint32(i) % gamemap.Width 102 tile_y := uint32(i) / gamemap.Width 103 tile.x = float64(tile_x * t_width) 104 tile.y = float64(tile_y * t_height) 105 tile.Options.GeoM.Translate(tile.x, tile.y) 106 sx := int((tile.Gid % tileset.Columns) * t_width) 107 sy := int((tile.Gid / tileset.Columns) * t_height) 108 tile.LayerId = l.Id 109 tile.Image = tileset.Atlas.SubImage(image.Rect(sx, sy, sx+int(t_width), sy+int(t_height))).(*ebiten.Image) 110 // transforming tile coordinates into a single number 111 tile_key := gamemap.GetTileKey(tile_x, tile_y) 112 tiles := gamemap.Tiles[tile_key] 113 gamemap.Tiles[tile_key] = append(tiles, tile) 114 } 115 } 116 117 return nil 118 } 119 120 func (t *Tileset) Init() error { 121 var err error 122 123 t.Atlas, _, err = ebitenutil.NewImageFromFile(assetsDir + t.Image) 124 if err != nil { 125 return err 126 } 127 128 return nil 129 } 130 131 func (m *Gamemap) Init(filename string) error { 132 data, err := os.ReadFile(filename) 133 if err != nil { 134 return err 135 } 136 err = json.Unmarshal(data, m) 137 if err != nil { 138 return err 139 } 140 141 m.Tiles = make(map[uint64][]*Tile) 142 143 for _, tileset := range m.Tilesets { 144 if err := tileset.Init(); err != nil { 145 return err 146 } 147 } 148 149 for _, layer := range m.Layers { 150 if layer.Type == "tilelayer" { 151 if err := layer.Init(m); err != nil { 152 return err 153 } 154 } 155 } 156 157 m.RealWidth = float64(m.TileWidth) * float64(m.Width) 158 m.RealHeight = float64(m.TileHeight) * float64(m.Height) 159 160 return nil 161 } 162 163 func (m *Gamemap) findTileset(gid uint32) *Tileset { 164 var ret *Tileset = nil 165 for _, tileset := range m.Tilesets { 166 if gid >= tileset.FirstGid { 167 if (ret != nil && ret.FirstGid < tileset.FirstGid) || ret == nil { 168 ret = tileset 169 } 170 } 171 } 172 return ret 173 } 174 175 func (m *Gamemap) GetTileKey(x, y uint32) uint64 { 176 // first 4 bytes are x, second 4 bytes - y. 177 var ret uint64 = 0 178 ret |= uint64(x) << 32 179 ret |= uint64(y) 180 return ret 181 } 182 183 func (t *Tile) IsOnScreen(cam *Camera, gamemap *Gamemap) bool { 184 var left float64 = cam.x - float64(gamemap.TileWidth) 185 var right float64 = cam.x + float64(gamemap.TileWidth) + cam.width 186 var top float64 = cam.y - float64(gamemap.TileHeight) 187 var bottom float64 = cam.y + float64(gamemap.TileHeight) + cam.height 188 189 return t.x >= left && t.x < right && t.y >= top && t.y < bottom 190 191 }