Learning C++ by Creating Games With UE4(15.05.19)(Chapter 10)Coding

来源:互联网 发布:网络诈骗主题班会ppt 编辑:程序博客网 时间:2024/05/23 13:08

Chapter10 库存系统和可捡起的物品

 

我们希望我们的玩家有能力去捡起一些物品在游戏世界之中,这一章,我们将会编写并设计整个背包系统提供给我们的玩家来存储物品。我们将会展示当按下I键的时候玩家可以收集到的物品。

 

作为一些数据的存储,我们将会使用TMap<FString,int>items来将前几章节我们所学的内容应用起来。当玩家捡起一些物品,我们将他们加入map之中。如果这些物品已经存在背包之中,那么我们将会提升该物品的数量。

 

 

了解背包

 

打开我们的人物控制的头文件  Avatar.h

 

 

 

前言

 

AAvatar类的前面,你需要先声明APickItem。下面这些物品就是我们要制作的道具

 

背包中的效果

 

举个例子,我们可以用TArray<Item>来制作我们背包中的物品。

 

Struct Item

{

FString name;

Int qty;

UTexture2D* tex;

}

 

我们在这之后可以将物品按照线性的关系添加进入

 

导入资源:这部分其实就是我们在创建虚幻4C++项目时导入初学者内容。导入过的同学可以忽视。。

 

 

将我们捡起的效果与按键绑定

 

打开项目设置中的输入选项

 

设置好I键作为绑定捡起物品的按键

打开Avatar.h,我们需要添加一个成员函数来运行当玩家的库存需要显示时

 

在源文件中我们需要添加

 

 

运行后点击I键左上角提示

 

 

 

基础的捡起物品类

 

首先我们需要选择一个Actor的父类,创建名称为PickupItem

 

 

一旦你创建了PickupItem那么你就需要考虑这个类里面需要哪些内容:

 

1.使用一个FString变量来存放这个物品的名称

2.一个int32变量来存放这个物品的数量

3.USphereComponent变量来让这个物体有个碰撞器,你可以触碰得到

4.一个UStaticMeshComponent变量来修改这个物体的样子网格也就是mesh

5.一个UTexture2D变量来制作图标显示这些条目

6.一个指向HUD的指针

 

那么下面我们来编写捡起物品类的头文件内容

 

// Fill out your copyright notice in the Description page of Project Settings.

 

#pragma once

 

#include "GameFramework/Actor.h"

#include "PickupItem.generated.h"

 

UCLASS()

class CPLUSLEARN_API APickupItem : public AActor

{

GENERATED_UCLASS_BODY()

public:

 

//默认构造函数

APickupItem();

 

//该可捡起物体名称

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item)

FString Name;

//该物体数量

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item)

int32 Quantity;

//碰撞器

UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Item)

TSubobjectPtr<USphereComponent>ProxSphere;

//网格

UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = Item)

TSubobjectPtr<UStaticMeshComponent>Mesh;

//图标

UPROPERTY(EditAnywhere,BlueprintReadWrite,Category=Item)

UTexture2D * Icon;

//与其他物体碰撞后函数

UFUNCTION(BlueprintNativeEvent, Category = Collision)

void Prox(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

 

};

 

.cpp文件

 

 

 

// Fill out your copyright notice in the Description page of Project Settings.

 

#include "CplusLearn.h"

#include "PickupItem.h"

#include "Avatar.h"

#include "MyHUDMessage.h"

 

// Sets default values

APickupItem::APickupItem(const class FPostConstructInitializeProperties &PCIP) :Super(PCIP)

{

//初始化默认物体名称和数量

Name = "UNKNOW ITEM";

Quantity = 0;

 

//初始化物体

ProxSphere = PCIP.CreateDefaultSubobject<USphereComponent>(thisTEXT("ProxSphere"));

Mesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(thisTEXT("Mesh"));

 

//放置根节点为Mesh

RootComponent = Mesh;

Mesh->SetSimulatePhysics(true);

//绑定Prox的方法,当有物体进入直接激发函数

ProxSphere->OnComponentBeginOverlap.AddDynamic(this, &APickupItem::Prox);

ProxSphere->AttachTo(Mesh);//很重要,要把这个碰撞体和表面的网格相结合

 

}

 

