UDN
Search public documentation:

DevelopmentKitGemsCreatingAMouseInterfaceKR
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년 1월 버전으로 최종 테스팅, PC 호환

개요


언리얼 엔진은 기본적으로 1인칭 슈팅 엔진으로 고안되었습니다. 그러나 코드만 제대로 갖추면 실시간 전략 게임같은 다른 장르도 완벽히 소화해 낼 수 있습니다.

화면에 커서 추가하기


화면에 커서를 추가하기란 약간의 스크립트 추가 작업을 동반합니다. UIScene 이 제거되었기에, 마우스 위치를 추출해 내기 위한 예전 메서드는 이제 사용할 수 없습니다. 대체할 수 있는 방법은 둘, 언리얼스크립트와 스케일폼 두 가지 입니다.

언리얼스크립트

자체 마우스 포지셔닝 코드를 만들 수 있습니다. 어떤 식이냐면, 변경된 것이 있는지 알아보기 위해 aMouseXaMouseY 를 폴링하는 커스텀 플레이어 인풋을 추가하는 식입니다. 변경된 것이 있으면 MousePosition 변수에 덧붙인다음 마우스 커서가 화면 범위내에 있도록 제한(clamp)합니다.

새 GameInfo 는 이렇습니다. 사용할 HUD 클래스와 PlayerController 클래스를 정의하는 단순 GameInfo 입니다.

MouseInterfaceGameInfo.uc
class MouseInterfaceGameInfo extends GameInfo;

defaultproperties
{
  // HUD 타입을 마우스 인터페이스 HUD 로 설정
  HUDType=class'MouseInterfaceHUD'
  // 플레이어 콘트롤러를 마우스 인터페이스 플레이어 콘트롤러로 설정
  PlayerControllerClass=class'MouseInterfacePlayerController'
}

화면상에 커서를 렌더링하는 데 사용될 새 HUD 입니다. 필요에 따라 머티리얼을 사용할 수도 있습니다.

MouseInterfaceHUD.uc
class MouseInterfaceHUD extends HUD;

// 화면상에 커서를 나타내는 텍스처
var const Texture2D CursorTexture;
// 커서의 색
var const Color CursorColor;

event PostRender()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;

  // 가용 PlayerOwner 및 CursorTexture 가 있는지 확인
  if (PlayerOwner != None && CursorTexture != None)
  {
    // MouseInterfacePlayerInput 를 구하기 위해 형 변환
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

    if (MouseInterfacePlayerInput != None)
    {
      // 마우스 위치로 캔버스 위치 설정
      Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
      // 커서 컬러 설정
      Canvas.DrawColor = CursorColor;
      // 화면상에 텍스처 그리기
      Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
    }
  }

  Super.PostRender();
}

defaultproperties
{
  CursorColor=(R=255,G=255,B=255,A=255)
  CursorTexture=Texture2D'EngineResources.Cursors.Arrow'
}

코드 배후의 로직은 이렇습니다:

  • PostRender 가 실행되면 PlayerOwner 에서 새 PlayerInput 을 구하는데, 이는 PlayerController 입니다.
  • 새 PlayerInput 이 있으면, Canvas 위치를 새 PlayerInput 내에 저장된 MousePosition 으로 설정합니다.
  • 그런 다음 컬러를 default properties 내에 정의된 커서 컬러에 설정합니다.
  • 마지막으로 마우스 커서를 나타내는 텍스처를 그립니다.

사용할 새 PlayerInput 을 정의하는 새 PlayerController 는 이렇습니다. 이 젬에서 원래 함수는 필요하지 않기에 UpdateRotation 을 토막(stub)화했습니다.

MouseInterfacePlayerController.uc
class MouseInterfacePlayerController extends PlayerController;

// 이 함수 null
function UpdateRotation(float DeltaTime);

defaultproperties
{
  // 인풋 클래스를 마우스 인터페이스 플레이어 인풋으로 설정
  InputClass=class'MouseInterfacePlayerInput'
}

새 PlayerInput 은 이렇습니다.

MouseInterfacePlayerInput.uc
class MouseInterfacePlayerInput extends PlayerInput;

// 저장된 마우스 위치. 다른 클래스가 변경은 못해도 접근은 할 수 있도록 private write 로 설정.
var PrivateWrite IntPoint MousePosition;

event PlayerInput(float DeltaTime)
{
  // 마우스 처리
  // 가용 HUD 있는지 확인
  if (myHUD != None)
  {
    // 마우스 위치에 aMouseX 를 더한 다음 뷰포트 폭 내로 제한
    MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, myHUD.SizeX);
    // 마우스 위치에 aMouseY 를 더한 다음 뷰포트 높이 내로 제한
    MousePosition.Y = Clamp(MousePosition.Y - aMouseY, 0, myHUD.SizeY);
  }

  Super.PlayerInput(DeltaTime);
}

defaultproperties
{
}

코드 배후의 로직은 이렇습니다:

  • PlayerInput 이 실행되면 마우스 위치 조절을 처리합니다.
  • HUD 는 마우스 위치를 뷰포트 내로 제한시키기 위해 필요합니다. PlayerInput 은 PlayerController 내의 오브젝트이므로, PlayerController 내의 변수에 직접 접근할 수 있습니다.
  • input config 내에 묶여 있는 aMouseX 및 aMouseY 를 사용하여 그 값을 우리 MousePosition 변수에 더합니다. 올바른 결과를 내기 위해서는 세로 계산을 거꾸로 해야 합니다.
  • 결과를 제한하여 마우스 위치가 항상 뷰포트 내에 있도록 합니다.

컴파일합니다. 새 맵을 만들고, MouseInterfaceGameInfo 에 PIE GameInfo 를 설정한 다음 테스트합니다. 커서를 확인하고 움직일 수 있을 것입니다. 아래 이미지에서, 커서는 빨강 원 안에 있습니다.

MouseInterfacePhaseOne.jpg

스케일폼

