Replication要点数组
1.simulated function
在网络环境中只有exec,client , simulated 函数才在客户端进行调用。若是一个函数没有任何前缀,它只会在Server中进行调用。
另外,对于一个simulated function,他要么是被另一个simulated function 调用,要么就是在native函数中被调用才能在客户端执行。
应用举例
simulated function PostBeginPlay()
{
`log("PostBeginPlay");
NumberOne();
}
simulated function NumberOne()
{
`log("Number One");
NumberTwo();
}
function NumberTwo()
{
`log("Number Two");
NumberThree();
}
simulatedf function NumberThree()
{
`log("Number Three");
}
Server端的运行结果:
PostBeginPlay
NumberOne
NumberTwo
NumberThree
Client端的运行结果:
PostBeginPlay
NumberOne
结论:
因为NumberTwo()没有simulated前缀,咱们在前边总结到,任何没有前缀修饰的函数只会在Server端进行调用。
因为节省带宽的须要,咱们尽量的让客户端和服务器端保持同样的运行代码,因此可使用simulated来进行这样的工做。
可是咱们又不能把所有的函数都写成simulated,由于在客户端不能访问到全部的Actor。(例如一些生成器Spawner Actor,即便在其Simulated PostBeginPlay中打Log都不会有任何的输出)
2.哪些在服务器端运行,哪些在客户端运行
咱们知道非simulated function 是必定再也不客户端上运行的,可是当咱们在simulated function中咱们也想有一些权限,设置一些咱们不肯意在客户端中运行的代码段。
还有的是,咱们怎么知道咱们当前的游戏是彻底运行在网络环境的。
Role和RemoteRole
在Actor中有一个枚举类型ENetRole ,定义了服务器和客户端怎么来对待这一个Actor
Role_None
Role_SimulatedProxy
Role_AutonomousProxy
Role_Authority
var ENetRole Role,RemoteRole;
默认情况下,在服务器端Role=Role_Authority。这是由于,在服务器端,服务器须要维护整个世界中的全部Actor,所以他有全部Actor的控制权。
RemoteRole设置咱们在客户端如何对待这个Actor。例如在如下RemoteRole的几个属性设置中:
Role_None: 这个Actor永远不会被复制到客户端。例如GameInfo永远是在Server中运行的,其RemoteRole=Role_None,游戏中的ActorFactor或是Spawner均可以这样设置,由于你不须要客户端有这些内容。
Role_SimulatedProxy:基本上全部须要复制的Actor都是这个属性,例如Projectile,Vehicle,全部客户端须要预测其物理和其余行为的Actor。
Role_AutonomousProxy:只用在两个地方,一个是client本身的Pawn,也就是玩家Pawn,另外一个是DemoRecSpectators extends UTPlayerController。该属性和Role_SimulatedProxy比较接近,除过他们不限于simulated
Role_Authority:RemoteRole不能使用,该属性表示这个actor要么在服务器端,要么是单人游戏。
运行下面的代码
simulated function PostBeginPlay()
{
`log("PostBeginPlay");
`log("Role="@Role);
`log("RemoteRole"@RemoteRole);
}
在服务器端的结果:
PostBeginPlay
Role=Role_Authority;
RemoteRole=Role_SimulatedProxy;
在客户端的结果:
PostBeginPlay
Role=Role_SimulatedProxy
RemoteRole=Role_Authority
彻底是相反的结果,这是为何呢?
服务器端如咱们预料的,可是对于客户端彻底相反,咱们从新作一个实验。
simulated function PostBeignPlay()
{
`log("PostBeginPlay");
if(Role==Role_Authority)
`log("I'm on Server");
else
`log("I'm on Client");
}
在Server端运行的话
I'm on Server
在客户端的话
I'm on Server
由于在服务器端运行的话,Actor的Role为Role_Authority
在客户端Actor的Role为Role_SimulatedProxy
根据这一个特性,咱们能够划定有一些功能在特定的端口环境运行,例如一个溅血例子不须要在服务器端运行,因此只须要判断当前的Role<Role_Authoriy,若是不是的话就说明其在客户端中运行。
if(Role<Role_Authority)
Spawn(Particle); //溅血
有时有一些游戏性的代码又只须要在服务器端,咱们能够反着来便可。
3.是否运行在网络环境NetMode
在WorldInfo中有一个枚举ENetMode
NM_Standalone, //单机运行: 没有服务器
NM_DedicatedServer, //侦测服务器: 运行在一个服务器上,其余客户端链接过去。MMO
NM_ListenServer, //监听服务器:一个本地客户端主持做为服务器,Host Game,其余玩家链接过来,代替了远程机器。
NM_Client //客户端: 做为一个客户端链接到服务器。
因为是在WorldInfo中,因此咱们能够划定和查询该关卡的NetMode
simulated function PostBeginPlay()
{
`log("NetMode:"@WorldInfo.NetMode);
}
对于服务器端
NetMode: NM_DedicatedServer
对于客户端
NetMode:NM_Client
既然有了Role为何还须要NetMode这种枚举区分?
由于NetMode在服务器端有两种:NM_DedicatedServer或NM_ListenServer,而对于Role在服务器端永远是Role_Authority
那么何时使用NetMode呢?
前面说过不让血粒子在服务器端进行播放,可是对于NM_ListenServer服务器,像CS中的玩家主机(其实就是客户端在作HostGame主机),固然也要播放血粒子,所以能够用NM_ListenServer作出区别。
4.Replication变量复制
变量复制任何状态下都是Reliable。
数组变量复制除外。static数组能够复制,动态数组不能。除非你将其每个单独值传递给中间变量,但最好不要。
作一个实例:
var int TestNum;
replication
{
if(bNetDirty)
TestNum;
}
simulated function PostBeginPlay()
{
if(Role=Authority) //在服务器端执行
SetTimer(1,true,'ServerTest');
else //在客户端执行
SetTimer(1,true,'ClientTest');
}
function ServerTest()
{
TestNum++;
`log("Server:"@TestNum);
}
function ClientTest()
{
`log("Client:"@TestNum);
}
结果分析:
在Server端游
Server: 1
Server: 2
Server:3
...
在Client端有
Client: 1
Client: 2
Client: 3
...
由于Server端不断地变化,bNetDirty==true,将会一直将值传递给Client
变量复制的一些关键词:
NetPriority: 优先级,注意提升这个变量的优先级,复制能够比别的变量优先,可是不能使其复制变得更快,由于还存在别的Actor变量优先级。
bNetDirty:变量值发生了变化。
bNetInitial: true,若是全部变量初始化复制完成。
bNetOwner: 若是本地PlayerController拥有这个变量则为true
bAlwaysRelevant: 为true的话,这个变量将会针对全部client进行更新。注意一些状况,例如别的玩家根本看不见这个变量相关引发的内容就应该不要使其为true,以节省带宽。
bReplicateInstigator: 若是这个变量有instigator,将其复制给Clients,例如给这个actor形成伤害的pawn。
bReplicateMovement:复制位置和运动变量,例如velocity。
bSkipActorPropertyReplication:不要复制这个Actor的任何属性
NetUpdateFrequency:更新复制的频率,数值越低优先级就越低
if ( (!bSkipActorPropertyReplication || bNetInitial) &&
(Role==ROLE_Authority)
&& bNetDirty && bReplicateInstigator )
Instigator;
Reading it, this tells us that if we're not skipping property replication or we're still initializing,
and we're the server, and a replicated property has changed, and we want to replicate the
instigator, then replicate the Instigator variable. These replication statements can seem
confusing at first, but examining what each variable does and taking a look at other examples
in the source code will give you an understanding of when they should be used.
5.ReplicateEvent
在一些变量前加修饰符 Repnotify,当这个变量被Replicate的时候会引发RelicateEvent被调用。
实例:
结果是在服务器端:
ServerTest:1
在客户端:
This Replicated Event is Working
6.对Actor的一些设置
通常网络Actor的出现
若是Actor只在Server显示而没有在Client上存在,设置其Actor属性
RemoteRole=Role_SimulatedProxy //告知客户端如何对待Actor,大部分的Projectile和Vehicle,能够被击碎的箱子均可以这样设置
bAlwaysRelevant=true //这个也能够随着距离设置以便节省带宽
RemoteRole=Role_SimulatedProxy- 告知游戏的客户端怎么处理这个Actor,意思是:这个Client有一个复制于Server的本地copy表示这个Actor。
bAlwaysRelevant=true 针对于玩家这个是相关的
获取本地PlayerController
GetALocalPlayerController()和Foreach LocalPlayerControllers是永远失效的,所以必须使用别的遍历方式例如DynamicActors等
7.GameReplicationInfo
GameInfo不存在于Client端,所以咱们建立了GameReplicationInfo来给Client端传递GameInfo中的信息
在GameInfo的DefaultProperties中有
defaultproperties
{
GameReplicationInfoClass=class'ArtGameReplicationInfo'
}
下面举一个示例:
若是GameInfo中有一些变量须要玩家知道,例如当前场景中还有多少敌人
var int EnemyNumLeft;
在ArtGame中有
ArtGameReplicationInfo(ArtGameReplicationInfo).EnemyNumLeft=EnemyNumLeft;
而后在class ArtGameReplication中
var int EnemyNumLeft;
replication
{
if(bNetDirty)
EnemyNumLeft;
}
一样的道理,大部分在PlayerController中和Pawn中的数据咱们能够搬到ArtPlayerReplicationInfo中去处理HUD显示,例如玩家当前的经验和等级
8.关于function的修饰符
Reliabale VS Unreliable
Reliable老是可靠地传输,基于TCP传输模式即便在网络环境糟糕的状况下也会最终以从新发送的方式到达目的地。
Unreliable基于UDP方式,虽然效率高,可是有可能会传输丢失,一些不重要的执行能够用这种方式,例如溅血等。
client function:
当服务器想在客户端上调用一个函数,这个函数永远不会在服务器端而在只在客户端执行。例如在PlayerController中有一个GivePawn函数,该函数负责给客户端设置一个Pawn,而服务器端不关心,仅仅是对客户端说:“嘿,这是你的责任,赶快去处理吧!”。
reliable client function GivePawn(Pawn NewPawn)
作一个示例:
unreliable client function PlayTeleportEffect()
{
//在客户端播放一个传送效果的粒子,
}
固然client function只在被这个客户端拥有的Actor上执行,什么Actor一般被这个Client拥有呢?
通常状况下,像PlayerController,Pawn,Weapon,Inventory,PlayerReplicationInfo等被这个client拥有
大部分在场景中放置的Actor都不被Client拥有,Spawner每每也不被client拥有。
作一个实例:
在场景中放置一个Actor,而后PostBeginPlay中调用
//只在客户端执行的函数
reliable client function ClientCall()
{
`log("Client Function Called by the Server");
}
发如今服务器端和客户端都不会出现该Log.
再在ArtPawn和ArtPlayerController的客户端中会出现.
Server function:
Server funcionn是从客户端发送给扶我端的调用,例如客户端会表达:“我要执行一个动做,你也得保证你在服务器端对这个事件作出对应的响应。”
例如一个exec函数只在本地客户端执行,而服务器端不会发生相应的动做,这个时候就得通知。
进行一个实例:
exec function use()
{
`log("I'm using the trigger!") ;
}
在客户端运行将会执行log,可是服务器端不会进行动做。这个时候只须要
exec function use()
{
`log("I'm using the trigger!") ;
ServerUse();
}
reliable server function ServerUse()
{
`log("Server using the trigger");
}
这时候服务器端将会执行对应的动做而客户端不会
服务器端输出:
Server using the trigger
固然咱们也能够在客户端传递参数给服务器端:
reliable server function ServerUse(int Num)
{
`log("Server using the trigger:"@num);
}
在use()中
exec function use()
{
`log("I'm using the trigger!") ;
ServerUse(3);
}
最后服务器端会输出
Server using the trigger 3