NoBodyNoOne

白驹过隙

什么是ReliableBunch溢出

当DS日志显示:SendBunch: Reliable partial bunch overflows reliable buffer!, 即当前未响应的ReliableBunch过多, 造成DS关闭了连接.

1
SendBunch: Reliable partial bunch overflows reliable buffer!

SendBunch时触发Overflow.

SendRPC时触发Overflow

阅读全文 »

定义:

1
2
3
4
5
6
7
8
9
10
UENUM()
enum EPlayNetMode
{
/** A standalone game will be started. This will not create a dedicated server, nor automatically connect to one. A server can be launched by enabling bLaunchSeparateServer if you need to test offline -> server connection flow for your game. */
PIE_Standalone UMETA(DisplayName="Play Standalone"),
/** The editor will act as both a Server and a Client. Additional instances may be opened beyond that depending on the number of clients. */
PIE_ListenServer UMETA(DisplayName="Play As Listen Server"),
/** The editor will act as a Client. A server will be started for you behind the scenes to connect to. */
PIE_Client UMETA(DisplayName="Play As Client"),
};
阅读全文 »

Dormancy定义

Dormancy表示休眠, 即在Dormancy期间, Actor不进行属性同步, 直接略过. Dormancy的对象是Actor, 和Replication(ActorChannel)的单位一致. 一般用于场景中不常发生变化的物体上, 如果预期其一定时间内不会发生变化, 即可设置为Dormancy.

Dormancy状态转换图:

阅读全文 »

延迟绑定

UE对UObject指针的映射是通过GUID进行的, 例如当客户端ABasePlayerController.TestReplicationComp2需要根据DS传来的新数据进行赋值, 这个值就是GUID其代表一个对象.

阅读全文 »

客户端和DS通过Bunch.ChIndex相互映射关联. 这样同一个Actor在每个端都会通过同一个Channel进行处理.

搭建测试用例

首先搭建一个能主动创建和销毁Channel的示例.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 在DS端主动创建Character
if (ALSVCharacter==nullptr && CreateCharacterFrameIndex ==0)
{
CreateCharacterFrameIndex =1;
FVector Loc = FVector::ZeroVector;
FRotator Rot = FRotator::ZeroRotator;
FActorSpawnParameters Par;
Par.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ALSVCharacter = GetWorld()->SpawnActor<AALSVCharacter>(AALSVCharacter::StaticClass(), Loc, Rot, Par);
}

// 在DS端主动销毁Character
if (CreateCharacterFrameIndex>50)
{
CreateCharacterFrameIndex = -1;
ALSVCharacter->Destroy();
ALSVCharacter = nullptr;
}
阅读全文 »

概述

概述: FaceRotation表明由程序自定义接管. PhysicsRotation表明通过Lerp形式按照一定速率转向目标方向(速度方向或者Controller朝向). 并且是速度位置计算完成后, 才设置朝向. 

  1. bUseControllerRotationYaw+bUseControllerRotationPitch+bUseControllerRotationRoll, bOrientRotationToMovement, bUseControllerDesiredRotation三者互斥.
  2. 如果同时开启, bOrientRotationToMovement优先级高于bUseControllerDesiredRotation
  3. FaceRotation表明朝向有程序接管, 直接控制.
  4. PhysicsRotation表示根据目标朝向, 平滑转动. 目标朝向由bOrientRotationToMovementbUseControllerDesiredRotation确定. 速率由RotationRate决定.
阅读全文 »

bMaintainHorizontalGroundVelocity:

  • true, 则直接丢弃竖直方向速度, 使用水平方向速度. 速度变小了
  • false, 则速度大小不变, 但是竖直方向速度设置为0. 即将竖直方向的速度转移到水平方向, 速度大小不变, 方向发生了变化(竖直方向被丢弃了).
阅读全文 »

思考

如果一个数组只用关心插入和删除, 不用关心其位置. 即使中间位置的元素删除后也不用将后续元素前移. 构建一个这样的数组需要解决的问题:

  1. 如何找到空闲位置?
  2. 某个元素删除之后如何再次被使用?

