UDN
Search public documentation:

DevelopmentKitGemsAddingOnScreenIndicatorsJP
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 ホーム > Unreal Development Kit Gems > オンスクリーン インジケータの追加

オンスクリーン インジケータの追加


2011年5月に UDK で最終テスト実施済み

PC 対応

概要


「Unreal Engine 3」には、デフォルトで、単純なオンスクリーン インジケータが備わっており、プレイヤーがカメラの前にいる場合に名前が表示されます。開発キットのための本文書では、オンスクリーンのターゲットを指し示すオンスクリーン インジケータを作成する方法について解説します。ターゲットが、カメラの背後にいるためオフスクリーンになっている場合は、オンスクリーン インジケータがスクリーン最下部に置かれます。ターゲットが、カメラの視錘台の外にあるためオフスクリーンになっている場合は、オンスクリーン インジケータがスクリーン両端のいずれかに位置しながらターゲットの位置を指し示します。ターゲットが現在オンスクリーンでレンダリングされていない場合は、オンスクリーン インジケータもフェードアウトします。最後に、ターゲットが所属するチームに応じてオンスクリーン インジケータの色は変化します。

  1. これらのポーンはオンスクリーンで表示されています。青のチームに所属しています。これらのオンスクリーン インジケータは左から右に若干移動し、それにともなって矢印の向きが変わります。
  2. このポーンは橋の下に隠れています。そのため、オンスクリーン インジケータが透明になっています。
  3. このポーンは同じチームに所属しています。カメラの視錘台の外に出ているため表示されていません。
  4. これらのポーンはプレイヤーの背後にいるため表示されていません。

OnScreenRadarExample.jpg

インジケータのマテリアル


オンスクリーン インジケータのマテリアルによって処理されることがいくつかあります。それは、回転、オパシティ、チームのカラー分けです。オンスクリーン インジケータのために使用されるテクスチャをここに紹介します。下の画像は、テクスチャの各チャンネルを表しています。

  1. オンスクリーン インジケータに使用されるグレイスケール ディフューズです。これがなければ、テクスチャのラッピングのせいでテクスチャ回転アーティファクトが出現した場合に、空白が大量にできてしまいます。
  2. アイコンのオパシティをオンスクリーン インジケータの中央にセットするために使用するマスクです。
  3. オンスクリーン インジケータのためのシャドウを作成するために使用するマスクです。
  4. オンスクリーン インジケータのためのオパシティとして使用するマスクです。

シャドウマスクとオパシティマスクを分離することによって、シャドウの力が個別に調整されるようにしました。ただし、最終的には追加されませんでした。必要な場合は、追加することができます。

OnScreenIndicatorThumbnail.jpg

オンスクリーン インジケータは透明でアンリットのマテリアルです。マテリアルは、HUD 上にレンダリングされるならば、アンリットでなければなりません。エミッシブチャンネルのみが同様に使用されます。