마우스 위치를 폴링하는 데는 스케일폼을 사용할 수도 있습니다. 특히 이 젬에서 대부분의 스크립트는 거의 그대로입니다. 예외라면 스케일폼을 사용하는 경우, 위에서 만든 커스텀 플레이어 인풋 클래스는 더이상 마우스 변화를 폴링하지 않는다는 점입니다. 대신 스케일폼이 마우스 위치 변화를 감지하면 새 위치를 언리얼스크립트로 넘겨줍니다.

어도비 플래시로 쓴 액션스크립트입니다. 마우스가 움직이면 액션스크립트는 커서를 호출한 레이어의 위치를 스케일폼의 마우스 위치로 설정한 다음, 언리얼스크립트에도 스케일폼의 마우스 위치를 전송합니다.

MouseInterfaceCursor.fla > actions: Frame 1
import flash.external.ExternalInterface;

// 보통의 "윈도우" 포인터 숨김
Mouse.hide();

var mouseListener:Object = new Object();

mouseListener.onMouseMove = function() {
  // 커서 인스턴스 위치를 마우스 위치로 설정
  cursor._x = _root._xmouse;
  cursor._y = _root._ymouse;

  // 언리얼스크립트에 새 마우스 좌표 전달
  ExternalInterface.call("UpdateMousePosition", _root._xmouse, _root._ymouse);

  updateAfterEvent();
};

Mouse.addListener(mouseListener);

스케일폼 무비의 언리얼스크립트 측면입니다. 스케일폼이 그 마우스 위치 값을 전송하는 데 사용하는 이벤트를 담습니다.

MouseInterfaceGFx.uc
class MouseInterfaceGFx extends GFxMoviePlayer;

var MouseInterfaceHUD MouseInterfaceHUD;

function Init(optional LocalPlayer LocalPlayer)
{
  // 스케일폼 무비 초기화
  Super.Init(LocalPlayer);

  Start();
  Advance(0);
}

event UpdateMousePosition(float X, float Y)
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;

  if (MouseInterfaceHUD != None && MouseInterfaceHUD.PlayerOwner != None)
  {
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(MouseInterfaceHUD.PlayerOwner.PlayerInput);

    if (MouseInterfacePlayerInput != None)
    {
      MouseInterfacePlayerInput.SetMousePosition(X, Y);
    }
  }
}

defaultproperties
{
  bDisplayWithHudOff=false
  TimingMode=TM_Game
  MovieInfo=SwfMovie'MouseInterfaceContent.MouseInterfaceCursor'
  bPauseGameWhileActive=false
}

HUD 는 스케일폼이 생성되고 초기화되는 곳입니다. HUD 가 소멸되면 다른 스케일폼의 참조 역시도 확인하여 닫습니다. PreCalcValues 는 화면 해상도가 변경되었는지 감지할 수 있는 함수입니다. 뷰포트를 리셋시키고 스케일 모드와 얼라인먼트를 그 해상도에 일치시킬 수 있도록, 스케일폼이 그러한 변화를 알고 있는지 확인해야 합니다. 마지막으로, 이제 스케일폼이 커서를 렌더해 내고 있기 때문에, HUD 에게는 커서를 렌더하지 말라 이릅니다.

MouseInterfaceHUD.uc
class MouseInterfaceHUD extends HUD;

// 화면상의 커서를 나타내는 텍스처
var const Texture2D CursorTexture;
// 커서의 색
var const Color CursorColor;
// 스케일폼 사용?
var bool UsingScaleForm;
// 스케일폼 마우스 무비
var MouseInterfaceGFx MouseInterfaceGFx;

simulated event PostBeginPlay()
{
  Super.PostBeginPlay();

  // 스케일폼을 사용중이라면, 스케일폼 무비를 생성
  if (UsingScaleForm)
  {
    MouseInterfaceGFx = new () class'MouseInterfaceGFx';
    if (MouseInterfaceGFx != None)
    {
      MouseInterfaceGFx.MouseInterfaceHUD = Self;
      MouseInterfaceGFx.SetTimingMode(TM_Game);

      MouseInterfaceGFx.Init(class'Engine'.static.GetEngine().GamePlayers[MouseInterfaceGFx.LocalPlayerOwnerIndex]);
    }
  }
}

simulated event Destroyed()
{
  Super.Destroyed();

  // 스케일폼 무비가 존재하면, 소멸시킴
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.Close(true);
    MouseInterfaceGFx = None;
  }
}

function PreCalcValues()
{
  Super.PreCalcValues();

  // 스케일폼 무비가 존재하면, 뷰포트를 리셋시키고, 스케일 모드와 얼라인먼트를
  // 화면 해상도에 일치.
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.SetViewport(0, 0, SizeX, SizeY);
    MouseInterfaceGFx.SetViewScaleMode(SM_NoScale);
    MouseInterfaceGFx.SetAlignment(Align_TopLeft);
  }
}

event PostRender()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local MouseInterfaceInteractionInterface MouseInteractionInterface;
  local Vector HitLocation, HitNormal;

  Super.PostRender();

  // 스케일폼을 사용중이지 않고 적합한 커서가 있음을 확인
  if (!UsingScaleForm && CursorTexture != None)
  {
    // 적합한 PlayerOwner 가 있는지 확인
    if (PlayerOwner != None)
    {
      // MouseInterfacePlayerInput 을 구하기 위해 형 변환
      MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

      // 스케일폼을 사용중이지 않고 적합한 커서 텍스처가 있으면, 렌더.
      if (MouseInterfacePlayerInput != None)
      {
        // 캔버스 위치를 마우스 위치로 설정
        Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
        // 커서 컬러를 설정
        Canvas.DrawColor = CursorColor;
        // 화면상에 텍스처 그리기
        Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
      }
    }
  }

  Super.PostRender();
}

최종적으로, MouseInterfacePlayerInput 에 새 함수를 추가하여, 다른 클래스가 MousePosition 변수에 write 할 수 있도록 합니다. 마우스 위치가 항상 캔버스의 렌더링 범위 내에 있는지 확인하기 위해 내부적으로 수행됩니다.

MouseInterfacePlayerInput.uc
class MouseInterfacePlayerInput extends PlayerInput;

var PrivateWrite IntPoint MousePosition;

