zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

clock.go (3559B)


      1 // Copyright 2017 The Ebiten Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 // Package clock manages game timers.
     16 package clock
     17 
     18 import (
     19 	"sync"
     20 	"time"
     21 )
     22 
     23 var (
     24 	lastNow int64
     25 
     26 	// lastSystemTime is the last system time in the previous Update.
     27 	// lastSystemTime indicates the logical time in the game, so this can be bigger than the curren time.
     28 	lastSystemTime int64
     29 
     30 	currentFPS  float64
     31 	currentTPS  float64
     32 	lastUpdated int64
     33 	fpsCount    = 0
     34 	tpsCount    = 0
     35 
     36 	m sync.Mutex
     37 )
     38 
     39 func init() {
     40 	n := now()
     41 	lastNow = n
     42 	lastSystemTime = n
     43 	lastUpdated = n
     44 }
     45 
     46 func CurrentFPS() float64 {
     47 	m.Lock()
     48 	v := currentFPS
     49 	m.Unlock()
     50 	return v
     51 }
     52 
     53 func CurrentTPS() float64 {
     54 	m.Lock()
     55 	v := currentTPS
     56 	m.Unlock()
     57 	return v
     58 }
     59 
     60 func max(a, b int64) int64 {
     61 	if a < b {
     62 		return b
     63 	}
     64 	return a
     65 }
     66 
     67 func calcCountFromTPS(tps int64, now int64) int {
     68 	if tps == 0 {
     69 		return 0
     70 	}
     71 	if tps < 0 {
     72 		panic("clock: tps must >= 0")
     73 	}
     74 
     75 	diff := now - lastSystemTime
     76 	if diff < 0 {
     77 		return 0
     78 	}
     79 
     80 	count := 0
     81 	syncWithSystemClock := false
     82 
     83 	// Detect whether the previous time is too old.
     84 	// Use either 5 ticks or 5/60 sec in the case when TPS is too big like 300 (#1444).
     85 	if diff > max(int64(time.Second)*5/tps, int64(time.Second)*5/60) {
     86 		// The previous time is too old.
     87 		// Let's force to sync the game time with the system clock.
     88 		syncWithSystemClock = true
     89 	} else {
     90 		count = int(diff * tps / int64(time.Second))
     91 	}
     92 
     93 	// Stabilize the count.
     94 	// Without this adjustment, count can be unstable like 0, 2, 0, 2, ...
     95 	// TODO: Brush up this logic so that this will work with any FPS. Now this works only when FPS = TPS.
     96 	if count == 0 && (int64(time.Second)/tps/2) < diff {
     97 		count = 1
     98 	}
     99 	if count == 2 && (int64(time.Second)/tps*3/2) > diff {
    100 		count = 1
    101 	}
    102 
    103 	if syncWithSystemClock {
    104 		lastSystemTime = now
    105 	} else {
    106 		lastSystemTime += int64(count) * int64(time.Second) / tps
    107 	}
    108 
    109 	return count
    110 }
    111 
    112 func updateFPSAndTPS(now int64, count int) {
    113 	fpsCount++
    114 	tpsCount += count
    115 	if now < lastUpdated {
    116 		panic("clock: lastUpdated must be older than now")
    117 	}
    118 	if time.Second > time.Duration(now-lastUpdated) {
    119 		return
    120 	}
    121 	currentFPS = float64(fpsCount) * float64(time.Second) / float64(now-lastUpdated)
    122 	currentTPS = float64(tpsCount) * float64(time.Second) / float64(now-lastUpdated)
    123 	lastUpdated = now
    124 	fpsCount = 0
    125 	tpsCount = 0
    126 }
    127 
    128 const SyncWithFPS = -1
    129 
    130 // Update updates the inner clock state and returns an integer value
    131 // indicating how many times the game should update based on given tps.
    132 // tps represents TPS (ticks per second).
    133 // If tps is SyncWithFPS, Update always returns 1.
    134 // If tps <= 0 and not SyncWithFPS, Update always returns 0.
    135 //
    136 // Update is expected to be called per frame.
    137 func Update(tps int) int {
    138 	m.Lock()
    139 	defer m.Unlock()
    140 
    141 	n := now()
    142 	if lastNow > n {
    143 		// This ensures that now() must be monotonic (#875).
    144 		panic("clock: lastNow must be older than n")
    145 	}
    146 	lastNow = n
    147 
    148 	c := 0
    149 	if tps == SyncWithFPS {
    150 		c = 1
    151 	} else if tps > 0 {
    152 		c = calcCountFromTPS(int64(tps), n)
    153 	}
    154 	updateFPSAndTPS(n, c)
    155 
    156 	return c
    157 }