[2023 하계 모각소] 어만사 2주차

jungizz_·2023년 7월 13일
0

모각소

목록 보기
2/12
post-thumbnail

📝 2주차

  • 언리얼 오류 해결
  • 언리얼 엔진 입문 섹션1. 유니티vs언리얼 까지 🔗
  • 백준 단계별로 풀어보기🔗

📅 7월 16일 21:00-24:00



📖 언리얼

💬 1주차 언리얼 빌드 오류

  • 코드를 수정하고 비주얼 스튜디오 내에서 빌드를 하거나 언리얼 엔진의 컴파일 버튼을 눌러서 새롭게 언리얼에 적용된다고 생각했는데 (강의 영상 언리얼4 기준)
  • 언리얼5에는 컴파일 버튼이 없고, 버전 문제인지는 몰라도 비주얼 스튜디오에서 빌드하면 오류가 발생했다.

해결방법

1. 라이브 코딩으로 컴파일

  • 언리얼 우측 하단의 라이브 코딩 버튼 또는 [Ctrl+Alt+F11] 단축키
  • 라이브 코딩을 진행하면 아래와 같은 창이 뜨면서 컴파일이 진행되고 수정된 내용이 적용된다.

2. 라이브 코딩을 비활성화 하고 비주얼 스튜디오 내에서 빌드

  • 라이브 코딩을 끄면 강의 영상처럼 비주얼 스튜디오에서 빌드하여 컴파일 할 수 있고 오류가 뜨지 않는다!! (이것이 오류의 원인)
  • 라이브 코딩을 비활성화 하면 아까의 라이브 코딩 버튼이 언리얼4의 컴파일 버튼 역할을 하는 것 같다.

💬 언리얼 C++ 자동완성 속도

  • 코드를 작성할 때 뜨는 자동완성이 엄청 느리므로 속도를 빠르게 하려면 도구 > 옵션 > 덱스트 편집기 > c/c++ -> 고급 에서 자동 업데이트 사용 안 함을 True로 설정
  • 전보단 빠르지만 엄청 빠르진 않다..


3. 로그와 디버깅

유니티언리얼
Start()BeginPlay()
Update()Tick()

◾ 로그 출력

  • 오류나 어떠한 값을 확인할 때 유용하게 확인되는 로그 출력
  • 로그 상세 수준 중 log file에도 출력 되는 로그는 프로젝트 폴더의 Saved>Logs에서 확인할 수 있다.

    UE_LOG 함수 - 매개변수

    1. 로그 카테고리: 출력된 로그에서 내가 원하는 카테고리의 로그만 검색할 수 있는 등 구분을 해줌
    2. 로그 상세 수준: error, warning, log 등의 종류가 있으며 색상이 다르게 나타남
    3. 로그 내용(형식): TEXT("~~")
    4. 인자: 로그 내용이 TEXT("%d")인 경우, %d에 들어갈 인자
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
	Super::BeginPlay();

	//시작 로그
	UE_LOG(LogTemp, Warning, TEXT("BeginPlay"));
	
}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	//매 프레임마다 로그
	UE_LOG(LogTemp, Error, TEXT("Tick %f"), DeltaTime);
}


◾ 디버깅

  • 언리얼 에디터를 띄운 상태에서 break point를 잡고 디버깅은 불가능
  • 언리얼 에디터를 종료한 뒤 비주얼 스튜디오 상에서 디버거를 실행하면 언리얼 에디터가 켜진다.
  • 그 상태에서 플레이하면 break point에서 멈추며 디버깅이 가능하다.
  • 디버깅 중지 버튼을 누르면 언리얼 에디터도 같이 종료된다(..)
  • 핫 리로드 시스템에서 로그를 출력하며 확인하거나, 언리얼 에디터가 종료된 상태에서 디버깅하는 방법으로 개발하곤 한다.

    핫 리로드

    • 언리얼 에디터의 실행 중에 에디터가 사용중인 모듈을 컴파일하면 이를 감지하여 기존 모듈을 내리고 신규 모듈로 바꾸는 작업
    • 기존 모듈을 덮어쓰지 않고 새로운 파일로 생성되며 언리얼 에디터를 완전히 종료하고 컴파일하면 임시 모듈들은 제거됨
    • 라이브 코딩을 대신할 수 있는 기능이지만 라이브코딩이 더 빠르고 유연

