UDN
Search public documentation:

DevelopmentKitGemsCreatingActorSelectionBoxesOrBracketsKR
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 홈 > UDK 젬 > 액터 선택 박스나 브래킷 만들기
UE3 홈 > 유저 인터페이스와 HUD > 액터 선택 박스나 브래킷 만들기

액터 선택 박스나 브래킷 만들기


문서 변경내역: James Tan 작성. 홍성진 번역.
UDK 2011년 3월 버전으로 최종 테스팅, PC 와 iOS 호환

개요


일부 게임 오브젝트는 플레이어의 의사 결정을 돕는 화면상 시각 정보를 더 필요로 합니다. 이러한 결정은 아이템 줍기, 타게팅하기나 정보 얻기 등 다양합니다.

GameInfo


액터 선택 렌더링은 HUD 내에서 수행됩니다. 자체 커스텀 HUD 를 사용하기 위해선, 새 game info 를 만들어야 합니다. 새 game info 는 테스트를 쉽게 하기 위해 UTDeathMatch 를 extend 합니다. 디폴트로 UTDeathMatch 는 더 새로운 ScaleForm HUD 를 사용할 것입니다. bUseClassicHUD 참으로 설정하면 game info 가 'classic' HUD 를 강제로 사용하게 합니다. HUDType 을 새 HUD 클래스로 설정하는 것으로 새 game info 는 끝입니다.

ASIGameInfo.uc
class ASIGameInfo extends UTDeathMatch;

defaultproperties
{
  bUseClassicHUD=true
  HUDType=class'ASIHUD'
}

관련 토픽

HUDInterface


액터에서 추가 검사를 하거나 부가 정보를 얻는 데는 빈 인터페이스가 사용됩니다. 거기서 이 인터페이스를 구현한다면 테스트를 위해 액터 cast 하기도 훨씬 간단하며, 그렇다면 액터 선택 인터페이스를 렌더링합니다. 자체 서브클래스에서만 이 작업을 하려할 경우 좋습니다. 커다란 type casting 블록을 피할 수 있는 것입니다.

ASIHUDInterface.uc
interface ASIHUDInterface;

예로 인터페이스 구현 메서드 없이라면, 이렇게 해야 할 것입니다.

if (PawnSubClass(Actor) != None || VehicleSubClass(Actor) != None || DroppedPickupSubClass(Actor) != None)
{
  RenderActorSelection(Actor);
}

보듯이 아래줄로 내려갈수록 type cast 가 남발되고 있습니다. 새 클래스를 추가할 때마다 자동으로 찍어 주지도 못하니 매우 성가시기도 합니다.

구현 메서드를 사용하면 ASIHUDInterface 를 구현하는 모든 클래스를 자동으로 지원합니다. 나중에 추가할 수도 있는 클래스를 전부 포함하기도 합니다.

if (ASIHUDInterface(Actor) != None)
{
  RenderActorSelection(Actor, ASIHUDInterface(Actor));
}

관련 토픽

HUD


플레이어 앞의 액터를 찾는 데는 trace 가 사용됩니다. trace 액터는 플레이어 앞에 가끔 안보이는 블로킹 액터가 있을 수 있기에 사용됩니다. trace 액터 반복처리의 각 결과에 대해, 필터가 적용되어 액터 선택 인터페이스를 렌더링하기 위한 액터가 적절히 선택되었나 확인합니다.

ASIHUD.uc
class ASIHUD extends UTHUD;

event PostRender()
{
  local Actor HitActor;
  local Vector HitLocation, HitNormal, EyeLocation;
  local Rotator EyeRotation;

  Super.PostRender();

  // player owner 가 설정되었는지 확인
  if (PlayerOwner != None)
  {
    // 플레이어 카메라 위치 및 회전 구하기
    PlayerOwner.GetPlayerViewPoint(EyeLocation, EyeRotation);

    // 플레이어가 바라보는 것을 찾기 위해 trace 수행
    // 중요치 않은 오브젝트를 무시할 수 있도록 trace 액터 사용
    ForEach TraceActors
    (
      class'Actor',
      HitActor,
      HitLocation,
      HitNormal,
      EyeLocation + Vector(EyeRotation) * PlayerOwner.InteractDistance,
      EyeLocation,
      Vect(1.f, 1.f, 1.f),,
      TRACEFLAG_Bullet
    )
    {
      // hit actor 가 player owner 라면, player owner 의 pawn 또는 hit actor 는 안보임
      if (HitActor == PlayerOwner || HitActor == PlayerOwner.Pawn || !FastTrace(HitActor.Location, EyeLocation))
      {
   // 이 hit actor 무시
   continue;
      }

      // 여기에 액터 선택 렌더링
    }
  }
}

