前言:纠结了很长时间,remote actor(模拟本地actor的行为) 到底如何创建的呢?是自己手动,抑或是系统自动创建?由于delta3d,本身的说明文档少之又少,而其自带的demo 也没有这方面的应用。Oman大哥和我通过跟踪基于delta3d的开源类库SimCore中的例子netdemo,了解到 这些是通过对本地的actor的稍加些属性,就可以由系统自动创建。
正文:用到的主要类库:dtGame、dtDAL,相关类:GameManager、GMComponent、GameActor、GameActorProxy。不懂的请仔细看下delta3d官网自带的说明文档(末尾的qq群共享里也有O(∩_∩)O~)。
首先,在本地创建actor ,如下:
1 dtCore::RefPtr<dtGame::GameActorProxy>gap;
2 GetGameManager()->CreateActor(*dtActors::EngineActorRegistry::GAME_MESH_ACTOR_TYPE,gap);
3 dtActors::GameMeshActor *gma = static_cast<dtActors::GameMeshActor *>(gap->GetActor());
4 gma->GetGameActorProxy().SetInitialOwnership(dtGame::GameActorProxy::Ownership::SERVER_PUBLISHED);
5 gma->SetName("Tank1");
6 gma->SetUniqueId(dtCore::UniqueId("guo"));
7 gma->SetMesh("E:/project/Client06/brdm.ive");
8 gma->SetModelTranslation(osg::Vec3(0.0,6.0,10.0));
9 gma->SetPrototypeName(gma->GetPrototypeName());
10 //mServerGameManager->AddActor(*actorProxy.get());
11 GetGameManager()->AddActor(gma->GetGameActorProxy(),false,true);
这里的关键属性为
dtGame::GameActorProxy::Ownership::SERVER_PUBLISHED。GameActorProxy中的Owership属性掌管着当map加载完毕时,actor如何加载到各个客户端。
其各个属性的含义如下图所示。其中published代表对外发布,就是可以通过网络发布关于其自己的消息。以我的理解,不具备published性质的actor是不会通过系统
自动创建actor(手动,理论上就可以,感觉没有必要)。
除了这个属性,还要看关键函数,
GetGameManager()->AddActor(gma->GetGameActorProxy(),false,true);其原型为:
void GameManager::AddActor(GameActorProxy& gameActorProxy, bool isRemote, bool publish),参一为要添加的代理,参二为是否为远程,
如果是远程的话,就没有发布的必要了,参三即是否要发布。主要看参三,publish为true的话,就要通过网络将要添加actor的属性传输到另一个网络端。
详细看代码:
AddActor()
1 void GameManager::AddActor(GameActorProxy& gameActorProxy, bool isRemote, bool publish)
2 {
3 if (gameActorProxy.GetId().ToString().empty())
4 {
5 throw dtGame::InvalidActorStateException(
6 "Actors may not be added the GM with an empty unique id", __FILE__, __LINE__);
7 }
8
9 // Fail early here so that it doesn't fail is PublishActor and need to wait a tick to
10 // clean up the actor.
11 if (publish && isRemote)
12 {
13 throw dtGame::ActorIsRemoteException( "A remote game actor may not be published", __FILE__, __LINE__);
14 }
15
16 gameActorProxy.SetGameManager(this);
17 gameActorProxy.SetRemote(isRemote);
18
19 if (mGMImpl->mEnvironment.valid())
20 {
21 if (mGMImpl->mEnvironment.get() != &gameActorProxy)
22 {
23 IEnvGameActor* ea = static_cast<IEnvGameActor*>(mGMImpl->mEnvironment->GetActor());
24 ea->AddActor(*gameActorProxy.GetActor());
25 mGMImpl->mGameActorProxyMap.insert(std::make_pair(gameActorProxy.GetId(), &gameActorProxy));
26 }
27 else
28 {
29 mGMImpl->mGameActorProxyMap.insert(std::make_pair(mGMImpl->mEnvironment->GetId(), mGMImpl->mEnvironment.get()));
30 mGMImpl->mScene->AddDrawable(mGMImpl->mEnvironment->GetActor());
31 mGMImpl->SendEnvironmentChangedMessage(*this, mGMImpl->mEnvironment.get());
32 }
33 }
34 else
35 {
36 mGMImpl->mGameActorProxyMap.insert(std::make_pair(gameActorProxy.GetId(), &gameActorProxy));
37 mGMImpl->mScene->AddDrawable(gameActorProxy.GetActor());
38 }
39
40 // Remote actors are normally created in response to a create message, so sending another is silly.
41 // Also, this doen't currently send messages when loading a map, so check here for that state.
42 if (!isRemote && mGMImpl->mMapChangeStateData->GetCurrentState() == MapChangeStateData::MapChangeState::IDLE)
43 {
44 dtCore::RefPtr<Message> msg = mGMImpl->mFactory.CreateMessage(MessageType::INFO_ACTOR_CREATED);
45 gameActorProxy.PopulateActorUpdate(static_cast<ActorUpdateMessage&>(*msg));
46 SendMessage(*msg);
47 }
48
49 gameActorProxy.SetIsInGM(true);
50
51 try
52 {
53 // If publishing fails. we need to delete the actor as well.
54 if (publish)
55 {
56 PublishActor(gameActorProxy);
57 }
58
59 gameActorProxy.InvokeEnteredWorld();
60 }
61 catch (const dtUtil::Exception& ex)
62 {
63 ex.LogException(dtUtil::Log::LOG_ERROR, *mGMImpl->mLogger);
64 DeleteActor(gameActorProxy);
65 throw ex;
66 }
67 }
主要意思,把actor加入到GM里面,并把添加actor的消息通知到各组件,并没有通过网络把MessageType::INFO_ACTOR_CREATED的消息传输出去
。如果isRemote 为false,publish为true的话,通过函数
PublishActor(gameActorProxy)进行处理。继续跟踪,上其代码:
View Code
1 void GameManager::PublishActor(GameActorProxy& gameActorProxy)
2 {
3 GMImpl::GameActorMap::iterator itor = mGMImpl->mGameActorProxyMap.find(gameActorProxy.GetId());
4
5 if (itor == mGMImpl->mGameActorProxyMap.end())
6 {
7 throw dtGame::InvalidActorStateException(
8 "A GameActor may only be published if it's added to the GameManager as a game actor.", __FILE__, __LINE__);
9 }
10
11 if (gameActorProxy.IsRemote())
12 {
13 throw dtGame::ActorIsRemoteException( "A remote game actor may not be published", __FILE__, __LINE__);
14 }
15
16 gameActorProxy.SetPublished(true);
17 dtCore::RefPtr<Message> msg = mGMImpl->mFactory.CreateMessage(MessageType::INFO_ACTOR_PUBLISHED);
18 msg->SetDestination(&GetMachineInfo());
19 msg->SetAboutActorId(gameActorProxy.GetId());
20 msg->SetSendingActorId(gameActorProxy.GetId());
21 SendMessage(*msg);
22 }
主要作用,就是设置,gameActorProxy的属性为Published,创建一条MessageType::INFO_ACTOR_PUBLISHED的消息,发布本GM的各个组件。到此为止,
代码没有了,并没有通过网络传输一条MessageType::INFO_ACTOR_CREATED的消息。到这里,阻塞了。
。。。。。。。
。。。。。。。
通过Oman大哥提醒,消息是传输各个组件里面,肯定有组件进行了相应的处理。看DefaultNetworkPublishingComponent组件。其ProcessMessage()
函数中:if (msg.GetMessageType() == MessageType::INFO_ACTOR_PUBLISHED)
{
GameActorProxy* ga = GetGameManager()->FindGameActorById(msg.GetAboutActorId());
if (ga != NULL && ga->IsPublished())
ProcessPublishActor(msg, *ga);
}
看来,
PublishActor()函数把相关属性的消息发到了这里,进行了处理。看函数ProcessPublishActor(msg, *ga);代码如下
1 void DefaultNetworkPublishingComponent::ProcessPublishActor(const Message& msg, GameActorProxy& gap)
2 {
3 if (mLogger->IsLevelEnabled(dtUtil::Log::LOG_INFO))
4 {
5 mLogger->LogMessage(dtUtil::Log::LOG_INFO, __FUNCTION__, __LINE__,
6 "Publishing Actor \"" + gap.GetName() + "\" With type \"" + gap.GetActorType().GetFullName() + "\"");
7 }
8
9 dtCore::RefPtr<Message> newMsg = GetGameManager()->GetMessageFactory().CreateMessage(MessageType::INFO_ACTOR_CREATED);
10 gap.PopulateActorUpdate(static_cast<ActorUpdateMessage&>(*newMsg));
11 GetGameManager()->SendNetworkMessage(*newMsg);
12 }
终于找到,原来是通过这里把创建actor的消息发送出去了。发送过程到此结束。
下面来看delta3d中GM的默认处理过程。其处理过程是在组件DefaultMessageProcessor中进行处理的。其ProcessMessage()函数:
if (msg.GetMessageType() == MessageType::INFO_ACTOR_CREATED)
{
ProcessCreateActor(static_cast<const ActorUpdateMessage&>(msg));
},直接交给ProcessCreateActor()进行处理。
View Code
1 void DefaultMessageProcessor::ProcessCreateActor(const ActorUpdateMessage& msg)
2 {
3 GameActorProxy* proxy = GetGameManager()->FindGameActorById(msg.GetAboutActorId());
4 if (proxy == NULL)
5 {
6 // just to make sure the message is actually remote
7 if (msg.GetSource() != GetGameManager()->GetMachineInfo())
8 {
9 try
10 {
11 dtCore::RefPtr<GameActorProxy> gap = ProcessRemoteCreateActor(msg);
12 if (gap.valid())
13 {
14 ProcessRemoteUpdateActor(msg, gap.get());
15 GetGameManager()->AddActor(*gap, true, false);
16 }
17 }
18 catch (const dtUtil::Exception& ex)
19 {
20 LOG_ERROR("Exception encountered trying to create a remote actor named \"" + msg.GetName()
21 + "\". The actor will be ignored. Message: " + ex.What()
22 + " | Actor ID: " + dtUtil::ToString(msg.GetAboutActorId()));
23 }
24 }
25 }
26 else if (!proxy->IsRemote())
27 {
28 ProcessLocalCreateActor(msg);
29 }
30 else
31 {
32 ProcessRemoteUpdateActor(msg, proxy);
33 }
34 }
GameActorProxy* proxy = GetGameManager()->FindGameActorById(msg.GetAboutActorId());查看本地是否有此actor,没有的话,此消息
携带的actor不是本地的,即为其他客户端的远程actor,完全是模拟(mimc)别的客户端的行为。跳转到:
dtCore::RefPtr<GameActorProxy> gap = ProcessRemoteCreateActor(msg);,通过此函数就成功创建remote actor。当然创建的是没有
纹理的,必须自己手工添加,并添加到GM里,在客户端就显示出来了。
流程就是这样。不足之后。以后补充。感谢Oman大哥的无私帮助。
做个广告:Delta3d技术交流群:12483772。欢迎加入!
转载请注明出处。