일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 미니프로젝트
- 언리얼프로그래머
- 스터디
- 카렌
- 알고풀자
- VUE
- Express
- 프린세스메이커
- node
- EnhancedInput
- 언리얼
- 디자드
- 으
- 정글사관학교
- Unseen
- 스마일게이트
- Ajax
- Enhanced Input System
- 언리얼뮤지컬
- flask
- Jinja2
- JWT
- 파이썬서버
- 게임개발
- 마인크래프트뮤지컬
- Bootstrap4
- 프메
- R
- 레베카
- 데이터베이스
- Today
- Total
Showing
[Unreal] Line Trace 블루프린트와 C++로 구현 본문
CS384G - 프로젝트 2: 레이 트레이싱 (utexas.edu)
Line Trace를 사용하면 눈에 보이는 웬만한 것들을 캐치할 수 있다.
블루프린트
콜리전에서 Object Channels와 Trace Chaanel로 구분되어 있는 것을 볼 수 있다. 이는 언리얼 개발진들이 경험적으로 쌓은 노하우이다.
개발자는 오브젝트마다 채널을 달 수도 있고 트레이스 라인에 따라 반응을 어떻게 할지 결정함으로써 라인트레이스를 사용할 수도 있을 것이다.
single line
플레이어로부터 1500 거리 정도 되는 레이트레이싱을 쏘는 예시
Trace Chaanel
start로 액터의 위치를 지정하고 액터의 앞방향 벡터에 거리만큼 곱한 값을 더한 위치를 End로 지정한다.
Object Channels
Make Array 목록은 만들어둔 콜리젼 오브젝트 채널
Ignore Self는 끌 일이 없다.
Out Hit는 끝점에 대한 데이터를 가지고 있는 구조체이다.
break는 ray에 부딪힌 것들의 정보를 분리해서 가져올 수 있다. 예시에서는 부딪힌 액터의 이름을 가져와서 print 해준 예제
mult line
확대
mult는 충돌결과를 여러개 가지고 오고 싶을 때 쓰면 된다.
즉 선 안에 들어오는 것들을 array로 가져와 알 수 있다는 뜻이다.
box 스타일
sphere 스타일
이번에는 카메라와 카메라arm을 이용해서 플레이어의 위치를 지정해보도록 한다.
우선 코드를 아래와 같이 변경해준다.
c++
E를 눌렀을 때 라인트레이싱이 나올 수 있도록 C++로 작성해보도록 한다.
플레이어의 시점을 반환, AI의 경우 이는 Pawn의 '눈' 시점을 의미한다. (인간) 플레이어의 경우 이는 카메라의 시점을 의미
GetController()->GetPlayerViewPoint(_Location, _Rotation);
플레이어 h에서 e관련 프로퍼티와 함수 추가
// 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 "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "MyPlayer.generated.h"
class UInputMappingContext;
class UInputAction;
class UNiagaraComponent;
UCLASS()
class THIRDPERSONTEMPLATE_API AMyPlayer : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMyPlayer();
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;
UPROPERTY(EditAnywhere, Category = "Anim")
UAnimMontage* attackMontage;
public:
UPROPERTY(VisibleAnywhere, Category="Camera")
class USpringArmComponent* springArmComp;
UPROPERTY(VisibleAnywhere, Category = "Camera")
class UCameraComponent* cameraComp;
UPROPERTY(EditAnywhere, Category = "Input")
UInputMappingContext* PlayerMappingContext;
UPROPERTY(VisibleAnywhere, Category = "Weapon")
class UStaticMeshComponent* staffMesh;
UPROPERTY(EditAnywhere, Category = "FX")
class UNiagaraComponent* jumpNiagara;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* MoveIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* LookUpIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* TurnIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* JumpIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* FireIA;
UPROPERTY(EditAnywhere, Category = "Input")
UInputAction* InteractionIA;
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);
void InteractionPositive(const FInputActionValue& Value);
void Fire();
float fireTimerTime; // 타이머의 시간
bool fireReady; // 타이머 도달 여부
public:
UPROPERTY(EditAnywhere, Category = "Move")
float moveSpeed;
UPROPERTY(EditAnywhere)
TSubclassOf<class APBullet> bulletFactory;
UPROPERTY(EditAnyWhere)
float fireCoolTime = 2.0f;
bool isJump = false;
private:
FVector moveDirection;
protected:
void FireCoolTimer(float cooltime, float deltaTime);
void ShowFX(bool show);
};
void AMyPlayer::InteractionPositive(const FInputActionValue& Value)
{
//start와 end 가 중요
FVector _Location;
FRotator _Rotation;
FHitResult _HitOut;
//카메라가 보고 있는 위치
GetController()->GetPlayerViewPoint(_Location, _Rotation);
FVector _Start = _Location;
int ViewDis = 2000;
FVector _End = (_Rotation.Vector() * ViewDis); // 2000은 무작위 거리
FCollisionQueryParams _traceParams; //trace의 params들
GetWorld()->LineTraceSingleByChannel(_HitOut, _Start, _End, ECC_Visibility, _traceParams); // 충돌 결과, 시작점, 결과, 콜리전채널, params(안넣으면 디폴트)
DrawDebugLine(GetWorld(), _Start, _End, FColor::Green, false, 2.0f);
}
전체 코드
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyPlayer.h"
#include "Components/ArrowComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputSubsystems.h" //https://docs.unrealengine.com/5.0/en-US/enhanced-input-in-unreal-engine/
#include "EnhancedInputComponent.h" //https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/Input/EnhancedInput/
#include "PBullet.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "DrawDebugHelpers.h"
// Sets default values
AMyPlayer::AMyPlayer()
{
// 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;
//ConstructorHelpers 라는 유틸리티로 에셋 가져오기(찾기)
// InitMesh 굳이 이렇게 하기는 했으나 콘텐츠 브라우저에서 선택하는게 사실 더 좋음
ConstructorHelpers::FObjectFinder<USkeletalMesh> InitMesh(TEXT("/Script/Engine.SkeletalMesh'/Game/MyResouce/unitychan.unitychan'"));
if (InitMesh.Succeeded()) { // 제대로 오브젝트를 가져왔다면~
// GetMesh는 actor(근본)의 상속자인 character에서 제공!
GetMesh()->SetSkeletalMesh(InitMesh.Object);
GetMesh()->SetRelativeLocationAndRotation(
FVector(0, 0, -88)
, FRotator(0, -90, 0));
}
springArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
springArmComp->SetupAttachment(RootComponent);
springArmComp->SetRelativeLocationAndRotation(FVector(0, 0, 50), FRotator(-20,0,0));
springArmComp->TargetArmLength = 530;
springArmComp->bUsePawnControlRotation = true;
//카메라도 생성 초기화
cameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComponent"));
cameraComp->SetupAttachment(springArmComp);
cameraComp->bUsePawnControlRotation = false;
//staff mesh
staffMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("staff meshComp"));// 스태프 외관 컴포넌트 등록
// 두번째 인자 캐릭터 메쉬에 부착, FName이 문자열 속도가 빠름
staffMesh->SetupAttachment(GetMesh(), FName("Character1_RightHandSocket"));
//staffMesh->SetRelativeLocationAndRotation
//pawn이 들고 있음
bUseControllerRotationYaw = true;
moveSpeed = 100.0f;
fireTimerTime = 0;
fireCoolTime = 1.85f;
fireReady = true; // 발사 준비 On
}
// Called when the game starts or when spawned
void AMyPlayer::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem
= ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer())) {
Subsystem->AddMappingContext(PlayerMappingContext, 0);
}
}
jumpNiagara = GetComponentByClass<UNiagaraComponent>();
}
// Called every frame
void AMyPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (fireReady == false) {
FireCoolTimer(fireCoolTime, DeltaTime);
}
if (GetCharacterMovement()->IsFalling()) {
ShowFX(true);
}
else {
ShowFX(false);
}
const TArray<USceneComponent*>& AttachChildren = GetRootComponent()->GetAttachChildren();
const int32 Count = AttachChildren.Num();
// 이동방향을 컨트롤 방향 기준으로 변환
moveDirection = FTransform(GetControlRotation()).TransformVector(moveDirection);
/*
// 플레이어 이동 - 등속운동
FVector P0 = GetActorLocation();//현재 위치
FVector vt = moveDirection * moveSpeed * DeltaTime; //이동거리
FVector P = P0 + vt;
SetActorLocation(P);
*/
AddMovementInput(moveDirection);
// 방향 초기화
// 방향이 누적되지 않게
moveDirection = FVector::ZeroVector;
}
// Called to bind functionality to input
void AMyPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent)) {
EnhancedInputComponent->BindAction(MoveIA, ETriggerEvent::Triggered, this, &AMyPlayer::Move);
EnhancedInputComponent->BindAction(LookUpIA, ETriggerEvent::Triggered, this, &AMyPlayer::LookUp);
EnhancedInputComponent->BindAction(TurnIA, ETriggerEvent::Triggered, this, &AMyPlayer::Turn);
EnhancedInputComponent->BindAction(JumpIA, ETriggerEvent::Triggered, this, &AMyPlayer::InputJump);
EnhancedInputComponent->BindAction(FireIA, ETriggerEvent::Triggered, this, &AMyPlayer::InputFire);
EnhancedInputComponent->BindAction(InteractionIA, ETriggerEvent::Started, this, &AMyPlayer::InteractionPositive);
}
}
void AMyPlayer::Move(const FInputActionValue& Value)
{
//UE_LOG(LogTemp, Log, TEXT("Log Message %f"), moveSpeed);
const FVector _currentValue = Value.Get<FVector>();
if (Controller) {
moveDirection.Y = _currentValue.X;
moveDirection.X = _currentValue.Y;
}
//FVector newLocation = GetActorLocation() + _currentValue;
//SetActorLocation(newLocation);
}
void AMyPlayer::LookUp(const FInputActionValue& Value)
{
// mouse y - 한 축의 값(float)
const float _currentValue = Value.Get<float>();
APawn::AddControllerPitchInput(_currentValue);
}
void AMyPlayer::Turn(const FInputActionValue& Value)
{
// mouse x - 한 축의 값(float)
const float _currentValue = Value.Get<float>();
APawn::AddControllerYawInput(_currentValue);
}
void AMyPlayer::InputJump(const FInputActionValue& Value)
{
Jump();
}
void AMyPlayer::InputFire(const FInputActionValue& Value)
{
if (fireReady == true) {
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance) {
AnimInstance->Montage_Play(attackMontage);
}
fireReady = false;
}
}
void AMyPlayer::InteractionPositive(const FInputActionValue& Value)
{
//start와 end 가 중요
FVector _Location;
FRotator _Rotation;
FHitResult _HitOut;
//카메라가 보고 있는 위치
GetController()->GetPlayerViewPoint(_Location, _Rotation);
FVector _Start = _Location;
int ViewDis = 2000;
FVector _End = (_Rotation.Vector() * ViewDis); // 2000은 무작위 거리
FCollisionQueryParams _traceParams; //trace의 params들
GetWorld()->LineTraceSingleByChannel(_HitOut, _Start, _End, ECC_Visibility, _traceParams); // 충돌 결과, 시작점, 결과, 콜리전채널, params(안넣으면 디폴트)
DrawDebugLine(GetWorld(), _Start, _End, FColor::Green, false, 2.0f);
}
void AMyPlayer::FireCoolTimer(float cooltime, float deltaTime)
{
if (fireTimerTime < cooltime) {
fireTimerTime += deltaTime;
}
else
{
fireTimerTime = 0;
fireReady = true;
}
}
void AMyPlayer::ShowFX(bool show)
{
if (nullptr == jumpNiagara) {
jumpNiagara = GetComponentByClass<UNiagaraComponent>();
}
jumpNiagara->SetVisibility(show);
}
void AMyPlayer::Fire()
{
FTransform firePosition = staffMesh->GetSocketTransform(TEXT("FirePosition"));
GetWorld()->SpawnActor<APBullet>(bulletFactory, firePosition);
}
'Unreal' 카테고리의 다른 글
[Unreal] 액터 C++ 클래스와 블루프린트 호환 및 컴포넌트의 이해 (1) | 2023.11.24 |
---|---|
[Unreal] 블루프린트를 이용한 캐릭터 hp/mp 상태바 위젯 구현 (1) | 2023.11.24 |
[Unreal] 언리얼 Json 직렬화 및 역직렬화 (0) | 2023.11.23 |
[Unreal] 언리얼 직렬화, Serialize (1) | 2023.11.23 |
[Unreal] singleton UGameInstance와 interface의 활용 예시 (0) | 2023.11.23 |