263 lines
6.2 KiB
Go
263 lines
6.2 KiB
Go
package scenes
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
_ "image/jpeg"
|
|
_ "image/png"
|
|
"log"
|
|
|
|
"mountain/internal/entities"
|
|
|
|
"github.com/go-gl/mathgl/mgl64"
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
)
|
|
|
|
const (
|
|
Gravity = 0.5
|
|
JumpForce = -8.0
|
|
MoveSpeed = 4.0
|
|
ViewSize = 380 // 800 width, we have 2 viewports (380 each with 20 padding)
|
|
)
|
|
|
|
type Player struct {
|
|
Pos mgl64.Vec3 // 3D Kordinater: X (vänster/höger), Y (upp/ner), Z (djup)
|
|
Vel mgl64.Vec3 // Hastighet
|
|
|
|
Width, Height float64 // För (AABB)
|
|
IsGrounded bool
|
|
|
|
// Sprites
|
|
SpriteRun *ebiten.Image
|
|
SpriteIdle *ebiten.Image
|
|
SpriteTop *ebiten.Image
|
|
AnimationFrame int
|
|
AnimationCounter int
|
|
|
|
FacingRight bool
|
|
}
|
|
|
|
type PlayScene struct {
|
|
world *entities.World
|
|
player *Player
|
|
leftView *ebiten.Image
|
|
rightView *ebiten.Image
|
|
}
|
|
|
|
func NewPlayScene() *PlayScene {
|
|
w := entities.NewWorld(10, 10, 10)
|
|
|
|
tile12, _, _ := ebitenutil.NewImageFromFile("assets/images/1 Tiles/Tile_12.png")
|
|
tile02, _, _ := ebitenutil.NewImageFromFile("assets/images/1 Tiles/Tile_02.png")
|
|
tile31, _, _ := ebitenutil.NewImageFromFile("assets/images/1 Tiles/Tile_31.png")
|
|
|
|
for x := 0; x < 10; x++ {
|
|
for z := 0; z < 10; z++ {
|
|
// Surface grass
|
|
w.SetEntityAt(x, 9, z, &entities.Tile{
|
|
SideImg: tile02,
|
|
TopImg: tile31,
|
|
})
|
|
// Sub dirt
|
|
w.SetEntityAt(x, 8, z, &entities.Tile{
|
|
SideImg: tile12,
|
|
TopImg: tile12,
|
|
})
|
|
}
|
|
}
|
|
|
|
pRun, _, err := ebitenutil.NewImageFromFile("assets/images/Warrior_1/Run.png")
|
|
if err != nil {
|
|
log.Println("Run sprite error:", err)
|
|
pRun = ebiten.NewImage(96, 96)
|
|
}
|
|
pIdle, _, _ := ebitenutil.NewImageFromFile("assets/images/Warrior_1/Idle.png")
|
|
if pIdle == nil {
|
|
pIdle = pRun
|
|
}
|
|
|
|
pTop := ebiten.NewImage(32, 32)
|
|
pTop.Fill(color.RGBA{255, 255, 0, 255})
|
|
|
|
player := &Player{
|
|
Pos: mgl64.Vec3{4.0 * entities.TileSize, 7.0 * entities.TileSize, 4.0 * entities.TileSize},
|
|
Width: 20,
|
|
Height: 40,
|
|
SpriteRun: pRun,
|
|
SpriteIdle: pIdle,
|
|
SpriteTop: pTop,
|
|
FacingRight: true,
|
|
}
|
|
|
|
return &PlayScene{
|
|
world: w,
|
|
player: player,
|
|
leftView: ebiten.NewImage(ViewSize, ViewSize),
|
|
rightView: ebiten.NewImage(ViewSize, ViewSize),
|
|
}
|
|
}
|
|
|
|
func (s *PlayScene) Update() error {
|
|
p := s.player
|
|
|
|
p.Vel[0] = 0
|
|
p.Vel[2] = 0
|
|
isMoving := false
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeyLeft) || ebiten.IsKeyPressed(ebiten.KeyA) {
|
|
p.Vel[0] = -MoveSpeed
|
|
p.FacingRight = false
|
|
isMoving = true
|
|
}
|
|
if ebiten.IsKeyPressed(ebiten.KeyRight) || ebiten.IsKeyPressed(ebiten.KeyD) {
|
|
p.Vel[0] = MoveSpeed
|
|
p.FacingRight = true
|
|
isMoving = true
|
|
}
|
|
if ebiten.IsKeyPressed(ebiten.KeyUp) || ebiten.IsKeyPressed(ebiten.KeyW) {
|
|
p.Vel[2] = -MoveSpeed
|
|
isMoving = true
|
|
}
|
|
if ebiten.IsKeyPressed(ebiten.KeyDown) || ebiten.IsKeyPressed(ebiten.KeyS) {
|
|
p.Vel[2] = MoveSpeed
|
|
isMoving = true
|
|
}
|
|
|
|
if ebiten.IsKeyPressed(ebiten.KeySpace) && p.IsGrounded {
|
|
p.Vel[1] = JumpForce
|
|
p.IsGrounded = false
|
|
}
|
|
|
|
if !p.IsGrounded {
|
|
p.Vel[1] += Gravity
|
|
}
|
|
|
|
nextX := p.Pos[0] + p.Vel[0]
|
|
nextY := p.Pos[1] + p.Vel[1]
|
|
nextZ := p.Pos[2] + p.Vel[2]
|
|
|
|
gridY := int((nextY + p.Height) / entities.TileSize)
|
|
gridX := int((nextX + p.Width/2) / entities.TileSize)
|
|
gridZ := int((nextZ + p.Width/2) / entities.TileSize)
|
|
|
|
entY := s.world.GetEntityAt(gridX, gridY, gridZ)
|
|
if entY != nil && entY.IsBlocking() && p.Vel[1] >= 0 {
|
|
nextY = float64(gridY)*entities.TileSize - p.Height - 0.1
|
|
p.Vel[1] = 0
|
|
p.IsGrounded = true
|
|
} else {
|
|
p.IsGrounded = false
|
|
}
|
|
|
|
p.Pos[0] = nextX
|
|
p.Pos[1] = nextY
|
|
p.Pos[2] = nextZ
|
|
|
|
if isMoving {
|
|
p.AnimationCounter++
|
|
if p.AnimationCounter > 5 {
|
|
p.AnimationFrame = (p.AnimationFrame + 1) % 6
|
|
p.AnimationCounter = 0
|
|
}
|
|
} else {
|
|
p.AnimationFrame = 0
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *PlayScene) Draw(screen *ebiten.Image) {
|
|
screen.Fill(color.RGBA{20, 20, 20, 255})
|
|
|
|
s.leftView.Fill(color.RGBA{135, 206, 235, 255})
|
|
s.rightView.Fill(color.RGBA{50, 150, 50, 255})
|
|
|
|
playerGridZ := int((s.player.Pos[2] + s.player.Width/2) / entities.TileSize)
|
|
playerGridY := int((s.player.Pos[1] + s.player.Height/2) / entities.TileSize)
|
|
|
|
camX := s.player.Pos[0] - float64(ViewSize)/2.0
|
|
camY := s.player.Pos[1] - float64(ViewSize)/2.0
|
|
camZ := s.player.Pos[2] - float64(ViewSize)/2.0
|
|
|
|
// Vänster vy: Sido-vy (renderar alla X och Y objekt vid spelarens Z)
|
|
for x := 0; x < s.world.Width; x++ {
|
|
for y := 0; y < s.world.Height; y++ {
|
|
ent := s.world.GetEntityAt(x, y, playerGridZ)
|
|
if ent != nil {
|
|
px := float64(x)*entities.TileSize - camX
|
|
py := float64(y)*entities.TileSize - camY
|
|
ent.DrawSide(s.leftView, px, py)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Höger vy: Ovan-vy (renderar alla X och Z objekt vid underliggande rad)
|
|
viewY := playerGridY
|
|
if s.player.IsGrounded {
|
|
viewY += 1
|
|
}
|
|
|
|
for x := 0; x < s.world.Width; x++ {
|
|
for z := 0; z < s.world.Depth; z++ {
|
|
ent := s.world.GetEntityAt(x, viewY, z)
|
|
if ent != nil {
|
|
px := float64(x)*entities.TileSize - camX
|
|
pz := float64(z)*entities.TileSize - camZ
|
|
ent.DrawTop(s.rightView, px, pz)
|
|
}
|
|
}
|
|
}
|
|
|
|
s.drawPlayerSide(s.leftView, camX, camY)
|
|
s.drawPlayerTop(s.rightView, camX, camZ)
|
|
|
|
opL := &ebiten.DrawImageOptions{}
|
|
opL.GeoM.Translate(10, 100)
|
|
screen.DrawImage(s.leftView, opL)
|
|
|
|
opR := &ebiten.DrawImageOptions{}
|
|
opR.GeoM.Translate(410, 100)
|
|
screen.DrawImage(s.rightView, opR)
|
|
|
|
ebitenutil.DebugPrint(screen, "VÄNSTER (Sido vy) HÖGER (Top vy: Slice av din Hojd)")
|
|
}
|
|
|
|
func (s *PlayScene) drawPlayerSide(screen *ebiten.Image, camX, camY float64) {
|
|
p := s.player
|
|
|
|
sx := p.AnimationFrame * 96
|
|
srcRect := image.Rect(sx, 0, sx+96, 96)
|
|
var activeSprite *ebiten.Image
|
|
|
|
if p.Vel[0] != 0 || p.Vel[2] != 0 { // Is moving X or Z
|
|
activeSprite = p.SpriteRun
|
|
} else {
|
|
activeSprite = p.SpriteIdle
|
|
}
|
|
|
|
subImg := activeSprite.SubImage(srcRect).(*ebiten.Image)
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
if !p.FacingRight {
|
|
op.GeoM.Scale(-1, 1)
|
|
op.GeoM.Translate(96, 0)
|
|
}
|
|
|
|
drawX := p.Pos[0] - camX - 38
|
|
drawY := p.Pos[1] - camY - 56
|
|
|
|
op.GeoM.Translate(drawX, drawY)
|
|
screen.DrawImage(subImg, op)
|
|
}
|
|
|
|
func (s *PlayScene) drawPlayerTop(screen *ebiten.Image, camX, camZ float64) {
|
|
p := s.player
|
|
op := &ebiten.DrawImageOptions{}
|
|
|
|
drawX := p.Pos[0] - camX
|
|
drawZ := p.Pos[2] - camZ
|
|
op.GeoM.Translate(drawX, drawZ)
|
|
screen.DrawImage(p.SpriteTop, op)
|
|
}
|