原博客地址:http://www.cnblogs.com/kanego/archive/2011/11/03/2234939.html
前言:纠结了很长时间,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的属性传输到另一个网络端。 详细看代码:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
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)进行处理。继续跟踪,上其代码:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
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); } 看来,
1 | 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 }
1 | 终于找到,原来是通过这里把创建actor的消息发送出去了。发送过程到此结束。<br>下面来看delta3d中GM的默认处理过程。其处理过程是在组件DefaultMessageProcessor中进行处理的。其ProcessMessage()函数:<br> if (msg.GetMessageType() == MessageType::INFO_ACTOR_CREATED)<br> {<br> ProcessCreateActor( static_cast < const ActorUpdateMessage&>(msg));<br> },直接交给ProcessCreateActor()进行处理。 |
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
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,没有的话,此消息<br>携带的actor不是本地的,即为其他客户端的远程actor,完全是模拟(mimc)别的客户端的行为。跳转到:<br>dtCore::RefPtr<GameActorProxy> gap = ProcessRemoteCreateActor(msg);,通过此函数就成功创建remote actor。当然创建的是没有<br>纹理的,必须自己手工添加,并添加到GM里,在客户端就显示出来了。<br><br><br>流程就是这样。不足之后。以后补充。感谢Oman大哥的无私帮助。<br><br><span style= "color: #ff0000; font-size: 14pt;" ><strong>做个广告:Delta3d技术交流群:12483772。欢迎加入!</strong></span> |