我们将数组分成两部分: 已使用部分+空闲部分. 这样可以在每次添加元素后将它放入已使用部分, 在每次将数据删除后将其放入空闲部分. 将空闲部分串联起来, 每次取头部元素使用, 归还时放入空闲列表的头部. 并使用一个索引记录第一个空闲元素的索引.

阅读全文 »

概述

核心: HashArray+ElementArray. 即可以理解成TSet由两个层组成,Key层(索引层)+Value层(值层).

  1. Key层存储着Hash/Size之后的值, 用于定位Value层数据.
  2. Value层存储这所有数据. 其本身是无序的. 对于同一个Key的不同Value, 其用链表方式链接

关键流程

一般Hash的实现一般由以下步骤实现:

  1. 将Element映射成Hash.
  2. 通过Hash值找到对应成员.

具体实现: UE使用两个数组HashArray+ElementArray来实现TSet, HashArray中存放着ElementArrayIndex, ElementArray中存放着具体的值.

  1. TSet将映射方式暴露给用户, 由程序员自定义实现:uint32 GetTypeHash(...), 最终返回类型为uint32KeyHash.
  2. TSet内部实现了通过KeyHash找到对应Element的方式:
    1. 根据HashIndex & (HashSize - 1), 计算HashIndex, 然后取出HashElementValue, 此处HashElementValue代表ElementArrayIndex.
    2. 根据IndexElementArray获取具体的ElementValue, 如果发生冲突, 拉链法解决冲突. 每个Element记录着NextElementIndex, 如果该Element发生冲突, 通过NextElementIndex寻找到下一个Element.
阅读全文 »

概述

先了解TSet, 再看TMap.  原理是一样的, 也是索引层+值层, 按照这个思路会很好理解, 剩下的就是维护这两个层的数据正确.

TMap是继承自TMapBase, 最终操作的也是其成员变量ElementSetType Pairs, 即TSet类型. TMapElement类型设置为typedef TPair<KeyType, ValueType> ElementType;, 即Pair类型.

阅读全文 »

思考

如果能计算一个属性成员的偏移, 记录该偏移后, 根据指针指向位置, 偏移后可得该属性. 然后可以直接对该属性进行操作.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

#define STRUCT_OFFSET( struc, member ) offsetof(struc, member) // 官方
#define MEMBER_OFFSET( InClass, Member ) ((int)(reinterpret_cast<int>(&(((InClass*)0)->Member))))

class TestClass
{
public:
int x;
int y;
};

int main()
{
TestClass c;
int Offset = MEMBER_OFFSET(TestClass, y);
unsigned char *p = (unsigned char*) & c;
int* p1 = (int*)(p + Offset);
*p1 = 999;
cout << "y:"<<c.y << endl;
return 0;
}
阅读全文 »

根据通用国际标准IEEE754,详解单精度浮点数. 除特殊标注外,下文中的浮点数专指单精度浮点数.

浮点数表示方法

32位浮点数存储格式:1bit符号+8bit指数为+23bit尾数.

  1. 使用S表示符号位
  2. 使用e表示指数位, 为了节省空间我们使用无符号整数来, 那负数如何表示呢? 将e+127的结果存储在指数中. 我们将其称为阶数, 用E表示,则有:\(e=E-127\) . 其中E>1且E所有位不全为1,也不全为0.
  3. 使用m表示尾数位, 并且规定, 尾数必须是 \(1 \le m \le 2\)​ , 可以去掉尾数固定的1, 使用M表示尾数中小数部分.

可以得到浮点数的计算公式:\((-1)^{s}*1.M*2^{E-127} \quad (E>0 且E的所有位非全1)\)

阅读全文 »

前言

查看一个模型碰撞大致类型的方法:

  1. show Collision
  2. show CollisionVisibility
  3. show CollisionPawn
  4. 查看具体碰撞模型数据

阅读全文 »