//捡起物体函数

void APickupItem::Prox_Implementation(AActorOtherActorUPrimitiveComponentOtherCompint32 OtherBodyIndexbool bFromSweepconst FHitResultSweepResult)

{

//如果碰到的物体无法转化为Avatar类型那么返回空

if (Cast<AAvatar>(OtherActor) == nullptr)

{

return;

}

 

//获取玩家的avatarPlayerPawn 是AAvatar一个实例化 

AAvatar *avatar = Cast<AAvatar>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));

 

//可捡起当前物体

avatar->PickUp(this);

//获取控制器

APlayerController* PController = GetWorld()->GetFirstPlayerController();

//获取玩家HUD

AMyHUDMessage *hud = Cast<AMyHUDMessage>(PController->GetHUD());

//捡起物体发送该物体的信息

hud->addMessage(Message(FString("pick up") + FString::FromInt(Quantity) + FString("") + Name, 5.f, FColor::White,Icon));

 

Destroy();

 

最终效果

}

 

 

画出玩家的库存

 

// Fill out your copyright notice in the Description page of Project Settings.

 

#pragma once

 

#include "GameFramework/HUD.h"

#include "MyHUDMessage.generated.h"

 

/**

*

*/

 

//创建一个完整信息的结构体

struct Message 

{

FString message;

float time;

FColor color;

UTexture2D *tex;

Message()

{

time = 5.f;

color = FColor::White;

}

 

Message(FString iMessagefloat iTimeFColor iColorUTexture2DiTex)

{

message = iMessage;

time = iTime;

color = iColor;

tex = iTex;

}

};

 

 

//图标结构体

struct Icon

{

FString name;

UTexture2D *tex;

Icon(){ name = "UNKNOWN ICON"; tex = 0; }

Icon(FString & iNameUTexture2D * iTex)

{

name = iName;

tex = iTex;

}

};

 

//控件

struct Widget

{

Icon icon;

FVector2D pos, size;

Widget(Icon iicon)

{

icon = iicon;

}

 

float left(){ return pos.X; }

float right(){ return pos.X+size.X; }

float top(){ return pos.Y; }

float bottom(){ return pos.Y+size.Y; }

};

 

 

 

 

 

UCLASS()

class CPLUSLEARN_API AMyHUDMessage : public AHUD

{

GENERATED_BODY()

 

public :

 

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUDFont)

UFont *hudFont;

 

virtual void DrawHUD() override;

//信息列表

TArray<Message> messages;

//添加信息

void addMessage(Message msg);

 

void Drawhealthbar();

 

 

//控件列表

TArray<Widget> widgets;

//绘制控件

void DrawWidgets();

//添加控件

void addWidget(Widget widget);

//清除所有控件

void clearWidgets();

 

 

};

这个是我们前面已经写到过的我们自己的HUD,现在要加上一个图标的结构体和一个控件的结构体。同时添加了控件列表以及控件增加绘制和清除所有控件列表的函数。

 

.cpp文件

// Fill out your copyright notice in the Description page of Project Settings.

 

#include "CplusLearn.h"

#include "MyHUDMessage.h"

#include "Avatar.h"

 

void AMyHUDMessage::DrawHUD()

{

Super::DrawHUD();

 

Drawhealthbar();

DrawWidgets();

//DrawLine(200, 300, 400, 500, FLinearColor::Blue);

 

//DrawText("hello", FVector2D(0, 0), hudFont, FVector2D(1, 1), FColor::White);

 

for (int c = messages.Num() - 1; c >= 0; c--)

{

float outputWidth, outputHeight, pad = 10.f;//定义输出的宽度高度和间隔

//获取出信息内容大小

GetTextSize(messages[c].message, outputWidth, outputHeight, hudFont, 1.f);

 

float messageH = outputHeight + 2.f*pad;//信息的高度就是输出高度+2倍的间隔

float x = 0.f, y = c*messageH;

 

//绘制出背景

DrawRect(FLinearColor::Black, x, y, Canvas->SizeX, messageH);

 

//使用字体绘制出我们的信息(信息,颜色,宽度+边距,高度+边距,字体)

DrawText(messages[c].message, messages[c].color, x + pad, y + pad, hudFont);

 

//绘出图片头像

 

DrawTexture(messages[c].tex, x, y, messageH, messageH, 0, 0, 1, 1);

 

//时间逐步缩减

messages[c].time -=GetWorld()->GetDeltaSeconds();

 

//当该条消息的时间小于0的时候将该条信息移除

if (messages[c].time < 0)

{

messages.RemoveAt(c);

}

}

}

 

 

