第一次引擎编程尝试
软硬件要求
第一次引擎编程尝试
给打出的子弹增加爆炸效果
默认创建的FPS游戏子弹击中物体无特殊效果,仅子弹消失,现在我们希望子弹击中物体之后有爆炸的效果,如下所示:
粒子系统
粒子系统是计算机图形引擎中模拟一些特定模糊现象的技术,如火、爆炸、烟雾、水流等效果;
在项目工程中,美术在引擎里面制作粒子特效,保存下来就是uassets格式文件,程序直接load即可
目录结构
创建项目之后,初始状态下目录结构如下:
其中:
(1)Content:用于存放游戏所需的资源,这个资源包括:动画、蓝图、音频、特效等,这些资源文件的编排无特殊规则,以方便检索为准
(2)C++ classses:下放工程项目代码
逻辑实现
直接贴代码:
#include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "FPSTpl2Projectile.generated.h" class USphereComponent; class UProjectileMovementComponent; class UParticleSystem; UCLASS(config=Game) class AFPSTpl2Projectile : public AActor { GENERATED_BODY() /** Sphere collision component */ UPROPERTY(VisibleDefaultsOnly, Category=Projectile) USphereComponent* CollisionComp; /** Projectile movement component */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true")) UProjectileMovementComponent* ProjectileMovement; UPROPERTY(EditDefaultsOnly, Category = "FX") UParticleSystem* ExplosionEffect; public: AFPSTpl2Projectile(); /** called when projectile hits something */ UFUNCTION() void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit); /** Returns CollisionComp subobject **/ USphereComponent* GetCollisionComp() const { return CollisionComp; } /** Returns ProjectileMovement subobject **/ UProjectileMovementComponent* GetProjectileMovement() const { return ProjectileMovement; } };
(1)AActor
①不同于字面意思,UE中所有能被放进关卡的都是一个Actor(不仅是可活动的对象),这个对象可以是角色、NPC、摄像头,也可以是本例中的子弹
②Actor的功能主要包括:组件容器、接口调用和网络上的属性同步
③而AActor则是C++所有Actor的基类
(2)Component
①Actor功能各异,为了便于代码的复用,从设计层面除了一些通用能力,UE将各个功能组件化,Actor按需取用实现自己个性化的功能
②在本例中,我们看到AFPSTpl2Projectile
(AActor的子类),包含有以下组件:
(3)USphereComponent
:球体组件,在本例中,这个球体组件是用来进行碰撞检测用的,碰撞体的范围多大,管理碰撞后的回调
(4)UProjectileMovementComponent
:抛物体组件,用于模拟炮弹、子弹等的运动
(5)对于Component
来说,在创建的时候已经把这个Component
所述的Actor
设置上了,后续流程通过GetOwner
获取所述Actor
的指针,在完成逻辑之后,可以对Actor
进行相关的修改和检查
(6)编译系统
①在读写UE的代码经常会看到UFUNCTION
、UCLASS
等宏,这些宏的目的是告诉UnrealHeaderToole(UHT)当遇到下面的代码该如何处理,通过
UHT,在编译前对代码文件进行处理生成
.generated.h和.generated.cpp`文件
②GENERATED_BODY
· 当遇到GENERATED_BODY
时,表示这个类是继承自UE4引擎定义的类,需要帮助生成包括构造函数在内的相关函数
· GENERATED_UCLASS_BODY
③UPROPERTY
,属性说明符,用于对变量属性进行描述,指明属性与引擎和编辑器的相处方式,如本例:
· VisibleDefaultsOnly
:说明此属性只在原型的属性窗口可见,但是不可编辑
· VisibleAnywhere
:说明此属性在所有属性窗口可见,但无法被编辑
· EditDefaultsOnly
:说明此属性可通过属性窗口编辑,但只能在原型上进行
· Category
:指定在蓝图编辑工具中显示的属性的类别
· BlueprintReadOnly
:说明此属性可以由蓝图读取,但不能被修改
· meta
:AllowPrivateAccess
⑤UFUNCTION
:函数说明符,用以控制函数相对于引擎和编辑器各个方面的行为方式
· 函数标签可以规定函数是可以被客户端调用、蓝图调用、还是控制台执行等;也可以描述是RPC、属性同步、还是广播逻辑
⑥UCLASS
(7)蓝图
①蓝图是什么?
· 游戏开发者为游戏添加的视觉脚本
· 在这个脚本中,我们可以通过连线将节点、事件、功能、变量链接在一起,创造复杂的游戏元素
②所以上面UPROPERTY
的一些选项,可以理解程序中定义的对象的属性不仅受C++逻辑修改,还可以被蓝图脚本逻辑修改,通过属性标签可以规定属性在两个系统的操作权限
#include "FPSTpl2Projectile.h" #include "GameFramework/ProjectileMovementComponent.h" #include "Components/SphereComponent.h" #include "Kismet/GameplayStatics.h" AFPSTpl2Projectile::AFPSTpl2Projectile() { // Use a sphere as a simple collision representation CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp")); CollisionComp->InitSphereRadius(5.0f); CollisionComp->BodyInstance.SetCollisionProfileName("Projectile"); CollisionComp->OnComponentHit.AddDynamic(this, &AFPSTpl2Projectile::OnHit); // set up a notification for when this component hits something blocking // Players can't walk on it CollisionComp->SetWalkableSlopeOverride(FWalkableSlopeOverride(WalkableSlope_Unwalkable, 0.f)); CollisionComp->CanCharacterStepUpOn = ECB_No; // Set as root component RootComponent = CollisionComp; // Use a ProjectileMovementComponent to govern this projectile's movement ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp")); ProjectileMovement->UpdatedComponent = CollisionComp; ProjectileMovement->InitialSpeed = 3000.f; ProjectileMovement->MaxSpeed = 3000.f; ProjectileMovement->bRotationFollowsVelocity = true; ProjectileMovement->bShouldBounce = true; // Die after 3 seconds by default InitialLifeSpan = 3.0f; } void AFPSTpl2Projectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) { // Only add impulse and destroy projectile if we hit a physics if ((OtherActor != nullptr) && (OtherActor != this) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics()) { OtherComp->AddImpulseAtLocation(GetVelocity() * 100.0f, GetActorLocation()); Destroy(); } UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionEffect, GetActorLocation()); }
(8)AddDynamic
①委托(delegate)
· 委托意义在于解耦对象之间的关联,主要是委托触发者对象和委托监听者对象;委托监听者通过将响应函数绑定在委托上,这样在委托被触发时能收到通知,进行处理
· 实现上,委托本质是一个特殊的对象,里面存有若干函数指针、参数及返回值
· 功能上,委托可以理解为是一个函数指针,但是委托更加安全
②委托分类
· UE4的委托分为:单播委托、多播委托、动态委托
· 其中相比于单播委托,多播委托特点是可以绑定多个接口,在委托被触发时可以进行多个逻辑处理的处理
· 而动态委托,可以被序列化意味着可以在蓝图里面触发、修改
(9)Root Component
①Root Component定义了Actor在游戏世界的transform信息(包括:位置、大小、朝向)
②Root Component必须为USceneComponent
的子类对象
③一个Actor里面可以有多个USceneComponent
对象,在选择哪个作为root component
无强制要求
④root component
重要在于,其他actor component
需要通过它获取在游戏世界的位置信息
(10)组件管理
· CreateDefaultSubobject
:在通过接口创建这个component
之后,所创建的component
实际上也被纳入到Actor
的ConstructedSubobjects
里面管理了
问题与解决
创建UE项目之后,没有C++代码?
UE进行工程创建的时候会选择设置是蓝图工程还是C++工程,若是选择蓝图工程则不会生成代码,如下图所示
参考资料
级联粒子系统 | 虚幻引擎文档 (unrealengine.com)
Actors | 虚幻引擎文档 (unrealengine.com)
《InsideUE4》GamePlay架构(一)Actor和Component - 知乎 (zhihu.com)