박하의 나날

[26]_2

프로그래밍/Unreal

4.인벤토리 아이템 클릭 확인

:마우스 클릭된 위젯은 마우스를 드래그할때 움직일 것.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma once
 
#include "GameFramework/HUD.h"
#include "MyHUD.generated.h"
struct Widget{
    Icon icon;
    FVector2D pos, size;
    Widget(Icon iicon){
        icon = iicon;
    }
    float left(){ return pos.X;}
    float right(){ return pos.X + size.X; }
    float top(){ return pos.Y; }
    float bottom(){ return pos.Y + size.Y; }
 
    bool hit(FVector2D p){
        //+---+ 위(0)
        //|      |
        //+---+ 아래(2) (아래>위)
        //L R
        return p.X > left() && p.X < right() && p.Y > top() && p.Y < bottom();
    }
};
 
cs

 

left()의 오른쪽(p.X>left())

right()의 왼쪽(p.X<right())

top()의 아래(p.Y > top())

bottom()의 위(p.Y <bottom()) 이 모두 만족할때 p.X가 클릭된 것.

 

 

 

(UE4에서 Y축은 반전되어있어서 y는 점점 줄어든다. 그래서 top()은 bottom()보다 아래인 곳에 있다.)

 

마우스를 클릭하면 실행되어 클릭반응을 HUD로 전달하도록 하고 SetupPlayerInputComponent에서 응답자와 연결한다.

1
2
3
4
5
6
7
8
9
void AMyCharacter::MouseClicked(){
    APlayerController* pcon = GetWorld()->GetFirstPlayerController();
    AMyHUD* hud = Cast<AMyHUD>(pcon->GetHUD());
    hud->MouseClicked();
}
void AMyCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
    InputComponent->BindAction("MouseClickedLMB", IE_Pressed, this&AMyCharacter::MouseClicked);
}
cs

그다음 widget 충돌 검색을 시작한다.

MousedClicked 함수에서 화면의 모든 위젯에 대해 루프를 돌면서 현재 마우스 위치와 접촉했는지를

확인한다. 각 위젯은 현재 마우스 위치와 비교해서 확인된다.

1
2
3
4
5
6
7
8
9
10
11
12
void AMyHUD::MouseClicked(){
    FVector2D mouse;
    APlayerController *pcon = GetWorld()->GetFirstPlayerController();
    pcon->GetMousePosition(mouse.X, mouse.Y);
    heldWidget = NULL;
    for (int c = 0; c < widgets.Num(); c++){
        if (widgets[c].hit(mouse)){
            heldWidget = &widgets[c];
            return;
        }
    }
}
cs

5)마지막에 건드린 위젯에 대한 정보를 지운다.

6~)마우스 클릭 좌표가 어떤 위젯과 접촉했는지 살펴본다.

pcon->GetMousePosition()을 확인하는 것으로 언제든지 컨트롤러에서

현재 마우스의 위치를 얻을수 있다.

8)위젯 저장

9)어떤 위젯이 클릭당했는지 확인한 후로는 확인작업을 중단해야해서 return.

 

마우스가 움직일때 클릭된 위젯도 움직이게 한다.

드래그 함수는 이전 프레임과 현재 프레임 사이의 마우스 위치 차이를 보고 선택된 위젯을 그 차이만큼

움직인다. 정적변수는 MouseMoved()함수의 호출 사이의 lastMouse 위치를 기억한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void AMyHUD::GetMouseMoved(){
    APlayerController *pcon = GetWorld()->GetFirstPlayerController();
    float time = pcon->GetInputKeyTimeDown(EKeys::LeftMouseButton);
 
    static FVector2D lastMouse;
    FVector2D thisMouse, dMouse;
    
    pcon->GetMousePosition(thisMouse.X, thisMouse.Y);
    dMouse = thisMouse - lastMouse;
 
    
    if (time > 0.&& heldWidget){
        heldWidget->pos.X += dMouse.X;
        heldWidget->pos.Y += dMouse.Y;
    }
    lastMouse = thisMouse;
}
 
cs