event PlayerInput(float DeltaTime)
{
  local MouseInterfaceHUD MouseInterfaceHUD;

  // 마우스 이동 처리
  // 적절한 HUD 클래스가 있는지 검사
  MouseInterfaceHUD = MouseInterfaceHUD(MyHUD);
  if (MouseInterfaceHUD != None)
  {
    if (!MouseInterfaceHUD.UsingScaleForm)
    {
      // 스케일폼을 사용중이지 않으면, 마우스 인풋을 직접 읽음.
      // 마우스 위치에 aMouseX 를 더한 다음 뷰포트 폭 범위 안으로 제한
      MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, MouseInterfaceHUD.SizeX);
      // 마우스 위치에 aMouseY 를 더한 다음 뷰포트 높이 범위 안으로 제한
      MousePosition.Y = Clamp(MousePosition.Y - aMouseY, 0, MouseInterfaceHUD.SizeY);
    }
  }

  Super.PlayerInput(DeltaTime);
}

function SetMousePosition(int X, int Y)
{
  if (MyHUD != None)
  {
    MousePosition.X = Clamp(X, 0, MyHUD.SizeX);
    MousePosition.Y = Clamp(Y, 0, MyHUD.SizeY);
  }
}

defaultproperties
{
}

즉 여기서 스케일폼에는 로직이 그리 많이 바뀌지 않습니다. 여기서 주로 바꾼 것은, 마우스가 현재 어느 위치에 있는지 알아내는 데 무엇을 사용할 것인지 입니다.

관련 토픽

마우스 2D 좌표를 원해!

마우스 2D 좌표를 구하려면, MouseInterfacePlayerInput 에 접근해야 합니다.

HUD 내에서 구하려면.

MouseInterfaceHUD.uc
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local IntPoint MousePosition;

  // 적합한 PlayerOwner 가 있는지 확인
  if (PlayerOwner != None)
  {
    // MouseInterfacePlayerInput 를 구하기 위해 형 변환
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

    if (MouseInterfacePlayerInput != None)
    {
      // 마우스 X 위치를 구하고/사용
      MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
      // 마우스 Y 위치를 구하고/사용
      MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
    }
  }

PlayerController 안에서 구하려면.

MouseInterfacePlayerController.uc
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local IntPoint MousePosition;

  // MouseInterfacePlayerInput 를 구하기 위해 형 변환
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerInput);

  if (MouseInterfacePlayerInput != None)
  {
    // 마우스 X 위치를 구하고/사용
    MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
    // 마우스 Y 위치를 구하고/사용
    MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  }

관련 토픽

마우스 인터액션 인터페이스 추가하기


이는 마우스 인터액션가능 액터가 구현할 인터페이스입니다. 이 인터페이스를 패키지에 추가하십시오. 여분의 버튼이나 마우스 축 이동같은 마우스 함수를 더 구현하려면, 모든 마우스 상호작용 가능한 액터에 상속할 수 있도록 이 인터페이스에 덧붙이시기 바랍니다.

MouseInterfaceInteractionInterface.uc
interface MouseInterfaceInteractionInterface;

// 왼쪽 마우스 버튼이 눌렸을 때 호출
function MouseLeftPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal);

// 왼쪽 마우스 버튼이 놓였을 때 호출
function MouseLeftReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 오른쪽 마우스 버튼이 눌렸을 때 호출
function MouseRightPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal);

// 오른쪽 마우스 버튼이 놓였을 때 호출
function MouseRightReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 가운데 마우스 버튼이 눌렸을 때 호출
function MouseMiddlePressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal);

