State Machine for game development in GoLang
Tags: go, gamedev, statemachine
Published: 2020-01-01 08:10:19

Recently I started learning **Go programming language** and wanted to build a 2D game. So I choose [pixel](https://github.com/faiface/pixel) a GoLang 2D game library. Below post describes my learning process.

The pixel lib's examples are really good for learning Go and Pixel. I made this map using Tiled tilemap editor and loaded the .tmx files with [tilepix](github.com/bcvery1/tilepix), earlier I was using go-tmx.

<img width="510" alt="GoLang 2D game" src="https://user-images.githubusercontent.com/3184210/71617110-cb6bb880-2bb9-11ea-9c6c-b6716bfacebb.png">

_tl;dr;_
__I wanted to implement my characters different walk and wait states. But Wanted something easy to maintain and easy to manage the memory. Below is a my take on State Machine implementation with Go__

p.s. StateMachine.go is at the bottom of article. Github [Source](https://github.com/steelx/golang-rpg-basic)


{% youtube yZpoXFUG6nc %}

As I was looking for some solutions, I started with Graph nodes. https://github.com/steelx/go-graph-traversing/blob/master/main.go


And did a simple implementation with Nodes (a simple text based game with Go): https://github.com/steelx/go-story-mode

```
type Choice struct {
	cmd         string
	description string
	nextNode    *StoryNode
}

type StoryNode struct {
	Text    string
	choices []*Choice
}
```


But I wanted to make a 2D old school type RPG game. Which is still a work in progress. You can see below work so far, using State Machine in Go.


<img width="600" alt="GoLang 2D game" src="https://user-images.githubusercontent.com/3184210/71634721-a2c9da00-2c1e-11ea-9531-edb7fe10657b.gif">

When we move the character he jumps from tile to tile.

My character has different frames for animations, but Wanted to keep my `Update()` loop clean so following code does not contain any code regarding Animations and Tween.

The following solution is using State Machine for my game in GO. So our game hero character will have an Entity (Entity can be any character, or NPC) and a Controller (state-machine)


##Our Character Object
```
type Character struct {
	mEntity     *Entity
	mController *StateMachine
}
```


My `setup()` is below *(Setup runs outside of the Pixel FOR loop, gets called inside the `Run` function [pixel Run guide](https://github.com/faiface/pixel-examples/blob/master/guide/01_creating_a_window/main.go) )*
```go
gHero = Character{
		mEntity: CreateEntity(CharacterDefinition{
			texture: pic, width: 16, height: 24,
			startFrame: 1,
			tileX:      9,
			tileY:      2,
		}),
		mController: StateMachineCreate(
			map[string]func() State{
				"wait": func() State {
					return WaitStateCreate(gHero, *CastleRoomMap)
				},
				"move": func() State {
					return MoveStateCreate(gHero, *CastleRoomMap)
				},
			},
		),
	}

// Give game Hero an Initial state
gHero.mController.Change("wait", Direction{0, 0})
```

`gHero` is defined outside `func main` to be able to declare it as Global.


The state machine is made of two states, `move` and `wait,` as shown below. Input from the user is only checked during the `wait` state.


![state-machine-logic](https://user-images.githubusercontent.com/3184210/71631226-dd744800-2c07-11ea-94c7-0347766e9536.png)




The StateMachine type requires each state to have four functions: `Enter, Exit, Render and Update.` But to keep 2 different return types; thats possible with Interface. Instead of specific `struct` as return type, we can specify `State interface` as return type for our `mController` which is a State Machine.


##State Interface
```go
type State interface {
	Enter(data Direction)
	Render()
	Exit()
	Update(dt float64)
}

```



The state machine uses two state types, `WaitState` and `MoveState`. We need to implement both of these types but let’s start with the **WaitState**.


**state_machine_wait_state.go**
```go
package main

import "github.com/faiface/pixel/pixelgl"

type WaitState struct {
	mCharacter  Character
	mMap        GameMap
	mEntity     *Entity
	mController *StateMachine
}

func WaitStateCreate(character Character, gMap GameMap) State {
	s := &WaitState{}
	s.mCharacter = character
	s.mMap = gMap
	s.mEntity = character.mEntity
	s.mController = character.mController
	return s
}


//State interface implemented below
func (s *WaitState) Enter(data Direction) {
	// Reset to default frame
	s.mEntity.SetFrame(s.mEntity.startFrame)
}

func (s *WaitState) Render() {
	//pixelgl renderer
}

func (s *WaitState) Exit() {}

func (s *WaitState) Update(dt float64) {
	if global.gWin.JustPressed(pixelgl.KeyLeft) {
		s.mController.Change("move", Direction{-1, 0})
	}
	if global.gWin.JustPressed(pixelgl.KeyRight) {
		s.mController.Change("move", Direction{1, 0})
	}
	if global.gWin.JustPressed(pixelgl.KeyDown) {
		s.mController.Change("move", Direction{0, 1})
	}
	if global.gWin.JustPressed(pixelgl.KeyUp) {
		s.mController.Change("move", Direction{0, -1})
	}
}


```

The **WaitState**, shown in above, really does only one thing; it waits until an arrow `key` has been pressed and then changes the state to the **MoveState**, passing along the direction the player wants to move.

```go
type Direction struct {
	x, y float64
}
```


The `OnEnter` function for the **WaitState** resets the entity frame back to its original starting frame. This means if we tell a character to wait and they’re mid-run, they don’t stay stuck mid-run; instead they revert to the default standing frame.


In `WaitState`'s `Update` func calls with an x and y field is passed into the Change function when the state changes. This data tells the `MoveState` which direction we want the player to move. A figure showing the movement offsets can be seen below;

![direction](https://user-images.githubusercontent.com/3184210/71620023-c9a8f180-2bc7-11ea-870d-7c0248a4ccd7.png)




##Let’s implement the *MoveState*


The MoveState has a bit more going on than the WaitState.

**state_machine_move_state.go**
```go
package main

type MoveState struct {
	mCharacter  Character
	mMap        GameMap
	mEntity     *Entity
	mController *StateMachine
	// ^above common with WaitState
	mTileWidth       float64
	mMoveX, mMoveY   float64
	mPixelX, mPixelY float64
	mMoveSpeed       float64
}

func MoveStateCreate(character Character, gMap GameMap) State {
	s := &MoveState{}
	s.mCharacter = character
	s.mMap = gMap
	s.mTileWidth = gMap.mTileWidth
	s.mEntity = character.mEntity
	s.mController = character.mController
	s.mMoveX = 0
	s.mMoveY = 0
	//Additional motion tween can be added here e.g. 
        //s.mTween = TweenCreate(0, 0, 1),
	s.mMoveSpeed = 0.3
	return s
}

//StateMachine requires each state to have
// four functions: Enter, Exit, Render and Update

func (s *MoveState) Enter(data Direction) {
	//save Move X,Y value to used inside Update call
	s.mMoveX = data.x
	s.mMoveY = data.y
	s.mPixelX = s.mEntity.mTileX
	s.mPixelY = s.mEntity.mTileY
	//s.mTween = TweenCreate(0, 1, s.mMoveSpeed)
}

func (s *MoveState) Exit() {
	s.mEntity.TeleportAndDraw(s.mMap)
}

func (s *MoveState) Render() {
	//pending
}

func (s *MoveState) Update(dt float64) {
	//s.mTween.Update(dt)
	//update tween if any

	x := s.mPixelX + s.mMoveX
	y := s.mPixelY + s.mMoveY
	s.mEntity.mTileX = x
	s.mEntity.mTileY = y

	//If you have implemented tween animation
	// change the state here once its finished
	//Change("wait")

	s.mController.Change("wait", Direction{0, 0})
}

```

At `Enter()` function we receive the Direction and keep a copy of direction; and set the next tile position
```go
s.mMoveX = data.x
s.mMoveY = data.y
```

`Exit()` function reads those tile position and draws the character to next tileX and tileY position.

In short;
Moving from the `MoveState` to the `WaitState` causes the state machine to call the `MoveState.Exit()` function. In the exit function we update the entity tile position and teleport to that position.



**state_machine.go**
```go
package main

/*
    mController :
	StateMachineCreate({
		"wait" : func() return WaitStateCreate(Entity, GameMap),
		"move" : func() return MoveStateCreate(Entity, GameMap),
	})
*/

//StateMachine aka mController
type StateMachine struct {
	states  map[string]func() State
	current State
}

func StateMachineCreate(states map[string]func() State) *StateMachine {
	return &StateMachine{
		states:  states,
		current: nil,
	}
}

//Change state
// e.g. mController.Change("move", {x = -1, y = 0})
func (m *StateMachine) Change(stateName string, enterParams Direction) {
	if m.current != nil {
		m.current.Exit()
	}
	m.current = m.states[stateName]()
	m.current.Enter(enterParams) //thinking.. pass enterParams
}

//dt here is Delta time
func (m *StateMachine) Update(dt float64) {
	m.current.Update(dt)
}

func (m *StateMachine) Render() {
	m.current.Render()
}

```

I hope my post will help you in implementation of State Machine in Go, you can even use this code in your animation projects for game. It's upto you now how to move ahead with the above code, good luck.


{% youtube SQApg6oDkHM %}