◾ 빌드 모드

  • DebugGameDevelopment모드는 DebugvsRelease와 비슷하다
  • DebugGame모드가 최적화가 덜 되어있지만 디버깅에 유용하다.
  • Shipping은 최종 배포에 적합한 모드로 최적화를 많이 해주므로 디버깅에 부적합하다.

    ✔ Editor

    • Editor가 붙은 모드로 빌드 시 dll파일이 만들어지며 에디터 위에서 dll파일만 교체하는 것이고
    • 붙지 않은 모드는 exe 실행 파일을 생성하는데 아트 리소스 파일을 따로 지정해주지 않으면 오류가 발생한다.
❗ 이미지에 잘려서 안보이는데 Development Editor가 기본값으로 설정되어있다.

◾ 회전하는 오브젝트

📌 클래스.h

private:
	. . .
    
	float RotateSpeed = 30.f;

📌 클래스.cpp

void AMyActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
    
    //z축 기준으로 회전
	AddActorLocalRotation(FRotator(0.f, RotateSpeed * DeltaTime, 0.f));
}


4. 게임 플레이 프레임워크

  • 게임의 틀을 이루는 게임 규칙, 플레이어 입력과 컨트롤, 카메라, 유저 인터페이스 등의 코어 시스템
  • 월드 세팅 창의 Game Mode에서 게임의 규칙을 설정

    ➕ 새로운 맵 생성 [Ctrl + N]

◾ 게임 모드 베이스

  • 새로운 게임의 규칙을 만들 수 있는 액터
  • 새 C++ 클래스에서 Game Mode Base를 생성
  • 월드 세팅 창의 Game Mode에서 게임모드 오버라이드를 새로운 Game Mode Base를 적용

◾ 키보드로 움직일 수 있는 게임모드

1. Pawn 생성

  • 컨트롤러의 입력을 받기 위해 새로운 Pawn 클래스 생성
  • 이전에 만들었던 Actor 클래스 코드와 동일하게 작성

📌 MyPawn.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"

UCLASS()
class PRACTICEUNREAL_API AMyPawn : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AMyPawn();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:
	UPROPERTY(VisibleAnyWhere)
	UStaticMeshComponent* Mesh;
};

📌 MyPawn.cpp

#include "MyPawn.h"

// Sets default values
AMyPawn::AMyPawn()
{
 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MESH"));

	RootComponent = Mesh;

	static ConstructorHelpers::FObjectFinder<UStaticMesh> SM(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Props/SM_Couch.SM_Couch'"));

	if (SM.Succeeded())
	{
		Mesh->SetStaticMesh(SM.Object);
	}

}

// Called when the game starts or when spawned
void AMyPawn::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AMyPawn::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}
  • Actor의 상속을 받은 Pawn이므로 다른 함수들이 등장 (움직일 수 있는 액터)

2. Default Pawn Class 변경

  • Defaulf Pawn Class는 간단히 말하면 게임 모드에서 사용할 메인 캐릭터로 카메라가 달려있다.
  • 게임 모드 베이스 클래스에 생성자를 생성하여 Default Pawn Class에 원하는 스태틱을 적용 (위에서 만든 Pawn을 적용함)

📌 MyGameModeBase.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class PRACTICEUNREAL_API AMyGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

	//생성자 생성
	AMyGameModeBase(); 
};

📌 MyGameModeBase.cpp

#include "MyGameModeBase.h"
#include "MyPawn.h"

AMyGameModeBase::AMyGameModeBase() 
{
	DefaultPawnClass = AMyPawn::StaticClass();
}

3. 키 입력으로 폰 움직이기

  • Update()함수에서 입력 받는 코드를 작성하는 유니티와 다르게
  • 폰의 함수인SetupPlayerInputComponent(UInputComponent*)안에서 입력을 받는다.

    언리얼의 입력

    • Axis: 지정된 상수값을 전달하는 입력으로, 해당 키가 눌리는 동안 정해놓은 값이 전달되고 입력이 없으면 0이 전달 - 조이스틱(연속적, 정도)
    • Action: 키 입력과 비슷하며 입력한 순간, 입력 도중, 입력 끝 등에 따른 이벤트 - 버튼(T/F)
  • 프로젝트 세팅 > 엔진 > 입력 > 축 매핑에서 UpDown, LeftRight에 해당하는 키보드를 설정하고 Scale도 1-1로 설정