エミッシブチャンネルは 2 つのブランチから構成されています。第 1 のブランチは、オンスクリーン インジケータそのものを扱います。オンスクリーン インジケータ テクスチャ サンプラーが、 Rotation (回転) というスカラーパラメータによって回転します。さらに、red のチャンネル (#1) が TeamColor (チームカラー) というベクターパラメータによって乗じられます。これはさらに、正弦曲線を使って作成されたパルスするグレイカラーによって乗じられます。第 2 のブランチは、中央に収まるオンスクリーン インジケータのアイコンを扱います。 Portrait (ポートレイト) というテクスチャパラメータによってテクスチャが供給されます。これは縮小されて中央に押し込まれます。その結果、適切な量のポートレイトテクスチャが表示されます。TV スクリーン オーバーレイ エフェクトは、パンするテクスチャをその上で乗じることによって実現されます。これはさらに、オンスクリーン インジケータ テクスチャの green のチャンネル (#2) で乗じられます。これら 2 つのブランチの結果は、足し合わせられ、オンスクリーン インジケータ テクスチャの alpha チャンネル (#4) によって乗じられます。

オパシティチャンネルは、単に、blue のチャンネル (#3) と alpha チャンネル (#4) を足し合わせます。さらにこの結果は、 Opacity (オパシティ) というスカラーパラメータによって乗じられます。

OnScreenIndicatorMaterialThumbnail.jpg

Unrealscript


SRadarInfo 構造体

各オンスクリーン インジケータに関する情報を格納するために構造体が使用されます。

  • UTPawn - 当該のオンスクリーン インジケータが指すポーンへの参照です。
  • MaterialInstanceConstant - オンスクリーン インジケータによって使用されるマテリアルのインスタンスです。
  • DeleteMe - 削除のためのブール型です。
  • Offset - アニメーションのために使用されるオンスクリーン インジケータのオフセットです。
  • Opacity - オンスクリーン インジケータの現在のオパシティです。

YourHUD.uc
struct SRadarInfo
{
  var UTPawn UTPawn;
  var MaterialInstanceConstant MaterialInstanceConstant;
  var bool DeleteMe;
  var Vector2D Offset;
  var float Opacity;
};

AddPostRenderedActor 関数

「Unreal Tournament 3」では、デフォルトでポストレンダリングされたアクタを使用して、既存の名前インジケータをレンダリングします。この関数は、 UTPawn のためにその動作を除去します。

YourHUD.uc
function AddPostRenderedActor(Actor A)
{
  // Remove post render call for UTPawns as we don't want the name bubbles showing
  if (UTPawn(A) != None)
  {
    return;
  }

  Super.AddPostRenderedActor(A);
}

PostRender イベント

Unrealscript のポスト レンダリング イベントがエンジンによって呼び出されることによって、テクスチャまたはマテリアル、テキストをスクリーン上にレンダリングする機会が Unrealscript に与えられます。ここでは、この開発キット文書のために、そのロジックの大部分を扱います。ロジックは、複数の小さなセクションに分割します。

Update (更新) パス

このパスでは、レーダー情報 (rader info) すべてが削除のためにテストされます。それにはまず、DeleteMe を true にセットします。

次に、ForEach を使用して、すべての UTPawn をイタレートします。当該プレイヤーによって所有されていないポーンすべてについて、まず、レーダー情報がこれを参照しているか否かをチェックします。そのためには、RadarInfo 配列で有効なインデックスを見つけようとします。インデックスが有効かつポーンにヘルスがある場合は、削除チェックに合格したため、DeleteMe を false にセットします。インデックスが無効かつポーンにヘルスがある場合は、新たなレーダー情報を作成します。新たなレーダー情報が作成する際、そのレーダー情報のために新たなマテリアルインスタンスも作成します。これも削除チェックに合格したため、チームカラーもセットし、DeleteMe を false にセットします。

YourHUD.uc
  // Set all radar infos to delete if not found
  for (i = 0; i < RadarInfo.Length; ++i)
  {
    RadarInfo[i].DeleteMe = true;
  }

  // Update the radar infos and see if we need to add or remove any
  ForEach DynamicActors(class'UTPawn', UTPawn)
  {
    if (UTPawn != PlayerOwner.Pawn)
    {
      Index = RadarInfo.Find('UTPawn', UTPawn);
      // This pawn was not found in our radar infos, so add it
      if (Index == INDEX_NONE && UTPawn.Health > 0)
      {
        i = RadarInfo.Length;
        RadarInfo.Length = RadarInfo.Length + 1;
        RadarInfo[i].UTPawn = UTPawn;
        RadarInfo[i].MaterialInstanceConstant = new () class'MaterialInstanceConstant';

        if (RadarInfo[i].MaterialInstanceConstant != None)
        {
          RadarInfo[i].MaterialInstanceConstant.SetParent(Material'GemOnscreenRadarContent.PointerMaterial');

          if (UTPawn.PlayerReplicationInfo != None && UTPawn.PlayerReplicationInfo.Team != None)
          {
            TeamLinearColor = (UTPawn.PlayerReplicationInfo.Team.TeamIndex == 0) ? Default.RedLinearColor : Default.BlueLinearColor;
            RadarInfo[i].MaterialInstanceConstant.SetVectorParameterValue('TeamColor', TeamLinearColor);
          }
          else
          {
            RadarInfo[i].MaterialInstanceConstant.SetVectorParameterValue('TeamColor', Default.DMLinearColor);
          }
        }

        RadarInfo[i].DeleteMe = false;
      }
      else if (UTPawn.Health > 0)
      {
        RadarInfo[Index].DeleteMe = false;
      }
    }
  }

Rendering (レンダリング) パス

このパスが計算されるに先立って、まず、オンスクリーン インジケータのサイズが計算されて PointerSize (ポインタサイズ) として保存されます。この実装では、スクリーン解像度の横幅に対する比として算出されます。カメラの視線方向も、カメラの位置と回転に基づきます。カメラの視線方向は、ポーンがプレイヤーの背後にいるか否かを検知するために使用されます。

このパスでは、DeleteMe が false である場合に、レーダー情報がレンダリングされます。そうでない場合は、 RadarInfo 配列から削除されます。

オンスクリーン インジケータがレンダリングされる際、まず、関連するポーンがここ 0.1 秒でレンダリングされているか否かがチェックされます。まだであれば、そのオパシティは 40 % まで線形補間されます。レンダリングされている場合は、100 % まで線形補間されます。オパシティが計算されると、マテリアルインスタンス コンスタントの中にセットされます。

プレイヤーのポーンとポーンの間の方向が計算されます。カメラの方向と、プレイヤーポーンからポーンへの方向の間の内積によって、プレイヤーが現在ポーンを見ているのか否かが決定されます。下のイメージでは、赤い矢印がカメラの方向を表しています。緑の矢印は、プレイヤーポーンのポーンに対する方向を表しています。内積の値は、1.f と 0.f の間になるため、カメラの前に位置することになります。青い矢印は、プレイヤーポーンの別のポーンに対する方向を表しています。内積の値は、0.f から -1.f の間になるため、カメラの背後に位置することになります。

DotProduct.jpg

YourHUD.uc
  // Handle rendering of all of the radar infos
  PointerSize = Canvas.ClipX * 0.083f;
  PlayerOwner.GetPlayerViewPoint(CameraLocation, CameraRotation);
  CameraViewDirection = Vector(CameraRotation);

  for (i = 0; i < RadarInfo.Length; ++i)
  {
    if (!RadarInfo[i].DeleteMe)
    {
      if (RadarInfo[i].UTPawn != None && RadarInfo[i].MaterialInstanceConstant != None)
      {
        // Handle the opacity of the pointer. If the player cannot see this pawn,
        // then fade it out half way, otherwise if he can, fade it in
        if (WorldInfo.TimeSeconds - RadarInfo[i].UTPawn.LastRenderTime > 0.1f)
        {
          // Player has not seen this pawn in the last 0.1 seconds
          RadarInfo[i].Opacity = Lerp(RadarInfo[i].Opacity, 0.4f, RenderDelta * 4.f);
        }
        else
        {
          // Player has seen this pawn in the last 0.1 seconds
          RadarInfo[i].Opacity = Lerp(RadarInfo[i].Opacity, 1.f, RenderDelta * 4.f);
        }
        // Apply the opacity
        RadarInfo[i].MaterialInstanceConstant.SetScalarParameterValue('Opacity', RadarInfo[i].Opacity);

        // Get the direction from the player's pawn to the pawn
        PawnDirection = Normal(RadarInfo[i].UTPawn.Location - PlayerOwner.Pawn.Location);

        // Check if the pawn is in front of me
        if (PawnDirection dot CameraViewDirection >= 0.f)
        {
          // Handle rendering the on screen indicator when the actor is in front of the camera
        }
        else
        {
          // Handle rendering the on screen indicator when the actor is behind the camera
        }
      }
    }
    else
    {
      // Null the variables previous stored so garbage collection can occur
      RadarInfo[i].UTPawn = None;
      RadarInfo[i].MaterialInstanceConstant = None;
      // Remove from the radar info array
      RadarInfo.Remove(i, 1);
      // Back step one, to maintain the for loop
      --i;
    }
  }

カメラの前でレンダリングするパス

まず、 WorldHUDLocation (ワールド HUD 位置) が、オフセットとして、コリジョンの高さとポーンの位置を足し合わせることによって求められます。さらにこれは、スクリーン座標に投影されます。スクリーン座標がスクリーンの左側にある場合は、オフセットが左側に補間されます。あるいは、スクリーン座標がスクリーンの右側にある場合、オフセットが右側に補間されます。これによって、オンスクリーン インジケータに少しばかりのアニメーションが提供されます。このオフセットは、どの方向においても大きくなりすぎないようにするために、クランプされています。さらに、マテリアルインスタンスの回転が計算されセットされます。最後に、オンスクリーン インジケータがスクリーン上にレンダリングされます。

YourHUD.uc
          // Get the world HUD location, which is just above the pawn's head
          WorldHUDLocation = RadarInfo[i].UTPawn.Location + (RadarInfo[i].UTPawn.GetCollisionHeight() * Vect(0.f, 0.f, 1.f));
          // Project the world HUD location into screen HUD location
          ScreenHUDLocation = Canvas.Project(WorldHUDLocation);

          // If the screen HUD location is more to the right, then swing it to the left
          if (ScreenHUDLocation.X > (Canvas.ClipX * 0.5f))
          {
            RadarInfo[i].Offset.X -= PointerSize * RenderDelta * 4.f;
          }
          else
          {
            // If the screen HUD location is more to the left, then swing it to the right
            RadarInfo[i].Offset.X += PointerSize * RenderDelta * 4.f;
          }
          RadarInfo[i].Offset.X = FClamp(RadarInfo[i].Offset.X, PointerSize * -0.5f, PointerSize * 0.5f);

          // Set the rotation of the material icon
          ActualPointerLocation.X = Clamp(ScreenHUDLocation.X, 8, Canvas.ClipX - 8) + RadarInfo[i].Offset.X;
          ActualPointerLocation.Y = Clamp(ScreenHUDLocation.Y - PointerSize + RadarInfo[i].Offset.Y, 8, Canvas.ClipY - 8 - PointerSize) + (PointerSize * 0.5f);
          RadarInfo[i].MaterialInstanceConstant.SetScalarParameterValue('Rotation', GetAngle(ActualPointerLocation, ScreenHUDLocation));

          // Draw the material pointer
          Canvas.SetPos(ActualPointerLocation.X - (PointerSize * 0.5f), ActualPointerLocation.Y - (PointerSize * 0.5f));
          Canvas.DrawMaterialTile(RadarInfo[i].MaterialInstanceConstant, PointerSize, PointerSize, 0.f, 0.f, 1.f, 1.f);

カメラの背後でレンダリングするパス

アクタがカメラの背後にある場合、オンスクリーン インジケータは常にスクリーン最下部に位置します。投影の関数を使用することは依然可能ですが、戻される結果を改変する必要があります。これは、投影された水平座標が逆になっているためです。水平スクリーン座標が左端にある場合、水平のオフセットが右に押しやられます。また、その逆も成立します。これによって、オンスクリーン インジケータが常にスクリーン上に位置できるようになります。同様の回転に関する計算が行われ、マテリアルインスタンスに適用されます。最後に、オンスクリーン インジケータがスクリーン上にレンダリングされます。

YourHUD.uc
          // Project the pawn's location
          ScreenHUDLocation = Canvas.Project(RadarInfo[i].UTPawn.Location);

          // Inverse the Screen HUD location
          ScreenHUDLocation.X = Canvas.ClipX - ScreenHUDLocation.X;

          // If the screen HUD location is on the right edge, then swing it to the left
          if (ScreenHUDLocation.X > (Canvas.ClipX - 8))
          {
            RadarInfo[i].Offset.X -= PointerSize * RenderDelta * 4.f;
            RadarInfo[i].Offset.X = FClamp(RadarInfo[i].Offset.X, PointerSize * -0.5f, PointerSize * 0.5f);
          }
          else if (ScreenHUDLocation.X < 8)
          {
            // If the screen HUD location is on the left edge, then swing it to the right
            RadarInfo[i].Offset.X += PointerSize * RenderDelta * 4.f;
            RadarInfo[i].Offset.X = FClamp(RadarInfo[i].Offset.X, PointerSize * -0.5f, PointerSize * 0.5f);
          }
          else
          {
            // If the screen HUD location is somewhere in the middle, then straighten it up
            RadarInfo[i].Offset.X = Lerp(RadarInfo[i].Offset.X, 0.f, 4.f * RenderDelta);
          }

          // Set the screen HUD location
          ScreenHUDLocation.X = Clamp(ScreenHUDLocation.X, 8, Canvas.ClipX - 8);
          ScreenHUDLocation.Y = Canvas.ClipY - 8;

          // Set the actual pointer location
          ActualPointerLocation.X = ScreenHUDLocation.X + RadarInfo[i].Offset.X;
          ActualPointerLocation.Y = ScreenHUDLocation.Y - (PointerSize * 0.5f);

          // Set the rotation of the material icon
          RadarInfo[i].MaterialInstanceConstant.SetScalarParameterValue('Rotation', GetAngle(ActualPointerLocation, ScreenHUDLocation));

          // Draw the material pointer
          Canvas.SetPos(ActualPointerLocation.X - (PointerSize * 0.5f), ActualPointerLocation.Y - (PointerSize * 0.5f));
          Canvas.DrawMaterialTile(RadarInfo[i].MaterialInstanceConstant, PointerSize, PointerSize, 0.f, 0.f, 1.f, 1.f);

角度の計算

この角度計算の方法では、黒の矢印が参照ベクターです。2 つのベクターによって、赤の線または緑の線が形成されます。計算される角度は、青の弧で示されています。計算はラジアンで出されます。これは、マテリアルの回転の値にラジアンが求められるためです。初期のチェックが実行され、0.f (0 度)、Pi (180 度), Pi * 1.5f (270 度)、Pi * 0.5f (90 度) といった単純な結果が返されます。この方法は、四分円によるチェック方法 (SOH (正弦)、CAH (余弦)、TOA (正接) を使用する) や、他の逆余弦による方法よりも速度が速いです。

AngleCalculation.jpg

YourHUD.uc
function float GetAngle(Vector PointB, Vector PointC)
{
  // Check if angle can easily be determined if it is up or down
  if (PointB.X == PointC.X)
  {
    return (PointB.Y < PointC.Y) ? Pi : 0.f;
  }

  // Check if angle can easily be determined if it is left or right
  if (PointB.Y == PointC.Y)
  {
    return (PointB.X < PointC.X) ? (Pi * 1.5f) : (Pi * 0.5f);
  }

  return (2.f * Pi) - atan2(PointB.X - PointC.X, PointB.Y - PointC.Y);
}

完成形の UnrealScript クラス

次は、完成形の UnrealScript クラスです。各部分をどのように組み合わせるかを明確にしています。

YourHUD.uc
class UTRadarHUD extends UTTeamHUD;

struct SRadarInfo
{
  var UTPawn UTPawn;
  var MaterialInstanceConstant MaterialInstanceConstant;
  var bool DeleteMe;
  var Vector2D Offset;
  var float Opacity;
};

var array<SRadarInfo> RadarInfo;

function AddPostRenderedActor(Actor A)
{
  // Remove post render call for UTPawns as we don't want the name bubbles showing
  if (UTPawn(A) != None)
  {
    return;
  }

  Super.AddPostRenderedActor(A);
}

event PostRender()
{
  local int i, Index;
  local Vector WorldHUDLocation, ScreenHUDLocation, ActualPointerLocation, CameraViewDirection, PawnDirection, CameraLocation;
  local Rotator CameraRotation;
  local UTPawn UTPawn;
  local LinearColor TeamLinearColor;
  local float PointerSize;

  if (PlayerOwner == None || PlayerOwner.Pawn == None)
  {
    return;
  }

  // Set up the render delta
  RenderDelta = WorldInfo.TimeSeconds - LastHUDRenderTime;

  // Set all radar infos to delete if not found
  for (i = 0; i < RadarInfo.Length; ++i)
  {
    RadarInfo[i].DeleteMe = true;
  }

  // Update the radar infos and see if we need to add or remove any
  ForEach DynamicActors(class'UTPawn', UTPawn)
  {
    if (UTPawn != PlayerOwner.Pawn)
    {
      Index = RadarInfo.Find('UTPawn', UTPawn);
      // This pawn was not found in our radar infos, so add it
      if (Index == INDEX_NONE && UTPawn.Health > 0)
      {
        i = RadarInfo.Length;
        RadarInfo.Length = RadarInfo.Length + 1;
        RadarInfo[i].UTPawn = UTPawn;
        RadarInfo[i].MaterialInstanceConstant = new () class'MaterialInstanceConstant';

        if (RadarInfo[i].MaterialInstanceConstant != None)
        {
          RadarInfo[i].MaterialInstanceConstant.SetParent(Material'GemOnscreenRadarContent.PointerMaterial');

          if (UTPawn.PlayerReplicationInfo != None && UTPawn.PlayerReplicationInfo.Team != None)
          {
            TeamLinearColor = (UTPawn.PlayerReplicationInfo.Team.TeamIndex == 0) ? Default.RedLinearColor : Default.BlueLinearColor;
            RadarInfo[i].MaterialInstanceConstant.SetVectorParameterValue('TeamColor', TeamLinearColor);
          }
          else
          {
            RadarInfo[i].MaterialInstanceConstant.SetVectorParameterValue('TeamColor', Default.DMLinearColor);
          }
        }

        RadarInfo[i].DeleteMe = false;
      }
      else if (UTPawn.Health > 0)
      {
        RadarInfo[Index].DeleteMe = false;
      }
    }
  }

  // Handle rendering of all of the radar infos
  PointerSize = Canvas.ClipX * 0.083f;
  PlayerOwner.GetPlayerViewPoint(CameraLocation, CameraRotation);
  CameraViewDirection = Vector(CameraRotation);

  for (i = 0; i < RadarInfo.Length; ++i)
  {
    if (!RadarInfo[i].DeleteMe)
    {
      if (RadarInfo[i].UTPawn != None && RadarInfo[i].MaterialInstanceConstant != None)
      {
        // Handle the opacity of the pointer. If the player cannot see this pawn,
        // then fade it out half way, otherwise if he can, fade it in
        if (WorldInfo.TimeSeconds - RadarInfo[i].UTPawn.LastRenderTime > 0.1f)
        {
          // Player has not seen this pawn in the last 0.1 seconds
          RadarInfo[i].Opacity = Lerp(RadarInfo[i].Opacity, 0.4f, RenderDelta * 4.f);
        }
        else
        {
          // Player has seen this pawn in the last 0.1 seconds
          RadarInfo[i].Opacity = Lerp(RadarInfo[i].Opacity, 1.f, RenderDelta * 4.f);
        }
        // Apply the opacity
        RadarInfo[i].MaterialInstanceConstant.SetScalarParameterValue('Opacity', RadarInfo[i].Opacity);

        // Get the direction from the player's pawn to the pawn
        PawnDirection = Normal(RadarInfo[i].UTPawn.Location - PlayerOwner.Pawn.Location);

        // Check if the pawn is in front of me
        if (PawnDirection dot CameraViewDirection >= 0.f)
        {
          // Get the world HUD location, which is just above the pawn's head
          WorldHUDLocation = RadarInfo[i].UTPawn.Location + (RadarInfo[i].UTPawn.GetCollisionHeight() * Vect(0.f, 0.f, 1.f));
          // Project the world HUD location into screen HUD location
          ScreenHUDLocation = Canvas.Project(WorldHUDLocation);

          // If the screen HUD location is more to the right, then swing it to the left
          if (ScreenHUDLocation.X > (Canvas.ClipX * 0.5f))
          {
            RadarInfo[i].Offset.X -= PointerSize * RenderDelta * 4.f;
          }
          else
          {
            // If the screen HUD location is more to the left, then swing it to the right
            RadarInfo[i].Offset.X += PointerSize * RenderDelta * 4.f;
          }
          RadarInfo[i].Offset.X = FClamp(RadarInfo[i].Offset.X, PointerSize * -0.5f, PointerSize * 0.5f);

          // Set the rotation of the material icon
          ActualPointerLocation.X = Clamp(ScreenHUDLocation.X, 8, Canvas.ClipX - 8) + RadarInfo[i].Offset.X;
          ActualPointerLocation.Y = Clamp(ScreenHUDLocation.Y - PointerSize + RadarInfo[i].Offset.Y, 8, Canvas.ClipY - 8 - PointerSize) + (PointerSize * 0.5f);
          RadarInfo[i].MaterialInstanceConstant.SetScalarParameterValue('Rotation', GetAngle(ActualPointerLocation, ScreenHUDLocation));

          // Draw the material pointer
          Canvas.SetPos(ActualPointerLocation.X - (PointerSize * 0.5f), ActualPointerLocation.Y - (PointerSize * 0.5f));
          Canvas.DrawMaterialTile(RadarInfo[i].MaterialInstanceConstant, PointerSize, PointerSize, 0.f, 0.f, 1.f, 1.f);
        }
        else
        {
          // Handle rendering the on screen indicator when the actor is behind the camera
          // Project the pawn's location
          ScreenHUDLocation = Canvas.Project(RadarInfo[i].UTPawn.Location);

          // Inverse the Screen HUD location
          ScreenHUDLocation.X = Canvas.ClipX - ScreenHUDLocation.X;

          // If the screen HUD location is on the right edge, then swing it to the left
          if (ScreenHUDLocation.X > (Canvas.ClipX - 8))
          {
            RadarInfo[i].Offset.X -= PointerSize * RenderDelta * 4.f;
            RadarInfo[i].Offset.X = FClamp(RadarInfo[i].Offset.X, PointerSize * -0.5f, PointerSize * 0.5f);
          }
          else if (ScreenHUDLocation.X < 8)
          {
            // If the screen HUD location is on the left edge, then swing it to the right
            RadarInfo[i].Offset.X += PointerSize * RenderDelta * 4.f;
            RadarInfo[i].Offset.X = FClamp(RadarInfo[i].Offset.X, PointerSize * -0.5f, PointerSize * 0.5f);
          }
          else
          {
            // If the screen HUD location is somewhere in the middle, then straighten it up
            RadarInfo[i].Offset.X = Lerp(RadarInfo[i].Offset.X, 0.f, 4.f * RenderDelta);
          }

          // Set the screen HUD location
          ScreenHUDLocation.X = Clamp(ScreenHUDLocation.X, 8, Canvas.ClipX - 8);
          ScreenHUDLocation.Y = Canvas.ClipY - 8;

          // Set the actual pointer location
          ActualPointerLocation.X = ScreenHUDLocation.X + RadarInfo[i].Offset.X;
          ActualPointerLocation.Y = ScreenHUDLocation.Y - (PointerSize * 0.5f);

          // Set the rotation of the material icon
          RadarInfo[i].MaterialInstanceConstant.SetScalarParameterValue('Rotation', GetAngle(ActualPointerLocation, ScreenHUDLocation));

          // Draw the material pointer
          Canvas.SetPos(ActualPointerLocation.X - (PointerSize * 0.5f), ActualPointerLocation.Y - (PointerSize * 0.5f));
          Canvas.DrawMaterialTile(RadarInfo[i].MaterialInstanceConstant, PointerSize, PointerSize, 0.f, 0.f, 1.f, 1.f);
        }
      }
    }
    else
    {
      // Null the variables previous stored so garbage collection can occur
      RadarInfo[i].UTPawn = None;
      RadarInfo[i].MaterialInstanceConstant = None;
      // Remove from the radar info array
      RadarInfo.Remove(i, 1);
      // Back step one, to maintain the for loop
      --i;
    }
  }

  // Setup the render delta
  LastHUDRenderTime = WorldInfo.TimeSeconds;
  Super.PostRender();
}

function float GetAngle(Vector PointB, Vector PointC)
{
  // Check if angle can easily be determined if it is up or down
  if (PointB.X == PointC.X)
  {
    return (PointB.Y < PointC.Y) ? Pi : 0.f;
  }

  // Check if angle can easily be determined if it is left or right
  if (PointB.Y == PointC.Y)
  {
    return (PointB.X < PointC.X) ? (Pi * 1.5f) : (Pi * 0.5f);
  }

  return (2.f * Pi) - atan2(PointB.X - PointC.X, PointB.Y - PointC.Y);
}

defaultproperties
{
}

ダウンロード


  • この開発キット文書で使用されているコンテンツとソースコードは、 ここから ダウンロードすることができます。(OnScreenIndicator.zip)