UDN
Search public documentation:

DevelopmentKitGemsCreatingActorSelectionBoxesOrBrackets
日本語訳
中国翻译
한국어

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 Home > Unreal Development Kit Gems > Creating actor selection boxes or brackets
UE3 Home > User Interfaces & HUDs > Creating actor selection boxes or brackets

Creating actor selection boxes or brackets


Last tested against UDK Mar, 2011
PC and iOS compatible

Overview


Some game objects require more visual information on screen which help the player in making decisions. These decisions can range from picking up an item, attacking a target or acquiring some other kind of information.

GameInfo


Rendering the actor selections is done within the HUD. In order to use our own custom HUD, a new game info has to be created. The new game info extends UTDeathMatch to make it easier to test. By default, UTDeathMatch will use the newer ScaleForm HUD. Setting bUseClassicHUD to true will enforce the game info to use the 'classic' HUD. Set the HUDType to the new HUD class and the new game info is finished.

ASIGameInfo.uc
class ASIGameInfo extends UTDeathMatch;

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

Related topics

HUDInterface


An empty interface is used to do extra checks or retrieve extra information from actors. It's also simplier to cast actors to test if they implement this interface, and if they do, you render an actor selection interface. This is useful if you want to only do this on your own subclasses. This prevents a large type casting block.

ASIHUDInterface.uc
interface ASIHUDInterface;

For example, without the interface implementation method; you would need to do this.

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

As you can see, this is very wasteful as you do a lot of type casts as you go further down the line. It's also very brittle as it won't automatically pick up new classes you add.

By using the implementation method, you support all classes that implement ASIHUDInterface automatically. This also includes all future classes that you may add.

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

Related topics

HUD


To find the actor in front of the player a trace is used. Trace actors is used because occasionally invisible blocking actors can be in front of the player. For each result of the trace actor iteration, a filter is applied to ensure an appropriate actor is chosen to be render the actor selection interface.

ASIHUD.uc
class ASIHUD extends UTHUD;

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

  Super.PostRender();

  // Ensure the player owner is set
  if (PlayerOwner != None)
  {
    // Retrieve the player camera location and rotation
    PlayerOwner.GetPlayerViewPoint(EyeLocation, EyeRotation);

    // Perform a trace to find what the player is looking at
    // Use trace actors so that we can ignore unimportant objects
    ForEach TraceActors
    (
      class'Actor', 
      HitActor, 
      HitLocation, 
      HitNormal, 
      EyeLocation + Vector(EyeRotation) * PlayerOwner.InteractDistance, 
      EyeLocation, 
      Vect(1.f, 1.f, 1.f),, 
      TRACEFLAG_Bullet
    )
    {
      // If the hit actor is the player owner, the player owner's pawn or the hit actor isn't visible
      if (HitActor == PlayerOwner || HitActor == PlayerOwner.Pawn || !FastTrace(HitActor.Location, EyeLocation))
      {
        // Ignore this hit actor
        continue;
      }

      // Render the actor selection here
    }
  }
}

Once the actor the player is looking at has been detected, the selection box or bracket is then drawn. The selection render style is set within the HUD, but it is trivial to make the ASIHUDInterface return which style the selection is rendered as. Here, an enum is used to set the selection rendering style.

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

var EActorBracketStyle ActorBracketStyle;

Finally, a switch is used to delegate the rendering styles determined by the value of ActorBracketStyle. (In hind sight, a delegate could have also been used).

// Render the actor selection here
// Test what rendering style we want
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;
}

Related topics

2D bounds calculation


