這一個章節(jié)對于第一次接觸虛幻的人來說可以說是最繞的一個點,因為老師突然給你塞了很多的概念,對于這一塊的學習,我個人的推薦是:先把藍圖搞明白了,再對應到C++的代碼中,不然一定會被整的暈頭轉向。還是,有不懂的點就來問我。
主要有幾個大點:
- 接口
- 自定義組件
- 如何在角色身上使用自定義組件
這些東西在藍圖里面真的非常好實現(xiàn),但是到了C++里面,由于語法,做的工作會比藍圖里面拉節(jié)點多很多,所以這一塊我的建議就最上面的:先把藍圖整明白。
這一節(jié)課的第一個重點:接口Interface
說白了就是,一個地方定義,多個地方實現(xiàn),假設我們在A的情況如果要調用接口的函數(shù),直接調用A這個類實現(xiàn)的接口函數(shù)。接口方便我們用一個按鍵去完成不同的功能(比如你用E鍵對物品的時候是拾起物品,對人的時候就是進行對話)
具體功能聽老師講解,還是老樣子,上代碼以及注意事項:
SGamePlayInterface.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "SGameplayInterface.generated.h"
// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class USGameplayInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class ACTIONROGUELIKE_API ISGameplayInterface
{
GENERATED_BODY()
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
UFUNCTION(BlueprintNativeEvent)
void Interact(APawn* InstigatorPawn);
};
注:我們不需要在SGamePlayInterface.cpp文件中對我們的函數(shù)進行實現(xiàn),實現(xiàn)是別的繼承接口的類的工作,而不是我們接口類的工作,接口類的工作就是定義函數(shù),說白了就是.cpp文件你不用動,在.h文件里面寫了東西,就行了。
接口定義完成之后,我們需要一個能交互功能。首先定義一個可以交互的物體,按照斯坦福教程老師的代碼走:
SItemChest.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SGameplayInterface.h"
#include "SItemChest.generated.h"
UCLASS()
class ACTIONROGUELIKE_API ASItemChest : public AActor, public ISGameplayInterface//這里的父類除了AActor,還有ISGameplayInterface,也就是說,這個類繼承了我們的接口類,所以理所當然的,我們需要實現(xiàn)這個接口的函數(shù)
{
GENERATED_BODY()
//對接口類定義的函數(shù)進行實現(xiàn)
void Interact_Implementation(APawn* InstigatorPawn) override;//看到這個override沒,就是重寫這個函數(shù),也就是實現(xiàn)這個函數(shù)。
public:
//一個float類型數(shù)據(jù),可在藍圖內編輯
UPROPERTY(EditAnywhere)
float TargetPitch;
//兩個靜態(tài)網(wǎng)格體組件
UPROPERTY(VisibleAnywhere)
class UStaticMeshComponent* BaseMeshComp;
UPROPERTY(VisibleAnywhere)
class UStaticMeshComponent* LidMeshComp;
public:
// Sets default values for this actor's properties
ASItemChest();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
SItemChest.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SItemChest.h"
#include "Components/StaticMeshComponent.h"
//實現(xiàn)接口類的函數(shù)的函數(shù)體
void ASItemChest::Interact_Implementation(APawn* InstigatorPawn)
{
//這個函數(shù)的作用就是,將lidmesh按照pitch的方向進行旋轉,旋轉角度就是我們初始化那里定義的float數(shù)據(jù),這個數(shù)據(jù)可以在藍圖里面變換
LidMeshComp->SetRelativeRotation(FRotator(TargetPitch, 0, 0));
}
// Sets default values
ASItemChest::ASItemChest()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//組件初始化,前面幾章的內容,忘記的就回到前幾個視頻重新看,這是最基本的東西
BaseMeshComp=CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BaseMesh"));
RootComponent = BaseMeshComp;
LidMeshComp=CreateDefaultSubobject<UStaticMeshComponent>(TEXT("LidMesh"));
LidMeshComp->SetupAttachment(BaseMeshComp);
//數(shù)據(jù)的初始化
TargetPitch = 110.0f;
}
// Called when the game starts or when spawned
void ASItemChest::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ASItemChest::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
我想這里的問題應該都不是很大,因為這里除了接口類,其它的都是前幾章的內容,感覺有問題的就往回看,還是不明白就問我。
有了接口,有了實現(xiàn)接口的方法,有了對應我們要交互的類,那么接下來就需要寫連接玩家操作和物體的那一個鏈接了,這個鏈接我們通過自定義組件來解決。自定義組件在ue編輯器里頭長這樣:
其實你也可以直接寫到SCharacter的文件內,但是這么做是為了將玩家基本信息和功能實現(xiàn)分開,更好的進行代碼管理,不然一個游戲,你什么都往SCharacter里頭寫,也許寫到最后你發(fā)現(xiàn)你SCharacter文件里面有幾萬行代碼,你怎么維護?你怎么debug?很困難的,所以把這些功能分開,有助于我們后期維護代碼。
SInteractionComponent.h文件
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SInteractionComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ACTIONROGUELIKE_API USInteractionComponent : public UActorComponent
{
GENERATED_BODY()
public:
//此處只定義了這一個函數(shù),就如上面所說,你可以把這個函數(shù)丟到SCharacter里頭,但是為了方便后期維護,最好還是寫一個組件來將兩者分開
void PrimaryInteract();
public:
// Sets default values for this component's properties
USInteractionComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};
SInteractionComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SInteractionComponent.h"
#include "SGameplayInterface.h"
#include "Engine/World.h"
#include "Engine/HitResult.h"
//后面的很長一串就是我們的實現(xiàn)交互的函數(shù)
void USInteractionComponent::PrimaryInteract()
{
//碰撞的結果,這是一個結構體
//FHitResult HitResult;
FVector StartLocation;
FVector EndLocation;
FRotator EyeRotation;
//被檢測物體的屬性
FCollisionObjectQueryParams objectQueryParams;
objectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic);
//碰撞體形狀
FCollisionShape Sphere;
//球形碰撞體半徑
float Radius = 30.0f;
Sphere.SetSphere(Radius);
//一個碰撞結果的數(shù)組
TArray<FHitResult>Hits;
//GetOwner()函數(shù)對于組件來說就是獲得它的父組件,也就是我們的SCharacter對象,也就是玩家
AActor* Owner = GetOwner();
//StartLocation = Owner->GetActorLocation();
//找到玩家的其實位置和停止位置
Owner->GetActorEyesViewPoint(StartLocation, EyeRotation);
//我沒有用Gideon的模型,我的模型是一個正方體,所以我直接從正方體中心出發(fā)
StartLocation = Owner->GetActorLocation();
EndLocation = StartLocation + (EyeRotation.Vector()*100.0f);
//LineTraceSingleByObjectType函數(shù)返回一個bool值,表示是否檢測到了一個我們指定的物體
//bool bBlockingHit = GetWorld()->LineTraceSingleByObjectType(HitResult, StartLocation, EndLocation, objectQueryParams);
//SweepMultiByObjectType函數(shù)返回一個bool值,表示是否檢測到了一個我們指定的物體,這個指定的屬性就是objectQueryParams
bool bBlockingHit = GetWorld()->SweepMultiByObjectType(Hits, StartLocation, EndLocation, FQuat::Identity, objectQueryParams, Sphere);
//設置顏色,如果有碰撞就是綠色,沒有碰撞就是紅色
FColor LineColor = bBlockingHit ? FColor::Green : FColor::Red;
//for循環(huán)進行遍歷
for (FHitResult HitResult : Hits)
{
//檢查是否有碰撞
if (AActor* HitActor = HitResult.GetActor())
{
//檢查我們碰撞到的對象是否實現(xiàn)了這個接口
if (HitActor->Implements<USGameplayInterface>())
{
APawn* MyPawn = Cast<APawn>(Owner);
ISGameplayInterface::Execute_Interact(HitActor,MyPawn);
//遍歷到的第一個實現(xiàn)了這個接口的類,就停止遍歷了
break;
}
}
//畫debg用的球
DrawDebugSphere(GetWorld(), HitResult.ImpactPoint, Radius, 32, LineColor, false, 10.0f);
}
//畫視線線條
DrawDebugLine(GetWorld(), StartLocation, EndLocation, LineColor , false, 10.0f, 0U, 1.0f);
}
// Sets default values for this component's properties
USInteractionComponent::USInteractionComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void USInteractionComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void USInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
有沒有發(fā)現(xiàn),就一個函數(shù),這個組件內的代碼長度能那么長,以后還不知道有多少個這一類的功能要實現(xiàn),你想想看,是不是用組件的方式獨立出來比較好?
好了,我們的組件也寫好了,那么最后一步就是:將我們的組件綁定到我們的SCharacter身上,就如同我們前面幾章一樣的方法
SCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "SCharacter.generated.h"
UCLASS(config = Game)
class ACTIONROGUELIKE_API ASCharacter : public ACharacter
{
GENERATED_BODY()
UPROPERTY(VisibleAnywhere)
class USpringArmComponent* SpringArmComp;
UPROPERTY(VisibleAnywhere)
class UCameraComponent* CameraComp;
UPROPERTY(EditAnywhere, Category = Input)
class UInputMappingContext* DefaultMappingContext;
UPROPERTY(EditAnywhere, Category = Input)
class UInputAction* MoveAction;
UPROPERTY(EditAnywhere, Category = Input)
class UInputAction* JumpAction;
UPROPERTY(EditAnywhere, Category = Input)
class UInputAction* LookAction;
UPROPERTY(EditAnywhere, Category = Input)
class UInputAction* PrimaryAttackAction;
//新增的一個輸入事件
UPROPERTY(EditAnywhere, Category = Input)
class UInputAction* InteractionAction;
//新增的我們的自定義組件
UPROPERTY(VisibleAnywhere)
class USInteractionComponent* InteractionComp;
protected:
UPROPERTY(EditAnywhere)
class TSubclassOf<AActor> ProjectileClass;
public:
// Sets default values for this character's properties
ASCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
protected:
void Move(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
void PrimaryAttack();
//新增的交互函數(shù)
void PrimaryInteract();
//新增的delay之后調用的函數(shù)
void PrimaryAttack_TimeElasped();
protected:
FTimerHandle TimerHandle_PrimaryAttack;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};
SCharacter.cpp文章來源:http://www.zghlxwxcb.cn/news/detail-756119.html
// Fill out your copyright notice in the Description page of Project Settings.
#include "SCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "SInteractionComponent.h"
#include "TimerManager.h"
//#include "../../../../../UE_5.1/Engine/Plugins/EnhancedInput/Source/EnhancedInput/Public/EnhancedInputSubsystems.h"
//#include "../../../../../UE_5.1/Engine/Plugins/EnhancedInput/Source/EnhancedInput/Public/EnhancedInputComponent.h"
// Sets default values
ASCharacter::ASCharacter()
{
// 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;
bUseControllerRotationPitch = false;
bUseControllerRotationRoll = false;
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
SpringArmComp->SetupAttachment(RootComponent);
SpringArmComp->bUsePawnControlRotation = true;
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
CameraComp->SetupAttachment(SpringArmComp);
CameraComp->bUsePawnControlRotation = false;
//我們的自定義組件
InteractionComp = CreateDefaultSubobject<USInteractionComponent>(TEXT("InteractionComp"));
}
// Called when the game starts or when spawned
void ASCharacter::BeginPlay()
{
Super::BeginPlay();
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
void ASCharacter::Move(const FInputActionValue& Value)
{
FVector2D MovementVector = Value.Get<FVector2D>();
if (Controller!=nullptr)
{
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
AddMovementInput(ForwardDirection, MovementVector.Y);
AddMovementInput(RightDirection, MovementVector.X);
}
}
void ASCharacter::Look(const FInputActionValue& Value)
{
FVector2D LookAxisVector = Value.Get<FVector2D>();
if (Controller!=nullptr)
{
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
void ASCharacter::PrimaryAttack()
{
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack,this,&ASCharacter::PrimaryAttack_TimeElasped,0.2f);
}
//新增的組件觸發(fā)事件
void ASCharacter::PrimaryInteract()
{
//只要組件存在就會觸發(fā)組件內部的那個函數(shù)
if (InteractionComp)
{
InteractionComp->PrimaryInteract();
}
}
void ASCharacter::PrimaryAttack_TimeElasped()
{
FTransform SpawnTM = FTransform(GetControlRotation(), GetActorLocation());
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
GetWorld()->SpawnActor < AActor >(ProjectileClass, SpawnTM, SpawnParams);
}
// Called every frame
void ASCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ASCharacter::Move);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASCharacter::Look);
EnhancedInputComponent->BindAction(PrimaryAttackAction, ETriggerEvent::Triggered, this, &ASCharacter::PrimaryAttack);
//Interaction
//新增的輸入事件
EnhancedInputComponent->BindAction(InteractionAction, ETriggerEvent::Triggered, this, &ASCharacter::PrimaryInteract);
}
}
發(fā)現(xiàn)沒有,我們只需要綁定組件,然后SCharacter的文件內部就啥都不用加了,加一個輸入事件,就是E鍵嘛,交互鍵,這樣一來,SCharacter的文件看著就很簡潔,不會很龐雜。文章來源地址http://www.zghlxwxcb.cn/news/detail-756119.html
到了這里,關于UE5.1.1C++從0開始(4.虛幻的接口以及交互功能)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!