플레이어가 바라보는 액터가 검출되면, 선택 박스나 브래킷을 그립니다. 선택 렌더링 스타일은 HUD 내에 설정되나, ASIHUDInterface 가 선택이 렌더링되는 스타일을 반환하게 만들 필요는 없습니다. 여기 선택 렌더링 스타일을 설정하는 데 enum 이 사용됩니다.

enum EActorBracketStyle
{
   EABS_2DActorBrackets,
   EABS_3DActorBrackets,
   EABS_2DBox,
   EABS_3DBox,
   EABS_3DCircle
};

var EActorBracketStyle ActorBracketStyle;

마지막으로 ActorBracketStyle 값으로 결정된 렌더링 스타일을 대표(delegate)하기 위해 switch 를 사용합니다. (뒷쪽에서는, delegate 가 사용되었을 수도 있습니다.)

      // 여기 액터 선택을 렌더링
      // 원하는 렌더링 스타일 테스트
      switch (ActorBracketStyle)
      {
      case EABS_2DActorBrackets:
   RenderTwoDeeActorBrackets(HitActor, HUDInterface);
   break;

      case EABS_3DActorBrackets:
   RenderThreeDeeActorBrackets(HitActor, HUDInterface);
   break;

      case EABS_2DBox:
   RenderTwoDeeBox(HitActor, HUDInterface);
   break;

      case EABS_3DBox:
   RenderThreeDeeBox(HitActor, HUDInterface);
   break;

      case EABS_3DCircle:
   RenderThreeDeeCircle(HitActor, HUDInterface);
   break;

      default:
   break;
      }

관련 토픽

2D 경계 계산


2D 박스 또는 2D 브래킷 선택을 렌더링하려면 액터 컴포넌트 경계 박스의 2D 표현을 계산해야 합니다. 이를 위해 각 박스 캔버스 공간을 구하고자 각 박스 좌표가 투영(project)됩니다. 투영된 각 X Y 좌표에 대해, 최소와 최대가 검출됩니다.

ASIHUD.uc
function Box GetTwoDeeActorBoundingBox(Actor Actor)
{
  local Box ComponentsBoundingBox, OutBox;
  local Vector BoundingBoxCoordinates[8];
  local int i;

  Actor.GetComponentsBoundingBox(ComponentsBoundingBox);

  // Z1
  // X1, Y1
  BoundingBoxCoordinates[0].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[0].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[0].Z = ComponentsBoundingBox.Min.Z;
  BoundingBoxCoordinates[0] = Canvas.Project(BoundingBoxCoordinates[0]);
  // X2, Y1
  BoundingBoxCoordinates[1].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[1].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[1].Z = ComponentsBoundingBox.Min.Z;
  BoundingBoxCoordinates[1] = Canvas.Project(BoundingBoxCoordinates[1]);
  // X1, Y2
  BoundingBoxCoordinates[2].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[2].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[2].Z = ComponentsBoundingBox.Min.Z;
  BoundingBoxCoordinates[2] = Canvas.Project(BoundingBoxCoordinates[2]);
  // X2, Y2
  BoundingBoxCoordinates[3].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[3].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[3].Z = ComponentsBoundingBox.Min.Z;
  BoundingBoxCoordinates[3] = Canvas.Project(BoundingBoxCoordinates[3]);

  // Z2
  // X1, Y1
  BoundingBoxCoordinates[4].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[4].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[4].Z = ComponentsBoundingBox.Max.Z;
  BoundingBoxCoordinates[4] = Canvas.Project(BoundingBoxCoordinates[4]);
  // X2, Y1
  BoundingBoxCoordinates[5].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[5].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[5].Z = ComponentsBoundingBox.Max.Z;
  BoundingBoxCoordinates[5] = Canvas.Project(BoundingBoxCoordinates[5]);
  // X1, Y2
  BoundingBoxCoordinates[6].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[6].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[6].Z = ComponentsBoundingBox.Max.Z;
  BoundingBoxCoordinates[6] = Canvas.Project(BoundingBoxCoordinates[6]);
  // X2, Y2
  BoundingBoxCoordinates[7].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[7].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[7].Z = ComponentsBoundingBox.Max.Z;
  BoundingBoxCoordinates[7] = Canvas.Project(BoundingBoxCoordinates[7]);

  // 좌 상 우 하 좌표 찾기
  OutBox.Min.X = Canvas.ClipX;
  OutBox.Min.Y = Canvas.ClipY;
  OutBox.Max.X = 0;
  OutBox.Max.Y = 0;

  // 경계 박스 좌표를 통해 반복처리
  for (i = 0; i < ArrayCount(BoundingBoxCoordinates); ++i)
  {
    // 가장 작은 X 좌표 검출
    if (OutBox.Min.X > BoundingBoxCoordinates[i].X)
    {
      OutBox.Min.X = BoundingBoxCoordinates[i].X;
    }

    // 가장 작은 Y 좌표 검출
    if (OutBox.Min.Y > BoundingBoxCoordinates[i].Y)
    {
      OutBox.Min.Y = BoundingBoxCoordinates[i].Y;
    }

    // 가장 큰 X 좌표 검출
    if (OutBox.Max.X < BoundingBoxCoordinates[i].X)
    {
      OutBox.Max.X = BoundingBoxCoordinates[i].X;
    }

    // 가장 큰 Y 좌표 검출
    if (OutBox.Max.Y < BoundingBoxCoordinates[i].Y)
    {
      OutBox.Max.Y = BoundingBoxCoordinates[i].Y;
    }
  }

  return OutBox;
}