To render the 2D box or 2D bracket selection, a 2D representation of the actor component bounding box has to be calculated. To do this, each box coordinate is projected to get each box coordinates canvas space. For each X and Y coordinate projected, the smallest and largest of each is detected.

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]);

  // Find the left, top, right and bottom coordinates
  OutBox.Min.X = Canvas.ClipX;
  OutBox.Min.Y = Canvas.ClipY;
  OutBox.Max.X = 0;
  OutBox.Max.Y = 0;

  // Iterate though the bounding box coordinates
  for (i = 0; i < ArrayCount(BoundingBoxCoordinates); ++i)
  {
    // Detect the smallest X coordinate
    if (OutBox.Min.X > BoundingBoxCoordinates[i].X)
    {
      OutBox.Min.X = BoundingBoxCoordinates[i].X;
    }

    // Detect the smallest Y coordinate
    if (OutBox.Min.Y > BoundingBoxCoordinates[i].Y)
    {
      OutBox.Min.Y = BoundingBoxCoordinates[i].Y;
    }

    // Detect the largest X coordinate
    if (OutBox.Max.X < BoundingBoxCoordinates[i].X)
    {
      OutBox.Max.X = BoundingBoxCoordinates[i].X;
    }

    // Detect the largest Y coordinate
    if (OutBox.Max.Y < BoundingBoxCoordinates[i].Y)
    {
      OutBox.Max.Y = BoundingBoxCoordinates[i].Y;
    }
  }

  return OutBox;
}

Related topics

2D selection box


Actor selected with a 2D selection box

ActorSelection2DBox.jpg

To render this style, the actor component bounding box is projected on to the canvas space. From there, a box is simply rendered which represents the projected bounding box.

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

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

  ActorBoundingBox = GetTwoDeeActorBoundingBox(Actor);

  // Draw the actor brackets
  Canvas.SetDrawColor(255, 255, 255);
  // Top left position
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Min.Y);
  // Draw the box
  Canvas.DrawBox
  (
    (ActorBoundingBox.Max.X - ActorBoundingBox.Min.X), 
    (ActorBoundingBox.Max.Y - ActorBoundingBox.Min.Y)
  );
}

2D selection brackets


Actor selected with a 2D selection bracket

ActorSelection2DBracket.jpg

To render this style, the actor component bounding box is projected on to the canvas space. From there each corner of the bracket is rendered.

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

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

  ActorBoundingBox = GetTwoDeeActorBoundingBox(Actor);

  // Calculate the width and height
  ActualWidth = (ActorBoundingBox.Max.X - ActorBoundingBox.Min.X) * 0.3f;
  ActualHeight = (ActorBoundingBox.Max.Y - ActorBoundingBox.Min.Y) * 0.3f;

  // Draw the actor brackets
  Canvas.SetDrawColor(255, 255, 255);

  // Top left
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Min.Y);
  Canvas.DrawRect(ActualWidth, 2);
  Canvas.SetPos(ActorBoundingBox.Min.X, ActorBoundingBox.Min.Y);
  Canvas.DrawRect(2, ActualHeight);

  // Top right
  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);

  // Bottom left
  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);

  // Bottom right
  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 selection box


Actor selected with a 3D selection box

ActorSelection3DBox.jpg

To render the 3D selection box, the Draw3DLine function within HUD was used here. This function is limited in that it can only draw lines that have a width of 1 pixel. To fix this limitation, you can project each of the bounding box's coordinates into canvas space and then draw 2D lines instead.

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 selection bracket


Actor selected with a 3D selection bracket

ActorSelection3DBracket.jpg

To render the 3D bracket selection box, the Draw3DLine function within HUD was used. Again, you can fix its limitations by projecting all of the necessary coordinates into screen space and then drawing 2D lines. To render this selection style, inner coordinates were calculated first, then lines were drawn.

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;

  // Calc inner 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);
  }

  // Calc inner 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);
  }

  // Calc inner 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 selection circle


Actor selected with a 3D selection circle

ActorSelection3DCircle.jpg

This selection style uses the Draw3DLine function within the HUD. It is again possible to project all of the offsets into canvas space coordinates and then draw 2D lines. All of the offsets have been adjusted by increasing the Z component of the vector. This helps to move the circle upwards. By adding other vector offsets, you can shift the position of the circle. To increase the number of points within the circle, you need to adjust how many points are being calculated. Do this by increasing or decreasing the yaw incremental value in Unreal rotation units (currently at 4096). You also need to increase the size of offsets array to match the yaw incremental value (currently at 16, 65536/4096).

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)
  {
    // Calculate the offset
    Offsets[i] = Actor.Location + (Radius >> Angle) + Vect(0.f, 0.f, 16.f);
    i++;
  }
      
  // Draw all of the lines
  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);
    }
  }
}

Testing


To test this feature, load up DM-Deck and set the PIE game type to ASIGameInfo. Save and play.

Related topics

Downloads


  • Download the unrealscript code. (ActorSelectionInterface.zip)