第一次引擎编程尝试

ronald1年前职场5570

软硬件要求

第一次引擎编程尝试

给打出的子弹增加爆炸效果

    默认创建的FPS游戏子弹击中物体无特殊效果,仅子弹消失,现在我们希望子弹击中物体之后有爆炸的效果,如下所示:

image-20220214114143381.png

粒子系统

    粒子系统是计算机图形引擎中模拟一些特定模糊现象的技术,如火、爆炸、烟雾、水流等效果;

在项目工程中,美术在引擎里面制作粒子特效,保存下来就是uassets格式文件,程序直接load即可


目录结构

    创建项目之后,初始状态下目录结构如下:

image-20220214154638624.png

其中:

(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的代码经常会看到UFUNCTIONUCLASS等宏,这些宏的目的是告诉UnrealHeaderToole(UHT)当遇到下面的代码该如何处理,通过UHT,在编译前对代码文件进行处理生成.generated.h和.generated.cpp`文件

    ②GENERATED_BODY

    · 当遇到GENERATED_BODY时,表示这个类是继承自UE4引擎定义的类,需要帮助生成包括构造函数在内的相关函数

    · GENERATED_UCLASS_BODY

    ③UPROPERTY,属性说明符,用于对变量属性进行描述,指明属性与引擎和编辑器的相处方式,如本例:

    · VisibleDefaultsOnly:说明此属性只在原型的属性窗口可见,但是不可编辑

    · VisibleAnywhere:说明此属性在所有属性窗口可见,但无法被编辑

    · EditDefaultsOnly:说明此属性可通过属性窗口编辑,但只能在原型上进行

    · Category:指定在蓝图编辑工具中显示的属性的类别

    · BlueprintReadOnly:说明此属性可以由蓝图读取,但不能被修改

    · metaAllowPrivateAccess

    ⑤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实际上也被纳入到ActorConstructedSubobjects里面管理了



问题与解决

  • 创建UE项目之后,没有C++代码?

    UE进行工程创建的时候会选择设置是蓝图工程还是C++工程,若是选择蓝图工程则不会生成代码,如下图所示

image-20220214110434402.png

image-20220214110641606.png

参考资料

    级联粒子系统 | 虚幻引擎文档 (unrealengine.com)

    Actors | 虚幻引擎文档 (unrealengine.com)

    《InsideUE4》GamePlay架构(一)Actor和Component - 知乎 (zhihu.com)

    虚幻引擎编译系统总结 - 知乎 (zhihu.com)


相关文章

Lua的Upvalue和闭包(二)

Lua的Upvalue和闭包(二)

Lua闭包和Upvalue的实现    前面文章介绍了Lua闭包和upvalue的概念,本文简单过一下Lua对于闭包和upvalue的实现以加深理解。Lua闭包结构    Lua在内存的结构如下所示:#define ClosureHeader \ CommonHeader; lu_by...

第四次引擎编程尝试

第四次引擎编程尝试

本节我们将尝试在游戏中创建AI,并针根据游戏内的事件做出对应的反应。创建自己的AI类· 按照介绍的,先创建一个我们的AI类,因为AI可以移动、碰撞等,所以我们选择其继承自Character· 创建好AI类之后,我们创建其蓝图类· 创建好蓝图我们进入AI的编辑界面从界面中,我们可以看到:    我们的FPSTpl2AIGuard继承自Character,拥有A...

第三次引擎编程尝试

第三次引擎编程尝试

    本文用来介绍如何实现同步游戏状态给玩家,这里用到了前面介绍的HUD。基本概念HUD    游戏过程中,游戏系统和玩家交互是非常有必要的,UE4提供了HUD来实现将游戏系统状态同步给玩家的方式,也就是说HUD对于玩家来说是只读的;UI    不同于HUD是将游戏系统状态同步...

第五次引擎编程尝试

第五次引擎编程尝试

在前面的章节中,我们已经尝试了制作特效、为游戏添加AI并设置AI的UI,到现在这一步我们希望游戏可以多人联网,我们一步步来。开启多人游戏通过这个我们可以看到角色移动在CS之间同步,但是真正运行的时候发现,距离真正的多人联网游戏还很遥远:1)射击的时候CS端的弹道不同步2)NPC的状态CS端不同步3)游戏状态CS端不同步... ...针对上述问题,我们逐步来看:让发射物在CS之间同步原先的实现流程如...

第二次引擎编程尝试

第二次引擎编程尝试

在这一节,我们尝试在游戏中创建一个物品并可以被角色拾取。在游戏世界里面创建一个物品创建物品对象    在创建对象的时候,需要我们选择对象的父类,选项卡中的父类对象他们各自有自己的适用范围,这里挑几个常用的进行说明:None    顾名思义,None表示此C++对象为纯自己定义的类,所有逻辑和与引擎的交互由开发人员自己实...

Lua的垃圾回收(上)

Lua 5.3版本的垃圾回收垃圾回收算法    垃圾回收算法一般分为两类:引用计数法和标记扫描法。引用计数法    所谓引用计数法,是指在为对象申请内存的时候,在分配的内存块预留一块区域用于存放这块内存被引用的次数,当被引用的时候增一,解引用的时候减一,当这块内存的引用次数降到0的时候,这块内存被认为不可访问,直接被回...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。