관련 토픽

2D 선택 박스


2D 선택 박스로 선택된 액터

ActorSelection2DBox.jpg

이 스타일을 렌더링하기 위해, 액터 컴포넌트 경계 박스가 캔버스 공간으로 투영됩니다. 거기서 단순히, 투영된 경계 박스를 나타내는 박스가 렌더링됩니다.

ASIHUD.uc
function RenderTwoDeeBox(Actor Actor, ASIHUDInterface HUDInterface)
{
  local Box ActorBoundingBox;

  if (Canvas == None || Actor == None)
  {
    return;
  }

  ActorBoundingBox = GetTwoDeeActorBoundingBox(Actor);

  // 액터 브래킷 그리기
  Canvas.SetDrawColor(255, 255, 255);
  // 좌상단 위치
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Min.Y);
  // 박스 그리기
  Canvas.DrawBox
  (
    (ActorBoundingBox.Max.X - ActorBoundingBox.Min.X),
    (ActorBoundingBox.Max.Y - ActorBoundingBox.Min.Y)
  );
}

2D 선택 브래킷


2D 선택 브래킷으로 선택된 액터

ActorSelection2DBracket.jpg

이 스타일을 렌더링하기 위해, 액터 컴포넌트 경계 박스가 캔버스 공간 상에 투영됩니다. 거기서 브래킷의 각 코너가 렌더링됩니다.

ASIHUD.uc
function RenderTwoDeeActorBrackets(Actor Actor, ASIHUDInterface HUDInterface)
{
  local Box ActorBoundingBox;
  local int ActualWidth, ActualHeight;

  if (Canvas == None || Actor == None)
  {
    return;
  }

  ActorBoundingBox = GetTwoDeeActorBoundingBox(Actor);

  // 폭과 높이 계산
  ActualWidth = (ActorBoundingBox.Max.X - ActorBoundingBox.Min.X) * 0.3f;
  ActualHeight = (ActorBoundingBox.Max.Y - ActorBoundingBox.Min.Y) * 0.3f;

  // 액터 브래킷 그리기
  Canvas.SetDrawColor(255, 255, 255);

  // 좌상단
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Min.Y);
  Canvas.DrawRect(ActualWidth, 2);
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Min.Y);
  Canvas.DrawRect(2, ActualHeight);

  // 우상단
  Canvas.SetPos(ActorBoundingBox.Max.X - ActualWidth - 2, ActorBoundingBox.Min.Y);
  Canvas.DrawRect(ActualWidth, 2);
  Canvas.SetPos(ActorBoundingBox.Max.X - 2, ActorBoundingBox.Min.Y);
  Canvas.DrawRect(2, ActualHeight);

  // 좌하단
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Max.Y - 2);
  Canvas.DrawRect(ActualWidth, 2);
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Max.Y - ActualHeight - 2);
  Canvas.DrawRect(2, Actualheight);

  // 우하단
  Canvas.SetPos(ActorBoundingBox.Max.X - ActualWidth - 2, ActorBoundingBox.Max.Y - 2);
  Canvas.DrawRect(ActualWidth + 2, 2);
  Canvas.SetPos(ActorBoundingBox.Max.X - 2, ActorBoundingBox.Max.Y - ActualHeight - 2);
  Canvas.DrawRect(2, ActualHeight + 2);
}