12~)마우스클릭이 0초이상 되었는지 확인 - 드래그로 인식

 

 

MyCharacter클래스에서 Pitch와 Yaw를 이용해서 마우스를 연결했는데 이 두가지 함수를 확장하면

HUD로 마우스 입력을 보낼수 있다.

우선 인벤토리가 보여지고 있는지 확인하고 보여지고 있다면 입력은 MyCharacter에 영향을 주지 않고

바로 HUD로 간다. (HUD가 보여지고 있지 않다면 MyCharacter로.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void AMyCharacter::MoveStrafe(float amount){ //Pitch
    if (inventoryShowing){
        APlayerController* pcon = GetWorld()->GetFirstPlayerController();
        AMyHUD* hud = Cast<AMyHUD>(pcon->GetHUD());
        hud->GetMouseMoved();
        return;
    }
    FVector str = GetActorRightVector();
    AddMovementInput(str, amount);
}
 
void AMyCharacter::Yaw(float amount){
    if (inventoryShowing){
        APlayerController* pcon = GetWorld()->GetFirstPlayerController();
        AMyHUD* hud = Cast<AMyHUD>(pcon->GetHUD());
        hud->GetMouseMoved();
        return;
    }
    AddControllerYawInput(Velocity*amount*GetWorld()->GetDeltaSeconds());
cs

14)인벤토리가 출력되고 있을 때 입력을 HUD로 전달한다.

 

+0614

Npc가 아이템 건네주게끔.

PickupItem처럼 Npc도 이미 충돌을 체크하는 원이 있기 때문에 주려는 아이템 이름, 수량, 아이콘만 추가했다.

PickupItem이랑 다른점이라면 텍스쳐가 아이템 메시가 필요없다는거? 곧장 인벤토리로 넣어줄거니까.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class BOOK_0528_API ANPC : public ACharacter
{
    GENERATED_BODY()
 
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = NPCMessage)
        FString ItemName;
 
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = NPCMessage)
        int32 Quantity;
 
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = NPCMessage)
        UTexture2D* Icon;
};
 
cs

 

나머진 PickupItem이랑 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void ANPC::Prox_Implementation(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult){
    if (Cast<AMyCharacter>(OtherActor) == nullptr){
        return;
    }
 
    AMyCharacter *cha = Cast<AMyCharacter>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));
    APlayerController* pcon = GetWorld()->GetFirstPlayerController();
    AMyHUD *hud = Cast<AMyHUD>(pcon->GetHUD());    
 
    if (pcon){
        AMyHUD* hud = Cast<AMyHUD>(pcon->GetHUD());
        hud->addMessage(Message(name + TEXT(": "+ NpcMessage, 5.f, FColor::White));
        hud->addMessage(Message(ItemName + FString(TEXT(" Get!")), 5.f, FColor::White));
        cha->GetfromNPC(ItemName, Quantity, Icon);
    }    
}
cs

 6)플레이어에게 아이템을 주기 위해 플레이어 아바타의 참조를 얻는다.

PlayerPawn객체는 실제로 AAvatar인스턴스이고 Cast<AAvatar>명령을 사용하요 AAvatar클래스로 형변환한다.(AMyCharacter)

UGameStatics함수 집합은 전역함수라 어디서든지 접근할 수 있다.

7)컨트롤러의 참조를 얻는다.

8)컨트롤러에서 HUD의 참조를 얻는다.

HUD는 실제로 플레이어의 컨트롤러에 붙어있어서 이렇게 접근한다.

14)백팩에 아이템을 넣어줌.

NPC가 아이템을 전해주면, GetfromNPC로 넘어가 백팩에 아이템이 들어왔나 확인한다.

1
2
3
4
5
6
7
8
9
void AMyCharacter::GetfromNPC(FString name, int num, UTexture2D* icon){
    if (Backpack.Find(name)){
        Backpack[name] += num;
    }
    else{
        Backpack.Add(name, num);
        Icons.Add(name, icon);
    }
}
cs

 이미 백팩에 있는 아이템 이름이면 수량만 추가하고 없던 아이템이면 이름, 아이콘, 수량 추가.