斯坦福 UE4 C++ ActionRoguelike游戏实例教程 08.创建主HUD & 自定义作弊指令

发布时间 2023-04-09 22:31:33作者: 仇白

斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论

概述

本篇文章对应课程Lecture 14 ,56-58节。本篇文章将会教你将之前创建的各种UMG控件统合到一个主控件上。此外,还会教你如何在C++中创建自定义作弊指令,并在游戏中使用控制台执行它。

目录

  1. 创建主HUD
  2. 使用GameMode生成玩家
  3. 自定义控制台命令

创建主HUD

在之前的课程中,我们创建了各种各样的UMG控件,比如屏幕中心的准心,玩家的血条等,并通过分别调用CreateWidget的方式创建它们,最后添加到视口中。这样做有一个问题,每一个控件都是单独设计并添加的,当控件多起来后,我们很难管理它们之间的位置关系,而且分散创建的方式为程序的维护也带来了许多不便。为了解决这个问题,我们这里创建一个主控件,将之前创建的空间作为子控件统合在一起,并简单设置它们之间的位置。具体怎么操作,让我们边做边说。

顺带一提,在UE中HUD类和UMG是效果类似但是实际上几乎完全不一样的两个东西。通常HUD类继承自Actor,UMG控件继承自UObject,并且有着独特的设计方式。这里标题虽然写着创建HUD,是因为UMG控件所制作的UI,在概念上还是属于HUD(抬头显示),仅仅是为了和课程一致,实际上还是创建UMG控件,请读者不要在这里混淆。

创建Main_HUD

和之前创建控件的方式一样,我们创建一个Main_HUD,作为玩家HUD的容器。

image-20230323114241840

目前我们拥有的控件

在控制板中可以搜索到我们以前创建的控件,这里我们将玩家血条PlayerHealth_Widget和准星Crosshair_Widget空间添加到画布面板中。

image-20230323154725733

添加控件

为了将准星居中,我们设置准星空间的锚点为右下角最后一个。顺带一提,在页面布局中,锚点通常是用来控制与父级组件的相对位置关系的,读者可以移动锚点,尝试缩放界面来体会它的作用。

image-20230323155241658

修改锚点

修改准星锚点的大小如图所示:

image-20230323155310120

修改锚点大小

注意到,我们玩家血条控件拥有自己的画布面板。如果玩家血条是直接被添加到玩家视口中时,画布面板可以为其中的血条组件提供一个很好的容身之所(相对位置、大小尺寸),但是在这里,我们希望玩家血条的位置和大小由主HUD来控制。所以我们需要将玩家血条的画布面板删除掉。操作如图所示,进入玩家血条控件的设计界面,将画布面板替换为子项:

image-20230323154803239

画布面板替换为子项

可以看到原本画布面板的内容占据了整个界面。不要慌,这只是预览效果,玩家血条的显示效果最后还是得由父组件来决定。这里为了优化我们的设计体验,我们可以点击右上角,设置屏幕上所需或者自定义来自定义预览效果。

当然,到了这一步,我们之前创建控件添加到视口的操作就可以作废了,现在去运行游戏,你会被血条糊一脸。当然,这不是设计阶段该考虑的事情。

image-20230323154845180

预览界面

image-20230323155048689

将预览设置为屏幕上自定义,当然,这只是预览效果:)

现在我们可以把之前创建血条和准星控件的相关蓝图(代码)删除掉了,我们现在只需要留一个Main HUD控件即可。

image-20230323160408703

这里我在角色蓝图里创建

创建Credit_Widget

接下来我们可以丰富一下主HUD的显示。

创建一个Credit_Widget,用于显示玩家的积分等信息。我们不需要画布面板,就像之前一样,将其替换为子项。

最终布局如图所示:

image-20230323162705950

Credit_Widget的布局

创建GameModeInfo_Widget

我还想在HUD中看到当前游戏的进行时间。操作和上面一样

创建一个GameModeInfo_Widget:

image-20230323162945388

GameModeInfo_Widget的布局

为了做到实时获取游戏时间,我们需要为文本创建一个函数绑定,将其命名为GetGameTimeText,他将会每帧调用这个函数。

image-20230323163916387

绑定函数

这里使用蓝图的方式创建函数。注意到我们是通过游戏状态来获取服务器场景时间。因为如果我们是多人游戏,我们希望中途加入游戏时得到的是总的游戏时间,而不是从0开始。在进行多人游戏时,UE会不断地同步GameState,从中获取服务器场景时间十分合适。

image-20230323164138890

函数蓝图

