-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmain.go
182 lines (151 loc) · 4.63 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package main
import (
"embed"
"fmt"
"image"
_ "image/png"
"log"
"os"
"github.com/Rulox/ebitmx"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
// embeddedFS holds our game assets so we can distribute our game as a single binary
//go:embed map.tmx tileset.tsx overworld.png
var embeddedFS embed.FS
const (
screenWidth = 960
screenHeight = 960
)
type Game struct {
myMap *ebitmx.EbitenMap
tileset *ebitmx.EbitenTileset
atlas *ebiten.Image
currentTileType string
}
func (g *Game) Update() error {
// Set the tile type to "off-screen" the cursor ever goes out of bounds
cx, cy := ebiten.CursorPosition()
if cx < 0 || cy < 0 || cx >= screenWidth || cy >= screenHeight {
g.currentTileType = "off screen"
return nil
}
// Find the id of the tile in the first layer that the cursor is currently over
tx, ty := g.cursorPositionInTileSpace()
id := g.myMap.Layers[0][(ty*g.myMap.MapWidth)+tx]
// Unset tiles have an ID of 0 - handle them
if id == 0 {
g.currentTileType = "unset"
return nil
}
// Only some of our tiles are named (and as a result, only some of them appear in our
// .tsx file), so their position in g.tileset.Tiles[] isn't directly related to their id.
// As such, we have to iterate over the list each time and find the tile we're looking
// for. This could be improved by caching with a map of ids to *Tiles.
for _, t := range g.tileset.Tiles {
if t.Id+1 == id {
g.currentTileType = t.Type
return nil
}
}
// We've come across a tile that asn't explicitly stored in the .tsx
g.currentTileType = "implicit tile - not in .tsx file"
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
// Draw map using the same method as the official tiles example
// https://ebiten.org/examples/tiles.html
// The scaling we use is consistent across all tiles, so we'll
// calculate it outside of the tile-drawing loop
sx := float64(screenWidth / (g.myMap.MapWidth * g.tileset.TileWidth))
sy := float64(screenHeight / (g.myMap.MapHeight * g.tileset.TileHeight))
for _, l := range g.myMap.Layers {
for i, id := range l {
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(
float64((i%g.myMap.MapWidth)*g.myMap.TileWidth),
float64((i/g.myMap.MapHeight)*g.myMap.TileHeight),
)
op.GeoM.Scale(sx, sy)
screen.DrawImage(g.getTileImgByID(id), op)
}
}
cx, cy := ebiten.CursorPosition()
ebitenutil.DebugPrint(
screen,
fmt.Sprintf("cx:%d, cy:%d\ntype: %s\n", cx, cy, g.currentTileType),
)
}
func (g *Game) Layout(ow, oh int) (int, int) {
return ow, oh
}
func (g *Game) getTileImgByID(id int) *ebiten.Image {
// The tsx format starts counting tiles from 1, so to make these calculations
// work correctly, we need to decrement the ID by 1
id -= 1
x0 := (id % g.tileset.TilesetWidth) * g.tileset.TileWidth
y0 := (id / g.tileset.TilesetWidth) * g.tileset.TileHeight
x1, y1 := x0+g.tileset.TileWidth, y0+g.tileset.TileHeight
return g.atlas.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image)
}
// cursorPositionInTileSpace returns the coordinates of the tile the cursor is currently over
func (g *Game) cursorPositionInTileSpace() (int, int) {
cx, cy := ebiten.CursorPosition()
x := cx / (screenWidth / (g.myMap.MapWidth * g.tileset.TileWidth) * g.myMap.TileWidth)
y := cy / (screenHeight / (g.myMap.MapHeight * g.tileset.TileHeight) * g.myMap.TileHeight)
return x, y
}
func main() {
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("Game (Demo)")
var atlas *ebiten.Image
{
imgFile, err := embeddedFS.Open("overworld.png")
if err != nil {
fmt.Println(err)
os.Exit(2)
}
img, _, err := image.Decode(imgFile)
if err != nil {
fmt.Println(err)
os.Exit(2)
}
atlas = ebiten.NewImageFromImage(img)
}
// You can also read an image from your regular filesystem:
// tiles, _, err := ebitenutil.NewImageFromFile("overworld.png")
// if err != nil {
// fmt.Println(err)
// os.Exit(2)
// }
myMap, err := ebitmx.GetEbitenMapFromFS(embeddedFS, "map.tmx")
if err != nil {
fmt.Println(err)
os.Exit(2)
}
// You can also read a map from your regular filesystem:
// myMap, err = ebitmx.GetEbitenMap("map.tmx")
// if err != nil {
// fmt.Println(err)
// os.Exit(2)
// }
tileset, err := ebitmx.GetTilesetFromFS(embeddedFS, "tileset.tsx")
if err != nil {
fmt.Println(err)
os.Exit(2)
}
// You can also read a tileset from your regular filesystem:
// tileset, err = ebitmx.GetEbitenTileset("map.tmx")
// if err != nil {
// fmt.Println(err)
// os.Exit(2)
// }
game := &Game{
myMap: myMap,
tileset: tileset,
atlas: atlas,
}
if err := ebiten.RunGame(game); err != nil {
log.Fatal(err)
}
}