UDN
Search public documentation:

DevelopmentKitGemsCreatingADynamicNavMeshObstacleKR
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 홈 > AI와 내비게이션 > 동적인 내비게이션 메시 장애물 만들기


동적인 내비게이션 메시 장애물 만들기


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

개요


내비게이션 메시는 에전 패쓰노드 시스템에 비해 크게 향상된 장점이 많습니다. 그 중 하나를 꼽아보자면, 실시간으로 장애물을 정의하기가 정말 쉽다는 것입니다.

예전 패쓰노드 시스템에서는 패쓰노드를 꺼서 길찾기가 사용하지 못하도록 하는 식으로 했었으나, 보통 레벨 디자이너가 생각했던 것보다 패쓰 노드의 수가 훨씬 많아지는 결과를 내곤 했습니다. 매우 귀찮기도 하고 에러가 발생하기도 쉬웠습니다.

내비게이션 메시로는, 그저 장애물을 2D 모양으로 정의하고 등록하기만 하면 되니 참 간단합니다.

관련 토픽

동적 내비게이션 메시 장애물


다음 스크립트는 NavMeshObstacle 클래스에서 확장된 헬퍼 클래스입니다. 프로그래머가 사용하기 간편한 인터페이스 [SetAsSquare, SetAsRectangle, SetAsCircle] 를 제공하여 원하는 모양을 쉽게 만들 수 있도록 해 줍니다.

DynamicNavMeshObstacle.uc
  class DynamicNavMeshObstacle extends NavMeshObstacle;
  
  // 가능한 모양 목록
  enum EShape
  {
    EShape_None,
    EShape_Square,
    EShape_Rectangle,
    EShape_Circle
  };
  
  // 내비 메시 장애물 모양
  var PrivateWrite EShape ShapeType;
  // EShape_Square 에 사용
  var PrivateWrite float Width;
  // EShape_Square 및 EShape_Rectangle 에 사용
  var PrivateWrite float Height;
  // EShape_Circle 에 사용
  var PrivateWrite float Radius;
  // EShape_Circle 에 사용
  var PrivateWrite int Sides;
  // 장애물을 액터의 로테이션에 맞춤?
  var bool AlignToRotation;
  
  simulated function PostBeginPlay()
  {
    // 디폴트 PostBeginPlay 함수 생략
    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)
      {
        // 우상단 구석
        Offset.X = Width;
        Offset.Y = Width;
        Shape.AddItem(Location + (Offset >> Rotation));
        // 우하단 구석
        Offset.X = -Width;
        Offset.Y = Width;
        Shape.AddItem(Location + (Offset >> Rotation));
        // 좌하단 구석
        Offset.X = -Width;
        Offset.Y = -Width;
        Shape.AddItem(Location + (Offset >> Rotation));
        // 좌상단 구석
        Offset.X = Width;
        Offset.Y = -Width;
        Shape.AddItem(Location + (Offset >> Rotation));
      }
      else
      {
        // 우상단 구석
        Offset.X = Width;
        Offset.Y = Width;
        Shape.AddItem(Location + Offset);
        // 우하단 구석
        Offset.X = -Width;
        Offset.Y = Width;
        Shape.AddItem(Location + Offset);
        // 좌하단 구석
        Offset.X = -Width;
        Offset.Y = -Width;
        Shape.AddItem(Location + Offset);
        // 좌상단 구석
        Offset.X = Width;
        Offset.Y = -Width;
        Shape.AddItem(Location + Offset);
      }
  
      return true;
    }
    else if (ShapeType == EShape_Rectangle)
    {
      if (AlignToRotation)
      {
        // 우상단 구석
        Offset.X = Width;
        Offset.Y = Height;
        Shape.AddItem(Location + (Offset >> Rotation));
        // 우하단 구석
        Offset.X = -Width;
        Offset.Y = Height;
        Shape.AddItem(Location + (Offset >> Rotation));
        // 좌하단 구석
        Offset.X = -Width;
        Offset.Y = -Height;
        Shape.AddItem(Location + (Offset >> Rotation));
        // 좌상단 구석
        Offset.X = Width;
        Offset.Y = -Height;
        Shape.AddItem(Location + (Offset >> Rotation));
      }
      else
      {
        // 우상단 구석
        Offset.X = Width;
        Offset.Y = Height;
        Shape.AddItem(Location + Offset);
        // 우하단 구석
        Offset.X = -Width;
        Offset.Y = Height;
        Shape.AddItem(Location + Offset);
        // 좌하단 구석
        Offset.X = -Width;
        Offset.Y = -Height;
        Shape.AddItem(Location + Offset);
        // 좌상단 구석
        Offset.X = Width;
        Offset.Y = -Height;
        Shape.AddItem(Location + Offset);
      }
  
      return true;
    }
    else if (ShapeType == EShape_Circle && Sides > 0)
    {
      // 면의 수로 정의된 각 'slice' 각도 구하기
      Angle = 65536 / Sides;
      // 로테이션에 맞춰져 있다면, 시작 지점으로 로테이션 사용
      R = (AlignToRotation) ? Rotation : Rot(0, 0, 0);
      // 반경 설정
      Offset.X = Radius;
      Offset.Y = 0.f;
      // 각 면에 대해...
      for (i = 0; i < Sides; ++i)
      {
        // 왼편 지점에 추가
        Shape.AddItem(Location + (Offset >> R));
        // 다음편에 증가
        R.Yaw += Angle;
      }
  
      return true;
    }
  
    return false;
  }
  
  defaultproperties
  {
  }
  

DynamicNavMeshObstacle 사용하기


동적 내비게이션 메시 장애물 사용법은 간단합니다. 스폰시키고, 어떤 모양의 장애물로 만들지 구성하고, 등록하면 됩니다.

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

DynamicNavMeshObstacle 를 움직여서 이동가능 내비게이션 메시 장애물을 만들 수도 있습니다.

  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