Bunyip a game engine in Go GitHub

Package github.com/matjam/bunyip/timer

timer

Package timer schedules callbacks on game time, after a delay or on every interval. Timers run when the game calls Update, so they pause with the game and stay deterministic.

Advance a Scheduler each update with the step. After runs a function once, Every runs it repeatedly, and both return a handle to Cancel. Countdown is a simpler alternative. Start it, update it, and ask whether it is still Running. Because the time is the game's own, a paused game stops its timers by not calling Update, a replay reruns them identically, and a fast-forward advances them by a larger step. Schedulers are not goroutine-safe and fire on the goroutine that calls Update, the game loop's, so callbacks may touch game state freely.

Index

Types

type Countdown source

type Countdown struct {
	Left float64 // seconds remaining; may become negative on the final Update
}

Countdown is a simple timer a game polls rather than a callback: set it and ask each frame whether it has run out.

Example
package main

import (
	"fmt"

	"github.com/matjam/bunyip/timer"
)

func main() {
	var c timer.Countdown
	c.Start(1)
	for i := 1; ; i++ {
		if c.Update(0.4) {
			fmt.Println("ran out on update", i)
			break
		}
	}
}
Output
ran out on update 3

Running source

func (c *Countdown) Running() bool

Running reports whether time remains.

Start source

func (c *Countdown) Start(seconds float64)

Start sets the countdown.

Update source

func (c *Countdown) Update(dt float64) bool

Update advances the countdown and reports whether it ran out on this update (true once).

type Handle source

type Handle int

Handle identifies a scheduled timer for Cancel.

type Scheduler source

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler runs timers in game time. Its zero value is ready to use.

Example
package main

import (
	"fmt"

	"github.com/matjam/bunyip/timer"
)

func main() {
	var s timer.Scheduler
	s.After(1, func() { fmt.Println("door opens") })
	tick := s.Every(0.5, func() { fmt.Println("tick") })
	// Update from the game loop with the frame's delta; here two big
	// steps. Timers fire in time order, and at the same moment in the
	// order they were scheduled.
	s.Update(0.6)
	s.Update(0.6)
	s.Cancel(tick)
	s.Update(5)
}
Output
tick
door opens
tick

After source

func (s *Scheduler) After(seconds float64, fn func()) Handle

After runs a non-nil fn once, seconds from now. A nonpositive delay becomes due on the next Update, or in the current Update if scheduled by a callback. It does not run fn during After itself.

Cancel source

func (s *Scheduler) Cancel(h Handle)

Cancel stops a timer; cancelling one that already fired is harmless.

Every source

func (s *Scheduler) Every(seconds float64, fn func()) Handle

Every runs fn every interval seconds, starting one interval from now, until cancelled. Use a positive interval for repetition; a nonpositive interval fires once on the next Update.

Now source

func (s *Scheduler) Now() float64

Now is the scheduler's elapsed game time in seconds.

Pending source

func (s *Scheduler) Pending() int

Pending counts scheduled timers that have not fired or been cancelled.

Update source

func (s *Scheduler) Update(dt float64)

Update advances time by dt seconds and fires what is due, earliest first; timers due at the same moment fire in the order they were scheduled. Callbacks may schedule or cancel timers. Use finite, nonnegative dt. Repeating timers fire once for every elapsed interval, including multiple times in a large step. Callbacks must return and must not recursively call Update.

type Sequence source

type Sequence struct {
	// contains filtered or unexported fields
}

Sequence runs steps one after another on game time, the way a coroutine would: wait a while, do something, wait until a condition holds, run a function every update until it says it is done. It is how a cutscene, a turn's animation, a boss's attack pattern or a tutorial is written as a list rather than a state machine:

seq := timer.NewSequence().
	Do(func() { camera.PanTo(door) }).
	Wait(1).
	Until(func() bool { return camera.Arrived() }).
	Do(func() { door.Open() }).
	Run(func(dt float32) bool { return hero.WalkTowards(door, dt) })

The game calls Update each step; Done reports when the last step has finished. Steps run on the goroutine that calls Update.

NewSequence source

func NewSequence() *Sequence

NewSequence starts an empty sequence; add steps with the builder methods, which return the sequence for chaining.

Do source

func (s *Sequence) Do(fn func()) *Sequence

Do runs a function once and moves on in the same update.

Done source

func (s *Sequence) Done() bool

Done reports whether every step has finished; a looping sequence is never done unless Skip was called or it has no steps.

Loop source

func (s *Sequence) Loop() *Sequence

Loop makes the sequence start over when it ends, for patrols and idle behaviours.

Reset source

func (s *Sequence) Reset()

Reset starts the sequence over from its first step.

Run source

func (s *Sequence) Run(fn func(dt float32) bool) *Sequence

Run calls the function every update with the step until it returns true: a movement to carry out, an animation to play through.

Skip source

func (s *Sequence) Skip()

Skip jumps to the end, for a player who presses through a cutscene; Do steps that have not run yet do not run.

Until source

func (s *Sequence) Until(cond func() bool) *Sequence

Until waits, checking each update, until the condition holds.

Update source

func (s *Sequence) Update(dt float32) bool

Update advances the sequence by dt seconds, running as many steps as finish within it. It reports whether the sequence is done. Wait carries leftover time into following steps. A completed Run consumes the entire remaining step, so later Run steps in the same call receive zero dt. Use finite, nonnegative dt and do not call Update recursively from a step.

Wait source

func (s *Sequence) Wait(seconds float32) *Sequence

Wait pauses for a number of seconds.

Source files

bench_test.go example_test.go review_test.go sequence.go sequence_test.go timer.go timer_test.go