📌 MyPawn.cpp

  • Axis입력을 받는 코드 작성
  • 각 함수에 따라 움직임 코드 작성
#include "MyPawn.h"
#include "GameFramework/FloatingPawnMovement.h"

// Sets default values
AMyPawn::AMyPawn()
{
	PrimaryActorTick.bCanEverTick = true;

	Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MESH"));
    
    //움직임 컴포넌트 생성
	Movement = CreateDefaultSubobject<UFloatingPawnMovement>(TEXT("MOVEMENT"));
	. . .

}

. . .

// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	
    //Axis 입력 받기
    //입력에 따라 실행할 함수를 받음
	PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AMyPawn::UpDown);
	PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AMyPawn::LeftRight);
}

//매핑에서 설정한 Scale값이 Value로 넘어오는 것
void AMyPawn::UpDown(float Value)
{
	if (Value == 0.f) return;
	
    //Value에 따라 앞뒤 움직임
	AddMovementInput(GetActorForwardVector(), Value);
}

void AMyPawn::LeftRight(float Value)
{
	if (Value == 0.f) return;

	//Value에 따라 좌우 움직임
	AddMovementInput(GetActorRightVector(), Value);
}

📌 MyPawn.h

. . .

public:	
	. . .
	
    //입력 받았을 때 실행할 함수 선언
	void UpDown(float Value);
	void LeftRight(float Value);

private:
	UPROPERTY(VisibleAnyWhere)
	UStaticMeshComponent* Mesh;
	
    //움직임을 위한 컴포넌트
	UPROPERTY(VisibleAnyWhere)
	class UFloatingPawnMovement* Movement;
};
  • 의자 메시를 적용해놔서 앞이 잘 안보이긴 하지만.. 키보드 입력으로 움직임

5. 캐릭터 생성

◾ 에셋 다운

  • 에픽게임즈의 마켓플레이스에서 퀄리티 좋은 무료 에셋들을 다운받을 수 있다.
  • 원하는 캐릭터 에셋을 다운받아 프로젝트에 추가한다.


◾ 캐릭터 클래스 생성

  • 캐릭터를 생성하기 위해 캐릭터 클래스를 생성
  • 폰의 상속을 받은 클래스이며 좀 더 캐릭터에 맞는 함수들이 존재한다.
  • 캐릭터 메시를 적용하고 키보드 입력으로 움직임을 위한 코드를 작성 (이전과 거의 비슷한 형태)

📌 MyCharacter.cpp

#include "MyCharacter.h"

// Sets default values
AMyCharacter::AMyCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//메시 로드
	static ConstructorHelpers::FObjectFinder<USkeletalMesh> SM(TEXT("SkeletalMesh'/Game/ParagonSunWukong/Characters/Heroes/Wukong/Meshes/Wukong.Wukong'"));

	if (SM.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(SM.Object);
	}
}

. . .

// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	//키보드 입력
	PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AMyCharacter::UpDown);
	PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AMyCharacter::LeftRight);
}

void AMyCharacter::UpDown(float Value)
{
	if (Value == 0.f) return;

	AddMovementInput(GetActorForwardVector(), Value);
}

void AMyCharacter::LeftRight(float Value)
{
	if (Value == 0.f) return;

	AddMovementInput(GetActorRightVector(), Value);
}

📌 MyCharacter.h

. . .

public:	
	. . .

	void UpDown(float Value);
	void LeftRight(float Value);
};

📌 MyGameModeBase.h

#include "MyGameModeBase.h"
#include "MyCharacter.h"

AMyGameModeBase::AMyGameModeBase() 
{
	DefaultPawnClass = AMyCharacter::StaticClass();
}

◾ 카메라 위치 설정

  • 캐릭터와 키보드 움직임은 적용되지만 캐릭터에 카메라가 박힌 상태
  • SpringArm 컴포넌트로 카메라 위치를 설정한다. (셀카봉 같은 역할)