3D 선택 박스


3D 선택 박스로 선택된 액터

ActorSelection3DBox.jpg

3D 선택 박스를 렌더링하기 위해, 여기서는 HUD 내 Draw3DLine 함수가 사용되었습니다. 이 함수는 폭이 1 픽셀인 선만 그릴 수 있다는 제한이 있습니다. 이 제한을 고치려면, 각 경계 박스 좌표를 캔버스 공간으로 투영한 다음 2D 선을 대신 그립니다.

ASIHUD.uc
function RenderThreeDeeBox(Actor Actor, ASIHUDInterface HUDInterface)
{
  local Box ComponentsBoundingBox;
  local Vector BoundingBoxCoordinates[8];
  local int i;

  if (Actor == None)
  {
    return;
  }

  Actor.GetComponentsBoundingBox(ComponentsBoundingBox);

  // Z1
  // X1, Y1
  BoundingBoxCoordinates[0].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[0].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[0].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y1
  BoundingBoxCoordinates[1].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[1].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[1].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y2
  BoundingBoxCoordinates[2].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[2].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[2].Z = ComponentsBoundingBox.Min.Z;
  // X1, Y2
  BoundingBoxCoordinates[3].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[3].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[3].Z = ComponentsBoundingBox.Min.Z;

  // Z2
  // X1, Y1
  BoundingBoxCoordinates[4].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[4].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[4].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y1
  BoundingBoxCoordinates[5].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[5].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[5].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y2
  BoundingBoxCoordinates[6].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[6].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[6].Z = ComponentsBoundingBox.Max.Z;
  // X1, Y2
  BoundingBoxCoordinates[7].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[7].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[7].Z = ComponentsBoundingBox.Max.Z;

  for (i = 0; i < 4; ++i)
  {
    Draw3DLine(BoundingBoxCoordinates[i], BoundingBoxCoordinates[(i == 3) ? 0 : i + 1], class'HUD'.default.WhiteColor);
  }

  for (i = 4; i < 8; ++i)
  {
    Draw3DLine(BoundingBoxCoordinates[i], BoundingBoxCoordinates[(i == 7) ? 4 : i + 1], class'HUD'.default.WhiteColor);
  }

  for (i = 0; i < 4; ++i)
  {
    Draw3DLine(BoundingBoxCoordinates[i], BoundingBoxCoordinates[i + 4], class'HUD'.default.WhiteColor);
  }
}

3D 선택 브래킷


3D 선택 브래킷으로 선택된 액터

ActorSelection3DBracket.jpg

3D 브래킷 선택 박스를 렌더링하기 위해, HUD 내 Draw3DLine 함수가 사용되었습니다. 다시금 이 제한은 화면 공간에 필수 좌표 전부를 투영하고서 2D 선을 그려 제한을 고칠 수 있습니다. 이 선택 스타일을 렌더링하려면 먼저 내부 좌표를 계산한 다음 선을 그립니다.