将上述两个控件加入到主HUD中。如图所示,这是我们目前的HUD结构,为了获得更好的布局效果,我使用了垂直框将血条和Credit控件捆绑在了一起。除非是很简单的HUD,否则都不推荐使用手动调整的方式来设置页面布局:

image-20230323164913964

主HUD结构

调整GameModeInfo控件如图所示,注意我们HUD里的控件都勾选了大小到内容

image-20230323164831342

调整位置

调整垂直框为如图所示。有的同学选择了大小到内容后,血条会变得很短一条,这个问题可以通过调整血条材质图片的大小来解决。

image-20230323164855843

调整垂直框位置

最后是进入游戏后的效果:

image-20230323165206237

使用GameMode生成玩家

接下来是一些比较零散的知识点。如果你看过谌嘉诚大佬的UE4入门视频,他会教你通过设置场景中角色的自动控制玩家来让你在进入游戏的时候能自动控制一个角色。如下图所示,我们可以选择场景中已经存在的角色,通过选择自动控制玩家中的玩家0来自动控制角色:

image-20230323171045133

自动控制角色

然而在很多场景中,玩家角色并不是一开始就在场景里的,我们需要使用下面的方法来生成角色:

  1. 设置玩家出生点。玩家的出生点也是一个可以放置在场景中的Actor,我们可以在左侧搜索获得。

image-20230323170956984

设置玩家出生点
  1. 在世界场景设置中,选择一个游戏模式,并且在下方的默认Pawn类中选择我们要生成的角色类。

image-20230323201107177

设置默认Pawn类

通过以上两个步骤,UE就会根据GameMode里预设的默认Pawn类,在场景中的玩家出生点(如果有多个的话)中随机选取一个出生点,生成指定的Pawn类,并让玩家控制他。

我们同样可以在项目设置中设置整个游戏的默认游戏模式,这样在进入游戏时,如果世界场景设置的游戏模式为None时,就会加载默认游戏模式。不过,世界场景设置中设置的游戏模式优先级还是最高的,如果世界场景设置里有对游戏模式进行设置,就会执行世界场景设置里的游戏模式。

image-20230323170851086

设置整个游戏的默认游戏模式

自定义控制台命令

就拿大家最熟悉的MineCraft来举例,大家都知道MC这款游戏只要开启作弊模式,玩家就可以在下方的控制台(聊天框)输入各种各样的作弊指令进行作弊,例如获取物品、飞行等等。UE同样为游戏开发者提供了自定义控制台命令的方式,游戏开发者可以在C++里自定义控制台命令,并在游戏运行时打开控制台输入相关指令。

这里为大家演示了如何在C++中自定义作弊指令,值得一提的是,这个方式只有将函数添加到玩家控制的角色类上或者GameMode类或者CheatManager类上才能生效。如果是其他普通的Actor类就不会生效。也许是必须由玩家控制器类能访问到的对象才能进行自定义指令。

自愈指令

在玩家角色类中添加一个函数,这个函数必须用UFUNCTION修饰,并在里面加上Exec修饰词,这才能表明这个函数要在控制台上也能够访问。

这里的控制台命令将会执行回血的操作。具体用法为进入游戏,点击·(tab键上面的那个键), 以函数名为指令,后面的参数作为函数参数,回车即可执行函数。

//MyCharactor.h
UFUNCTION(Exec)
void HealSelf(float Amount = 100);

//.cpp
void AMyCharacter::HealSelf(float Amount)
{
	AttributeComponent->ApplyHealthChanges(this, Amount);
}

image-20230323174448870

执行作弊指令

杀死所有AI指令

同样的道理,在GameModeBase类里我们定义了一个杀死所有AI的指令,方法同上。

//ASurGameModeBase.h
UFUNCTION(Exec)
void KillAll();

//ASurGameModeBase.cpp
void ASurGameModeBase::KillAll()
{
	for(TActorIterator<ASurAiCharacter> It(GetWorld()); It; ++It)
	{
		ASurAiCharacter* Bot = *It;

		USurAttributeComponent* AttributeComp = USurAttributeComponent::GetAttributes(Bot);
		if(AttributeComp && AttributeComp->IsAlive())
		{
			AttributeComp->ApplyHealthChanges(this, AttributeComp->GetMaxHealth() * -1);
		}
	}
}

看左下角!

总结

在本篇文章里,我们更加深入了解了UMG控件的使用,将创建的大大小小的控件都塞进了一个主控件中。此外,还简单了解了一下由GameMode控制生成的玩家角色。最后,学习了在C++中自定义控制台指令的办法。