Home

Awesome

LittleBee

A little bee Advanced Version 目前正在把帧同步逻辑提取SDK,请看项目 https://github.com/dudu502/littlebee_libs 这是一个帧同步游戏实例,在游戏中同步上百个物体,上千个状态,游戏背景是一个行星系统下的射击游戏,以下是视频链接。 preview

[Watch playing the video(Youtube)]

[Watch replaying the video (Youtube)]

[Watch playing the video(bilibili)]

[Watch replaying the video(bilibili)]

https://github.com/dudu502/LittleBee/assets/36216406/3d1534a9-3c1c-483f-85dd-413151163f5e

https://github.com/dudu502/LittleBee/assets/36216406/ad711712-359e-47e4-87f9-dc47d3e3ac5a

帧同步与状态同步

优缺点

帧同步状态同步
一致性<font color=#219167>设计层面决定了必然一致</font><font color=#219167>可以保证一致性</font>
玩家数<font color=#bd2158>对多玩家支持有限</font><font color=#219167>多玩家有优势</font>
跨平台<font color=#bd2158>需要考虑浮点运算的一致性</font><font color=#219167>由于主要的计算都在服务器,因此不存在跨平台问题</font>
防作弊<font color=#bd2158>容易作弊,但是可以优化</font><font color=#219167>可以很好的防作弊 </font>
断线重连<font color=#bd2158>实现起来比较难,但不是不能</font><font color=#219167>只需要重新发送一次信息即可,好实现</font>
回放需求<font color=#219167>能完美实现</font><font color=#bd2158>无法实现</font>
暂停游戏<font color=#219167>好实现</font><font color=#bd2158>不好实现</font>
网络传输量<font color=#219167>比较小</font><font color=#bd2158>比较大</font>
开发难度<font color=#bd2158>相对复杂</font><font color=#219167>相对简单</font>
RTS类游戏<font color=#219167>适合</font><font color=#bd2158>不适合</font>
格斗类游戏<font color=#219167>适合</font><font color=#bd2158>不适合</font>
MOBA类游戏<font color=#219167>适合</font><font color=#bd2158>不适合</font>
MMO类游戏<font color=#bd2158>不适合</font><font color=#219167>适合</font>

帧同步需要克服的难点

帧同步与ECS的结合

了解了帧同步开发过程中需要克服的难点,我们接下来就要考虑选用一种比较好的实现方式,或者说是一种开发框架。由于帧同步开发非常需要数据和表现分离,分离到什么程度呢?就是数据计算部分甚至可以放在一个单独的线程里。这样编写逻辑的好处还可以让服务器运行以达到快速复盘游戏的功能,能做到这种程度的分离我想只有ECS了。帧同步加上ECS绝对是完美搭档。

ECS说明

首先要介绍一下ECS,ECS并非一种全新的技术,也不是Unity首先提出来的。这种名词的出现非常早,而近几年突然火爆,是因为暴雪的《守望先锋》。《守望先锋》的服务器和客户端框架完全基于ECS构建,在游戏机制、网络、渲染方面都有非常出色的表现。坦白地说ECS不像一种设计模式,我们以前用的设计模式都是在面向对象设计下谈论的,ECS都不是面向对象。Unity也有ECS,其实Unity本身的组件也是一种ECS,只不过还不够纯粹。ECS特别适合做Gameplay。关于ECS的变种也有很多,我这里也是稍微做了一些修改的ECS。

回放机制

这是帧同步游戏的一个特点,如果一个游戏有回放系统,那么这个游戏必然是通过帧同步实现的。回放也可以称为录像,但是与视频录像有着巨大的区别,以视频文件为载体的回放通常文件占用巨大,并且在播放过程中无法切换视窗,视频极易被盗用滥用,恶意修改,压缩,降低品质,因此视频回放有着很大的劣势。帧同步的回放可以做到文件极小,无法篡改,播放过程用户可以任意切换视窗。可以说帧同步游戏必备系统就是回放系统。

网络通信

这里推荐RevenantX/LiteNetLib,这个库很强大并且用法很简洁,它提供了可靠UDP传输,这正是我想要的。 网络通信的数据协议可以选择的有很多,我这里使用的是自制二进制流协议,主要实现的功能是序列化与反序列化,结构体内的字段支持可选。 就像这个PtRoom结构:

//Template auto generator:[AutoGenPt] v1.0
//Creation time:2021/1/28 16:43:48
using System;
using System.Collections;
using System.Collections.Generic;
namespace Net.Pt
{
public class PtRoom
{
    public byte __tag__ { get;private set;}

    public uint RoomId{ get;private set;}
    public byte Status{ get;private set;}
    public uint MapId{ get;private set;}
    public string RoomOwnerUserId{ get;private set;}
    public byte MaxPlayerCount{ get;private set;}
    public List<PtRoomPlayer> Players{ get;private set;}

    public PtRoom SetRoomId(uint value){RoomId=value; __tag__|=1; return this;}
    public PtRoom SetStatus(byte value){Status=value; __tag__|=2; return this;}
    public PtRoom SetMapId(uint value){MapId=value; __tag__|=4; return this;}
    public PtRoom SetRoomOwnerUserId(string value){RoomOwnerUserId=value; __tag__|=8; return this;}
    public PtRoom SetMaxPlayerCount(byte value){MaxPlayerCount=value; __tag__|=16; return this;}
    public PtRoom SetPlayers(List<PtRoomPlayer> value){Players=value; __tag__|=32; return this;}

    public bool HasRoomId(){return (__tag__&1)==1;}
    public bool HasStatus(){return (__tag__&2)==2;}
    public bool HasMapId(){return (__tag__&4)==4;}
    public bool HasRoomOwnerUserId(){return (__tag__&8)==8;}
    public bool HasMaxPlayerCount(){return (__tag__&16)==16;}
    public bool HasPlayers(){return (__tag__&32)==32;}

    public static byte[] Write(PtRoom data)
    {
        using(ByteBuffer buffer = new ByteBuffer())
        {
            buffer.WriteByte(data.__tag__);
            if(data.HasRoomId())buffer.WriteUInt32(data.RoomId);
            if(data.HasStatus())buffer.WriteByte(data.Status);
            if(data.HasMapId())buffer.WriteUInt32(data.MapId);
            if(data.HasRoomOwnerUserId())buffer.WriteString(data.RoomOwnerUserId);
            if(data.HasMaxPlayerCount())buffer.WriteByte(data.MaxPlayerCount);
            if(data.HasPlayers())buffer.WriteCollection(data.Players,(element)=>PtRoomPlayer.Write(element));

            return buffer.Getbuffer();
        }
    }

    public static PtRoom Read(byte[] bytes)
    {
        using(ByteBuffer buffer = new ByteBuffer(bytes))
        {
            PtRoom data = new PtRoom();
            data.__tag__ = buffer.ReadByte();
            if(data.HasRoomId())data.RoomId = buffer.ReadUInt32();
            if(data.HasStatus())data.Status = buffer.ReadByte();
            if(data.HasMapId())data.MapId = buffer.ReadUInt32();
            if(data.HasRoomOwnerUserId())data.RoomOwnerUserId = buffer.ReadString();
            if(data.HasMaxPlayerCount())data.MaxPlayerCount = buffer.ReadByte();
            if(data.HasPlayers())data.Players = buffer.ReadCollection( (rBytes)=>PtRoomPlayer.Read(rBytes) );

            return data;
        }       
    }
}
}

Client

littleBee

这是一个基于帧同步的Unity工程

Common

一些工具:Pt结构体生成工具,Excel2Json生成工具,General库项目,ServerDll库项目

Design

设计文档:大纲设计文档,原型设计文档,配置表。

以下三张图分别描述了帧同步模拟器在三种不同场景下的使用情况。

下图表示客户端,服务端在同一时刻的大致行为,还有回放逻辑也是对应一致的行为。 客户端服务端和回放时间轴概图

这张图是客户端和服务端在每一个逻辑TICK中执行逻辑。上半部分是客户端,客户端需要执行的逻辑包含ECSR部分,下半部分是服务端部分。 客户端服务端和回放时间轴概图细节

最后一张图是描述回放的每一个逻辑帧。 回放时间轴概图细节

通过这几张图结合具体做什么类型的游戏,我们可以设置自定义System和Component来处理相关逻辑。

Server

这是一个服务集合项目,包括WebServer,GateServer,RoomServer等

Star History

Star History Chart