作为业界内广泛使用的一款游戏引擎,虚幻引擎(Unreal Engine)以其优秀的图形表现力和强大的物理模拟能力而为人所知。但除此之外,以C++为基础,结合其专属扩展功能,实现高效、灵活的面向对象编程(OOP)方式,也是虚幻引擎的一大特色。本文将会详细探讨虚幻引擎中的面向对象编程理念,并给出实际示例,让大家更快地掌握相关技巧。
1. 面向对象的核心概念
面向对象编程(OOP)是虚幻引擎的核心基础,其核心概念包括封装、继承和多态。
a. 封装
封装简单来说,就是将数据和函数捆绑成一个封闭的单元,并限制对数据的直接访问权限,确保程序模块之间的独立性与安全性。在虚幻引擎中,封装通常体现为:
- 数据隐藏: 通过访问修饰符(public、private、protected)控制类内部数据的可见性。
- 属性定义: 引擎提供了强大的属性宏,例如UPROPERTY宏,可以轻松控制编写的游戏属性是否暴露给Unreal编辑器,极大提高了界面与逻辑之间的交互性。
b. 继承
继承允许开发者定义一个类时,复用另一个类的属性和功能,并能够扩展它们。虚幻引擎结构中最常见的例子是:
- 单继承体系: 基本上所有的游戏主要类(Actor、Component等)都是直接或间接继承自基础类 UObject。
- 替代多继承的方案: 虚幻引擎虽然主推单继承,但通过灵活使用组件机制,能在一定程度上实现类似多重继承的效果,增强代码的灵活性。
c. 多态
- 多态是指程序运行时能够根据实际对象类型的不同,选择合适调用的方法。在虚幻引擎中,这主要是通过虚函数(Virtual Function)的重写实现。
2. 虚幻引擎特有的扩展
虚幻引擎为了更好支持游戏开发的需求,在C++标准语法基础上开发了一系列独特扩展和宏工具,最关键的扩展技术包含:
a. 宏
UCLASS
: 用于定义得到引擎垃圾回收、反射功能支持的UObject派生类;UPROPERTY
: 定义游戏属性,可在编辑器直接查看或配置;UFUNCTION
: 定义可以通过蓝图调用的方法或绑定可触发的事件。
b. 组件化开发
虚幻引擎更加推荐使用组件而非单纯的复杂继承树。组件(例如UActorComponent
和USceneComponent
)能够新建、在运行时动态添加或删除,极大便利了功能的重用与扩展。
以下通过简单的项目来进行演示:
面向对象特性示例:
1、首先定义一个BaseActor
基类,其中封装了两个属性和两个方法:
BaseActor.h:
protected:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "test")
float testvalue1;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "test")
float testvalue2;
UFUNCTION(BlueprintCallable, Category = "test")
void TestFunc(float a,float b,float& c);//a,b作为输入,c作为函数输出
UFUNCTION(BlueprintCallable, Category = "test")
virtual void TestFunc2(float a, float b, float& c);
BaseActor.cpp
void ABaseActor::TestFunc(float a, float b, float& c)
{
c = a + b;
}
void ABaseActor::TestFunc2(float a, float b, float& c)
{
c = a - b;
}
2、通过BaseActor类创建蓝图类BP_BaseActor
在事件图表中对函数进行实现:
输出结果如下:
3、通过BaseActor类创建派生类DerivedActor,在派生类DerivedActor中对虚函数TestFunc2进行重写。
DerivedActor.h:
virtual void TestFunc2(float a, float b, float& c) override;
DerivedActor.cpp:
void ADerivedActor::TestFunc2(float a, float b, float& c)
{
c = a * b;
}
4、通过派生类创建蓝图类BP_DerivedActor,在BP_DerivedActor事件图表中对TestFunc和TestFunc2进行实现。
输出结果如下:
以上示例简单演示了在虚幻引擎中如何进行类和函的封装,如何继承父类的方法和属性,以及通过重写虚函数来实现多态。
组件化开发示例:
1、创建MyCharacter类,通过添加组件来实现添加相机和弹簧臂的功能。
MyCharacter.h:
//前置声明
class USpringArmComponent;
class UCameraComponent;
MyCharacter.h:
private:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"));
TObjectPtr<USpringArmComponent>CameraBoom;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"));
TObjectPtr<UCameraComponent>PlayerCamera;
MyCharacter.cpp:
#include "Camera/CameraComponent.h"
MyCharacter.cpp:
AMyCharacter::AMyCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);//相机杆附着到根组件
CameraBoom->TargetArmLength = 600.0f;//相机杆长度
CameraBoom->SetRelativeRotation(FRotator(-40.f, 0.f, 0.f));//相机杆旋转pitch,yaw,roll
//相机附着到相机杆
PlayerCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("PlayerCamera1"));
PlayerCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
}
以上示例通过组件化开发,无需进行继承即可使对象的功能变得可配置和模块化。