void AMyHUDMessage::addMessage(Message msg)

{

messages.Add(msg);

}

 

 

void AMyHUDMessage::Drawhealthbar()

{

//绘制生命值

 

 

//获取当前玩家

AAvatar *avatar = Cast<AAvatar>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));

//定义生命值条基本信息

float barWidth = 200, barHeight = 50, barPad = 12, barMargin = 50;

float percHp = avatar->HP / avatar->MAXHP;//当前生命数值百分比

 

//绘制出外层    整个界面水平宽度-预设好的生命条宽度-内边距-外边距   。。。         宽+2倍内边距+2倍外边距   后面同理

DrawRect(FLinearColor(0, 0, 0, 1), Canvas->SizeX - barWidth - barPad - barMargin,

Canvas->SizeY - barHeight - barPad - barMargin, barWidth + 2 * barPad, barMargin + 2 * barPad);

 

DrawRect(FLinearColor(1 - percHp, percHp, 0, 1), Canvas->SizeX - barWidth - barMargin, Canvas->SizeY - barHeight  - barMargin, barWidth*percHp, barHeight);

}

 

 

void AMyHUDMessage::DrawWidgets()

{

for (int c = 0; c < widgets.Num(); c++)

{

DrawTexture(widgets[c].icon.tex, widgets[c].pos.X, widgets[c].pos.Y,

widgets[c].size.X, widgets[c].size.Y, 0, 0, 1, 1);

 

DrawText(widgets[c].icon.name, FLinearColor::Yellow, widgets[c].pos.X,

widgets[c].pos.Y, hudFont,.6f,false);

}

}

 

void AMyHUDMessage::addWidget(Widget widget)

{

FVector2D start(200, 200), pad(12, 12);

widget.size = FVector2D(100, 100);//控件整个大小

widget.pos = start; //控件位置

 

for (int c = 0; c < widgets.Num(); c++)

{

widget.pos.X += widget.size.X + pad.X;//每当有一个控件,那么位置就要加上一个控件大小和边距

widget.pos.Y += widget.size.Y + pad.Y;

}

widgets.Add(widget);//将该控件添加如控件组

}

 

void AMyHUDMessage::clearWidgets()

{

widgets.Reset();

}

 

上面是整个HUD实现函数的方式(注:书上有一些代码内容并没有,因此有一部分我自己添加的,大家可以按照自己想法来写)

 

 

 

在Avatar中我们需要更新这样几个部分

1.玩家打开背包不可移动

void AAvatar::Yaw(float amount)

{

//背包打开玩家不可移动

if (inventoryShowing)return;

 

 

AddControllerYawInput(200.f*amount*GetWorld()->GetDeltaSeconds());

}

 

2.背包开关

//开关背包

void AAvatar::ToggleInventory()

{

/*if (GEngine)

{

GEngine->AddOnScreenDebugMessage(0, 5.f, FColor::Red, "Show Inventory");

}*/

 

//获取当前玩家控制器

APlayerController *PController = GetWorld()->GetFirstPlayerController();

//获取当前玩家UI界面

AMyHUDMessage *hud = Cast<AMyHUDMessage>(PController->GetHUD());

 

//当背包打开时触发

if (inventoryShowing)

{

//清除所有控件

hud->clearWidgets();

inventoryShowing = false;//背包处于关闭状态

PController->bShowMouseCursor = false;//设置鼠标不可见

return;

}

 

 

//若背包没有打开

inventoryShowing = true;

PController->bShowMouseCursor = true;

//循环读取背包中物品

for (TMap<FStringint>::TIterator it = Backpack.CreateIterator(); it; ++it)

{

FString fs = it->Key + FString::Printf(TEXT("x%d"), it->Value);

UTexture2D* tex;

if (Icons.Find(it->Key))

{

tex = Icons[it->Key];

//添加控件

hud->addWidget(Widget(Icon(fs, tex)));

}

}

}

 

