帧同步原理

帧同步和状态同步区别

在这里插入图片描述

在这里插入图片描述

状态同步:发操作,收状态
帧同步:发操作,收操作

随机数算法

为了保证每个客户端的表现相同,需要使用相同的随机数种子,比如以帧号为种子,还需要一个随机数容器,里面的随机数是固定的,这样相同的容器,相同的种子就可以得到相同的结果。

逻辑严格排序

经常会有需要排序的列表或者数组,比如攻击距离自己最近的敌人,这时候就需要将身边的敌人进行距离排序,一般来说只要排序距离就行了,但是如果出现两个敌人距离一样的时候,就会导致在不同的机器上选择的敌人是不同的。所以排序一定要排到id为止,才能避免出现条件相同顺序不同的问题。

浮点数与定点数

浮点数:精度为7位到8位小数,有一定的误差,不同的硬件软件平台也会有些许差异,随着游戏进行,这一点点误差会逐渐放大,导致不同客户端的计算结果不一致
定点数:把整数部分和小数部分拆分开来,都用整数的形式表示,小数点位置固定,缺点是占用空间更大

实现自定义定点数

using System;

/// <summary>
/// 自定义64位定点数
/// </summary>
[Serializable]
public struct Fixed64 : IEquatable<Fixed64>, IComparable, IComparable<Fixed64>
{
    public long value;
    //小数部分位数
    private const int FRACTIONALBITS = 12;
    private const long ONE = 1L << FRACTIONALBITS;
    public static Fixed64 Zero = new Fixed64(0);
    
    /// <summary>
    /// ֱ直接对value赋值
    /// </summary>
    Fixed64(long value)
    {
        this.value = value;
    }
    
    /// <summary>
    /// 传入具体数字的构造函数
    /// </summary>
    public Fixed64(int value)
    {
        this.value = value * ONE;
    }

    /// <summary>
    /// 重载运算符
    /// </summary>
    public static Fixed64 operator +(Fixed64 a, Fixed64 b)
    {
        return new Fixed64(a.value + b.value);
    }
    
    public static Fixed64 operator -(Fixed64 a,Fixed64 b)
    {
        return new Fixed64(a.value - b.value);
    }
    
    public static Fixed64 operator*(Fixed64 a,Fixed64 b)
    {
        //直接相乘后面会多出0,所以这里要右移
        return new Fixed64((a.value >> FRACTIONALBITS) * b.value);
    }
    
    public static Fixed64 operator/(Fixed64 a,Fixed64 b)
    {
        return new Fixed64((a.value << FRACTIONALBITS) / b.value);
    }
    
    public static bool operator ==(Fixed64 a,Fixed64 b)
    {
        return a.value == b.value;
    }
    
    public static bool operator !=(Fixed64 a,Fixed64 b)
    {
        return !(a == b);
    }
    
    public static bool operator>(Fixed64 a,Fixed64 b)
    {
        return a.value > b.value;
    }
    
    public static bool operator <(Fixed64 a, Fixed64 b)
    {
        return a.value < b.value;
    }
    
    public static bool operator >=(Fixed64 a, Fixed64 b)
    {
        return a.value >= b.value;
    }
    
    public static bool operator <=(Fixed64 a, Fixed64 b)
    {
        return a.value <= b.value;
    }
    
    /// <summary>
    /// 显式强制类型转换,Fixed64转换为long类型
    /// </summary>
    public static explicit operator long(Fixed64 value)
    {
        return value.value >> FRACTIONALBITS;
    }
    
    public static explicit operator Fixed64(long value)
    {
        return new Fixed64(value);
    }
    
    public static explicit operator float(Fixed64 value)
    {
        return (float)value.value / ONE;
    }
    
    public static explicit operator Fixed64(float value)
    {
        return new Fixed64((long)(value * ONE));
    }
    
    public int CompareTo(object obj)
    {
        return value.CompareTo(obj);
    }

    public int CompareTo(Fixed64 other)
    {
        return value.CompareTo(other.value);
    }

    public bool Equals(Fixed64 other)
    {
        return value == other.value;
    }
    
    public override int GetHashCode()
    {
        return value.GetHashCode();
    }
    
    public override bool Equals(object obj)
    {
        return obj is Fixed64 && ((Fixed64)obj).value == value;
    }
    
    public override string ToString()
    {
        return ((float)this).ToString();
    }
}

实现自定义定点数向量

using System;

/// <summary>
/// 自定义定点数三维向量
/// </summary>
public struct Fixed64Vector3 : IEquatable<Fixed64Vector3>
{
    public Fixed64 x;
    public Fixed64 y;
    public Fixed64 z;
    
