우리는 이전에 UserWidget을 상속받은 WBP_sniperUI를 만들었습니다. 이걸 C++에서 처리하려면 build.cs에 UMG 모듈을 추가하고 리빌드 해야지만 간단한 로딩정도는 다음과 같이 피해갈수 있습니다.
Userwidget을 상속받아 MyUserWidget C++클래스를 만듭니다. 이클래스를 UserWidget 대신 상속시킬겁니다.
2개의 파일이 만들어지지만 현재는 아무것도 하지 않습니다.
UMyUserWidget 클래스가 생성되었습니다. 이걸 나중에 사용합니다.
UCLASS()
class TPSPROJECT_API UMyUserWidget : public UUserWidget
{
GENERATED_BODY()
};
WBP_sniperUI를 열고 우상을 보면 Parent class가 UserWidget입니다. 이걸 방금만든 MyUserWidget으로 변경합니다.
우산 Class Settings를 클릭해보면
Details패널에서 Parent Class풀다운메뉴에서 MyUserWidget을 선택해서 ReParent해줍니다. 저장해줍니다.
WidgetBlue프린트를 하나더 만듭니다. 이번에는 MyUserWidget을 선택합니다. 이름은 WBP_Crosshair로 고쳐줍니다.
아까와 비슷하게 하고 Image Soruce는 Crosshair입니다. Tint를 빨간색으로 변경합니다.
앞에서만든 UMyUserWidget클래스를 사용하여 변수를 만들어 줍니다.
TPSPlayer.h
public:
// 스나이퍼 조준
void SniperAim();
// 스나이퍼 조준 중인지 여부
bool bSniperAim = false;
// 스나이퍼 UI 위젯 공장
UPROPERTY(EditDefaultsOnly, Category = Widget)
TSubclassOf<class UMyUserWidget> sniperUIFactory;
// 스나이퍼 UI 위젯 인스턴스
class UMyUserWidget* _sniperUI;
// 일반 조준 크로스헤어UI 위젯
UPROPERTY(EditDefaultsOnly, Category = Widget)
TSubclassOf<class UMyUserWidget> crosshairUIFactory;
// 크로스헤어 인스턴스
class UMyUserWidget* _crosshairUI;
// 총알 파편 효과 공장
UPROPERTY(EditAnywhere, Category = BulletEffect)
class UParticleSystem* bulletEffectFactory;
CreateWidget()은 UUserWdiget 타입을 리턴하기에 교재의 코드에 Cast< UMyUserWidget >()로 캐스팅을 해줘야 합니다.
_sniperUI = Cast< UMyUserWidget>(CreateWidget(GetWorld(), sniperUIFactory));
TPSPlayer.cpp
void ATPSPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
//... 추가
EnhancedInputComponent->BindAction(IA_1, ETriggerEvent::Triggered, this,
&ATPSPlayer::ChangeToGrenadeGun);
EnhancedInputComponent->BindAction(IA_2, ETriggerEvent::Triggered, this,
&ATPSPlayer::ChangeToSniperGun);
}
}
// Called when the game starts or when spawned
void ATPSPlayer::BeginPlay()
{
Super::BeginPlay();
//... 생략
// 1. 스나이퍼 UI 위젯 인스턴스 생성
_sniperUI = Cast< UMyUserWidget>(CreateWidget(GetWorld(), sniperUIFactory));
// 2. 일반 조준 UI 크로스헤어 인스턴스 생성
_crosshairUI = Cast< UMyUserWidget>(CreateWidget(GetWorld(), crosshairUIFactory));
// 3. 일반 조준 UI 등록
_crosshairUI->AddToViewport();
// 기본으로 스나이퍼건을 사용하도록 설정
ChangeToSniperGun();
}
// 유탄총으로 변경
void ATPSPlayer::ChangeToGrenadeGun()
{
// 유탄총 사용 중으로 체크
bUsingGrenadeGun = true;
sniperGunComp->SetVisibility(false);
gunMeshComp->SetVisibility(true);
}
// 스나이퍼건으로 변경
void ATPSPlayer::ChangeToSniperGun()
{
bUsingGrenadeGun = false;
sniperGunComp->SetVisibility(true);
gunMeshComp->SetVisibility(false);
}
void ATPSPlayer::SniperAim()
{
// 스나이퍼건 모드가 아니라면 처리하지 않는다.
if (bUsingGrenadeGun)
{
return;
}
// Pressed 입력 처리
if (bSniperAim == false)
{
// 1. 스나이퍼 조준 모드 활성화
bSniperAim = true;
// 2. 스나이퍼조준 UI 등록
//_sniperUI->AddToViewport();
// 3. 카메라의 시야각 Field Of View 설정
tpsCamComp->SetFieldOfView(45.0f);
// 4. 일반 조준 UI 제거
//_crosshairUI->RemoveFromParent();
}
// Released 입력 처리
else
{
// 1. 스나이퍼 조준 모드 비활성화
bSniperAim = false;
// 2. 스나이퍼 조준 UI 화면에서 제거
_sniperUI->RemoveFromParent();
// 3. 카메라 시야각 원래대로 복원
tpsCamComp->SetFieldOfView(90.0f);
// 4. 일반 조준 UI 등록
_crosshairUI->AddToViewport();
}
}
이제 빌드하고 BP_TPSPlayer에서 widget을 검색해 UI를 연결해 줍니다. IA_Aim도 연결해줍니다. 이걸 안하고 플레이하면 포인트체크를 안해서 언리얼이 죽습니다. 오동작을 막기위해 아까 연결한 이벤트그래프도 다 지워주 안나타나면 블루프린트를 컴파일해줍니다.
이제 스나이퍼 모드를 켤 IA_Aim Input Action을 bool로 만들고 IMC에 추가해서 Left Ctrl을 배정해줍니다.
TPSPlayer.h
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* IA_Aim;
다른키처럼 Triggered로 설정하면 계속 Zoom을 반복합니다. 이걸 방지하기 위해 Left Ctrl이 눌렸을때 떨어졌을때만 반응해야합니다. 따라서 Started, Completed를 사용합니다.
TPSPlayer.cpp
EnhancedInputComponent->BindAction(IA_Aim, ETriggerEvent::Started, this, &ATPSPlayer::SniperAim);
EnhancedInputComponent->BindAction(IA_Aim, ETriggerEvent::Completed, this, &ATPSPlayer::SniperAim);
다음은 전체코드입니다.
TPSPlayer.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputAction.h"
#include "TPSPlayer.generated.h"
UCLASS()
class TPSPROJECT_API ATPSPlayer : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
ATPSPlayer();
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;
public:
UPROPERTY(VisibleAnywhere, Category = Camera)
class USpringArmComponent* springArmComp;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
class UCameraComponent* tpsCamComp;
UPROPERTY(EditAnywhere, Category = "Input")
UInputMappingContext* PlayerMappingContext;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* MoveIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* LookUpIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* JumpIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* FireIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* IA_1;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* IA_2;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* IA_Aim;
public:
void Move(const FInputActionValue& Value);
void LookUp(const FInputActionValue& Value);
//void Turn(const FInputActionValue& Value);
void InputJump(const FInputActionValue& Value);
void InputFire(const FInputActionValue& Value); // 총알 발사 처리 함수
// 총 스켈레탈메시
UPROPERTY(VisibleAnywhere, Category = GunMesh)
class USkeletalMeshComponent* gunMeshComp;
// 총알 공장
UPROPERTY(EditDefaultsOnly, Category = BulletFactory)
TSubclassOf<class ABullet> bulletFactory;
// 스나이퍼건 스태틱메시 추가
UPROPERTY(VisibleAnywhere, Category = GunMesh)
class UStaticMeshComponent* sniperGunComp;
// 유탄총 사용 중인지 여부
bool bUsingGrenadeGun = true;
// 유탄총으로 변경
void ChangeToGrenadeGun();
// 스나이퍼건으로 변경
void ChangeToSniperGun();
// 스나이퍼 조준
void SniperAim();
// 스나이퍼 조준 중인지 여부
bool bSniperAim = false;
// 스나이퍼 UI 위젯 공장
UPROPERTY(EditDefaultsOnly, Category = Widget)
TSubclassOf<class UMyUserWidget> sniperUIFactory;
// 스나이퍼 UI 위젯 인스턴스
class UMyUserWidget* _sniperUI;
// 일반 조준 크로스헤어UI 위젯
UPROPERTY(EditDefaultsOnly, Category = Widget)
TSubclassOf<class UMyUserWidget> crosshairUIFactory;
// 크로스헤어 인스턴스
class UMyUserWidget* _crosshairUI;
// 총알 파편 효과 공장
UPROPERTY(EditAnywhere, Category = BulletEffect)
class UParticleSystem* bulletEffectFactory;
private:
void Locomotion();
FVector moveDirection;
bool fireReady;
float fireTimerTime;
};
TPSPlayr.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "TPSPlayer.h"
#include <GameFramework/SpringArmComponent.h>
#include <Camera/CameraComponent.h>
#include "EnhancedInputSubsystems.h"
#include "EnhancedInputComponent.h"
#include "Bullet.h"
#include "MyUserWidget.h"
#include <Kismet/GameplayStatics.h>
#include "TPSProject.h"
// Sets default values
ATPSPlayer::ATPSPlayer()
{
// 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;
// 1. 스켈레탈메시 데이터를 불러오고 싶다.
ConstructorHelpers::FObjectFinder<USkeletalMesh> tempMesh(TEXT("SkeletalMesh'/Game/Characters/Mannequins/Meshes/SKM_Manny.SKM_Manny'"));
if (tempMesh.Succeeded()) {
GetMesh()->SetSkeletalMesh(tempMesh.Object);
// 2. Mesh 컴포넌트의 위치와 회전 값을 설정하고 싶다.
GetMesh()->SetRelativeLocationAndRotation(FVector(0, 0, -90), FRotator(0, -90, 0));
}
// 3. TPS 카메라를 붙이고 싶다.
// 3-1. SpringArm 컴포넌트 붙이기
springArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
springArmComp->SetupAttachment(RootComponent);
springArmComp->SetRelativeLocation(FVector(0, 70, 90));
springArmComp->TargetArmLength = 400;
springArmComp->bUsePawnControlRotation = true;
// 3-2. Camera 컴포넌트 붙이기
tpsCamComp = CreateDefaultSubobject<UCameraComponent>(TEXT("TpsCamComp"));
tpsCamComp->SetupAttachment(springArmComp);
tpsCamComp->bUsePawnControlRotation = false;
bUseControllerRotationYaw = true;
// 2단 점프
JumpMaxCount = 2;
// 4. 총 스켈레탈메시 컴포넌트 등록
gunMeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("GunMeshComp"));
// 4-1. 부모 컴포넌트를 Mesh 컴포넌트로 설정
gunMeshComp->SetupAttachment(GetMesh());
// 4-2. 스켈레탈메시 데이터 로드
ConstructorHelpers::FObjectFinder<USkeletalMesh> TempGunMesh(TEXT("SkeletalMesh'/Game/FPWeapon/Mesh/SK_FPGun.SK_FPGun'"));
// 4-3. 데이터로드가 성공했다면
if (TempGunMesh.Succeeded())
{
// 4-4. 스켈레탈메시 데이터 할당
gunMeshComp->SetSkeletalMesh(TempGunMesh.Object);
// 4-5. 위치 조정하기
gunMeshComp->SetRelativeLocation(FVector(-14, 52, 120));
}
// 5. 스나이퍼건 컴포넌트 등록
sniperGunComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SniperGunComp"));
// 5-1. 부모 컴포넌트를 Mesh 컴포넌트로 설정
sniperGunComp->SetupAttachment(GetMesh());
// 5-2. 스태틱메시 데이터 로드
ConstructorHelpers::FObjectFinder<UStaticMesh> TempSniperMesh(TEXT("StaticMesh'/Game/SniperGun/sniper1.sniper1'"));
// 5-3. 데이터로드가 성공했다면
if (TempSniperMesh.Succeeded())
{
// 5-4. 스태틱메시 데이터 할당
sniperGunComp->SetStaticMesh(TempSniperMesh.Object);
// 5-5. 위치 조정하기
sniperGunComp->SetRelativeLocation(FVector(-22, 55, 120));
// 5-6. 크기 조정하기
sniperGunComp->SetRelativeScale3D(FVector(0.15f));
}
}
// Called when the game starts or when spawned
void ATPSPlayer::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem
= ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(PlayerMappingContext, 0);
}
}
// 1. 스나이퍼 UI 위젯 인스턴스 생성, UMyWidget으로 캐스팅해줘야함
_sniperUI = Cast< UMyUserWidget>(CreateWidget(GetWorld(), sniperUIFactory));
// 2. 일반 조준 UI 크로스헤어 인스턴스 생성, UMyWidget으로 캐스팅해줘야함
_crosshairUI = Cast< UMyUserWidget>(CreateWidget(GetWorld(), crosshairUIFactory));
// 3. 일반 조준 UI 등록
_crosshairUI->AddToViewport();
// 기본으로 스나이퍼건을 사용하도록 설정
ChangeToSniperGun();
}
// Called every frame
void ATPSPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
Locomotion();
}
// Called to bind functionality to input
void ATPSPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
EnhancedInputComponent->BindAction(MoveIA, ETriggerEvent::Triggered, this, &ATPSPlayer::Move);
EnhancedInputComponent->BindAction(LookUpIA, ETriggerEvent::Triggered, this, &ATPSPlayer::LookUp);
EnhancedInputComponent->BindAction(JumpIA, ETriggerEvent::Triggered, this, &ATPSPlayer::InputJump);
EnhancedInputComponent->BindAction(FireIA, ETriggerEvent::Triggered, this, &ATPSPlayer::InputFire);
EnhancedInputComponent->BindAction(IA_1, ETriggerEvent::Triggered, this, &ATPSPlayer::ChangeToGrenadeGun);
EnhancedInputComponent->BindAction(IA_2, ETriggerEvent::Triggered, this, &ATPSPlayer::ChangeToSniperGun);
EnhancedInputComponent->BindAction(IA_Aim, ETriggerEvent::Started, this, &ATPSPlayer::SniperAim);
EnhancedInputComponent->BindAction(IA_Aim, ETriggerEvent::Completed, this, &ATPSPlayer::SniperAim);
}
}
void ATPSPlayer::Locomotion()
{
moveDirection = FTransform(GetControlRotation()).TransformVector(moveDirection);
AddMovementInput(moveDirection);
moveDirection = FVector::ZeroVector; //ZeroVector; == FVector(0,0,0)
}
void ATPSPlayer::Move(const FInputActionValue& Value)
{
const FVector _currentValue = Value.Get<FVector>();
//PRINT_LOG(TEXT("%f %f %f"), _currentValue.X, _currentValue.Y, _currentValue.Z);
if (Controller)
{
moveDirection.Y = _currentValue.X;
moveDirection.X = _currentValue.Y;
}
}
void ATPSPlayer::LookUp(const FInputActionValue& Value)
{
const FVector _currentValue = Value.Get<FVector>();
//PRINT_LOG(TEXT("%f %f %f"), _currentValue.X, _currentValue.Y, _currentValue.Z);
AddControllerPitchInput(_currentValue.Y);
AddControllerYawInput(_currentValue.X);
}
void ATPSPlayer::InputJump(const FInputActionValue& Value)
{
Jump();
}
void ATPSPlayer::InputFire(const FInputActionValue& Value)
{
if (bUsingGrenadeGun)
{
// 총알 발사 처리
FTransform firePosition = gunMeshComp->GetSocketTransform(TEXT("FirePosition"));
GetWorld()->SpawnActor<ABullet>(bulletFactory, firePosition);
}
// 스나이퍼건 사용 시
else
{
// LineTrace 의 시작 위치
FVector startPos = tpsCamComp->GetComponentLocation();
// LineTrace 의 종료 위치
FVector endPos = tpsCamComp->GetComponentLocation() + tpsCamComp->GetForwardVector() * 5000;
// LineTrace 의 충돌 정보를 담을 변수
FHitResult hitInfo;
// 충돌 옵션 설정 변수
FCollisionQueryParams params;
// 자기 자신(플레이어) 는 충돌에서 제외
params.AddIgnoredActor(this);
// Channel 필터를 이용한 LineTrace 충돌 검출(충돌 정보, 시작 위치, 종료 위치, 검출 채널, 충돌 옵션)
bool bHit = GetWorld()->LineTraceSingleByChannel(hitInfo, startPos, endPos,
ECC_Visibility, params);
// LineTrace가 부딪혔을 때
if (bHit)
{
// 충돌 처리 -> 총알 파편 효과 재생
// 총알 파편 효과 트랜스폼
FTransform bulletTrans;
// 부딪힌 위치 할당
bulletTrans.SetLocation(hitInfo.ImpactPoint);
// 총알 파편 효과 인스턴스 생성
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), bulletEffectFactory, bulletTrans);
auto hitComp = hitInfo.GetComponent();
// 1. 만약 컴포넌트에 물리가 적용되어 있다면
if (hitComp && hitComp->IsSimulatingPhysics())
{
// 2. 날려버릴 힘과 방향이 필요
FVector force = -hitInfo.ImpactNormal * hitComp->GetMass() * 500000;
// 3. 그 방향으로 날려버리고 싶다.
hitComp->AddForce(force);
}
// 부딪힌 대상이 적인지 판단하기
auto enemy = hitInfo.GetActor()->GetDefaultSubobjectByName(TEXT("FSM"));
if (enemy)
{
//auto enemyFSM = Cast<UEnemyFSM>(enemy);
//enemyFSM->OnDamageProcess();
}
}
}
}
// 유탄총으로 변경
void ATPSPlayer::ChangeToGrenadeGun()
{
// 유탄총 사용 중으로 체크
bUsingGrenadeGun = true;
sniperGunComp->SetVisibility(false);
gunMeshComp->SetVisibility(true);
}
// 스나이퍼건으로 변경
void ATPSPlayer::ChangeToSniperGun()
{
bUsingGrenadeGun = false;
sniperGunComp->SetVisibility(true);
gunMeshComp->SetVisibility(false);
}
void ATPSPlayer::SniperAim()
{
// 스나이퍼건 모드가 아니라면 처리하지 않는다.
if (bUsingGrenadeGun)
{
return;
}
// Pressed 입력 처리
if (bSniperAim == false)
{
// 1. 스나이퍼 조준 모드 활성화
bSniperAim = true;
// 2. 스나이퍼조준 UI 등록
_sniperUI->AddToViewport();
// 3. 카메라의 시야각 Field Of View 설정
tpsCamComp->SetFieldOfView(45.0f);
// 4. 일반 조준 UI 제거
_crosshairUI->RemoveFromParent();
}
// Released 입력 처리
else
{
// 1. 스나이퍼 조준 모드 비활성화
bSniperAim = false;
// 2. 스나이퍼 조준 UI 화면에서 제거
_sniperUI->RemoveFromParent();
// 3. 카메라 시야각 원래대로 복원
tpsCamComp->SetFieldOfView(90.0f);
// 4. 일반 조준 UI 등록
_crosshairUI->AddToViewport();
}
}
LineTrace를 이용한 총알 발사하기
라인트레이스기능은 HitActor가 하나냐 복수개냐에 따라 LineTraceSingByXXX, LineTraceMultyByXXX등이 있습니다.
필터의 종류는 Channel, ObjectType, Profile등이 있습니다.
LineTrace는 Trigger가 아닌 Block이 체크되어 있어야 합니다.
bUsingGrenadeGun이 false면
bHit = GetWorld()->LineTraceSingleByChannel(hitInfo, startPos, endPos, ECC_Visibility, params);
로 라인트레이스를 수행하고 결과가 hitInfo에 담깁니다. hitInfo는 구조체라 hit된 Actor, Location, Rotation및 다양한 정보가 있습니다
총알파편처리
if(bHit) { 총알파편처리구현}
hitInfo의 충돌지점을 이용해 충돌효과를 SpawnEmittterAtLocation해줍니다. 충돌효과는 TPSPlayer.h에 변수로 선언되어 있고 P_BulletEffect를 지정해줘야 합니다.
// 총알 파편 효과 공장
UPROPERTY(EditAnywhere, Category = BulletEffect)
class UParticleSystem* bulletEffectFactory;
마지막으로 Hit된 물체에 Physics가 있다면 Addforce()를 이용하여 충격을 줍니다. -ImpactNormal을 주어야 반대로 날라갑니다.
if (hitComp && hitComp->IsSimulatingPhysics())
{
// 2. 날려버릴 힘과 방향이 필요
FVector force = -hitInfo.ImpactNormal * hitComp->GetMass() * 500000;
// 3. 그 방향으로 날려버리고 싶다.
hitComp->AddForce(force);
}
void ATPSPlayer::InputJump(const FInputActionValue& Value)
{
Jump();
}
void ATPSPlayer::InputFire(const FInputActionValue& Value)
{
if (bUsingGrenadeGun)
{
// 총알 발사 처리
FTransform firePosition = gunMeshComp->GetSocketTransform(TEXT("FirePosition"));
GetWorld()->SpawnActor<ABullet>(bulletFactory, firePosition);
}
// 스나이퍼건 사용 시
else
{
// LineTrace 의 시작 위치
FVector startPos = tpsCamComp->GetComponentLocation();
// LineTrace 의 종료 위치
FVector endPos = tpsCamComp->GetComponentLocation() + tpsCamComp->GetForwardVector() * 5000;
// LineTrace 의 충돌 정보를 담을 변수
FHitResult hitInfo;
// 충돌 옵션 설정 변수
FCollisionQueryParams params;
// 자기 자신(플레이어) 는 충돌에서 제외
params.AddIgnoredActor(this);
// Channel 필터를 이용한 LineTrace 충돌 검출(충돌 정보, 시작 위치, 종료 위치, 검출 채널, 충돌 옵션)
bool bHit = GetWorld()->LineTraceSingleByChannel(hitInfo, startPos, endPos,
ECC_Visibility, params);
// LineTrace가 부딪혔을 때
if (bHit)
{
// 충돌 처리 -> 총알 파편 효과 재생
// 총알 파편 효과 트랜스폼
FTransform bulletTrans;
// 부딪힌 위치 할당
bulletTrans.SetLocation(hitInfo.ImpactPoint);
// 총알 파편 효과 인스턴스 생성
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), bulletEffectFactory, bulletTrans);
auto hitComp = hitInfo.GetComponent();
// 1. 만약 컴포넌트에 물리가 적용되어 있다면
if (hitComp && hitComp->IsSimulatingPhysics())
{
// 2. 날려버릴 힘과 방향이 필요
FVector force = -hitInfo.ImpactNormal * hitComp->GetMass() * 500000;
// 3. 그 방향으로 날려버리고 싶다.
hitComp->AddForce(force);
}
// 부딪힌 대상이 적인지 판단하기
auto enemy = hitInfo.GetActor()->GetDefaultSubobjectByName(TEXT("FSM"));
if (enemy)
{
//auto enemyFSM = Cast<UEnemyFSM>(enemy);
//enemyFSM->OnDamageProcess();
}
}
}
}
일반모드에서도 조준모드를 위해 위젯을 하나 더 생성하고 왼쪽 Ctrl키를 누르면 스나이퍼 모드가 됩니다. bSniperAim변수를 이용해 스위칭합니다.
'인생언리얼TPS' 카테고리의 다른 글
3.1-9 적AI제어를 위한 FSM 제작하기 (0) | 2023.12.15 |
---|---|
3.1-8 적Enemy생성하기 (0) | 2023.12.15 |
스나이퍼 모드 구현하기 (0) | 2023.12.13 |
3.1-6 총알발사하기 (0) | 2023.12.13 |
3.1-5 총알 제작하기 (0) | 2023.12.13 |