一、对蓝图类中复制和复制引用的区别:
如果是复制引用,那么会自动复制该类的路径,如果是点击复制,那么重新生成一个新的类,切记如果类A在蓝图中复制到另一个文件夹中名字还叫A,实际他俩不再是同一个类了,切记如果复制后到另一个文件夹取名不要一样。
二、c++中\的用法
这玩意儿有个正式的名称叫做续行符,在普通代码行后面加不加都一样(VC是自动判断续行的),但是在宏定义里面就特别有用,因为宏定义规定必须用一行完成:
#define SomeFun(x, a, b) if(x)x=a+b;else x=a-b;
这一行定义是没有问题的,但是这样代码很不容易被理解,以后维护起来麻烦,如果写成:
#define SomeFun(x, a, b)
if (x)
x = a + b;
else
x = a - b;
这样理解是好理解了,但是编译器会出错,因为它会认为#define SomeFun(x, a, b)是完整的一行,if (x)以及后面的语句与#define SomeFun(x, a, b)没有关系.这时候我们就必须使用这样的写法:
#define SomeFun(x, a, b)\
if (x)\
x = a + b;\
else\
x = a - b;
注意:最后一行不要加续行符啊.VC的预处理器在编译之前会自动将\与换行回车去掉,这样一来既不影响阅读,又不影响逻辑,皆大欢喜
3、
dynamic_cast
该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,使用条件是只能用于含有虚函数的类。转换引用失败会抛出bad_cast异常,转换指针失败会返回null。
Base* base=new Derived();
Derived* p=dynamic_cast<Derived>(base);
if(p){...}else{...}
4、蓝图中如何和c++交互
(1)、C++自定义方法,蓝图调用
UFUNCTION(BlueprintCallable, Category = "MyFunc")
void dosth();
(2)、蓝图定义方法,C++调用
UFUNCTION(BlueprintImplementableEvent, Category="MyFunc")
void dosth();
当我们在蓝图中定义了一系列操作后,我们也想用C++调用,我们利用BlueprintImplementableEvent
这个参数,让在C++定义的这个函数在蓝图中实现成为一个event。另外:我们只要在头文件声明这个方法即可,不用实现函数体,不然会报错。
我们在蓝图中,通过中这个event打印一句话。 (备注:在写BlueprintImplementableEvent时,虚幻引擎在后台生成一个基础 C++ 函数实现;它理解如何调入蓝图 VM。这通常被称作 Thunk(形实转换程序)。如讨论中的蓝图不为此方法提供函数主体,函数的行为则与不含主体行为的 C++ 函数一样:不执行任何操作。)
在某些地方调用即可,这里我在Actor在BeginPlay()方法调用了dosth函数。
void AMyActor::BeginPlay()
{
Super::BeginPlay();
dosth();
}
(3)、C++有默认方法,蓝图可以覆盖
UFUNCTION(BlueprintNativeEvent, Category = "MyFunc")
void CountdownHasFinished();
然后我们要实现一个方法。
void AMyActor::CountdownHasFinished_Implementation()
{
//do something
}
这样的话,如何蓝图没有实现CountdownHasFinished
方法,那么我们就调用CountdownHasFinished_Implementation
这个方法,如果蓝图实现了CountdownHasFinished
,那么CountdownHasFinished_Implementation
就不会调用。
如果想先执行c++里面的AMyActor::CountdownHasFinished_Implementation()默认方法中的内容,再在蓝图的该方法中添加新的内容,那么在蓝图该事件中先调用父类函数,再继续写逻辑即可,如下:
5、有一次编译发现报错,有一个UStruct:Exposes this struct as a type that can be used for variables in Blueprints.
解决方法,找到该UStruct,在Ustruct的括号里面填BlueprintType,即:
UStruct(BlueprintType)
{
}
同理,需要定义Enum时,如果蓝图中也要调用,也需要加上
6、UObject和垃圾回收
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
public:
UPROPERTY()
MyGCType* SafeObject;
MyGCType* DoomedObject;
AMyActor(const FObjectInitializer& ObjectInitializer)
:Super(ObjectInitializer)
{
SafeObject = NewObject<MyGCType>();
DoomedObject = NewObject<MyGCType>();
}
};
void SpawnMyActor(UWorld* World, FVector Location, FRotator Rotation)
{
World->SpawnActor<AMyActor>(Location, Rotation);
}
Actor 的构建函数创建两个对象。一个指定到 UPROPERTY,另一个指定到裸指针。Actors 自动成为根集的一部分,SafeObject 将不会被垃圾回收,因为它从根集对象出到达。然而 DoomedObject 的进展不是十分顺利。我们未将其标为 UPROPERTY,因此回收器并不知道其正在被引用,而会将它逐渐销毁。
UObject 被垃圾回收时,对其的所有 UPROPERTY 引用将被设为 nullptr。这可使您安全地检查一个对象是否已被垃圾回收。
if (MyActor->SafeObject != nullptr)
{
// 使用 SafeObject
}
这十分重要,正如之前所述,已在自身上调用 Destroy() 的 actor 在垃圾回收器再次运行之前不会被移除。您可检查 IsPendingKill() 方法,确定 UObject 正等待被删除。如方法返回 true,则应将对象视为废弃物,不进行使用。
7、在添加相机的时候一般还要添加一个弹簧臂:
SpringArmComponent 。 弹簧臂是一种添加相机(或其它 组件 )的既好又方便的方法,它们在总体上不完全是刚体,在移动时感觉更有弹性。
9、向前声明
在.cpp中创建一个组件并且include写在.cpp(没有写在.h中)那么在.h中声明这个组件时编译器不知道这是什么东西,在class上面给编译器提醒下,告诉它这是个类即可编译成功
10、在调用函数传参时有个参数const UObject* WorldContextObject让引擎知道在哪个世界场景中注册这个事件,一般这个参数填this,表示在游戏世界(而不是编辑器世界等)来注册这个事 件
11、对于一个actor处理overlap事件逻辑有两种解决方法:
(1)调用OnComponentBeginOverlap方法,绑定函数,比如这样
(2)重写函数,在Actor.h中搜索overlap,找到
virtual void NotifyActorBeginOverlap(AActor* OtherActor)函数复制到自己文件中,重写即可
13、制作一个黑洞可以吸引所有物体的actor。
创建两个Sphere,外层球特别大,内层球就是看到的黑洞大小,禁用碰撞,当内层球和其他物体触发overlap事件时,销毁该actor
核心代码
在黑洞actor的tick里面,拿到里面的SphereComponent中overlap的所有component(这里用的是UPrimitiveComponent,该组件可以拥有变换和被物理模拟属性),遍历它们并给每个component施加一个力,沿着黑洞的半径
14、在写该函数时报错function parameter:“Instigator” can not be defined in OnNoiseHeard as it is already ....
原因是这个类继承类actor,在ue4点源码actor中已经声明了一个变量Instigator,那么在这个函数中写的Instigator是指参数的还是actor的变量,编译器就不知道了。
15、如何让一个类的属性多一个参数,如下,让该widget多传入一个参数
在该widget中创建一个变量,勾选Expose on Spawn
那么刷新下该节点
就多出来了
16、这里在UProperty中meta的作用:
在编辑器中如果勾选了bPatrol,那么下面两个变量也会可编辑
如果不勾选,下面的不可编辑
17、在actor中加入一个UDestructible Component组件的时候 #include "DestructibleComponent.h"
然而报错,无法识别这个include
发现这个组件的模块是ApexDestruction,那么在项目的Build.cs中加入这个模块即可编译成功
18、项目从perforce更新后要重新编译一次
19、多播每个客户端没有反应先想清楚该actor有没有Replicated
20、cast的使用,如果强转失败就返回nullptr
if (Cast<AGladiator>(OtherActor))
21、这样的层级
在c++中
BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComponent"));
RootComponent=BoxComponent;
DestructibleComponent = CreateDefaultSubobject<UDestructibleComponent>(TEXT("DestructibleComponent"));
DestructibleComponent->AttachTo(RootComponent);
ArrowComponent = CreateDefaultSubobject<UArrowComponent>(TEXT("ArrowComponent"));
ArrowComponent->AttachTo(RootComponent);
需要好好研究的博客
https://www.cnblogs.com/ghl_carmack/p/5701862.html
https://blog.csdn.net/u012999985/article/details/78244492
https://blog.csdn.net/cartzhang/article/details/72834164
https://blog.csdn.net/u011047958/article/details/78738674