ASIHUD.uc
function RenderThreeDeeActorBrackets(Actor Actor, ASIHUDInterface HUDInterface)
{
  local Box ComponentsBoundingBox;
  local Vector BoundingBoxCoordinates[8], InnerBoxCoordinates[8];
  local int i;
  local float f;

  if (Actor == None)
  {
    return;
  }

  Actor.GetComponentsBoundingBox(ComponentsBoundingBox);

  // Z1
  // X1, Y1
  BoundingBoxCoordinates[0].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[0].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[0].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y1
  BoundingBoxCoordinates[1].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[1].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[1].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y2
  BoundingBoxCoordinates[2].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[2].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[2].Z = ComponentsBoundingBox.Min.Z;
  // X1, Y2
  BoundingBoxCoordinates[3].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[3].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[3].Z = ComponentsBoundingBox.Min.Z;

  // Z2
  // X1, Y1
  BoundingBoxCoordinates[4].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[4].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[4].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y1
  BoundingBoxCoordinates[5].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[5].Y = ComponentsBoundingBox.Min.Y;
  BoundingBoxCoordinates[5].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y2
  BoundingBoxCoordinates[6].X = ComponentsBoundingBox.Max.X;
  BoundingBoxCoordinates[6].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[6].Z = ComponentsBoundingBox.Max.Z;
  // X1, Y2
  BoundingBoxCoordinates[7].X = ComponentsBoundingBox.Min.X;
  BoundingBoxCoordinates[7].Y = ComponentsBoundingBox.Max.Y;
  BoundingBoxCoordinates[7].Z = ComponentsBoundingBox.Max.Z;

  // 내부 X 계산
  f = VSize(BoundingBoxCoordinates[4] - BoundingBoxCoordinates[5]) * 0.3f;

  // Z1
  // X1, Y1
  InnerBoxCoordinates[0].X = ComponentsBoundingBox.Min.X + f;
  InnerBoxCoordinates[0].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[0].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y1
  InnerBoxCoordinates[1].X = ComponentsBoundingBox.Max.X - f;
  InnerBoxCoordinates[1].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[1].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y2
  InnerBoxCoordinates[2].X = ComponentsBoundingBox.Max.X - f;
  InnerBoxCoordinates[2].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[2].Z = ComponentsBoundingBox.Min.Z;
  // X1, Y2
  InnerBoxCoordinates[3].X = ComponentsBoundingBox.Min.X + f;
  InnerBoxCoordinates[3].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[3].Z = ComponentsBoundingBox.Min.Z;

  // Z2
  // X1, Y1
  InnerBoxCoordinates[4].X = ComponentsBoundingBox.Min.X + f;
  InnerBoxCoordinates[4].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[4].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y1
  InnerBoxCoordinates[5].X = ComponentsBoundingBox.Max.X - f;
  InnerBoxCoordinates[5].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[5].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y2
  InnerBoxCoordinates[6].X = ComponentsBoundingBox.Max.X - f;
  InnerBoxCoordinates[6].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[6].Z = ComponentsBoundingBox.Max.Z;
  // X1, Y2
  InnerBoxCoordinates[7].X = ComponentsBoundingBox.Min.X + f;
  InnerBoxCoordinates[7].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[7].Z = ComponentsBoundingBox.Max.Z;

  for (i = 0; i < 8; ++i)
  {
    Draw3DLine(BoundingBoxCoordinates[i], InnerBoxCoordinates[i], class'HUD'.default.WhiteColor);
  }

  // 내부 Y 계산
  f = VSize(BoundingBoxCoordinates[4] - BoundingBoxCoordinates[7]) * 0.3f;

  // Z1
  // X1, Y1
  InnerBoxCoordinates[0].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[0].Y = ComponentsBoundingBox.Min.Y + f;
  InnerBoxCoordinates[0].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y1
  InnerBoxCoordinates[1].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[1].Y = ComponentsBoundingBox.Min.Y + f;
  InnerBoxCoordinates[1].Z = ComponentsBoundingBox.Min.Z;
  // X2, Y2
  InnerBoxCoordinates[2].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[2].Y = ComponentsBoundingBox.Max.Y - f;
  InnerBoxCoordinates[2].Z = ComponentsBoundingBox.Min.Z;
  // X1, Y2
  InnerBoxCoordinates[3].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[3].Y = ComponentsBoundingBox.Max.Y - f;
  InnerBoxCoordinates[3].Z = ComponentsBoundingBox.Min.Z;

  // Z2
  // X1, Y1
  InnerBoxCoordinates[4].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[4].Y = ComponentsBoundingBox.Min.Y + f;
  InnerBoxCoordinates[4].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y1
  InnerBoxCoordinates[5].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[5].Y = ComponentsBoundingBox.Min.Y + f;
  InnerBoxCoordinates[5].Z = ComponentsBoundingBox.Max.Z;
  // X2, Y2
  InnerBoxCoordinates[6].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[6].Y = ComponentsBoundingBox.Max.Y - f;
  InnerBoxCoordinates[6].Z = ComponentsBoundingBox.Max.Z;
  // X1, Y2
  InnerBoxCoordinates[7].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[7].Y = ComponentsBoundingBox.Max.Y - f;
  InnerBoxCoordinates[7].Z = ComponentsBoundingBox.Max.Z;

  for (i = 0; i < 8; ++i)
  {
    Draw3DLine(BoundingBoxCoordinates[i], InnerBoxCoordinates[i], class'HUD'.default.WhiteColor);
  }

  // 내부 Z 계산
  f = VSize(BoundingBoxCoordinates[0] - BoundingBoxCoordinates[4]) * 0.3f;

  // Z1
  // X1, Y1
  InnerBoxCoordinates[0].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[0].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[0].Z = ComponentsBoundingBox.Min.Z + f;
  // X2, Y1
  InnerBoxCoordinates[1].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[1].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[1].Z = ComponentsBoundingBox.Min.Z + f;
  // X2, Y2
  InnerBoxCoordinates[2].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[2].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[2].Z = ComponentsBoundingBox.Min.Z + f;
  // X1, Y2
  InnerBoxCoordinates[3].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[3].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[3].Z = ComponentsBoundingBox.Min.Z + f;

  // Z2
  // X1, Y1
  InnerBoxCoordinates[4].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[4].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[4].Z = ComponentsBoundingBox.Max.Z - f;
  // X2, Y1
  InnerBoxCoordinates[5].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[5].Y = ComponentsBoundingBox.Min.Y;
  InnerBoxCoordinates[5].Z = ComponentsBoundingBox.Max.Z - f;
  // X2, Y2
  InnerBoxCoordinates[6].X = ComponentsBoundingBox.Max.X;
  InnerBoxCoordinates[6].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[6].Z = ComponentsBoundingBox.Max.Z - f;
  // X1, Y2
  InnerBoxCoordinates[7].X = ComponentsBoundingBox.Min.X;
  InnerBoxCoordinates[7].Y = ComponentsBoundingBox.Max.Y;
  InnerBoxCoordinates[7].Z = ComponentsBoundingBox.Max.Z - f;

  for (i = 0; i < 8; ++i)
  {
    Draw3DLine(BoundingBoxCoordinates[i], InnerBoxCoordinates[i], class'HUD'.default.WhiteColor);
  }
}