📌 MyCharacter.cpp

#include "MyCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"

// Sets default values
AMyCharacter::AMyCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SPRINGARM"));
	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("CAMERA"));
	
	//루트 컴포넌트에 붙이기
	SpringArm->SetupAttachment(GetCapsuleComponent());
	//SpringArm에 붙이기
	Camera->SetupAttachment(SpringArm);

	SpringArm->TargetArmLength = 500.f; //길이
	SpringArm->SetRelativeRotation(FRotator(-35.f, 0.f, 0.f)); //각도

	//위치와 각도 설정
	GetMesh()->SetRelativeLocationAndRotation(
		FVector(0.f, 0.f, -88.f), FRotator(0.f, -90.f, 0.f));


	static ConstructorHelpers::FObjectFinder<USkeletalMesh> SM(TEXT("SkeletalMesh'/Game/ParagonSunWukong/Characters/Heroes/Wukong/Meshes/Wukong.Wukong'"));

	if (SM.Succeeded())
	{
		GetMesh()->SetSkeletalMesh(SM.Object);
	}
}

. . .

📌 MyCharacter.h

. . .

private:
	UPROPERTY(VisibleAnyWhere)
	class USpringArmComponent* SpringArm;

	UPROPERTY(VisibleAnyWhere)
	class UCameraComponent* Camera;
  • 플레이 모드에서 카메라가 적당한 거리에 고정된 것을 확인할 수 있다.
  • cpp 코드에서 SpringArm을 루트 컴포넌트에 붙이고, Camera를 SpringArm에 붙인다는 것은 자식 컴포넌트로 적용하는 것을 의미
  • 길이, 위치, 각도 등을 설정해준 값은 디테일 창에서도 확인 가능하고 수정할 수 있다.

◾ 마우스로 회전

  • 축 매핑에 Yaw에 마우스X를 설정
  • Yaw축 코드를 추가하고, 이에 따른 움직임 함수를 적용한다.
  • 바닥부터 만들어나가는 유니티와 다르게 언리얼은 많은 기능들이 만들어져있고 이를 잘 사용해야한다.. (마우스로 회전하는 기능도 바닥부터 만드려고하면 복잡했을 것)

📌 MyCharacter.cpp

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

	PlayerInputComponent->BindAxis(TEXT("UpDown"), this, &AMyCharacter::UpDown);
	PlayerInputComponent->BindAxis(TEXT("LeftRight"), this, &AMyCharacter::LeftRight);
    
    //Yaw 축
	PlayerInputComponent->BindAxis(TEXT("Yaw"), this, &AMyCharacter::Yaw);
}

. . .

//마우스로 회전
void AMyCharacter::Yaw(float Value)
{
	AddControllerYawInput(Value);
}

📌 MyCharacter.h

. . .

public:	
	. . .
    
	void UpDown(float Value);
	void LeftRight(float Value);
	void Yaw(float Value); //Yaw 축
  • 플레이 모드에서 마우스 회전에 따라 화면과 캐릭터가 회전하는 것을 확인할 수 있다.
  • 폰의 컨트롤러 회전 요 사용이 활성화되어있어야한다.


6. 블루프린트 클래스

  • 콘텐츠에 Blueprints폴더를 만들고, 블루프린트 클래스BP_MyCharacter를 생성
  • c++ 클래스 만드는 것 처럼 상속을 받아 생성
  • 만든 블루프린트를 더블클릭하면 아래와 같은 창이 생성됨
  • C++클래스와 다르게 이름 수정, 삭제 등이 쉽다.
  • C++에 작성했던 과정을 시각적으로 구성하는 과정이라 더욱 간편하다.
  • 좌측 상단의 컴파일 버튼을 컴파일을 수행하고 저장한다.
  1. 뷰포트
  2. Constructor Script
  3. 이벤트 그래프

