UDN
Search public documentation:

ReplicationProxyKR
English Translation
日本語訳
中国翻译

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 홈 > 네트워킹과 리플리케이션 > 프록시 패턴

프록시 패턴


문서 변경내역: James Tan 작성. 홍성진 번역.

개요


프록시 패턴은 클라이언트에는 존재하지 않지만, 시/청각화시킬 필요가 있는 액터나 오브젝트가 있을 때 사용하는 패턴입니다. 클라이언트에는 액터가 존재하지 않기 때문에, 서버가 클라이언트로 직접 리플리케이트할 수 있는 방법이 없습니다. 오브젝트는 전혀 리플리케이션을 지원하지 않습니다. 액터나 오브젝트가 서버에서 하는 작업을 시뮬레이트하기 위해 프록시 패턴을 사용합니다. 클라이언트 상에서 프록시 자체를 리플리케이트할 필요 없이 프록시를 통해서 말입니다.

웨폰 어태치먼트


이 패턴은 언리얼 토너먼트의 웨폰 어태치먼트에 사용됩니다. 언리얼 엔진 3 의 웨폰 은 무기를 소유하는 클라이언트와 서버 사이에만 연관되어 있습니다. 그러나 클라이언트 역시 착용중인 무기에 대한 정보가 약간 필요합니다. 이 작업은 서버상에 웨폰 어태치먼트를 만든 다음 모든 클라이언트에 리플리케이트하여 이루어냅니다. 그 후 서버와 클라이언트 둘 다 웨폰 어태치먼트 액터를 스폰합니다. 웨폰은 서버상에서 업데이트되기에, 웨폰이 폰 오너에게 변경사항을 알립니다. 그리고서 이 변경사항을 클라이언트로 리플리케이트시켜 웨폰 어태치먼트를 업데이트하는 것입니다.

UTPawn.uc
var repnotify class<UTWeaponAttachment> CurrentWeaponAttachmentClass;
var UTWeaponAttachment CurrentWeaponAttachment;

replication
{
  if (bNetDirty)
    CurrentWeaponAttachmentClass;
}

simulated event ReplicatedEvent(name VarName)
{
  if (VarName == 'CurrentWeaponAttachmentClass')
  {
    WeaponAttachmentChanged();
  }
}

simulated function WeaponAttachmentChanged()
{
  if ((CurrentWeaponAttachment == None || CurrentWeaponAttachment.Class != CurrentWeaponAttachmentClass) && Mesh.SkeletalMesh != None)
  {
    if (CurrentWeaponAttachment!=None)
    {
      CurrentWeaponAttachment.DetachFrom(Mesh);
      CurrentWeaponAttachment.Destroy();
    }

    if (CurrentWeaponAttachmentClass!=None)
    {
      CurrentWeaponAttachment = Spawn(CurrentWeaponAttachmentClass,self);
      CurrentWeaponAttachment.Instigator = self;
    }
    else
      CurrentWeaponAttachment = none;

    if (CurrentWeaponAttachment != None)
    {
      CurrentWeaponAttachment.AttachTo(self);
      CurrentWeaponAttachment.SetSkin(ReplicatedBodyMaterial);
      CurrentWeaponAttachment.ChangeVisibility(bWeaponAttachmentVisible);
    }
  }
}

simulated function WeaponFired(Weapon InWeapon, bool bViaReplication, optional vector HitLocation)
{
  if (CurrentWeaponAttachment != None)
  {
    if (!IsFirstPerson())
    {
      CurrentWeaponAttachment.ThirdPersonFireEffects(HitLocation);
    }
    else
    {
      CurrentWeaponAttachment.FirstPersonFireEffects(Weapon, HitLocation);

      if (class'Engine'.static.IsSplitScreen() && CurrentWeaponAttachment.EffectIsRelevant(CurrentWeaponAttachment.Location, false, CurrentWeaponAttachment.MaxFireEffectDistance))
      {
        CurrentWeaponAttachment.CauseMuzzleFlash();
      }
    }

    if (HitLocation != Vect(0,0,0) && (WorldInfo.NetMode == NM_ListenServer || WorldInfo.NetMode == NM_Standalone || bViaReplication))
    {
      CurrentWeaponAttachment.PlayImpactEffects(HitLocation);
    }
  }
}

UTWeaponAttachment.uc
simulated function CauseMuzzleFlash()
{
  local ParticleSystem MuzzleTemplate;

  if ((!WorldInfo.bDropDetail && !class'Engine'.static.IsSplitScreen()) || WorldInfo.IsConsoleBuild(CONSOLE_Mobile))
  {
    if (MuzzleFlashLight == None)
    {
      if (MuzzleFlashLightClass != None)
      {
        MuzzleFlashLight = new(Outer) MuzzleFlashLightClass;

        if (Mesh != None && Mesh.GetSocketByName(MuzzleFlashSocket) != None)
        {
          Mesh.AttachComponentToSocket(MuzzleFlashLight, MuzzleFlashSocket);
        }
        else if (OwnerMesh != None)
        {
          OwnerMesh.AttachComponentToSocket(MuzzleFlashLight, AttachmentSocket);
        }
      }
    }
    else
    {
      MuzzleFlashLight.ResetLight();
    }
  }

  if (MuzzleFlashPSC != none)
  {
    if (!bMuzzleFlashPSCLoops || !MuzzleFlashPSC.bIsActive)
    {
      if (Instigator != None && Instigator.FiringMode == 1 && MuzzleFlashAltPSCTemplate != None)
      {
        MuzzleTemplate = MuzzleFlashAltPSCTemplate;
      }
      else
      {
        MuzzleTemplate = MuzzleFlashPSCTemplate;
      }

      if (MuzzleTemplate != MuzzleFlashPSC.Template)
      {
        MuzzleFlashPSC.SetTemplate(MuzzleTemplate);
      }

      SetMuzzleFlashParams(MuzzleFlashPSC);
      MuzzleFlashPSC.ActivateSystem();
    }
  }

  SetTimer(MuzzleFlashDuration, false, 'MuzzleFlashTimer');
}

simulated function ThirdPersonFireEffects(vector HitLocation)
{
  local UTPawn P;

  if (EffectIsRelevant(Location,false,MaxFireEffectDistance))
  {
    CauseMuzzleFlash();
  }

  P = UTPawn(Instigator);
  if (P != None && P.GunRecoilNode != None)
  {
    P.GunRecoilNode.bPlayRecoil = true;
  }

  if (Instigator.FiringMode == 1 && AltFireAnim != 'None')
  {
    Mesh.PlayAnim(AltFireAnim,,, false);
  }
  else if (FireAnim != 'None')
  {
    Mesh.PlayAnim(FireAnim,,, false);
  }
}

참고

UTPawn 과 UTWeaponAttachment 둘 다 일정 부분만 나타나 있으니, 전체 로직은 소스 파일을 확인하시기 바랍니다.

결론


이 패턴은 클라이언트에 존재하지 않은 액터나 오브젝트의 시/청각 효과가 필요할 때 사용합니다. 웨폰 어태치먼트 말고 이 패턴이 좋은 경우라면:
  • 게임플레이 이펙트가 있는 아머/클로딩 어태치먼트.
  • 게임플레이 이펙트가 있는 비히클 터렛 어태치먼트.