Golang实现简易的1位小数计数器
Golang实现简易的1位小数计数器
我正在尝试实现一个计数器,用于统计我的游戏中已经过去了多少秒。它仅基于秒数,我需要它用于一个游戏服务器项目。我想做的,真的只是 0 + 0.2,然后下一个 tick 0.2+0.2,0.4+0.2…… 使用浮点数无法实现这一点,因为最终会得到 1.99999999998 而不是 2,这会破坏计算。我研究过 Big Int、定点精度库,但我不确定应该使用什么。我不希望有太多开销,因为这每秒会被调用 5-10 次。简单的 0 + 0.2 会完美地工作,这样我就可以判断是否过去了一秒或半秒,并且我可以将之前的时间存储在一个简单的数字中。
想法是玩家会有一个上次执行操作的时间戳,我会根据时间检查是否已经过去了 X 时间(time- lastTime > X),如果为真,我将执行某些操作。我可以访问服务器的 tick 率,我将其设置为 0.2。如果没有其他办法,我总是可以使用操作系统时间。对于该怎么做有什么建议吗?
更多关于Golang实现简易的1位小数计数器的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你可以使用整数数据类型(int64)来表示200毫秒。
更多关于Golang实现简易的1位小数计数器的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
当然我可以,但 @j-forster 提供了一个很好的例子。
能详细说明一下吗?我更喜欢使用较小的十进制数,但我想使用较大的数字也能达到同样的效果。
对于游戏服务器中的时间计数,使用浮点数确实会累积精度误差。以下是几种实用的解决方案:
方案一:使用整数计数(推荐)
type Counter struct {
ticks int // 总tick数
tickRate int // 每tick的毫秒数
}
func NewCounter(tickRateMs int) *Counter {
return &Counter{tickRate: tickRateMs}
}
func (c *Counter) Tick() {
c.ticks++
}
func (c *Counter) ElapsedSeconds() float64 {
return float64(c.ticks * c.tickRate) / 1000.0
}
func (c *Counter) HasElapsed(lastTime int, intervalMs int) bool {
elapsed := c.ticks - lastTime
return elapsed*c.tickRate >= intervalMs
}
// 使用示例
counter := NewCounter(200) // 0.2秒 = 200毫秒
counter.Tick()
seconds := counter.ElapsedSeconds() // 0.2
方案二:使用 time.Duration
type TimeCounter struct {
startTime time.Time
tickRate time.Duration
tickCount int
}
func NewTimeCounter(tickRateMs int) *TimeCounter {
return &TimeCounter{
startTime: time.Now(),
tickRate: time.Duration(tickRateMs) * time.Millisecond,
}
}
func (tc *TimeCounter) Tick() {
tc.tickCount++
}
func (tc *TimeCounter) Elapsed() time.Duration {
return time.Duration(tc.tickCount) * tc.tickRate
}
func (tc *TimeCounter) HasElapsed(lastTick int, intervalTicks int) bool {
return tc.tickCount-lastTick >= intervalTicks
}
// 检查是否过去特定时间
func (tc *TimeCounter) HasTimeElapsed(lastTime time.Duration, interval time.Duration) bool {
return tc.Elapsed()-lastTime >= interval
}
方案三:使用定点数(避免浮点误差)
import "math/big"
type FixedPointCounter struct {
value *big.Rat
tickRate *big.Rat
}
func NewFixedPointCounter(tickRate float64) *FixedPointCounter {
return &FixedPointCounter{
value: big.NewRat(0, 1),
tickRate: big.NewRat(int64(tickRate*10), 10), // 0.2表示为2/10
}
}
func (fpc *FixedPointCounter) Tick() {
fpc.value.Add(fpc.value, fpc.tickRate)
}
func (fpc *FixedPointCounter) ElapsedSeconds() float64 {
result, _ := fpc.value.Float64()
return result
}
func (fpc *FixedPointCounter) GreaterThan(threshold float64) bool {
thresholdRat := big.NewRat(int64(threshold*10), 10)
return fpc.value.Cmp(thresholdRat) > 0
}
方案四:基于系统时间的简单实现
type GameTimer struct {
startTime time.Time
}
func NewGameTimer() *GameTimer {
return &GameTimer{startTime: time.Now()}
}
func (gt *GameTimer) ElapsedSeconds() float64 {
elapsed := time.Since(gt.startTime)
return elapsed.Seconds()
}
func (gt *GameTimer) CheckAction(lastTime time.Time, intervalSeconds float64) bool {
elapsed := time.Since(lastTime).Seconds()
return elapsed >= intervalSeconds
}
// 使用示例
timer := NewGameTimer()
lastAction := time.Now()
// 每tick检查
if timer.CheckAction(lastAction, 1.0) { // 检查是否过去1秒
// 执行操作
lastAction = time.Now()
}
对于游戏服务器,推荐使用方案一(整数计数),因为:
- 完全避免浮点数精度问题
- 性能开销最小
- 逻辑简单清晰
- 每秒5-10次的调用频率完全无压力
整数方案可以直接用tick数进行计算和比较,不需要处理浮点数误差,是最适合游戏循环时间统计的方法。