// 가운데 마우스 버튼이 놓였을 때 호출
function MouseMiddleReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 휠 버튼을 위로 굴렸을 때 호출
function MouseScrollUp(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 휠 버튼을 아래로 굴렸을 때 호출
function MouseScrollDown(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 마우스가 액터 위로 움직였을 때 호출
function MouseOver(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 마우스가 액터 (위에 있다가) 밖으로 움직였을 때 호출
function MouseOut(Vector MouseWorldOrigin, Vector MouseWorldDirection);

// 마우스 추적 적중 위치를 반환
function Vector GetHitLocation();

// 마우스 추적 적중 노멀을 반환
function Vector GetHitNormal();

// 캔버스 내 deprojection 으로 계산된 마우스 월드 원점을 반환
function Vector GetMouseWorldOrigin();

// 캔버스 내 deprojection 으로 계산된 마우스 월드 방향을 반환
function Vector GetMouseWorldDirection();

관련 토픽

마우스 상호작용을 내기 위해 PlayerController 와 HUD 사용하기


마우스 입력은 HUD 내에 계류중인(pending) 불리언에 의해 처리됩니다. 이렇게 하는 이유는, deprojection 이 캔버스로 접근해야 하기 때문입니다. 캔버스는 프레임마다 재할당되므로, 여러 프레임에 걸쳐 그 대상으로의 참조를 유지하기란 여의치 않은 일입니다. 그러므로 마우스 버튼이나 마우스 휠이 사용될 때, 그 입력 동작을 HUD 로 유예(defer)시킵니다.

MouseInterfacePlayerController.uc
class MouseInterfacePlayerController extends PlayerController;

// 마우스 이벤트 열거형
enum EMouseEvent
{
  LeftMouseButton,
  RightMouseButton,
  MiddleMouseButton,
  ScrollWheelUp,
  ScrollWheelDown,
};

// 마우스 입력 처리
function HandleMouseInput(EMouseEvent MouseEvent, EInputEvent InputEvent)
{
  local MouseInterfaceHUD MouseInterfaceHUD;

  // HUD 구하기 위해 형 변환
  MouseInterfaceHUD = MouseInterfaceHUD(myHUD);

  if (MouseInterfaceHUD != None)
  {
    // 이것의 입력 종류 검출
    if (InputEvent == IE_Pressed)
    {
      // pressed 이벤트 처리
      switch (MouseEvent)
      {
        case LeftMouseButton:
     MouseInterfaceHUD.PendingLeftPressed = true;
     break;

   case RightMouseButton:
     MouseInterfaceHUD.PendingRightPressed = true;
     break;

   case MiddleMouseButton:
     MouseInterfaceHUD.PendingMiddlePressed = true;
     break;

   case ScrollWheelUp:
     MouseInterfaceHUD.PendingScrollUp = true;
     break;

   case ScrollWheelDown:
     MouseInterfaceHUD.PendingScrollDown = true;
     break;

   default:
     break;
      }
    }
    else if (InputEvent == IE_Released)
    {
      // released 이벤트 처리
      switch (MouseEvent)
      {
        case LeftMouseButton:
     MouseInterfaceHUD.PendingLeftReleased = true;
     break;

   case RightMouseButton:
     MouseInterfaceHUD.PendingRightReleased = true;
     break;

   case MiddleMouseButton:
     MouseInterfaceHUD.PendingMiddleReleased = true;
     break;

   default:
     break;
      }
    }
  }
}

// 마우스 좌우버튼을 눌렀을 때 사용할 후크
exec function StartFire(optional byte FireModeNum)
{
  HandleMouseInput((FireModeNum == 0) ? LeftMouseButton : RightMouseButton, IE_Pressed);
  Super.StartFire(FireModeNum);
}

// 마우스 좌우버튼을 놓았을 때 사용할 후크
exec function StopFire(optional byte FireModeNum)
{
  HandleMouseInput((FireModeNum == 0) ? LeftMouseButton : RightMouseButton, IE_Released);
  Super.StopFire(FireModeNum);
}

// 가운데 마우스 버튼이 눌렸을 때 호출
exec function MiddleMousePressed()
{
  HandleMouseInput(MiddleMouseButton, IE_Pressed);
}

// 가운데 마우스 버튼이 놓였을 때 호출
exec function MiddleMouseReleased()
{
  HandleMouseInput(MiddleMouseButton, IE_Released);
}

// 휠 버튼을 위로 굴렸을 때 호출
exec function MiddleMouseScrollUp()
{
  HandleMouseInput(ScrollWheelUp, IE_Pressed);
}

// 휠 버튼을 아래로 굴렸을 때 호출
exec function MiddleMouseScrollDown()
{
  HandleMouseInput(ScrollWheelDown, IE_Pressed);
}

// 이 함수 null
function UpdateRotation(float DeltaTime);

// 이 함수에 있을 때는 StartFire 가 전역적으로 호출되지 않기에 이 상태 덮어쓰기
auto state PlayerWaiting
{
  exec function StartFire(optional byte FireModeNum)
  {
    Global.StartFire(FireModeNum);
  }
}

defaultproperties
{
  // 인풋 클래스를 마우스 인터페이스 플레이어 인풋으로 설정
  InputClass=class'MouseInterfacePlayerInput'
}

플레이어 콘트롤러의 로직은 다음과 같습니다:

  • exec 함수는 키 바인드를 통해 호출됩니다.
  • 어느 exec 함수라도 호출되면, 거기서 HandleMouseInput 을 호출하고, 그리고서 액션을 HUD 로 유예시킵니다.

새 exec 함수를 추가했으니, 바인딩하려면 DefaultInput.ini 를 변경해야 합니다. DefaultInput.ini 를 변경하면 잇따르는 환경설정 파일에도 그 변경사항이 업데이트되나, UDKInput.ini 도 변경할 수 있습니다.

DefaultInput.ini
; 기존 마우스 스크롤 업/다운 키 바인드 제거
-Bindings=(Name="MouseScrollUp",Command="PrevWeapon")
-Bindings=(Name="MouseScrollDown",Command="NextWeapon")
; 가운데 마우스 버튼 및 새 스크롤 휠 키 바인드 추가
.Bindings=(Name="MiddleMouseButton",Command="MiddleMousePressed | OnRelease MiddleMouseReleased")
.Bindings=(Name="MouseScrollUp",Command="GBA_PrevWeapon | MiddleMouseScrollUp")
.Bindings=(Name="MouseScrollDown",Command="GBA_NextWeapon | MiddleMouseScrollDown")

마우스 입력이 HUD 로 유예될 때, HUD 는 PostRender 함수 내에서 프레임 단위로 처리합니다. 프레임 마다, HUD 는 추적을 수행하여 월드 내부의 마우스를 나타내는 현재 마우스 인터액션 인터페이스를 구합니다.

좀 더 명확히 하자면, 아래 이미지에서 녹색 평면은 월드를, 빨강 절두체는 월드 뷰 절두체를, 파랑 점은 2D 마우스 위치를, 파랑 선은 추적을 나타냅니다. 절두체의 윗면이 화면을 나타내는 것입니다. (이 경우 월드를 위로 보는 것입니다.) 캔버스 deprojection 함수는 2D 위치를 월드 속으로 변환합니다.

FrustrumExplanation.jpg

MouseInterfaceHUD.uc
class MouseInterfaceHUD extends HUD;

// 화면상의 커서를 나타내는 텍스처
var const Texture2D CursorTexture;
// 커서의 컬러
var const Color CursorColor;
// 계류중인 마우스 왼쪽버튼 pressed 이벤트
var bool PendingLeftPressed;
// 계류중인 마우스 왼쪽버튼 released 이벤트
var bool PendingLeftReleased;
// 계류중인 마우스 오른쪽버튼 pressed 이벤트
var bool PendingRightPressed;
// 계류중인 마우스 오른쪽버튼 released 이벤트
var bool PendingRightReleased;
// 계류중인 마우스 가운데버튼 pressed 이벤트
var bool PendingMiddlePressed;
// 계류중인 마우스 가운데버튼 released 이벤트
var bool PendingMiddleReleased;
// 계류중인 마우스 휠 버튼 scroll up 이벤트
var bool PendingScrollUp;
// 계류중인 마우스 휠 버튼 scroll down 이벤트
var bool PendingScrollDown;
// 캐시된 마우스 월드 원점
var Vector CachedMouseWorldOrigin;
// 캐시된 마우스 월드 방향
var Vector CachedMouseWorldDirection;
// 지난 마우스 인터액션 인터페이스
var MouseInterfaceInteractionInterface LastMouseInteractionInterface;
// Use ScaleForm?
var bool UsingScaleForm;
// Scaleform mouse movie
var MouseInterfaceGFx MouseInterfaceGFx;

simulated event PostBeginPlay()
{
  Super.PostBeginPlay();

  // 스케일폼을 사용중이라면, 스케일폼 무비 생성
  if (UsingScaleForm)
  {
    MouseInterfaceGFx = new () class'MouseInterfaceGFx';
    if (MouseInterfaceGFx != None)
    {
      MouseInterfaceGFx.MouseInterfaceHUD = Self;
      MouseInterfaceGFx.SetTimingMode(TM_Game);

      MouseInterfaceGFx.Init(class'Engine'.static.GetEngine().GamePlayers[MouseInterfaceGFx.LocalPlayerOwnerIndex]);
    }
  }
}

simulated event Destroyed()
{
  Super.Destroyed();

  // 스케일폼 무비가 존재하면, 소멸시킴
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.Close(true);
    MouseInterfaceGFx = None;
  }
}

function PreCalcValues()
{
  Super.PreCalcValues();

  // 스케일폼 무비가 존재하면, 그 뷰포트를 리셋시키고, 스케일 모드와 얼라인먼트를
  // 화면 해상도에 일치
  if (MouseInterfaceGFx != None)
  {
    MouseInterfaceGFx.SetViewport(0, 0, SizeX, SizeY);
    MouseInterfaceGFx.SetViewScaleMode(SM_NoScale);
    MouseInterfaceGFx.SetAlignment(Align_TopLeft);
  }
}

event PostRender()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local MouseInterfaceInteractionInterface MouseInteractionInterface;
  local Vector HitLocation, HitNormal;

  Super.PostRender();

  // Ensure that we aren't using ScaleForm and that we have a valid cursor
  if (!UsingScaleForm && CursorTexture != None)
  {
  // 가용 PlayerOwner 있는지 확인
  if (PlayerOwner != None)
  {
    // MouseInterfacePlayerInput 를 구하기 위해 형 변환
    MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

      // 스케일폼을 사용하지 않고 적합한 커서 텍스처가 있으면 렌더
    if (MouseInterfacePlayerInput != None)
    {
      // 캔버스 위치를 마우스 위치로 설정
      Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
      // 커서 컬러 설정
      Canvas.DrawColor = CursorColor;
      // 화면상에 텍스처 그리기
      Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
      }
    }
  }

  // 현재 마우스 인터액션 인터페이스 구하기
  MouseInteractionInterface = GetMouseActor(HitLocation, HitNormal);

  // 마우스 오버 및 마우스 아웃 처리
  // 기존에 마우스 인터액션 인터페이스가 있었는지?
  if (LastMouseInteractionInterface != None)
  {
    // 지난 마우스 인터액션 인터페이스가 현재 마우스 인터액션과 다르다면
    if (LastMouseInteractionInterface != MouseInteractionInterface)
    {
      // 마우스 아웃 함수 호출
      LastMouseInteractionInterface.MouseOut(CachedMouseWorldOrigin, CachedMouseWorldDirection);
      // 새 마우스 인터액션 인터페이스 할당
      LastMouseInteractionInterface = MouseInteractionInterface;

      // 지난 마우스 인터액션 인터페이스가 none 이 아니면
      if (LastMouseInteractionInterface != None)
      {
        // 마우스 오버 함수 호출
        LastMouseInteractionInterface.MouseOver(CachedMouseWorldOrigin, CachedMouseWorldDirection); // Call mouse over
      }
    }
  }
  else if (MouseInteractionInterface != None)
  {
    // 새 마우스 인터액션 인터페이스 할당
    LastMouseInteractionInterface = MouseInteractionInterface;
    // 마우스 오버 함수 호출
    LastMouseInteractionInterface.MouseOver(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  }

  if (LastMouseInteractionInterface != None)
  {
    // 왼쪽 마우스 버튼 처리
    if (PendingLeftPressed)
    {
      if (PendingLeftReleased)
      {
        // 왼쪽 클릭이니, 버리기
   PendingLeftPressed = false;
   PendingLeftReleased = false;
      }
      else
      {
        // 왼쪽 눌림
   PendingLeftPressed = false;
   LastMouseInteractionInterface.MouseLeftPressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
      }
    }
    else if (PendingLeftReleased)
    {
      // 왼쪽 뗌
      PendingLeftReleased = false;
      LastMouseInteractionInterface.MouseLeftReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 마우스 오른쪽 버튼 처리
    if (PendingRightPressed)
    {
      if (PendingRightReleased)
      {
   // 오른 클릭이니, 버리기
   PendingRightPressed = false;
   PendingRightReleased = false;
      }
      else
      {
   // 오른쪽 눌림
   PendingRightPressed = false;
   LastMouseInteractionInterface.MouseRightPressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
      }
    }
    else if (PendingRightReleased)
    {
      // 오른쪽 뗌
      PendingRightReleased = false;
      LastMouseInteractionInterface.MouseRightReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 가운데 마우스 버튼 처리
    if (PendingMiddlePressed)
    {
      if (PendingMiddleReleased)
      {
   // 가운데 클릭이니, 버리기
   PendingMiddlePressed = false;
   PendingMiddleReleased = false;
      }
      else
      {
   // 가운데 눌림
   PendingMiddlePressed = false;
   LastMouseInteractionInterface.MouseMiddlePressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
      }
    }
    else if (PendingMiddleReleased)
    {
      PendingMiddleReleased = false;
      LastMouseInteractionInterface.MouseMiddleReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 가운데 마우스 버튼 스크롤 업 처리
    if (PendingScrollUp)
    {
      PendingScrollUp = false;
      LastMouseInteractionInterface.MouseScrollUp(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }

    // 가운데 마우스 버튼 스크롤 다운 처리
    if (PendingScrollDown)
    {
      PendingScrollDown = false;
      LastMouseInteractionInterface.MouseScrollDown(CachedMouseWorldOrigin, CachedMouseWorldDirection);
    }
  }
}

function MouseInterfaceInteractionInterface GetMouseActor(optional out Vector HitLocation, optional out Vector HitNormal)
{
  local MouseInterfaceInteractionInterface MouseInteractionInterface;
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local Vector2D MousePosition;
  local Actor HitActor;

  // 가용 캔버스 및 플레이어 오너가 있는지 확인
  if (Canvas == None || PlayerOwner == None)
  {
    return None;
  }

  // 새 플레이어 인풋을 구하기 위해 형 변환
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

  // 플레이어 인풋이 사용가능한지 확인
  if (MouseInterfacePlayerInput == None)
  {
    return None;
  }

  // 마우스 위치를 IntPoint 로 저장했으나, Vector2D 여야 함
  MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  // 마우스 위치를 Deproject 하여 캐시된 벡터에 저장
  Canvas.DeProject(MousePosition, CachedMouseWorldOrigin, CachedMouseWorldDirection);

  // 트레이스 액터 이터레이터 수행. 가장 위의 마우스 인터액션 인터페이스를 구할 수 있도록
  // 하나의 이터레이터 사용. 이는 (스태틱 메시처럼) 다른 추적가능 오브젝트가
  // 마우스 인터액션 인터페이스 위에 있는 경우를 다룸.
  ForEach TraceActors(class'Actor', HitActor, HitLocation, HitNormal, CachedMouseWorldOrigin + CachedMouseWorldDirection * 65536.f, CachedMouseWorldOrigin,,, TRACEFLAG_Bullet)
  {
    // HitActor 가 그 마우스 인터액션 인터페이스를 구현하는지 확인하기 위해 형 변환
    MouseInteractionInterface = MouseInterfaceInteractionInterface(HitActor);
    if (MouseInteractionInterface != None)
    {
      return MouseInteractionInterface;
    }
  }

  return None;
}

function Vector GetMouseWorldLocation()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local Vector2D MousePosition;
  local Vector MouseWorldOrigin, MouseWorldDirection, HitLocation, HitNormal;

  // 적합한 캔버스와 플레이어 오너가 있는지 확인
  if (Canvas == None || PlayerOwner == None)
  {
    return Vect(0, 0, 0);
  }

  // 새 플레이어 인풋을 구하기 위해 형 변환
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

  // 플레이어 인풋이 적합한지 확인
  if (MouseInterfacePlayerInput == None)
  {
    return Vect(0, 0, 0);
  }

  // 마우스 위치를 IntPoint 로 저장하나, Vector2D 로 필요
  MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  // 마우스 위치를 Deproject 하고 캐시 벡터에 저장
  Canvas.DeProject(MousePosition, MouseWorldOrigin, MouseWorldDirection);

  // 실제 마우스 월드 위치를 구하기 위해 trace 수행
  Trace(HitLocation, HitNormal, MouseWorldOrigin + MouseWorldDirection * 65536.f, MouseWorldOrigin , true,,, TRACEFLAG_Bullet);
  return HitLocation;
}

defaultproperties
{
  // 마우스 좌표를 구하기 위해 언리얼의 플레이어 인풋을 사용하려면 거짓으로 설정
  UsingScaleForm=true
  CursorColor=(R=255,G=255,B=255,A=255)
  CursorTexture=Texture2D'EngineResources.Cursors.Arrow'
}

HUD 로직은 이렇습니다:

  • 렌더링되는 매 프레임마다 마우스 커서 렌더링합니다.
  • 월드 내 추적을 수행하여 현재 마우스 인터액션 인터페이스를 구합니다. 현재 마우스 인터액션 인터페이스가 지난 번 것과 다르다면, 적절한 마우스 오버 및/또는 마우스 아웃 함수를 호출합니다.
  • 가용 마우스 인터액션 인터페이스가 있으면, HUD 는 곧 유예된 마우스 입력을 처리합니다.
  • 각 마우스 입력에 대해 적절한 인터페이스 함수를 호출하고 불리언을 리셋시킵니다.

마우스 3D 좌표를 원해!

마우스 2D 좌표에 따라 마우스 3D 좌표를 구하려면, HUD 클래스 내에서 이 함수를 사용하면 됩니다. HUD 클래스가 필요한 이유는 Deproject 함수가 필요하기 때문입니다.

MouseInterfaceHUD.uc
function Vector GetMouseWorldLocation()
{
  local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  local Vector2D MousePosition;
  local Vector MouseWorldOrigin, MouseWorldDirection, HitLocation, HitNormal;

  // 적합한 캔버스와 플레이어 오너가 있는지 확인
  if (Canvas == None || PlayerOwner == None)
  {
    return Vect(0, 0, 0);
  }

  // 새 플레이어 인풋을 구하기 위해 형 변환
  MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput);

  // 플레이어 인풋이 적합한지 확인
  if (MouseInterfacePlayerInput == None)
  {
    return Vect(0, 0, 0);
  }

  // 마우스 위치를 IntPoint 로 저장하나, Vector2D 로 저장 필요
  MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  // 마우스 위치를 Deproject 하여 캐시 벡터에 저장
  Canvas.DeProject(MousePosition, MouseWorldOrigin, MouseWorldDirection);

  // 실제 마우스 월드 위치를 구하기 위해 trace 수행
  Trace(HitLocation, HitNormal, MouseWorldOrigin + MouseWorldDirection * 65536.f, MouseWorldOrigin , true,,, TRACEFLAG_Bullet);
  return HitLocation;
}

관련 토픽

키즈멧 Mouse Input 이벤트 추가하기


키즈멧이 플레이어와 액터의 상호작용 시점을 검출해 낼 수 있도록 하기 위한 커스텀 키즈멧 이벤트입니다.

SeqEvent_MouseInput.uc
class SeqEvent_MouseInput extends SequenceEvent;

var Vector HitLocation;
var Vector HitNormal;
var Vector MouseWorldOrigin;
var Vector MouseWorldDirection;

event Activated()
{
  local MouseInterfaceInteractionInterface MouseInteractionInterface;

  // originator 를 형 변환하여 마우스 인터액션 인터페이스임을 확인
  MouseInteractionInterface = MouseInterfaceInteractionInterface(Originator);

  if (MouseInteractionInterface != None)
  {
    // 이벤트가 활성화되었을 때 적절한 값을 밀어줄 수 있도록 값 구해오기
    MouseWorldOrigin = MouseInteractionInterface.GetMouseWorldOrigin();
    MouseWorldDirection = MouseInteractionInterface.GetMouseWorldDirection();
    HitLocation = MouseInteractionInterface.GetHitLocation();
    HitNormal = MouseInteractionInterface.GetHitNormal();
  }
}

defaultproperties
{
  ObjName="Mouse Input"
  ObjCategory="Input"

  bPlayerOnly=false
  MaxTriggerCount=0

  OutputLinks(0)=(LinkDesc="Left Pressed")
  OutputLinks(1)=(LinkDesc="Left Released")
  OutputLinks(2)=(LinkDesc="Right Pressed")
  OutputLinks(3)=(LinkDesc="Right Released")
  OutputLinks(4)=(LinkDesc="Middle Pressed")
  OutputLinks(5)=(LinkDesc="Middle Released")
  OutputLinks(6)=(LinkDesc="Scroll Up")
  OutputLinks(7)=(LinkDesc="Scroll Down")
  OutputLinks(8)=(LinkDesc="Mouse Over")
  OutputLinks(9)=(LinkDesc="Mouse Out")

  VariableLinks(1)=(ExpectedType=class'SeqVar_Vector',LinkDesc="HitLocation",bWriteable=true,PropertyName=HitLocation)
  VariableLinks(2)=(ExpectedType=class'SeqVar_Vector',LinkDesc="HitNormal",bWriteable=true,PropertyName=HitNormal)
  VariableLinks(3)=(ExpectedType=class'SeqVar_Vector',LinkDesc="MouseWorldOrigin",bWriteable=true,PropertyName=MouseWorldOrigin)
  VariableLinks(4)=(ExpectedType=class'SeqVar_Vector',LinkDesc="MouseWorldDirection",bWriteable=true,PropertyName=MouseWorldDirection)
}

키즈멧 Mouse Input 시퀸스 내에는 별 로직이랄 게 없습니다. 키즈멧 이벤트는 정말이지 언리얼스크립트와 키즈멧 사이의 게이트웨이 역할을 합니다. HUD 로부터 약간의 계산 결과값을 출력할 수도 있습니다. 액터는 인터액션 인터페이스를 구현해야 하며, SupportedEvents 배열 내에 이 키즈멧 노드를 넣어야 합니다.

관련 토픽

예제로 서브클래싱된 KActor 추가하기


마우스 인터액션 인터페이스와 키즈멧 마우스 인풋 이벤트 노드의 용례입니다.

마우스 인터액션 인터페이스의 구현으로, 마우스 인터페이스 HUD 가 그것을 사용할 수 있습니다. 이는 Pawn, Projectile, Vehicles 같은 여러 클래스로 확장될 수 있습니다.

마우스 인풋 키즈멧 시퀸스 이벤트의 지원으로, 레벨 디자이너는 코드 한 줄 직접 작성할 필요 없이도 코드의 득을 보아 마우스 인터액션을 만들어낼 수 있습니다.

MouseInterfaceKActor.uc
class MouseInterfaceKActor extends KActor
  Implements(MouseInterfaceInteractionInterface);

var Vector CachedMouseHitLocation;
var Vector CachedMouseHitNormal;
var Vector CachedMouseWorldOrigin;
var Vector CachedMouseWorldDirection;

// ===
// MouseInterfaceInteractionInterface 구현
// ===
function MouseLeftPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = HitLocation;
  CachedMouseHitNormal = HitNormal;
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 0);
}

function MouseLeftReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 1);
}

function MouseRightPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = HitLocation;
  CachedMouseHitNormal = HitNormal;
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 2);
}

function MouseRightReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 3);
}

function MouseMiddlePressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, Vector HitNormal)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = HitLocation;
  CachedMouseHitNormal = HitNormal;
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 4);
}

function MouseMiddleReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 5);
}

function MouseScrollUp(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 6);
}

function MouseScrollDown(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 7);
}

function MouseOver(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 8);
}

function MouseOut(Vector MouseWorldOrigin, Vector MouseWorldDirection)
{
  CachedMouseWorldOrigin = MouseWorldOrigin;
  CachedMouseWorldDirection = MouseWorldDirection;
  CachedMouseHitLocation = Vect(0.f, 0.f, 0.f);
  CachedMouseHitNormal = Vect(0.f, 0.f, 0.f);
  TriggerEventClass(class'SeqEvent_MouseInput', Self, 9);
}

function Vector GetHitLocation()
{
  return CachedMouseHitLocation;
}

function Vector GetHitNormal()
{
  return CachedMouseHitNormal;
}

function Vector GetMouseWorldOrigin()
{
  return CachedMouseWorldOrigin;
}

function Vector GetMouseWorldDirection()
{
  return CachedMouseWorldDirection;
}

defaultproperties
{
  SupportedEvents(4)=class'SeqEvent_MouseInput'
}

