[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목차 플레이어 Role 머리 위에 띄우기 Travel in Multiplayer 게임에 입장한 플레이어가 Server쪽인지 Client쪽인지 구분해야 한다.코드를 구현할 때 RPC를 고려하여야 한다. OverheadWidget OverheadWidget.h더보기#pragma once#include "CoreMinimal.h"#include "Blueprint/UserWidget.h"#include "OverheadWidget.generated.h"UCLASS()class MULTIPLAYER_API UOverheadWidget : public UUserWidget{ GENERATED_BODY()public: UPROPERTY(meta = (BindWidget)) class UTextBlock* D… -
[UE] Multiplayer 11: Menu Subsystem 수정하기
[UE] Multiplayer 11: Menu Subsystem 수정하기
2023.07.17
댓글을 사용할 수 없습니다.