    public Fixed64Vector3(Fixed64 x, Fixed64 y, Fixed64 z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    
    public Fixed64 this[int index]
    {
        get
        {
            if (index == 0)
                return x;
            else if (index == 1)
                return y;
            else
                return z;
        }
        set
        {
            if (index == 0)
                x = value;
            else if (index == 1)
                y = value;
            else
                y = value;
        }
    }
    
    public static Fixed64Vector3 Zero
    {
        get { return new Fixed64Vector3(Fixed64.Zero, Fixed64.Zero, Fixed64.Zero); }
    }

    public static Fixed64Vector3 operator +(Fixed64Vector3 a, Fixed64Vector3 b)
    {
        Fixed64 x = a.x + b.x;
        Fixed64 y = a.y + b.y;
        Fixed64 z = a.z + b.z;
        return new Fixed64Vector3(x, y, z);
    }

    public static Fixed64Vector3 operator -(Fixed64Vector3 a, Fixed64Vector3 b)
    {
        Fixed64 x = a.x - b.x;
        Fixed64 y = a.y - b.y;
        Fixed64 z = a.z - b.z;
        return new Fixed64Vector3(x, y, z);
    }

    public static Fixed64Vector3 operator *(Fixed64 d, Fixed64Vector3 a)
    {
        Fixed64 x = a.x * d;
        Fixed64 y = a.y * d;
        Fixed64 z = a.z * d;
        return new Fixed64Vector3(x, y, z);
    }

    public static Fixed64Vector3 operator *(Fixed64Vector3 a, Fixed64 d)
    {
        Fixed64 x = a.x * d;
        Fixed64 y = a.y * d;
        Fixed64 z = a.z * d;
        return new Fixed64Vector3(x, y, z);
    }

    public static Fixed64Vector3 operator /(Fixed64Vector3 a, Fixed64 d)
    {
        Fixed64 x = a.x / d;
        Fixed64 y = a.y / d;
        Fixed64 z = a.z / d;
        return new Fixed64Vector3(x, y, z);
    }
    
    public static bool operator ==(Fixed64Vector3 lhs, Fixed64Vector3 rhs)
    {
        return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
    }

    public static bool operator !=(Fixed64Vector3 lhs, Fixed64Vector3 rhs)
    {
        return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z;
    }

    /// <summary>
    /// 进行左移和右移是为了增加哈希值的混淆性和分布性,参考Vector3中的写法
    /// </summary>
    public override int GetHashCode()
    {
        return x.GetHashCode() ^ (y.GetHashCode() << 2) ^ (z.GetHashCode() >> 2);
    }
    
    public override bool Equals(object obj)
    {
        return ((Fixed64Vector3)obj) == this;
    }
    
    public override string ToString()
    {
        return string.Format("x:{0} y:{1} z:{2}", x, y, z);
    }
    
    public bool Equals(Fixed64Vector3 other)
    {
        return this == other;
    }
}

帧同步流程

逻辑帧:游戏被划分为连续的游戏帧,每一帧就是一个时间片段,帧同步的目标是把每一帧中所有客户端的操作同步起来

在这里插入图片描述

客户端-服务端架构:每个客户端把自己的一个或多个操作发给服务端,服务端把这些操作应用到游戏状态中,并将结果发给所有客户端,保持一致性,上图中方框表示服务端

在这里插入图片描述

  • 严格帧同步:每个关键帧只有当服务器集齐了所有玩家的操作指令,才可以进行转发,进入下一个关键帧,否则就要等待最慢的玩家。
    上图表示严格帧同步流程,服务器和客户端约定每5帧同步一次,这里每一帧的时间间隔由服务器决定,UPDATE 0表示服务器发的第0帧数据,CTRL 0表示客户端发的第0帧操作,服务器在第5帧开始前收到了客户端A,B的数据,进行打包再转发给A,B。服务端在第10帧开始时没有收到所有客户端的数据,开始等待,直到收到所有客户端的数据。
  • 乐观帧同步:服务器定时广播操作指令,而不必等待慢的客户端

帧同步服务端代码编写相对简单,只是转发数据

预测,回滚,追帧

预测:当网络不好时,没收到服务端转发的信息,客户端需要进行预测,比如根据当前的速度和方向进行移动
回滚:收到服务端信息后发现预测不准确,需要回到过去某个时间点的状态,修正一些东西,客户端需要进行一些插值运算
追帧:回滚后快速向前模拟到当前时间

反外挂

服务器收集数据后,运行部分客户端逻辑,作为权威,比如服务器计算玩家移动和物理碰撞

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值