pixel-examples/community/tilemap/main.go

155 lines
3.4 KiB
Go

package main
import (
"fmt"
"image/png"
"math"
"os"
"time"
"golang.org/x/image/colornames"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
"github.com/salviati/go-tmx/tmx"
)
var clearColor = colornames.Skyblue
func gameloop(win *pixelgl.Window, tilemap *tmx.Map) {
batches := make(map[string]*pixel.Batch)
// Load the sprites
sprites := make(map[string]*pixel.Sprite)
for _, tileset := range tilemap.Tilesets {
if _, alreadyLoaded := sprites[tileset.Image.Source]; !alreadyLoaded {
sprite, pictureData := loadSprite(tileset.Image.Source)
sprites[tileset.Image.Source] = sprite
batches[tileset.Image.Source] = pixel.NewBatch(&pixel.TrianglesData{}, pictureData)
}
}
var (
camPos = pixel.ZV
camSpeed = 1000.0
camZoom = 0.2
camZoomSpeed = 1.2
)
last := time.Now()
for !win.Closed() {
dt := time.Since(last).Seconds()
last = time.Now()
// Camera movement
cam := pixel.IM.Scaled(camPos, camZoom).Moved(win.Bounds().Center().Sub(camPos))
win.SetMatrix(cam)
if win.Pressed(pixelgl.KeyLeft) {
camPos.X -= camSpeed * dt
}
if win.Pressed(pixelgl.KeyRight) {
camPos.X += camSpeed * dt
}
if win.Pressed(pixelgl.KeyDown) {
camPos.Y -= camSpeed * dt
}
if win.Pressed(pixelgl.KeyUp) {
camPos.Y += camSpeed * dt
}
camZoom *= math.Pow(camZoomSpeed, win.MouseScroll().Y)
win.Clear(clearColor)
// Draw tiles
for _, batch := range batches {
batch.Clear()
}
for _, layer := range tilemap.Layers {
for tileIndex, tile := range layer.DecodedTiles {
ts := layer.Tileset
tID := int(tile.ID)
if tID == 0 {
// Tile ID 0 means blank, skip it.
continue
}
// Calculate the framing for the tile within its tileset's source image
numRows := ts.Tilecount / ts.Columns
x, y := tileIDToCoord(tID, ts.Columns, numRows)
gamePos := indexToGamePos(tileIndex, tilemap.Width, tilemap.Height)
iX := float64(x) * float64(ts.TileWidth)
fX := iX + float64(ts.TileWidth)
iY := float64(y) * float64(ts.TileHeight)
fY := iY + float64(ts.TileHeight)
sprite := sprites[ts.Image.Source]
sprite.Set(sprite.Picture(), pixel.R(iX, iY, fX, fY))
pos := gamePos.ScaledXY(pixel.V(float64(ts.TileWidth), float64(ts.TileHeight)))
sprite.Draw(batches[ts.Image.Source], pixel.IM.Moved(pos))
}
}
for _, batch := range batches {
batch.Draw(win)
}
win.Update()
}
}
func tileIDToCoord(tID int, numColumns int, numRows int) (x int, y int) {
x = tID % numColumns
y = numRows - (tID / numColumns) - 1
return
}
func indexToGamePos(idx int, width int, height int) pixel.Vec {
gamePos := pixel.V(
float64(idx%width)-1,
float64(height)-float64(idx/width),
)
return gamePos
}
func run() {
// Create the window with OpenGL
cfg := pixelgl.WindowConfig{
Title: "Pixel Tilemaps",
Bounds: pixel.R(0, 0, 800, 600),
VSync: true,
}
win, err := pixelgl.NewWindow(cfg)
panicIfErr(err)
// Initialize art assets (i.e. the tilemap)
tilemap, err := tmx.ReadFile("gameart2d-desert.tmx")
panicIfErr(err)
fmt.Println("use WASD to move camera around")
gameloop(win, tilemap)
}
func loadSprite(path string) (*pixel.Sprite, *pixel.PictureData) {
f, err := os.Open(path)
panicIfErr(err)
img, err := png.Decode(f)
panicIfErr(err)
pd := pixel.PictureDataFromImage(img)
return pixel.NewSprite(pd, pd.Bounds()), pd
}
func main() {
pixelgl.Run(run)
}
func panicIfErr(err error) {
if err != nil {
panic(err)
}
}