3D 선택 원


3D 선택 원으로 선택된 액터

ActorSelection3DCircle.jpg

이 선택 스타일은 HUD 내 Draw3DLine 함수를 사용합니다. 모든 오프셋을 캔버스 좌표 속으로 투영한 다음 2D 선을 그리는 것도 가능합니다. 모든 오프셋은 벡터의 Z 성분을 늘려 조절했습니다. 이를 통해 원을 위쪽으로 올릴 수 있습니다. 다른 벡터 오프셋을 추가하여 원의 위치를 옮길 수 있습니다. 원 내의 점 수를 늘리려면, 계산되는 점의 수를 조절해야 합니다. 언리얼 회전 단위(현재 4096)로 yaw 증감값을 늘리거나 줄이면 됩니다. 오프셋 배열의 크기도 (현재 65536/4096, 16인) yaw 증감값에 일치시키기 위해 늘려야 합니다.

ASIHUD.uc
function RenderThreeDeeCircle(Actor Actor, ASIHUDInterface HUDInterface)
{
  local Rotator Angle;
  local Vector Radius, Offsets[16];
  local Box ComponentsBoundingBox;
  local float Width, Height;
  local int i;

  if (Actor == None)
  {
    return;
  }

  Actor.GetComponentsBoundingBox(ComponentsBoundingBox);

  Width = ComponentsBoundingBox.Max.X - ComponentsBoundingBox.Min.X;
  Height = ComponentsBoundingBox.Max.Y - ComponentsBoundingBox.Min.Y;

  Radius.X = (Width > Height) ? Width : Height;
  i = 0;

  for (Angle.Yaw = 0; Angle.Yaw < 65536; Angle.Yaw += 4096)
  {
    // 오프셋 계산
    Offsets[i] = Actor.Location + (Radius >> Angle) + Vect(0.f, 0.f, 16.f);
    i++;
  }

  // 모든 라인 그리기
  for (i = 0; i < ArrayCount(Offsets); ++i)
  {
    if (i == ArrayCount(Offsets) - 1)
    {
      Draw3DLine(Offsets[i], Offsets[0], class'HUD'.default.WhiteColor);
    }
    else
    {
      Draw3DLine(Offsets[i], Offsets[i + 1], class'HUD'.default.WhiteColor);
    }
  }
}

테스팅


이 기능을 테스트하려면, DM-Deck 을 로드하고 PIE 게임 타입을 ASIGameInfo 로 설정합니다. 저장하고 플레이.

관련 토픽

다운로드