Deterministic JavaScript intervals + Code Cleanup
* Remove several unused functions in doodad.Drawing (velocity, acceleration, grounded, etc.) - uix.Actor is where these are actually managed. * In the JavaScript API, setTimeout() and setInterval() will translate the milliseconds from wallclock time into a fixed number of game ticks to match the target frame rate for better deterministic timing.
This commit is contained in:
parent
c4456ac51b
commit
866e5e7fd8
|
@ -15,6 +15,12 @@ const (
|
||||||
FormatZipfile // v2: zip archive with external chunks
|
FormatZipfile // v2: zip archive with external chunks
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Target frame rate for the game (also ticks per second for logic).
|
||||||
|
const (
|
||||||
|
TargetFPS = 60
|
||||||
|
TargetClockRate = 1000 / 60 // wallclock milliseconds per tick
|
||||||
|
)
|
||||||
|
|
||||||
// Numbers.
|
// Numbers.
|
||||||
var (
|
var (
|
||||||
// Window dimensions.
|
// Window dimensions.
|
||||||
|
|
|
@ -41,52 +41,14 @@ func (d *Drawing) Position() render.Point {
|
||||||
return d.point
|
return d.point
|
||||||
}
|
}
|
||||||
|
|
||||||
// Velocity returns the Drawing's velocity.
|
|
||||||
func (d *Drawing) Velocity() render.Point {
|
|
||||||
return d.velocity
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVelocity to set the speed.
|
|
||||||
func (d *Drawing) SetVelocity(v render.Point) {
|
|
||||||
d.velocity = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Acceleration returns the Drawing's acceleration.
|
|
||||||
func (d *Drawing) Acceleration() int {
|
|
||||||
return d.accel
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetAcceleration to set the acceleration.
|
|
||||||
func (d *Drawing) SetAcceleration(v int) {
|
|
||||||
d.accel = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the Drawing's size.
|
// Size returns the Drawing's size.
|
||||||
func (d *Drawing) Size() render.Rect {
|
func (d *Drawing) Size() render.Rect {
|
||||||
return d.size
|
return d.size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grounded returns whether the Drawing is standing on solid ground.
|
|
||||||
func (d *Drawing) Grounded() bool {
|
|
||||||
return d.grounded
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetGrounded sets the grounded state.
|
|
||||||
func (d *Drawing) SetGrounded(v bool) {
|
|
||||||
d.grounded = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// MoveBy a relative value.
|
|
||||||
func (d *Drawing) MoveBy(by render.Point) {
|
|
||||||
d.point.Add(by)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MoveTo an absolute world value.
|
// MoveTo an absolute world value.
|
||||||
|
//
|
||||||
|
// NOTE: used only by unit test.
|
||||||
func (d *Drawing) MoveTo(to render.Point) {
|
func (d *Drawing) MoveTo(to render.Point) {
|
||||||
d.point = to
|
d.point = to
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the drawing.
|
|
||||||
func (d *Drawing) Draw(e render.Engine) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,9 +29,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TargetFPS is the frame rate to cap the game to.
|
|
||||||
TargetFPS = 1000 / 60 // 60 FPS
|
|
||||||
|
|
||||||
// Millisecond64 is a time.Millisecond casted to float64.
|
// Millisecond64 is a time.Millisecond casted to float64.
|
||||||
Millisecond64 = float64(time.Millisecond)
|
Millisecond64 = float64(time.Millisecond)
|
||||||
)
|
)
|
||||||
|
@ -230,8 +227,8 @@ func (d *Doodle) Run() error {
|
||||||
if !fpsDoNotCap {
|
if !fpsDoNotCap {
|
||||||
elapsed := time.Now().Sub(start)
|
elapsed := time.Now().Sub(start)
|
||||||
tmp := elapsed / time.Millisecond
|
tmp := elapsed / time.Millisecond
|
||||||
if TargetFPS-int(tmp) > 0 { // make sure it won't roll under
|
if balance.TargetClockRate-int(tmp) > 0 { // make sure it won't roll under
|
||||||
delay = uint32(TargetFPS - int(tmp))
|
delay = uint32(balance.TargetClockRate - int(tmp))
|
||||||
}
|
}
|
||||||
d.Engine.Delay(delay)
|
d.Engine.Delay(delay)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package scripting
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||||
|
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,8 +12,8 @@ import (
|
||||||
type Timer struct {
|
type Timer struct {
|
||||||
id int
|
id int
|
||||||
callback goja.Value
|
callback goja.Value
|
||||||
interval time.Duration // milliseconds delay for timeout
|
ticks uint64 // interval (milliseconds) converted into game ticks
|
||||||
next time.Time // scheduled time for next invocation
|
nextTick uint64 // next tick to trigger the callback
|
||||||
repeat bool // for setInterval
|
repeat bool // for setInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,12 +49,16 @@ AddTimer loads timeouts and intervals into the VM's memory and returns the ID.
|
||||||
func (vm *VM) AddTimer(callback goja.Value, interval int, repeat bool) int {
|
func (vm *VM) AddTimer(callback goja.Value, interval int, repeat bool) int {
|
||||||
// Get the next timer ID. The first timer has ID 1.
|
// Get the next timer ID. The first timer has ID 1.
|
||||||
vm.timerLastID++
|
vm.timerLastID++
|
||||||
id := vm.timerLastID
|
|
||||||
|
var (
|
||||||
|
id = vm.timerLastID
|
||||||
|
ticks = float64(interval) * (float64(balance.TargetFPS) / 1000)
|
||||||
|
)
|
||||||
|
|
||||||
t := &Timer{
|
t := &Timer{
|
||||||
id: id,
|
id: id,
|
||||||
callback: callback,
|
callback: callback,
|
||||||
interval: time.Duration(interval),
|
ticks: uint64(ticks),
|
||||||
repeat: repeat,
|
repeat: repeat,
|
||||||
}
|
}
|
||||||
t.Schedule()
|
t.Schedule()
|
||||||
|
@ -71,7 +77,7 @@ func (vm *VM) TickTimer(now time.Time) {
|
||||||
var clear []int
|
var clear []int
|
||||||
|
|
||||||
for id, timer := range vm.timers {
|
for id, timer := range vm.timers {
|
||||||
if now.After(timer.next) {
|
if shmem.Tick > timer.nextTick {
|
||||||
if function, ok := goja.AssertFunction(timer.callback); ok {
|
if function, ok := goja.AssertFunction(timer.callback); ok {
|
||||||
function(goja.Undefined())
|
function(goja.Undefined())
|
||||||
}
|
}
|
||||||
|
@ -104,5 +110,5 @@ func (vm *VM) ClearTimer(id int) {
|
||||||
|
|
||||||
// Schedule the callback to be run in the future.
|
// Schedule the callback to be run in the future.
|
||||||
func (t *Timer) Schedule() {
|
func (t *Timer) Schedule() {
|
||||||
t.next = time.Now().Add(t.interval * time.Millisecond)
|
t.nextTick = shmem.Tick + t.ticks
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user