3.捡起物品信息的存储

void AAvatar::PickUp(APickupItem *item)

{

if (Backpack.Find(item->Name))

{

Backpack[item->Name] += item->Quantity;

}

else

 

{

Backpack.Add(item->Name, item->Quantity);

Icons.Add(item->Name, item->Icon);

}

}

 

最后附上效果图

 

捡起物体之前,那两个物体是我自己配置的

 

 

捡起后

 

 

最终打开背包效果

 

 

 

在本章的最后将会增加侦测背包的鼠标点击拖动事件(就是我们按住背包中一个物品进行位置拖动)

 

首先要在avatar

//绑定鼠标点击事件

InputComponent->BindAction("MouseClickedLMB"IE_Pressedthis, &AAvatar::MouseClicked);

 

 

void AAvatar::MouseClicked()

{

APlayerController* PController = GetWorld()->GetFirstPlayerController();

AMyHUDMessage *hud = Cast<AMyHUDMessage>(PController->GetHUD());

hud->MouseClicked();

}

 

void AAvatar::Yaw(float amount)

{

//背包打开玩家不可移动

if (inventoryShowing)

{

APlayerController *PController = GetWorld()->GetFirstPlayerController();

AMyHUDMessage *hud = Cast<AMyHUDMessage>(PController->GetHUD());

hud->MouseMoved();

return;

}

else{

 

 

AddControllerYawInput(200.f*amount*GetWorld()->GetDeltaSeconds());

}

}

 

 

然后打开AMyHUDMessage

 

在头文件中控件中添加一个函数,修改如下

 

//控件

struct Widget

{

Icon icon;

FVector2D pos, size;

Widget(Icon iicon)

{

icon = iicon;

}

 

float left(){ return pos.X; }

float right(){ return pos.X+size.X; }

float top(){ return pos.Y; }

float bottom(){ return pos.Y+size.Y; }

 

//鼠标点击的位置是否在空间内

bool hit(FVector2D p)

{

return p.X > left() && p.X<right() && p.Y>top()&&p.Y < bottom();

}

};

 

 

.cpp文件中添加

 

void AMyHUDMessage::MouseClicked()

{

FVector2D mouse;

APlayerController *PController = GetWorld()->GetFirstPlayerController();

PController->GetMousePosition(mouse.X, mouse.Y);

 

 

heldWidget = NULL;

 

for (int c = 0; c < widgets.Num(); c++)

{

if (widgets[c].hit(mouse))

{

heldWidget = &widgets[c];//设为上一次点击中控件

return;

}

}

}

 

 

void AMyHUDMessage::MouseMoved()

{

static FVector2D lastMouse;

FVector2D thisMouse, dMouse;

APlayerController *PController = GetWorld()->GetFirstPlayerController();

PController->GetMousePosition(thisMouse.X, thisMouse.Y);

dMouse = thisMouse - lastMouse;

 

 

float time = PController->GetInputKeyTimeDown(EKeys::LeftMouseButton);//鼠标左键按下时间

//若鼠标点击下的时间》0且有上一个点击物体

if (time > 0.f&&heldWidget)

{

//将刚才控件所在的位置+上鼠标移动的位置

heldWidget->pos.X += dMouse.X;

heldWidget->pos.Y += dMouse.Y;

}

lastMouse=thisMouse;//将当前鼠标停留位置设置为上一次鼠标停留位置

}

注:这个是作为扩展的联系,整个的思路是:打开背包后,玩家鼠标点击背包。如果点到背包中的物品条目,hit函数返回true,激发mouseClick事件,此时记录点中物体以及鼠标按下的时间。我们在玩家Yaw这个函数中判定当有鼠标出现那么玩家不可以转动,此时记录玩家鼠标移动的偏移量同时改变物品条目的位置。我就大概讲解这么多,对着我的代码注释应该很好能理解这个功能的添加。那么下一章就是第11章了,这本C++在UE4中的应用也快学习完了,加油,(●ˇ∀ˇ●)!!!!!!!!!

0 0