2013年6月9日 星期日

優化Kismet節點AttachToEvent

當在Kismet中設置事件節點時,會需要設定事件的發起者。對於事先放置在場景中的Actor,可以先選取Actor再建立事件,就會自動設定好發起者。然而對於動態產生的Actor,由於無法在編輯時直接參照,就會需要使用AttachToEvent節點來動態設定發起者。

這個節點會在每次觸發時產生事件複本掛到發起者的事件列表下,因為它可以將一個事件掛上多個發起者,如果不複製事件節點,多個發起者將共用同一個事件實例導致per instance data出錯。然而如果不曉得這個特性,將可能導致不必用的複製動作。實際上任何可能對同一個Actor重覆觸發的AttachToEvent都會導致重覆複製的事件實例,它們在條件成立時也會造成多重觸發,這通常不會是想要的結果。

接下來示範一下要怎麼修改AttachToEvent來避免重覆複製。我並不想去動引擎的原始碼,所以另外自訂一個事件叫BindEvent:
class SeqAct_BindEvent extends SequenceAction
 native(Sequence);

/** prefer to attach events to Controllers instead of Pawns (for events you want to persist beyond the target dying and respawning) */
var() bool bPreferController;

cpptext
{
 void Activated();
};

defaultproperties
{
 ObjName="Bind Event"
 ObjCategory="Event"

 VariableLinks.Empty
 VariableLinks(0)=(ExpectedType=class'SeqVar_Object',LinkDesc="Attachee")
 EventLinks(0)=(LinkDesc="Event")
}
程式碼大致跟AttachToEvent一樣,只是在綁定事件前先檢查是否已經綁定到此目標了:
void USeqAct_BindEvent::Activated()
{
...
 if (targets.Num() > 0 &&
  Events.Num() > 0)
 {
  USequence *Seq = ParentSequence;
  // then add the Events to the targets
  for (INT Idx = 0; Idx < targets.Num(); Idx++)
  {
   for (INT EventIdx = 0; EventIdx < Events.Num(); EventIdx++)
   {
    UBOOL bNeedsDuplicate = TRUE;
    AActor* Target = targets(Idx);
    TArray<USequenceEvent*>& DuplicateEvts = Events(EventIdx)->DuplicateEvts;
    INT DuplCount = DuplicateEvts.Num();
    for(INT DuplIdx = 0; DuplIdx < DuplCount; DuplIdx++ )
    {
     if( DuplicateEvts(DuplIdx)->Originator == Target )
     {
      bNeedsDuplicate = FALSE;
      break;
     }
    }

    if( bNeedsDuplicate )
    {
     // create a duplicate of the Event to avoid collision issues
...
}
然而還可以再進一步減少一次複製成本,第一次綁定時其實可以直接使用該事件實例,無需複製,只要確定不會有兩個Actor同時使用同一個事件實例即可:
void USeqAct_BindEvent::Activated()
{
...
 if (targets.Num() > 0 &&
  Events.Num() > 0)
 {
  USequence *Seq = ParentSequence;
  // then add the Events to the targets
  for (INT Idx = 0; Idx < targets.Num(); Idx++)
  {
   for (INT EventIdx = 0; EventIdx < Events.Num(); EventIdx++)
   {
    // If this event is not attached, use it instead of a duplicate.
    if( Events(EventIdx)->Originator == NULL )
    {
     USequenceEvent *evt = Events(EventIdx);
     evt->Originator = targets(Idx);
     targets(Idx)->GeneratedEvents.AddItem(evt);
     evt->Originator->eventReceivedNewEvent(evt);
    }
    else
    {
     UBOOL bNeedsDuplicate = TRUE;
     AActor* Target = targets(Idx);
     TArray<USequenceEvent*>& DuplicateEvts = Events(EventIdx)->DuplicateEvts;
     INT DuplCount = DuplicateEvts.Num();
     for(INT DuplIdx = 0; DuplIdx < DuplCount; DuplIdx++ )
     {
      if( DuplicateEvts(DuplIdx)->Originator == Target )
      {
       bNeedsDuplicate = FALSE;
       break;
      }
     }

     if( bNeedsDuplicate )
     {
      // create a duplicate of the Event to avoid collision issues
...
}

沒有留言:

張貼留言