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;
}
我们在这之后可以将物品按照线性的关系添加进入
导入资源:这部分其实就是我们在创建虚幻4的C++项目时导入初学者内容。导入过的同学可以忽视。。
将我们捡起的效果与按键绑定
打开项目设置中的输入选项
设置好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>(this, TEXT("ProxSphere"));
Mesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("Mesh"));
//放置根节点为Mesh
RootComponent = Mesh;
Mesh->SetSimulatePhysics(true);
//绑定Prox的方法,当有物体进入直接激发函数
ProxSphere->OnComponentBeginOverlap.AddDynamic(this, &APickupItem::Prox);
ProxSphere->AttachTo(Mesh);//很重要,要把这个碰撞体和表面的网格相结合
}
//捡起物体函数
void APickupItem::Prox_Implementation(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
//如果碰到的物体无法转化为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 iMessage, float iTime, FColor iColor, UTexture2D* iTex)
{
message = iMessage;
time = iTime;
color = iColor;
tex = iTex;
}
};
//图标结构体
struct Icon
{
FString name;
UTexture2D *tex;
Icon(){ name = "UNKNOWN ICON"; tex = 0; }
Icon(FString & iName, UTexture2D * 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<FString, int>::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_Pressed, this, &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中的应用也快学习完了,加油,(●ˇ∀ˇ●)!!!!!!!!!
- Learning C++ by Creating Games With UE4(15.05.19)(Chapter 10)Coding
- Learning C++ by Creating Games With UE4(15.05.11)-4(Chapter 8-3)Coding
- Learning C++ by Creating Games With UE4(15.05.18)-1(Chapter 9-1)Coding
- Learning C++ by Creating Games With UE4(15.05.18)-2(Chapter 9-2)Coding
- Learning C++ by Creating Games With UE4(15.05.08)-3(Chapter 1)
- Learning C++ by Creating Games With UE4(15.05.11)-4(Chapter 8-1)
- Learning C++ by Creating Games With UE4(15.05.11)-4(Chapter 8-2)
- Learning C++ by Creating Games With UE4(15.05.20)(Chapter 11-1)Monster
- Learning C++ by Creating Games With UE4(15.05.21)(Chapter 11-2)Monster
- Learning C++ by Creating Games With UE4(15.05.21)(Chapter 11-3)Monster
- Learning C++ by Creating Games With UE4(15.05.21)(Chapter 11-4)Monster
- Learning C++ by Creating Games With UE4(15.05.04)-1(前言)
- Learning C++ by Creating Games With UE4(15.05.04)-2(目录)
- Learning C++ by Creating Games With UE4(书籍)
- Step-by-step learning C + +(chapter fivesummary____Expressions)
- 创建模块化游戏 I(翻译)(Creating Moddable Games with XML and Scripting Part I)
- 创建模块化游戏(Creating Moddable Games with XML and Scripting Part I)代码阅读,关于整体架构!
- 创建模块化游戏(Creating Moddable Games with XML and Scripting Part I)代码阅读,关于整体架构!
- kafka入门:简介、使用场景、设计原理、主要配置及集群搭建
- 关于Tomcat配置管理员权限角色的问题
- 机器学习经典书籍小结
- GitHub上整理的一些工具
- 汇编
- Learning C++ by Creating Games With UE4(15.05.19)(Chapter 10)Coding
- 第9周项目5-方程类
- UIView_ScrollView
- 网络爬虫(一):抓取网页的含义和URL基本构成
- Linux命令
- sgu-248 Integer Linear Programming
- 系统声音服务与震动
- Visual Studio and .NET's Unit Test Framework (转)
- CockroachDB设计与实现-刘奇