Fat Old Yeti

Fat Old Yeti

Being a blog of thoughts and tutorials from a hobby game developer.

06 Feb 2021

Roguelike Tutorial 4

Roguelike in Go - Part 4 (Collisions)

The code for this tutorial can be found here.

Now that we have the player on the screen and moving, perhaps we should make sure they can’t run through walls. In addition, let’s refactor our Map a little, just to have better code.

To start out, let’s edit map.go and add our CurrentLevel to GameMap.

type GameMap struct {
	Dungeons []Dungeon
	CurrentLevel Level
}

Now update our constructor function to change this:

gm := GameMap{Dungeons: dungeons}

to this

gm := GameMap{Dungeons: dungeons, CurrentLevel: l}

Go to main.go and find the Draw function. Change this:

level := g.Map.Dungeons[0].Levels[0]

to this:

level := g.Map.CurrentLevel

Now if we add more levels later, we don’t have to hunt and peck to find all the shenanigans we left behind.

I thought we were doing collision detection…

Of course. I guess it’s time we got to that. Let’s change the TryMovePlayer function in player_move_system.go.

func TryMovePlayer(g *Game) {
	players := g.WorldTags["players"]
	x := 0
	y := 0
	if ebiten.IsKeyPressed(ebiten.KeyUp) {
		y = -1
	}
	if ebiten.IsKeyPressed(ebiten.KeyDown) {
		y = 1
	}
	if ebiten.IsKeyPressed(ebiten.KeyLeft) {
		x = -1
	}
	if ebiten.IsKeyPressed(ebiten.KeyRight) {
		x = 1
	} 

	level := g.Map.CurrentLevel

	for _, result := range g.World.Query(players) {
		pos := result.Components[position].(*Position)
		index := level.GetIndexFromXY(pos.X+x, pos.Y+y)
		  
		tile := level.Tiles[index]
		if tile.Blocked != true {
			pos.X += x
			pos.Y += y
		}
	}
}

You will notice that the only real change here is that we get the current level (now that refactor comes in handy) and get the tile we want to move into. If that tile is marked as blocked, we do not move into it. That’s it! Now if you run the application, you will be stopped by the walls on the outer edges of the map.

Of course, you still move way too fast for a turn-based game. Let’s just build some states really quick so that we can attend to that. Create a file called turnstate.go and add the following:

package main

type TurnState int

const (
	BeforePlayerAction = iota
	PlayerTurn
	MonsterTurn
)

func GetNextState(state TurnState) TurnState {
	switch state {
		case BeforePlayerAction:
			return PlayerTurn
		case PlayerTurn:
			return MonsterTurn
		case MonsterTurn:
			return BeforePlayerAction
		default:
			return PlayerTurn
	}
}

All we have done here is build a simple…very simple..state machine for turns. We will use this later on.

Now you have some better code and you can make the player collide with walls. As always, if you have any questions, feel free to contact me a fatoldyeti@gmail.com or @idiotcoder on the gopher slack.