UDN
Search public documentation:

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

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 a dynamic Navigation Mesh Obstacle
UE3 Home > AI & Navigation > Creating a dynamic Navigation Mesh Obstacle


Creating a dynamic Navigation Mesh Obstacle


Last tested against UDK June, 2011
PC and iOS compatible

Overview


Navigation meshes have many beneficial features which make it far superior to the older path node system. One of the best features is that it is really easy to define obstacles in real time.

The older path node system achieved this by turning off path nodes so that the pathing would not use it, however this often led to much larger path node counts as level designers had to bear this in mind. It was also very cumbersome and error prone to use.

With navigation meshes, it is as simple as defining a 2D shape as the obstacle and registering it.

Related topics

Dynamic Nav Mesh Obstacle


The following script is a helper class which extends the existing NavMeshObstacle class. It helps us create the shapes we want easily, by providing a simple to use interface [SetAsSquare, SetAsRectangle, SetAsCircle] for programmers.

DynamicNavMeshObstacle.uc
class DynamicNavMeshObstacle extends NavMeshObstacle;

// List of possible shapes
enum EShape
{
  EShape_None,
  EShape_Square,
  EShape_Rectangle,
  EShape_Circle
};

// Shape of the nav mesh obstacle
var PrivateWrite EShape ShapeType;
// Used in EShape_Square
var PrivateWrite float Width;
// Used in EShape_Square and EShape_Rectangle
var PrivateWrite float Height;   
// Used in EShape_Circle
var PrivateWrite float Radius;
// Used in EShape_Circle
var PrivateWrite int Sides;
// Align the obstacle to the rotation of the actor?
var bool AlignToRotation;

simulated function PostBeginPlay()
{
  // Skip default post begin play function
  Super(Actor).PostBeginPlay();
}

function SetAsSquare(float NewWidth)
{
  if (NewWidth > 0.f)
  {
    ShapeType = EShape_Square;
    Width = NewWidth;
  }
}

function SetAsRectangle(float NewWidth, float NewHeight)
{
  if (NewWidth > 0.f && NewHeight > 0.f)
  {
    ShapeType = EShape_Rectangle;
    Width = NewWidth;
    Height = NewHeight;
  }
}

function SetAsCircle(float NewRadius, float NewSides)
{
  if (NewRadius > 0.f && NewSides > 0)
  {
    ShapeType = EShape_Circle;
    Radius = NewRadius;
    Sides = NewSides;
  }
}

event bool GetObstacleBoudingShape(out array<vector> Shape)
{
  local Vector Offset;
  local int i, Angle;
  local Rotator R;

  if (ShapeType == EShape_Square)
  {
    if (AlignToRotation)
    {
      // Top right corner
      Offset.X = Width;
      Offset.Y = Width;
      Shape.AddItem(Location + (Offset >> Rotation));
      // Bottom right corner
      Offset.X = -Width;
      Offset.Y = Width;
      Shape.AddItem(Location + (Offset >> Rotation));
      // Bottom left corner
      Offset.X = -Width;
      Offset.Y = -Width;
      Shape.AddItem(Location + (Offset >> Rotation));
      // Top left corner
      Offset.X = Width;
      Offset.Y = -Width;
      Shape.AddItem(Location + (Offset >> Rotation));
    }
    else
    {
      // Top right corner
      Offset.X = Width;
      Offset.Y = Width;
      Shape.AddItem(Location + Offset);
      // Bottom right corner
      Offset.X = -Width;
      Offset.Y = Width;
      Shape.AddItem(Location + Offset);
      // Bottom left corner
      Offset.X = -Width;
      Offset.Y = -Width;
      Shape.AddItem(Location + Offset);
      // Top left corner
      Offset.X = Width;
      Offset.Y = -Width;
      Shape.AddItem(Location + Offset);
    }

    return true;
  }
  else if (ShapeType == EShape_Rectangle)
  {
    if (AlignToRotation)
    {
      // Top right corner
      Offset.X = Width;
      Offset.Y = Height;
      Shape.AddItem(Location + (Offset >> Rotation));
      // Bottom right corner
      Offset.X = -Width;
      Offset.Y = Height;
      Shape.AddItem(Location + (Offset >> Rotation));
      // Bottom left corner
      Offset.X = -Width;
      Offset.Y = -Height;
      Shape.AddItem(Location + (Offset >> Rotation));
      // Top left corner
      Offset.X = Width;
      Offset.Y = -Height;
      Shape.AddItem(Location + (Offset >> Rotation));
    }
    else
    {
      // Top right corner
      Offset.X = Width;
      Offset.Y = Height;
      Shape.AddItem(Location + Offset);
      // Bottom right corner
      Offset.X = -Width;
      Offset.Y = Height;
      Shape.AddItem(Location + Offset);
      // Bottom left corner
      Offset.X = -Width;
      Offset.Y = -Height;
      Shape.AddItem(Location + Offset);
      // Top left corner
      Offset.X = Width;
      Offset.Y = -Height;
      Shape.AddItem(Location + Offset);
    }

    return true;
  }
  else if (ShapeType == EShape_Circle && Sides > 0)
  {
    // Get the angle of each 'slice' defined by the number of sides
    Angle = 65536 / Sides;
    // If we are aligned to rotation, use the rotation as the starting point
    R = (AlignToRotation) ? Rotation : Rot(0, 0, 0);
    // Set the radius
    Offset.X = Radius;
    Offset.Y = 0.f;
    // For each side...
    for (i = 0; i < Sides; ++i)
    {
      // Add the the left side point
      Shape.AddItem(Location + (Offset >> R));
      // Increment to the next side
      R.Yaw += Angle;
    }

    return true;
  }

  return false;
}

defaultproperties
{
}

Using DynamicNavMeshObstacle


Using the dynamic navigation mesh obstacle is as simple as spawning it in, setting up what kind of obstacle shape you want and registering it.

YourClass.uc
var DynamicNavMeshObstacle PlacementObstacle;

function PostBeginPlay()
{
  Super.PostBeginPlay();
  PlacementObstacle = Spawn(class'DynamicNavMeshObstacle');
  PlacementObstacle.SetAsCircle(96, 8);
}

You can also move the DynamicNavMeshObstacle around to create a movable navigation mesh obstacle.

function Tick(float DeltaTime)
{
  Super.Tick(DeltaTime);

  ForEach TraceActors(class'Actor', HitActor, HitLocation, HitNormal, CachedMouseWorldOrigin + CachedMouseWorldDirection * 65536.f, CachedMouseWorldOrigin,,, class'Actor'.const.TRACEFLAG_Bullet)
  {
    if (HitActor.bWorldGeometry)
    {
      PlacementObstacle.UnRegisterObstacle();
      PlacementObstacle.SetLocation(HitLocation);
      PlacementObstacle.RegisterObstacle();
      break;
    }
  }
}

DynamicNavMeshObstacleScreenShot.jpg