Unreal Engine 4 道具捡拾教程 C++ 实现
物品捡拾是游戏比较常用的功能,这个教程讲解实现简单的物品捡拾的过程。
1、 首先新建C++工程,输入好工程名称,注意选择C++页签
2. 先配置一下输入控制。选择Edit->ProjectSettings…
选择左侧Engine下的Input 配置键盘鼠标操作键
右侧显示Engine-Input, 点选Action Mappings 旁边的+号。
将NewActionMaping_0改名为Pickup,点选下面的输入设备选择鼠标右键Right Mouse Button
再增加一个Action Mapping,命名为Inventory, 输入方式设置为键盘I键
到这里输入方法设置完成,用鼠标右键拾取,用I键显示背包内容。
2、 现在开始编码。首先创建父类为Actor的C++类Item。
Unreal Engine自动创建好了Item C++类
先编辑一下ItemPickup.Build.cs的内容,避免后面编译错误
public classItemPickup : ModuleRules
{
public ItemPickup(TargetInfo Target)
{
PublicDependencyModuleNames.AddRange(newstring[] { "Core","CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "GameplayTasks"});
}
}
主要代码在Item及ItemPickupPlayherController里,下面列出代码内容
Item.h
#include "GameFramework/Actor.h"
#include "Item.generated.h"
class AItemPickupCharacter;
class AItemPickupPlayerController;
UCLASS()
class ITEMPICKUP_API AItem : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AItem();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
UShapeComponent* TBox;
UPROPERTY(EditAnyWhere)
UStaticMeshComponent* SM_TBox;
AItemPickupCharacter* MyPlayerController;
AItemPickupPlayerController* PlCtl;
UPROPERTY(EditAnyWhere)
FString ItemName =FString(TEXT(""));
void Pickup();
void GetPlayer(AActor*Player);
bool bItemIsWithinRange;
UFUNCTION()
void TriggerEnter(class UPrimitiveComponent*MyComp,class AActor * OtherActor,class UPrimitiveComponent*OtherComp,int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult);
UFUNCTION()
void TriggerExit(class UPrimitiveComponent*MyComp,class AActor * OtherActor,class UPrimitiveComponent* OtherComp,int32OtherBodyIndex);
};
Item.cpp
// Fill out your copyright notice in theDescription page of Project Settings.
#include "ItemPickup.h"
#include "Item.h"
#include "Engine.h"
#include "ItemPickupCharacter.h"
#include "ItemPickupPlayerController.h"
// Sets default values
AItem::AItem()
{
// Set this actor tocall Tick() every frame. You can turnthis off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick= true;
TBox= CreateDefaultSubobject(TEXT("Box"));
TBox->bGenerateOverlapEvents= true;
TBox->OnComponentBeginOverlap.AddDynamic(this, &AItem::TriggerEnter);
TBox->OnComponentEndOverlap.AddDynamic(this, &AItem::TriggerExit);
RootComponent= TBox;
SM_TBox =CreateDefaultSubobject(TEXT("BoxMesh"));
SM_TBox->SetupAttachment(RootComponent);
}
// Called when the game starts or when spawned
void AItem::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AItem::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
if (MyPlayerController != NULL)
{
PlCtl= Cast(MyPlayerController->GetController());
if (PlCtl->bIsPickingUP && bItemIsWithinRange)
{
Pickup();
}
}
}
void AItem::Pickup()
{
PlCtl =Cast(MyPlayerController->GetController());
PlCtl->Inventory.Add(*ItemName);
GEngine->AddOnScreenDebugMessage(1,5.f, FColor::Green,TEXT("Pick Up the Item"));
Destroy();
}
void AItem::GetPlayer(AActor * Player)
{
MyPlayerController= Cast(Player);
}
void AItem::TriggerEnter(class UPrimitiveComponent*MyComp, class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
bItemIsWithinRange= true;
GEngine->AddOnScreenDebugMessage(1,5.f,FColor::Green,FString::Printf(TEXT("Press Mouse Right ButtonTo Pickup %s"),*ItemName));
GetPlayer(OtherActor);
}
void AItem::TriggerExit(class UPrimitiveComponent*MyComp, class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
bItemIsWithinRange= false;
}
ItemPickupPlayerController.h
// Copyright 1998-2016 Epic Games, Inc. All RightsReserved.
#pragma once
#include "GameFramework/PlayerController.h"
#include "ItemPickupPlayerController.generated.h"
UCLASS()
class AItemPickupPlayerController : public APlayerController
{
GENERATED_BODY()
public:
AItemPickupPlayerController();
protected:
/** True if the controlled character should navigate to the mouse cursor.*/
uint32 bMoveToMouseCursor : 1;
// Begin PlayerController interface
virtual void PlayerTick(float DeltaTime) override;
virtual void SetupInputComponent() override;
// End PlayerController interface
/** Resets HMD orientation in VR. */
void OnResetVR();
/** Navigate player to the current mouse cursor location. */
void MoveToMouseCursor();
/** Navigate player to the current touch location. */
void MoveToTouchLocation(constETouchIndex::Type FingerIndex,const FVector Location);
/** Navigate player to the given world location. */
void SetNewMoveDestination(constFVector DestLocation);
/** Input handlers for SetDestination action. */
void OnSetDestinationPressed();
void OnSetDestinationReleased();
void BeginPickup();
void EndPickup();
void ShowInventory();
public:
bool bIsPickingUP = false;
TArrayInventory;
};
ItemPickupPlayerController.cpp
// Copyright 1998-2016 Epic Games, Inc. All RightsReserved.
#include "ItemPickup.h"
#include "ItemPickupPlayerController.h"
#include "AI/Navigation/NavigationSystem.h"
#include "Runtime/Engine/Classes/Components/DecalComponent.h"
#include "Kismet/HeadMountedDisplayFunctionLibrary.h"
#include "ItemPickupCharacter.h"
#include "Engine.h"
AItemPickupPlayerController::AItemPickupPlayerController()
{
bShowMouseCursor= true;
DefaultMouseCursor= EMouseCursor::Crosshairs;
}
void AItemPickupPlayerController::PlayerTick(floatDeltaTime)
{
Super::PlayerTick(DeltaTime);
// keep updating the destination every tick while desired
if (bMoveToMouseCursor)
{
MoveToMouseCursor();
}
}
void AItemPickupPlayerController::SetupInputComponent()
{
// set up gameplay key bindings
Super::SetupInputComponent();
InputComponent->BindAction("SetDestination",IE_Pressed, this, &AItemPickupPlayerController::OnSetDestinationPressed);
InputComponent->BindAction("SetDestination",IE_Released, this, &AItemPickupPlayerController::OnSetDestinationReleased);
// support touch devices
InputComponent->BindTouch(EInputEvent::IE_Pressed,this, &AItemPickupPlayerController::MoveToTouchLocation);
InputComponent->BindTouch(EInputEvent::IE_Repeat,this, &AItemPickupPlayerController::MoveToTouchLocation);
InputComponent->BindAction("ResetVR",IE_Pressed, this, &AItemPickupPlayerController::OnResetVR);
//Set Pickup Callback
InputComponent->BindAction("Pickup",IE_Pressed, this, &AItemPickupPlayerController::BeginPickup);
InputComponent->BindAction("Pickup",IE_Released, this, &AItemPickupPlayerController::EndPickup);
InputComponent->BindAction("Inventory",IE_Pressed, this, &AItemPickupPlayerController::ShowInventory);
}
void AItemPickupPlayerController::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void AItemPickupPlayerController::MoveToMouseCursor()
{
if (UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled())
{
if (AItemPickupCharacter* MyPawn= Cast(GetPawn()))
{
if (MyPawn->GetCursorToWorld())
{
UNavigationSystem::SimpleMoveToLocation(this, MyPawn->GetCursorToWorld()->GetComponentLocation());
}
}
}
else
{
// Trace to see what is under the mouse cursor
FHitResult Hit;
GetHitResultUnderCursor(ECC_Visibility,false, Hit);
if (Hit.bBlockingHit)
{
// We hit something, move there
SetNewMoveDestination(Hit.ImpactPoint);
}
}
}
void AItemPickupPlayerController::MoveToTouchLocation(constETouchIndex::TypeFingerIndex, constFVector Location)
{
FVector2D ScreenSpaceLocation(Location);
// Trace to see what is under the touch location
FHitResult HitResult;
GetHitResultAtScreenPosition(ScreenSpaceLocation,CurrentClickTraceChannel,true,HitResult);
if (HitResult.bBlockingHit)
{
// We hit something, move there
SetNewMoveDestination(HitResult.ImpactPoint);
}
}
void AItemPickupPlayerController::SetNewMoveDestination(constFVector DestLocation)
{
APawn* const MyPawn= GetPawn();
if (MyPawn)
{
UNavigationSystem*const NavSys = GetWorld()->GetNavigationSystem();
float const Distance = FVector::Dist(DestLocation,MyPawn->GetActorLocation());
// We need to issue move command only if far enough in order for walkanimation to play correctly
if (NavSys && (Distance > 120.0f))
{
NavSys->SimpleMoveToLocation(this,DestLocation);
}
}
}
void AItemPickupPlayerController::OnSetDestinationPressed()
{
// set flag to keep updating destination until released
bMoveToMouseCursor= true;
}
void AItemPickupPlayerController::OnSetDestinationReleased()
{
// clear flag to indicate we should stop updating the destination
bMoveToMouseCursor= false;
}
void AItemPickupPlayerController::BeginPickup()
{
GEngine->AddOnScreenDebugMessage(-1,5.f,FColor::Blue,TEXT("Begin Pickup"));
bIsPickingUP= true;
}
void AItemPickupPlayerController::EndPickup()
{
GEngine->AddOnScreenDebugMessage(-1,5.f, FColor::Blue, TEXT("End Pickup"));
bIsPickingUP= false;
}
void AItemPickupPlayerController::ShowInventory()
{
GEngine->AddOnScreenDebugMessage(-1,5.f, FColor::Blue, TEXT("Show Inventory"));
for (auto& Item : Inventory)
{
GEngine->AddOnScreenDebugMessage(-1,5.f, FColor::Blue, FString::Printf(TEXT("Item: %s"),*Item));
}
}
3、 基于Item创建Blueprint 对象,先在Content主目录下创建一个Pickup子目录
建立New Folder, 命名为Pickup
在C++ Classes 下的ItemPickup找到Item对象,点鼠标右键选择Create Blueprint class based on Item.
命名为BP_PickItem,选择存放在Pickup下。
4、编辑BP_PickItem,首先要为BP_PickItem创建一个Mesh,先选中RootComponent,然后点击Add Component
出现下拉列表后选择Static Mesh
命名为PickBox
选中PickBox,在右侧Static Mesh选中SM_Rock模型
缩小一下这个模型
选择编译Compile并存储一下,完成后关闭这个窗口。
6、将这个BP_PickItem拖拽到场景中。
将ItemPickup及ItemPickup1分别命名为Box1和Box2
7、编译一下游戏,选择运行游戏,移动到拾取物体上,按鼠标右键。
物品消失,被我们拾取到了。
按I键,显示背包内容。
8、这个教程比较相对比较简单,主要目的是理解如果用C++实现游戏的简单交互。