MouseInterfaceKActor 배후의 로직은 이렇습니다:

  • 어느 인터페이스 구현 함수든 HUD 를 통해 실행되면, 부착된 Mouse Input 키즈멧 노드를 트리거시킵니다. 트리거 콜 마지막의 수치가 아웃풋 인덱스입니다.

키즈멧 Mouse Input 이벤트 사용하기


새 맵을 만들고 WorldInfo 속성을 엽니다. 방법은, "View" 메뉴를 클릭하고 "World Info Properties" 를 클릭하면 됩니다. "World Info" 탭을 확장합니다. GameType for PIE 를 MouseInterfaceGameInfo 로 설정합니다. 디폴트로 UTDeathmatch 를 사용할 것입니다.

Kismet_00_AdjustWorldInfo.jpg

"Lightmass" 탭을 펼치고, "Use Global Illumination" 체크를 해제합니다. 테스트 맵에 라이트매스를 사용할 필요는 없습니다.

Kismet_01_AdjustWorldInfoLightmass.jpg

"콘텐츠 브라우저"를 열고 "액터 클래스" 탭을 선택합니다. 클래스 트리를 확장하여 "MouseInterfaceKActor" 를 선택합니다. 이렇게 해 두고서 월드 뷰포트 안에 맥락 메뉴를 열어 보면, 월드에 추가시킬 수 있을 것입니다.