◾ BP-캐릭터 적용

  • 좌측 메시 컴포넌트에서 캐릭터를 만든다.
  • StaticMesh를 원하는 메시로 지정하고, 위치와 각도를 지정하여 알맞게 배치한다. (파란색 동그라미에 해당하는 화살표 방향을 향하도록)
    • 아래 코드와 같은 과정이다.

      //메시 지정
      static ConstructorHelpers::FObjectFinder<UStaticMesh> SM(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Props/SM_Couch.SM_Couch'"));
      if (SM.Succeeded()) Mesh->SetStaticMesh(SM.Object);
      
      //위치, 각도 지정
      GetMesh()->SetRelativeLocationAndRotation(FVector(0.f, 0.f, -88.f), FRotator(0.f, -90.f, 0.f));

◾ BP-컴포넌트 추가

  • 좌측 추가버튼을 눌러 컴포넌트를 쉽게 추가할 수 있다.
  • Spring Arm과 Camera컴포넌트를 추가한다.
    private:
    	UPROPERTY(VisibleAnyWhere)
    	class USpringArmComponent* SpringArm;
    
    	UPROPERTY(VisibleAnyWhere)
    	class UCameraComponent* Camera;
  • 드래그로 상속 구조를 맞춰준다.
    	SpringArm->SetupAttachment(GetCapsuleComponent());
    	Camera->SetupAttachment(SpringArm);
  • SpringArm의 길이와 회전 각도를 설정
    	SpringArm->TargetArmLength = 500.f;
    	SpringArm->SetRelativeRotation(FRotator(-35.f, 0.f, 0.f));

◾ BP-변수 추가

  • 좌측의 변수의 더하기 버튼을 눌러 생성할 수 있으며 자료형과 public/private를 설정
  • 이벤트그래프 창에서 SpringArm컴포넌트를 드래그 앤 드롭하여 가져온 블록 우측의 화살표 버튼을 드래그하여 SetTargetArmLength를 선택
  • ArmLength변수를 드래그 앤 드롭하여 get하고 타겟 암 길이에 연결
  • BeginPlay이벤트도 연결하여 이벤트에 따라 실행될 수 있도록 한다. (연결을 끊으러면 Alt를 누르고 이벤트 블록의 화살표 버튼 클릭)
    SpringArm->TargetArmLength = ArmLength;

◾ BP-캐릭터 움직임

  • 이벤트그래프 창에서 우클릭하여 축 이벤트를 가져온다.
  • 축 이벤트 실행 화살표에서 Add Movement Input을 선택하고, 빈 곳을 우클릭해 Get Actor Right Vector를 가져와 아래와 같이 연결한다.
    void AMyCharacter::LeftRight(float Value)
    {
    	AddMovementInput(GetActorRightVector(), Value);
    }
  • 다른 축도 동일하게 생성

◾ 블루프린트 클래스 캐릭터를 메인 캐릭터로 설정

  • MyGameModeBase 코드에서 블루 프린트 클래스를 연결

📌 MyGameModeBase.cpp

#include "MyGameModeBase.h"
#include "MyCharacter.h"

AMyGameModeBase::AMyGameModeBase() 
{
	//블루프린트 클래스 찾기
	//블루프린트 경로 설정 시 마지막에 '_C' 붙여야함
	static ConstructorHelpers::FClassFinder<ACharacter> BP_Char(TEXT("Blueprint'/Game/Blueprints/BP_MyCharacter.BP_MyCharacter_C'"));

	//블루프린트 연결
	if (BP_Char.Succeeded()) DefaultPawnClass = BP_Char.Class;

}
  • C++클래스로 만든 캐릭터와 동일한 결과가 나타난다.

✔ C++과 블루프린트 클래스

  • C++클래스를 상속받아 블루프린트 클래스를 만들 수는 있지만 반대는 안된다.
  • C++클래스를 상속받아 만든 블루프린트에는 구성 요소들이 전부 나타난다.
  • 시각적으로 보기 편하고 메시를 변경하거나 속성을 변경하는 등의 과정을 간단하게 할 수 있으므로 적절하게 사용하면 좋다.


📖 백준

  • CLASS 단계별로 풀려고했는데 C++ 기초가 부족한 것 같아서 다시 단계별로 풀어보기로 돌아옴 (ㅎ.ㅎ..)
  • 과거의 내가 띄엄띄엄 풀어놔서 구멍 메우기 중이다
  • <코딩테스트를 위한 자료구조와 알고리즘 With C++>책을 보며 기초부터 다시 다지려 함..

✔ 11문제 해결

profile
( •̀ .̫ •́ )✧

0개의 댓글