[UE5] 무기 줍기 - RPC 고려하기
목차
무기 줍기
BaseCharacter
BaseCharacter.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "BaseCharacter.generated.h"
UCLASS()
class MULTIPLAYER_API ABaseCharacter : public ACharacter
{
GENERATED_BODY()
public:
ABaseCharacter();
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual void PostInitializeComponents() override;
protected:
virtual void BeginPlay() override;
void EquipButtonPressed();
private:
UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
class UWidgetComponent* OverheadWidget;
//OnRep_OverlappingWeapon 함수가 client에 호출되었을때 Replicate해준다.
UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
class AWeapon* OverlappingWeapon;
UPROPERTY(VisibleAnywhere)
class UCombatComponent* Combat;
UFUNCTION()
void OnRep_OverlappingWeapon(AWeapon* LastWeapon);
//Reliable RPC는 무조건 실행, Unreliable RPC는 패킷 Drop 가능
UFUNCTION(Server, Reliable)
void ServerEquipButtonPressed();
public:
void SetOverlappingWeapon(AWeapon* Weapon);
};
BaseCharacter.cpp
더보기
#include "BaseCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/WidgetComponent.h"
#include "Net/UnrealNetwork.h"
#include "Multiplayer/Weapon/Weapon.h"
#include "Multiplayer/Components/CombatComponent.h"
ABaseCharacter::ABaseCharacter()
{
//...
OverheadWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("OverheadWidget"));
OverheadWidget->SetupAttachment(RootComponent);
Combat = CreateDefaultSubobject<UCombatComponent>(TEXT("CombatComponent"));
Combat->SetIsReplicated(true); //Combat을 Replicated 컴포넌트로 만들어준다.
}
void ABaseCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(ABaseCharacter, OverlappingWeapon, COND_OwnerOnly);//OwnerOnly: 해당 캐릭터를 가지고 있는 Client만 적용
}
void ABaseCharacter::PostInitializeComponents()
{
Super::PostInitializeComponents();
if (IsValid(Combat))
{
Combat->Character = this;
}
}
void ABaseCharacter::EquipButtonPressed()
{
if (Combat)
{
if (HasAuthority()) //서버 기준. Server에서 validate하는 HasAuthority()
{
Combat->EquipWeapon(OverlappingWeapon); //무기를 주어서 캐릭터에 장착시킨다
}
else //클라이언트 기준. Authority가 없는 경우, RPC를 통해 ServerEquipButtonPressed_Implementation()에서 무기장착 수행
{
ServerEquipButtonPressed(); //무기 장착
}
}
}
void ABaseCharacter::ServerEquipButtonPressed_Implementation()
{
// 서버를 통해 무기 장착하는 경우
if (Combat)
{
Combat->EquipWeapon(OverlappingWeapon);//무기를 줍고 캐릭터에 장착시킨다
}
}
void ABaseCharacter::SetOverlappingWeapon(AWeapon* Weapon)
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(false);
}
OverlappingWeapon = Weapon;
if (IsLocallyControlled())
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
}
}
void ABaseCharacter::OnRep_OverlappingWeapon(AWeapon* LastWeapon)
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
if (LastWeapon)
{
LastWeapon->ShowPickupWidget(false);
}
}
Weapon
Weapon.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Weapon.generated.h"
UENUM(BlueprintType)
enum class EWeaponState : uint8 //무기 상태 Enum
{
EWS_Initial UMETA(DisplayName = "Initial State"),
EWS_Equipped UMETA(DisplayName = "Equipped"),
EWS_Dropped UMETA(DisplayName = "Dropped"),
EWS_MAX UMETA(DisplayName = "DefaultMAX")
};
UCLASS()
class MULTIPLAYER_API AWeapon : public AActor
{
GENERATED_BODY()
public:
AWeapon();
virtual void Tick(float DeltaTime) override;
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
void ShowPickupWidget(bool bShowWidget);
protected:
virtual void BeginPlay() override;
UFUNCTION()
virtual void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
private:
UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
USkeletalMeshComponent* WeaponMesh;
UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
class USphereComponent* AreaSphere;
UPROPERTY(ReplicatedUsing = OnRep_WeaponState, VisibleAnywhere, Category = "Weapon Properties")
EWeaponState WeaponState; //무기 상태
UFUNCTION()
void OnRep_WeaponState();
UPROPERTY(VisibleAnywhere, Category = "Weapon Properties")
class UWidgetComponent* PickupWidget; //무기줍기 Widget(Press E-PickUp)
public:
void SetWeaponState(EWeaponState State);
FORCEINLINE USphereComponent* GetAreaSphere() const { return AreaSphere; }
};
Weapon.cpp
더보기
#include "Weapon.h"
#include "Components/SphereComponent.h"
#include "Components/WidgetComponent.h"
#include "Multiplayer/Character/BaseCharacter.h"
#include "Net/UnrealNetwork.h"
AWeapon::AWeapon()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WeaponMesh"));
SetRootComponent(WeaponMesh);
WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);//WeaponMesh의 channel 충돌을 block 처리
WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);//Pawn의 경우, 충돌 무시, 무기가 Pawn(ex.적 캐릭터)에 부딪혔을때 통과할 수 있도록 설정.
WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);//처음 무기가 나왔을때 No Collision
AreaSphere = CreateDefaultSubobject<USphereComponent>(TEXT("AreaSphere"));
AreaSphere->SetupAttachment(RootComponent);
AreaSphere->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);//channel 충돌x, AreaSphere는 충돌x
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
PickupWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("PickupWidget"));
PickupWidget->SetupAttachment(RootComponent);
}
void AWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AWeapon, WeaponState);
}
void AWeapon::ShowPickupWidget(bool bShowWidget)
{
if (PickupWidget)
{
PickupWidget->SetVisibility(bShowWidget);
}
}
void AWeapon::BeginPlay()
{
Super::BeginPlay();
//서버가 Weapon 객체를 관리할 수 있도록 한다
if (HasAuthority()) //HasAuthority()와 GetLocalRole() == ENetRole::ROLE_Authority는 같다.
{
AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
AreaSphere->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AWeapon::OnSphereOverlap); //무기 AreaSphere와 겹치면 OnSphereOverlap()함수 Delegate호출
AreaSphere->OnComponentEndOverlap.AddDynamic(this, &AWeapon::OnSphereEndOverlap); //무기 AreaSphere와 겹친게 해제되면 OnSphereEndOverlap()함수 Delegate호출
}
if (PickupWidget)
{
PickupWidget->SetVisibility(false); //PickupWidget을 꺼주고 시작한다.
}
}
void AWeapon::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
//ABaseCharacter* BaseCharacter = Cast<ABaseCharacter>(OtherActor);
TWeakObjectPtr<ABaseCharacter> BaseCharacter = Cast<ABaseCharacter>(OtherActor);
if (BaseCharacter.IsValid())
{
BaseCharacter->SetOverlappingWeapon(this);//캐릭터와 무기AreaSphere이 겹치면 무기정보 넘김
}
}
void AWeapon::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
TWeakObjectPtr<ABaseCharacter> BaseCharacter = Cast<ABaseCharacter>(OtherActor);
if (BaseCharacter.IsValid())
{
BaseCharacter->SetOverlappingWeapon(nullptr);//캐릭터와 무기AreaSphere이 안 겹치면 nullptr
}
}
void AWeapon::SetWeaponState(EWeaponState State)
{
WeaponState = State; //무기상태 변경
switch (WeaponState)
{
case EWeaponState::EWS_Equipped: //무기 장착상태 시
ShowPickupWidget(false); //PickupWidget 꺼줌
AreaSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);//AreaSphere충돌x
break;
}
}
void AWeapon::OnRep_WeaponState()
{
switch (WeaponState)
{
case EWeaponState::EWS_Equipped: //무기 장착상태 시
ShowPickupWidget(false); //PickupWidget 꺼줌
break;
}
}
CombatComponent
CombatComponent.h
더보기
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "CombatComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MULTIPLAYER_API UCombatComponent : public UActorComponent
{
GENERATED_BODY()
public:
UCombatComponent();
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
friend class ABaseCharacter;
void EquipWeapon(class AWeapon* WeaponToEquip);
protected:
virtual void BeginPlay() override;
private:
class ABaseCharacter* Character;
AWeapon* EquippedWeapon;
};
CombatComponent.cpp
더보기
#include "CombatComponent.h"
#include "Multiplayer/Weapon/Weapon.h"
#include "Multiplayer/Character/BaseCharacter.h"
#include "Engine/SkeletalMeshSocket.h"
#include "Components/SphereComponent.h"
UCombatComponent::UCombatComponent(){
PrimaryComponentTick.bCanEverTick = false;
}
void UCombatComponent::BeginPlay(){
Super::BeginPlay();
}
void UCombatComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction){
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
void UCombatComponent::EquipWeapon(AWeapon* WeaponToEquip)
{
if (Character == nullptr || WeaponToEquip == nullptr) return;
EquippedWeapon = WeaponToEquip;
EquippedWeapon->SetWeaponState(EWeaponState::EWS_Equipped);//무기 상태를 Equipped(=장착)으로 변경
const USkeletalMeshSocket* HandSocket = Character->GetMesh()->GetSocketByName(FName("RightHandSocket"));//소켓을 변수로 저장
if (IsValid(HandSocket)) // 해당 소켓이 존재하면
{
HandSocket->AttachActor(EquippedWeapon, Character->GetMesh()); //무기를 해당 소켓에 붙여준다.
}
EquippedWeapon->SetOwner(Character); // 무기의 Owner을 Character로 설정
}
실행화면
'⭐ Unreal Engine > UE Multiplayer FPS TPS + ListenServer' 카테고리의 다른 글
[UE5] 무기 시스템 만들기(무기, 발사체 타입무기, 발사체) (0) | 2023.09.27 |
---|---|
[UE5] Net Update Frequency 수정하기 (0) | 2023.09.27 |
[UE5] 플레이어 Role 머리 위에 띄우기 (0) | 2023.09.18 |
[UE] Multiplayer 11: Menu Subsystem 수정하기 (0) | 2023.07.17 |
[UE] Multiplayer 10: Lobby 레벨 기본설정 및 변경하기 (0) | 2023.07.17 |
댓글
이 글 공유하기
다른 글
-
[UE5] 무기 시스템 만들기(무기, 발사체 타입무기, 발사체)
[UE5] 무기 시스템 만들기(무기, 발사체 타입무기, 발사체)
2023.09.27 -
[UE5] Net Update Frequency 수정하기
[UE5] Net Update Frequency 수정하기
2023.09.27 -
[UE5] 플레이어 Role 머리 위에 띄우기
[UE5] 플레이어 Role 머리 위에 띄우기
2023.09.18 -
[UE] Multiplayer 11: Menu Subsystem 수정하기
[UE] Multiplayer 11: Menu Subsystem 수정하기
2023.07.17