Kismet_02_SelectMouseInterfaceKActor.jpg

월드 뷰포트 내에서 우클릭하여 뷰포트 맥락 메뉴를 엽니다. "Add MouseInterfaceKActor Here" 를 선택하여 MouseInterfaceKActor 를 추가합니다.

Kismet_03_AddMouseInterfaceKActor.jpg

MouseInterfaceKActor 에 보이는 스태틱 메시가 없기 때문에, 처음엔 선택된 변형 위젯(선택, 이동, 회전, 스케일 위젯)으로 나타날 것입니다. F4 키를 눌러 액터 속성 창을 엽니다. 선택을 정하려면 상단의 자물쇠 아이콘을 클릭합니다.

Kismet_04_OpenTheKActorProperties.jpg

"콘텐츠 브라우저"에서 괜찮은 스태틱 메시를 찾아 선택합니다.

Kismet_05_SelectAPhysicsStaticMesh.jpg

"Dynamic SMActor" 탭을 펼치고, "Static Mesh Component" 오브젝트를 확장한 다음, "Static Mesh Component" 탭을 확장합니다. "Static Mesh" 필드 옆의 녹색 화살표를 누르면 "콘텐츠 브라우저"에서 선택해 둔 스태틱 메시가 설정됩니다. 월드 뷰포트에 통을 볼 수 있을 것입니다.

