http://blog.sina.com.cn/s/blog_7c5fd2e90101ku0j.html
使用Unreal4引擎前需要安装 或者编译引擎。 下面就为同学们介绍下 Unreal4 安装配置。
这次主要是简单介绍下Unreal4引擎使用前的配置 和 VisualStudio2013调试插件的配置
对于大型游戏MMORPG游戏或是3D街景(虚拟现实)还有大型无缝地图都需要地图啊,关卡动态加载的机制来达到让用户感觉自己一直是在漫游,而不会出现Loading(读条),卡界面等情况。当然除非是垮区域无法依靠关卡和关卡中之间的关系做处理。
C++和蓝图
概述
蓝图可以继承C++类,从而使得程序员可以在代码中创建新的游戏性类,而关卡设计人员可以使用蓝图来继承该类并对其进行修改。 有很多种修饰符可以改变C++类和蓝图系统间交互方式,其中某些修饰符会在本示例中突出介绍。
类设置
在类设置的第一部分中,使用C++类向导创建一个名称为LightSwitchBoth 的类。
LightSwitchBoth类中的大部分代码设置都和仅使用C++的LightSwitch示例类似。尽管您可以让一个蓝图继承LightSwitchCodeOnly类,
但蓝图图表并不能访问该类中创建的组件、属性及函数。该示例将使用 UPROPERTY() 和 UFUNCTION() 修饰符,这两个修饰符使得LightSwitchBoth作为继承它的蓝图的模板。
您会发现首先参考下 仅使用C++的LightSwitch示例是有用的,这样您可以看下如何设置头文件及源文件 来创建LightSwitchComponent、SphereComponent、DesiredBrightness属性及OnOverlap函数。
这个头文件是从 仅使用C++的LightSwitch示例 改编而来,添加了以下功能:
PointLightComponent和SphereComponent是BlueprintReadOnly(仅蓝图可读的),并且将显示在 我的蓝图 选卡中的 Switch Components(切换组件) 类目中。
OnOverlap现在是一个BlueprintNativeEvent,将会显示在 我的蓝图 选卡中的 Switch Functions(切换函数) 类目中。
DesiredBrightness是BlueprintReadWrite(蓝图可读写的),将显示在 我的蓝图 选卡中的 Switch Properties(切换属性) 类目中。
DesiredBrightness现在是EditAnywhere(随处可编辑的),而不是VisibleAnywhere(随处可见的)。
UCLASS() 宏有个 Blueprintable 修饰符。在这个示例中,该修饰符不是必须的,因为LightSwitchBoth直接继承Actor,而Actor是Blueprintable(可蓝图化的),所以LightSwitchBoth会继承该修饰符。
加上 UPROPERTY() 和 UFUNCTION() 宏中的额外修饰符,LightSwitchBoth类的头文件如下所示:
LightSwitchBoth.h
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "LightSwitchBoth.generated.h"
UCLASS()
class ALightSwitchBoth : public AActor
{
GENERATED_UCLASS_BODY()
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Switch Components")
TSubobjectPtr PointLight1;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Switch Components")
TSubobjectPtr Sphere1;
UFUNCTION(BlueprintNativeEvent, Category="Switch Functions")
void OnOverlap(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Switch Properties")
float DesiredBrightness;
};
在LightSwitchBoth的源文件中,构造器仍然是一样的。但是,需要对 OnOverlap 函数做一点修改。这个函数现在是一个BlueprintNativeEvent。这意味着 在继承这个类的蓝图中,可以放置一个覆盖 OnOverlap 的事件,当正常调用该函数时会执行此事件。如果该事件不存在,那么则是执行那个函数的 C++实现。要想使这个设置正常工作,该C++函数需要重命名为 OnOverlap_Implementation 。稍后在本示例中将介绍这个蓝图设置。对 OnOverlap 函数 进行了修改后,LightSwitchBoth 的源文件如下所示:
LightSwitchBoth.cpp
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#include "BasicClasses.h"
#include "LightSwitchBoth.h"
ALightSwitchBoth::ALightSwitchBoth(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
DesiredBrightness = 15.0f;
PointLight1 = PCIP.CreateDefaultSubobject(this, "PointLight1");
PointLight1->Brightness = DesiredBrightness;
PointLight1->bVisible = true;
RootComponent = PointLight1;
Components.Add(PointLight1);
Sphere1 = PCIP.CreateDefaultSubobject(this, TEXT("Sphere1"));
Sphere1->InitSphereRadius(250.0f);
Sphere1->OnComponentBeginOverlap.AddDynamic(this, &ALightSwitchBoth::OnOverlap); // set up a notification for when this component overlaps something
Sphere1->OnComponentEndOverlap.AddDynamic(this, &ALightSwitchBoth::OnOverlap); // set up a notification for when this component overlaps something
Sphere1->AttachParent = RootComponent;
Components.Add(Sphere1);
}
void ALightSwitchBoth::OnOverlap_Implementation(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if ( OtherActor && (OtherActor != this) && OtherComp )
{
PointLight1->ToggleVisibility();
}
}
当创建类时,新的 UCLASS() 、UFUNCTION() 和/或 UPROPERTY() 宏意味着该代码必须在Visual Studio 或Xcode中进行编译,然后使用虚幻编辑器重新加载它们。 关闭虚幻编辑器,在Visual Studio 或Xcode中编译该项目,然后打开编辑器并重新加载该项目,以确保正确地重新加载该游戏模块。同时,需要注意的一点是, 要确保 Build Configuration(版本配置) 和您打开该项目使用的虚幻编辑器可执行文件的版本一致。 请在 编译游戏项目文档中阅读关于编译配置及编译项目的更多信息。
当重新打开虚幻编辑器并重新打开您的项目后,便可以创建一个新的类蓝图了。 在本示例中,选择LightSwitchBoth作为该蓝图的父类,蓝图名称为LightSwitchBoth_BP 。
在 C++代码中添加的PointLightComponent和SphereComponent 也会显示在 Blueprint Editor(蓝图编辑器) 的 组件模式中的 Components(组件) 选卡内。 它们的图标是深蓝色,表示它们是从父类LightSwitchBoth类继承而来的原生组件。而刚刚添加到 LightSwitchBoth_BP 蓝图中的新组件的图标 是浅蓝色的。关于使用 Components(组件) 选卡添加及排列组件的更多信息,请参照 组件选卡文档。
Blueprint Editor(蓝图编辑器) 的 图表模式是蓝图编辑的核心。在 Graph Mode(图表) 模式中,您可以在我的蓝图选卡中添加新变量、 函数及宏。您也可以访问该类蓝图中包含的 所有 图表。在这些图表中,各种节点连接到一起,来创建由类属性、游戏事件、甚至Actor的周边环境 驱动的设计及游戏功能。
在 Graph Mode(图标模式) 中, My Blueprint(我的蓝图) 选卡显示了在C++中添加到LightSwitchBoth类中的 PointLightComponent 和SphereComponent 。这是因为 BlueprintReadOnly 修饰符存在的缘故。 通过在 我的蓝图 选卡中点击并拖拽这些组件的名称到图表中,可以将这些组件的节点添加到图表中。然后,您可以把这些节点连接到改变像可见性 或光源颜色这样属性的节点上。DesiredBrightness 属性也会出现在 我的蓝图 选卡中。因为它是一个属性,而不是一个组件,所以可以使用 BlueprintReadWrite 修饰符。这意味着在蓝图图表中可以创建节点来获取及设置 DesiredBrightness 的值。请参照 我的蓝图文档来获得一般的应用信息。
默认情况下,可能不会显示父类LightSwitchBoth的组件和属性。因为当选中 我的蓝图 选卡底部的 Show user-created variables only(仅显示用户创建的变量) 复选框时, 会隐藏从父类继承的属性。
显示所有变量
仅显示用户创建的变量
有两个图表用于设置 LightSwitchBoth_BP 类的行为。第一个是构造脚本图表,它包含了一个专用的 Construction Script(构建脚本)
事件。如果没有该 Construction Script 设置,那么新的 LightSwitchBoth_BP Actor 将仅使用LightSwitchBoth的构造函数。然而,当Actor在关卡中移动时,及当 Desired Brightness 发生改变时,都会执行 Construction Script 。使用 Construction Script 意味着,可以轻松地改变暴露给蓝图的Actor属性, 并且可以快速地看到这些修改的效果。
在 LightSwitchBoth_BP 类中, Construction Script 事件连接到了 Set Brightness 节点上,以便当在关卡中添加或移动Actor时或者 Desired Brightness 发生改变时,将 Point Light 1 (PointLightComponent) 的亮度设置为 Desired Brightness 的值。
LightSwitch_BPOnly 类中设置的另一个图表是 事件图表 。EventGraph的执行是由事件触发的。在这个示例中, 任何时候当调用C++函数 OnOverlap 时,OnOverlap 就会执行。在LightSwitchBoth的源文件中,设置了代理,以便当一个Actor进入或离开SphereComponent时会执行 OnOverlap 。
Sphere1->OnComponentBeginOverlap.AddDynamic(this, &ALightSwitchBoth::OnOverlap); // set up a notification for when this component overlaps something Sphere1->OnComponentEndOverlap.AddDynamic(this, &ALightSwitchBoth::OnOverlap); // set up a notification for when this component overlaps something
OnOverlap 事件节点连接到了 Set Light Color 节点上。任何时候当执行该事件时,它都将会把PointLightComponent的光源颜色设置为一个随机颜色。这覆盖了 源码文件中用于切换的PointLightComponent可见性的 OnOverlap_Implementation 函数。
关于事件及处理图表的更多信息,请参照事件、 事件图表和事件模式 文档。
在变量的设置中, DesiredBrightness 变量设置为 EditAnywhere (随处可编辑) ,所以在 蓝图编辑器 的默认模式中它是可见的,并且可以进行编辑。 这意味着对于类的每个实例,这个变量是可以变化的,所以每个Actor可以有其自己的 DesiredBrightness 。因为 DesiredBrightness 也是 BlueprintReadWrite(蓝图可读写) 的,且 Construction Script 中使用了它,所以更新它还会导致再次执行 Construction Script 。
其他Class Blueprints(类蓝图)可以继承由蓝图创建的类,通过以下两种方式实现:使用 Class Viewer(类别查看器) 中的类附近的下拉列表按钮来创建一个新蓝图, 或者通过右击该蓝图并选择 Create New Blueprint Based on This(基于此蓝图创建一个新蓝图) 。
类蓝图 LightSwitchBoth_BP 位于 内容浏览器中,您可以从那里将其拖拽到关卡内。它也存在于 类别查看器中。 关于使用 内容浏览器 或 类别查看器 在关卡放置Actor的更多信息,请参照放置Actors 文档。
设置Visual Studio和虚幻引擎4协同工作有利于提高开发人员使用UE4 的效率和整体用户体验。
以下是为结合使用Visual Studio和虚幻引擎4的开发人员推荐的设置。
关闭 Show Inactive Blocks(显示不活动的代码块) 。如果您不关闭此项,在文本编辑器中,很多代码块会呈现出灰掉 状态。(Options(选项) > Text Editor(文本编辑器) > C/C++ > Formatting(格式)) 。
打开IntelliSense(智能编码)和"squiggles(波浪线提示)"功能,让它们正常运行。(请参照Intellisense(智能编码), Live Errors(实时错误)和Squiggles(波浪线提示)部分)
在Visual Assist X (VAX)中关闭 Format After Paste(粘帖后格式化) 功能。这个功能有时候会导致格式变得混乱。(VAX Options(VAX选项) > Advanced(高级) > Corrections(校正))
在解决方案浏览器中停用 External(外部) 文件夹, 它们会扰乱视图。( 在 Options(选项) > Text Editor(文本编辑器) > C/C++ > Advanced(高级) 中Disable External Dependencies Folder(禁用外部依赖文件夹) )
如果不需要 Edit & Continue(编辑&继续) 功能,请关闭它们。(Options(选项) > Debugging(调试) > Edit and Continue(编辑并继续))
关闭 Error List(错误列表) 窗口。如果启用该功能,当您的代码中出现合法错误时 Error List(错误列表) 窗口会自动弹出 。但是,当和虚幻引擎结合应用时, Error List(错误列表) 会显示不正确的错误信息。建议您最好 禁用该窗口,并使用 Output(输出) 窗口来查看您的代码错误。这个窗口将仅显示 真正的错误。要想禁用Error List(错误列表)窗口:
如果 Error List(错误列表) 窗口处于打开状态,请关闭它。
从 Tools(工具) 菜单中, 打开 Options(选项) 对话框。
选择 Projects and Solutions(项目和解决方案) 并取消选中 Always show error list if build finishes with error(如果编译中出现错误总是显示错误列表) 选项。
虚幻引擎4项目现在可以正常支持 IntelliSense(智能编码)功能,包括实时Error List(错误列表) 和 "squiggles(波浪线提示)"! (请参照以下介绍获得如何启用它的信息。)
VC10的IntelliSense功能可以在您编写代码的过程中重新编译C++。这比仅检查 VAX的语法强大很多: VC10使用完整的C++编译器,可以验证每一行代码。这个功能非常强大并且将会 加快您的工作流程!
当其和波浪线提示功能结合使用时,如果您正在查看的文件有任何IntelliSense 错误,都可以在Error List(错误列表)中看到。 您可以通过Error List(错误列表)的右击菜单来打开或关闭该功能。
由于Squiggles(波浪线提示)功能不能和虚幻引擎4项目协同工作,您可能已经禁用了它们。请确保点击 C/C++ Advanced(高级)选卡,并使用以下设置。
当您打开一个C++文件时,您可以通过查找这个图标来判断IntelliSense编译器是否正在“工作”:
当编写代码时显示波浪线提示有时候要花几秒钟的时间。
这是因为我们有很多包含文件,且IntelliSense目前不使用预编译头文件。
有时候您会看到“误报的”IntelliSense 错误。以下是几种可能的原因。
IntelliSense编译器(EDG)比MSVC编译器更严格。
某些针对IntelliSense的#defines设置和正常编译时的设置不同。
IntelliSense编译的C++总是被当做32位对待。
如果绝对需要,您可以把代码封装到 #ifdef __INTELLISENSE__
中,以消除波浪线提示。
IntelliSense的错误的表达方式和VC++ 编译器的错误的表达方式略有不同。他们只是表达不同而已。
头文件中的Squiggles(波浪线提示)功能通过编译包含它的已知 .cpp
对应的头文件来进行工作。
有时候IntelliSense把这个功能搞乱了,导致您会在头文件中看到波浪线提示。
如果需要,您可以增大 Max Cached Translation Units(最大缓存变换单元) 设置。
它会使用更多的内存,但可能会提高一点反应速度。
有少量C++文件还不能和IntelliSense相兼容。
Unreal Build Tool有一个新的 -IntelliSense
选项。
这将为我们的所有项目文件产生IntelliSense属性表。
当添加了新的模块或者项目包含发生改变时仅需要重新运行它即可。
Visual Studio的UnrealVS插件使您可以在使用虚幻引擎开发时轻松地访问常用操作。
功能包括:
设置启动项目
编译启动项目的可绑定命令
设置命令行参数
请参照UnrealVS插件页面 获得设置及使用该插件的信息。
Visual Studio支持通过‘可视化查看器’来扩展调试器,从而轻松地查看常见的虚幻数据类型, 比如对象FNames 和动态数组。根据您所使用的Visual Studio 2010或Visual Studio 2012的不同, 这个功能的设置也有所区别。
您会发现您的安装文件中包含了具备该可视化查看器逻辑的文件:
[ROCKETINSTALL]/Engine/Extras/VisualStudioDebugging/UE4.natvis
复制该文件到以下位置:
[VSINSTALLDIR]/Common7/Packages/Debugger/Visualizers/UE4.natvis [USERPROFILE]/My Documents/Visual Studio 2012/Visualizers/UE4.natvis
继续研究Blueprint,这次主要看看C++和Blueprint的结合方法。官方文档依然没有太大用处,但在wiki上还是有很多值得学习的文章的。当中有个叫Rama的家伙贡献了相当多的教程,他还写了一个很不错的插件,而且将源代码和实现的思路都写在了一个wiki页面里,对社区的帮助很大,MVP应该是没得跑了,要是多一些这样的人UE4就能更快地成熟并推广了。
不过在动手写代码之前,我首先把那个忍无可忍的VS2013自带的Intellisense给关掉了,我是完全按照官方文档的指引配置了所有的参数,但实际用起来却奇慢无比,再小的工程敲几行代码后要等二十几秒自动完成才能蹦出来,右下角那个处理提示符就几乎没消失过。而且代码帮助还非常不稳定,时有时无,另外还经常给出一些错误的提示。忍不了了,还是装了Visual Assist,瞬间一切都那么顺手又顺眼了。
工具搞定后,先来看看如何在C++中创建可以在Blueprint中使用的全局函数:
下面是具体代码:
MyBPLibrary.h:
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. 2 3 #pragma once 4 5 #include "MyBPLibrary.generated.h" 6 7 10 UCLASS() 11 class UMyBPLibrary : public UBlueprintFunctionLibrary 12 { 13 GENERATED_UCLASS_BODY() 14 15 UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Test") 16 static FString GetHappyMessage(); 17 18 UFUNCTION(BlueprintCallable, Category = "Test") 19 static bool SaveToFile(FString Dir, FString Name, FString Text, bool Overwrite = false); 20 21 UFUNCTION(BlueprintCallable, Category = "Test") 22 static bool MakeDir(FString Dir); 23 };
MyBPLibrary.cpp:
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. 2 3 #include "HelloWorld.h" 4 #include "MyBPLibrary.h" 5 6 7 UMyBPLibrary::UMyBPLibrary(const class FPostConstructInitializeProperties& PCIP) 8 : Super(PCIP) 9 { 10 11 } 12 13 FString UMyBPLibrary::GetHappyMessage() { 14 return FString("Hello, I'm Happy!"); 15 } 16 17 bool UMyBPLibrary::MakeDir(FString Dir) { 18 return FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*Dir); 19 } 20 21 bool UMyBPLibrary::SaveToFile( 22 FString Dir, 23 FString Name, 24 FString Text, 25 bool Overwrite 26 ) { 27 IPlatformFile& F = FPlatformFileManager::Get().GetPlatformFile(); 28 if (!F.DirectoryExists(*Dir)) 29 { 30 F.CreateDirectoryTree(*Dir); 31 if (!F.DirectoryExists(*Dir)) 32 { 33 return false; 34 } 35 } 36 37 Dir += "\\"; 38 Dir += Name; 39 40 if (!Overwrite) 41 { 42 if (F.FileExists(*Dir)) 43 { 44 return false; 45 } 46 } 47 48 return FFileHelper::SaveStringToFile(Text, *Dir); 49 }
这个例子中实现了3个全局函数:GetHappyMessage,MakeDir以及SaveToFile,其中GetHappyMessage为无副作用的函数。它们在Blueprint中就可以这么用了:
在调试的过程中遇到的一些问题在这里说一下:
通过在C++中实现供Blueprint调用的全局函数,就实现了Blueprint和C++交互的一种常用途径。后面再写一些其他交互方式的实现方法。
C++触发Blueprints事件
如果想实现只供某一个类使用的Blueprint函数,方式是类似的,只是不要再继承UBlueprintFunctionLibrary类,同时函数也无需再声明成static即可。
虽然能够在Blueprint中调用一个C++实现的方法是很不错,但在实际中我们还会需要其他的交互方式,比如由C++代码去触发一系列的Blueprint动作,以及让Blueprint能够和C++类的某些属性变量直接进行交互。
我们先来看看如何将C++类中的某些属性变量暴露出去,让Blueprint(或Editor)能够看见、读或写这些变量,从而实现和C++的通信。
其实非常简单:只需要在C++类的头文件中这样声明一下就可以了:
1 2 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PlayerMusicSkill") 3 int32 MusicSkillLevel;
只需要使用UPROPERTY宏,在加上一些枚举属性,就可以让一个变量以开发者希望的方式暴露给Blueprint,其中:
上面的代码在编译后,只需要在Editor中基于这个类创建一个Blueprint,然后就能够在它的Default属性界面看到下面的内容:
相当不错,而且简单。下面再来看看关键的:如何让C++去触发Blueprint,同时给Blueprint传递信息?
其实也非常简单,主要是使用UFUNTION+BlueprintImplementableEvent属性:
1 UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "Music skill is GOOD")) 2 virtual void MusicSkillGood(int32 CurrentSkill);
其中:
完整的类代码如下:
MyPlayerController.h:
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. 2 3 #pragma once 4 5 #include "GameFramework/PlayerController.h" 6 #include "MyPlayerController.generated.h" 7 8 11 UCLASS() 12 class AMyPlayerController : public APlayerController 13 { 14 GENERATED_UCLASS_BODY() 15 16 17 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PlayerMusicSkill") 18 int32 MusicSkillLevel; 19 20 UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "Music skill is GOOD")) 21 virtual void MusicSkillGood(int32 CurrentSkill); 22 23 virtual void PlayerTick(float DeltaTime) OVERRIDE; 24 };
MyPlayerController.cpp:
1 // Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. 2 3 #include "HelloWorld.h" 4 #include "MyPlayerController.h" 5 6 7 AMyPlayerController::AMyPlayerController(const class FPostConstructInitializeProperties& PCIP) 8 : Super(PCIP) 9 { 10 11 } 12 13 void AMyPlayerController::PlayerTick(float DeltaTime) { 14 Super::PlayerTick(DeltaTime); 15 16 if (MusicSkillLevel > 50) 17 { 18 MusicSkillGood(MusicSkillLevel); 19 } 20 }
上面代码的主要逻辑就是在每个Tick检查当前类实例的MusicSkillLevel变量值是否大于50,如果是,则对Blueprint触发MusicSkillGood事件,并将MusicSkillLevel的值传递过去。最终在Blueprint中可以这样用:
这个Blueprint的逻辑就是:
到此为止,Blueprint已经和C++代码实现了完全的交互:Blueprint能够主动调用C++中的函数,C++也能主动触发Blueprint的事件,而且双方还能通过暴露的变量进行交互。这样一来,整个游戏的底层平台模块完全可以用C++实现,然后给上层的Blueprint提供调用接口,由Blueprint来利用、组织这些模块来实现上层的完整游戏逻辑。这种结合方式既保留了C++的性能优势,又充分利用了Blueprint的易用性和灵活性来让游戏开发保持快速地迭代。这应该就是UE4所推崇的最佳开发模式。
接触UnrealEngine4大概有快两周了,从安装配置到编辑器的熟悉,再到官方实例的学习以及Blueprint的应用。从UE3的UnrealScript和各种classes到UE4现在的发开方式,昨天大概拟定了一个UE4引擎探究的计划,下周会围绕计划展开研究。