프로젝트 파일로 가서 source -> 프로젝트이름 폴더 -> 프로젝트이름 .build.cs 라는 c#문서를 열어서 UMG를
추가해준다.
에디터에서 마우스오른클 ->유저 인터페이스 -> 위젯 블루프린트를 만들고 대충 저런 모양 나오게
셋팅한다 (c++보는사람은 블루프린트 이미 알고있을거라 생각하니 사용법은 생략)
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "PuzzlePlatformsGameInstance.generated.h"
/**
*
*/
UCLASS()
class TEST_API UPuzzlePlatformsGameInstance : public UGameInstance
{
GENERATED_BODY()
//c++ 생성자 단에서 초기화
UPuzzlePlatformsGameInstance(const FObjectInitializer& ObjectInitializer);
public:
//언리얼 게임인스턴스 단에서 초기화
virtual void Init();
UFUNCTION(Exec)
void Host(); //호스트
UFUNCTION(Exec) //조인 + 접속아이피
void Join(const FString& Address);
UFUNCTION(BlueprintCallable)//메뉴불러오는 함수
void LoadMenu();
private:
//위젯가져오기
TSubclassOf<class UUserWidget> MenuClass;
//메인메뉴 클래스 포인터
class UMainMenu* Menu;
};
위젯을 다룰 인스턴스 소스의헤더파일에 블루프린트에서 사용할수있도록 위와같이 함수선언 +
위젯클래스를 가져올수 있도록 해준다.
void UPuzzlePlatformsGameInstance::LoadMenu()
{
if (!ensure(MenuClass != nullptr)) return;
UUserWidget* Menu = CreateWidget<UUserWidget>(this, MenuClass);
if (!ensure(Menu != nullptr)) return;
Menu->AddToViewport(); //뷰포트에 나타내기
}
그리고 cpp파일에서 이렇게 코딩해준후
레벨블루프린트에서 다음과 같이 스크립트를 짜준다.
게임화면이 이같이 뷰포트에 덮어씌워진다.
위와같이 나오는데. 캐릭터 asdw로 움직이고 회전 그대로되면서
버튼을 누르고싶은데 마우스커서가 안나옴.
그러니까 포커스가 위젯에 맞춰져야한다.
블루프린트로는 이런거.
그러니까 다음과같이 코딩.
void UPuzzlePlatformsGameInstance::LoadMenu()
{
if (!ensure(MenuClass != nullptr)) return;
UUserWidget* Menu = CreateWidget<UUserWidget>(this, MenuClass);
if (!ensure(Menu != nullptr)) return;
Menu->AddToViewport(); //뷰포트에 나타내기
//현제 이용중인 플레이어 컨트롤러를 얻어옴.
APlayerController* PlayerController = GetFirstLocalPlayerController();
if (!ensure(PlayerController != nullptr)) return;
//사용자 입력을 처리해줄 라이브러리
FInputModeUIOnly InputModeData;
//사용랗 위젯을 지정
InputModeData.SetWidgetToFocus(Menu->TakeWidget());
//마우스를 뷰포트에 고정할건지 자유롭게 놔둘건지 지정
InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
//위에서 구현한사항을 플레이어 컨트롤러에서 지정
PlayerController->SetInputMode(InputModeData);
//마우스커서 보이게하기
PlayerController->bShowMouseCursor = true;
}
이렇게하고 컴파일하면 키보드입력이 안먹고 마우스커서가 나타나서 버튼을 누를 수 있게된다.
그러나 아직 버튼을 눌러도 아무 동작을 하지 않는다
당연히 바인딩을 안해줬으니까.
준비물.
usetWidget을 부모로한 c++클래스를
다음과같은 이름으로 생성해준다.
저번에 만든 umg에서
1.그래프탭 선택.
2.클래스세팅
3.부모클래스를 아까 만든 c++클래스로 설정해준다.
헤더파일에 바인딩할 버튼들을 스샷처럼 코딩하는 방식.
주의. 이름똑같이 해줄것
mainmenu의 헤더파일
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "MainMenu.generated.h"
/**
*
*/
UCLASS()
class TEST_API UMainMenu : public UUserWidget
{
GENERATED_BODY()
public:
protected:
//초기화 + 유무를 검출하기위해 bool반환
virtual bool Initialize();
private:
//UMG의 버튼과 코드를 바인딩하기위한 버튼선언.
UPROPERTY(meta = (BindWidget))
class UButton* Host;
UPROPERTY(meta = (BindWidget))
class UButton* Join;
UFUNCTION() // 호스트가 되는 함수
void HostServer();
};
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/StaticMeshActor.h"
#include "MovingPlatform.generated.h"
/**
*
*/
UCLASS()
class TEST_API AMovingPlatform : public AStaticMeshActor
{
GENERATED_BODY()
public:
AMovingPlatform();
virtual void Tick(float DeltaTime) override;
virtual void BeginPlay() override;
UPROPERTY(EditAnywhere)//블프나 에디터상에서 모두 편집가능
float Speed = 20;
UPROPERTY(EditAnywhere, Meta = (MakeEditWidget = true))//에디터상 위젯에 나타냄
FVector TargetLocation;
void AddActiveTrigger(); //이동
void RemoveActiveTrigger(); //해제
private:
FVector GlobalTargetLocation;
FVector GlobalStartLocation;
UPROPERTY(EditAnywhere)
int ActiveTriggers = 0; //트리거 발동조건
};
구현부
// Fill out your copyright notice in the Description page of Project Settings.
#include "MovingPlatform.h"
//움직이는 스태틱메쉬
AMovingPlatform::AMovingPlatform() {
//틱 이벤트 사용
PrimaryActorTick.bCanEverTick = true;
//이물체가 이동성을 가지는지 설정 Enum형
SetMobility(EComponentMobility::Movable);
}
void AMovingPlatform::BeginPlay()
{
Super::BeginPlay();
if (HasAuthority()) {
//서버와 클라이언트를 동기화
SetReplicates(true);
SetReplicateMovement(true);
}
GlobalStartLocation = GetActorLocation();
GlobalTargetLocation = GetTransform().TransformPosition(TargetLocation);
}
void AMovingPlatform::Tick(float DeltaTime) {
//부모로부터 가상함수를 재정의 하고있는지 확인
Super::Tick(DeltaTime);
if(ActiveTriggers >0 ) //트리거 발동되었을때.
//참이면 서버, 거짓이면 단일 클라이언트일때를 분기
if (HasAuthority()){
//현재액터의 위치 변수저장하기
FVector location = GetActorLocation();
//틱마다 x축으로 델타시간*스피드 만큼 이동
float JourneyLength = (GlobalTargetLocation - GlobalStartLocation).Size(); //목표지점과 첫지점의 거리
float JourneyTravelled = (location - GlobalStartLocation).Size(); //현재내지점과 목표지점의 거리
if (JourneyTravelled >= JourneyLength) //목표지점 첫지점을 바꿈
{
FVector Swap = GlobalStartLocation;
GlobalStartLocation = GlobalTargetLocation;
GlobalTargetLocation = Swap;
}
//방향벡터구하기
FVector Direction = (GlobalTargetLocation - GlobalStartLocation).GetSafeNormal();
//이동
location += Speed * DeltaTime * Direction;
SetActorLocation(location);
}
}
void AMovingPlatform::AddActiveTrigger()
{
ActiveTriggers++;
}
void AMovingPlatform::RemoveActiveTrigger()
{
if (ActiveTriggers > 0) {
ActiveTriggers--;
}
}
버튼이눌렸을때 액터가 움직이도록 하는 액터의 헤더파일
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PlatformTrigger.generated.h"
UCLASS()
class TEST_API APlatformTrigger : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
APlatformTrigger();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
private:
UPROPERTY(VisibleAnywhere)
class UBoxComponent* TriggerVolume;
UFUNCTION() // 겹쳤을때 함수
void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION() // 나왔을대 함수
void OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UPROPERTY(EditAnywhere) //트리거가 발동되었을때 움직이는 플랫폼의 배열
TArray<class AMovingPlatform*> PlatformsToTrigger;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "PlatformTrigger.h"
#include "Components/BoxComponent.h"
#include "MovingPlatform.h"
// Sets default values
APlatformTrigger::APlatformTrigger()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
TriggerVolume = CreateDefaultSubobject<UBoxComponent>(FName("TriggerVolume"));
if(!ensure(TriggerVolume != nullptr)) return;
RootComponent = TriggerVolume;
TriggerVolume->OnComponentBeginOverlap.AddDynamic(this, &APlatformTrigger::OnOverlapBegin);
TriggerVolume->OnComponentEndOverlap.AddDynamic(this, &APlatformTrigger::OnOverlapEnd);
}
// Called when the game starts or when spawned
void APlatformTrigger::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void APlatformTrigger::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void APlatformTrigger::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
UE_LOG(LogTemp, Warning, TEXT("Activated"));
for (AMovingPlatform* Platform : PlatformsToTrigger) //배열에있는 트리거들 모두순회
{//이동트리거발동
Platform->AddActiveTrigger();
}
}
void APlatformTrigger::OnOverlapEnd(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
UE_LOG(LogTemp, Warning, TEXT("Deactivated"));
for (AMovingPlatform* Platform : PlatformsToTrigger)
{//이동트리거제거
Platform->RemoveActiveTrigger();
}
}