Kismet_06_SetTheStaticMesh.jpg

레벨에 필수 라이팅을 추가하고 레벨을 컴파일합니다.

Kismet_07_AddADominantDirectionalLight.jpg

에디터 툴바 내의 Kismet_08_OpenKismet.jpg 버튼을 눌러 키즈멧 창을 엽니다.

월드 뷰포트 내의 MouseInterfaceKActor 를 계속 선택해 둔 상태로, 키즈멧 창의 빈 공간에 우클릭하여 맥락 메뉴를 띄웁니다. 새 Mouse Input 이벤트를 추가합니다. 이렇게 하면 월드에 있는 MouseInterfaceKActor 로 부착된 이벤트가 나오게 됩니다.

Kismet_09_CreateNewMouseInputEvent.jpg

Mouse Input 이벤트입니다. 확인해 보면 모든 출력 링크들이 발생할 지 모르는 마우스 인풋 타입을 참조하고 있습니다. 이벤트는 우리가 사용할 수도 있는 다양한 변수를 출력하기도 합니다.

Kismet_10_TheMouseInputEvent.jpg

Mouse Input 이벤트를 테스트하기 위해, 약간의 로그 액션을 만듭시다.

Kismet_11_AddLogAction.jpg

테스트하고자 하는 이벤트 출력에 대해 적절한 문구를 약간 출력하도록 로그 액션을 설정합니다.

Kismet_12_SetLogAction.jpg

모든 이벤트 출력을 테스트하기 위한 로그 액션을 더 만듭니다. 전부 적절히 연결해 줍니다.

Kismet_13_DuplicateAndSetLogActions.jpg

PIE 내에서 레벨을 실행하면 마우스 인풋 키즈멧 인터페이스를 테스트할 수 있을 것입니다. 마우스 버튼과 스크롤링은 마우스가 MouseInterfaceKActor 위에 있을 때만 반응한다는 점, 기억하시기 바랍니다.

Kismet_14_Test.jpg

MouseInterfaceKActor 에 총을 쏠 수 있도록 하는 시퀸스를 만들어 이 예제를 확장해 봅시다. 새 벡터 변수를 만드는 것부터 시작합니다. 마우스 원점과 마우스 적중 위치를 저장할 수 있도록 하기 위해서입니다.

Kismet_15_AddVectorVariable.jpg

벡터 변수를 이벤트의 변수 출력, "HitLocation", "Mouse WorldOrigin" 에 바인딩합니다.

Kismet_16_BindVectorToEvent.jpg

새 "Spawn Projectile" 액션을 추가합니다. 이는 액션이 실행될 때 발사체를 만들어 냅니다.

Kismet_17_CreateSpawnProjectileAction.jpg

새로이 생성된 "Spawn Projectile" 액션입니다.

Kismet_18_SpawnProjectileAction.jpg

"Spawn Projectile" 액션을 왼쪽 마우스 버튼이 눌릴 때 활성화되는 "Log" 액션으로 연결합니다. "Mouse WorldOrigin" 변수를 "Spawn Projectile" 이벤트 상의 "Spawn Location" 입력으로 연결합니다. "HitLocation" 변수를 "Spawn Projectile" 이벤트 상의 "Target Location" 입력으로 연결합니다. 그러면 "Spawn Projectile" 이 마우스 월드 원점 위치에 마우스 적중 위치를 향해 나아가는 발사체를 스폰하게 합니다. 이를 통해 월드의 MouseInterfaceKActor 에 발사하는 듯한 모습을 낼 수 있습니다.

Kismet_19_ConnectSpawnProjectileAction.jpg

스폰시킬 발사체 종류를 설정합니다.

Kismet_20_SetSpawnProjectileAction.jpg

"All Players" 변수를 추가합니다. "Spawn Projectile" 액션의 필수 입력입니다.

Kismet_21_AddPlayerVariable.jpg

새로이 만든 "All Players" 변수입니다.

Kismet_22_PlayerVariable.jpg

"All Players" 변수를 "Spawn Projectile" 로 연결합니다.

Kismet_23_ConnectPlayerVariable.jpg

PIE 를 실행하고 키즈멧을 테스트합니다. 이제 MouseInterfaceKActor 에 발사할 수 있을 것입니다.

Kismet_24_Test.jpg

관련 토픽

다운로드