1、对于多人游戏,AI控制只受服务端的管理,客户端无需进行寻路计算,所以这些逻辑全部放到服务端
2、有时候看不到某些actor效果很可能是由于立即Destroy造成的,这是很常见的bug
3、求出物体向目标点倒下30度
4、判断一个Tarray的某个元素是否存在
不对(程序会崩):
if (TOuterItemMgrArray[0])
正确(原因:万一TOuterItemMgrArray不存在就会崩,该函数做了判断):
if (TOuterItemMgrArray.IsValidIndex(0))
5、
TSubclassOf转UObject*: 使用->GetDefaultObject()方法
TSubclassOf转UClass* : 前面加*
6、
UObject
为对象系统的基类。类层次为:
UObjectBase
UObjectBaseUtility
UObject
UClass
C++语言不像C#,Java那样提供完整的反射功能,我们需要定义一个数据结构(UClass)来描述C++中的类信息,这个数据结构也称为类的元数据。当然在UE4中UClass实例不仅仅用于描述C++(Native)类,也用来描述Blueprint生成的类。
类继承层次:
UObject
UField
UEnum
UProperty
UBoolProperty
UEnumProperty
UNumericProperty
UObjectProperty
...
UStruct
UClass
UFunction
UScriptStruct
链接:https://www.jianshu.com/p/1f2de6ea383c
7、
template <typename ElementType, typename IteratorType>
struct TDereferencingIterator
{
explicit TDereferencingIterator(IteratorType InIter)
: Iter(InIter)
{
}
FORCEINLINE ElementType& operator*() const
{
return *(ElementType*)*Iter;
}
}
这里ElementType&是返回值类型,因此返回ElementType的指针前面加个*,也即返回类本身
8、EditAnyWhere和EditDefaultsOnly的区别
如果一个UPERPROTY设为EditAnyWhere,该蓝图类拖到场景中,修改蓝图的这个变量,场景中的变量不会改变,场景中的保存以前的变量,
如果一个UPERPROTY设为EditDefaultsOnly,该蓝图类拖到场景中,修改蓝图的这个变量,场景中不能配置该变量,全部读取蓝图中的变量
9、在BeginPlay设置一些变量可能失效
解决方法:
delay 0.2秒再设置
10、TimeLine在同步时记得勾选Replicated
11、用于防止同步抖动的骚方法
//每一帧都更新rotation
if (fDeltaTimeServer>0.0f)
{
float fTimeSecondsDelay = UGameplayStatics::GetGameState(GetWorld())->GetServerWorldTimeSeconds() - UGameplayStatics::GetTimeSeconds(GetWorld());
fDeltaTimeServer = UKismetMathLibrary::FMax(fDeltaTimeServer, fTimeSecondsDelay);
}
else
{
fDeltaTimeServer = UGameplayStatics::GetGameState(GetWorld())->GetServerWorldTimeSeconds() - UGameplayStatics::GetTimeSeconds(GetWorld());
}
float fTimeSecondsAddTimeServer = UGameplayStatics::GetTimeSeconds(GetWorld()) + fDeltaTimeServer;
12、相机和物体发生碰撞bug:
解决方法:把物体对相机的碰撞改成overlap
13、SplineComponent
如果让一个物体沿着曲线轨迹运动,使用该组件,在场景中选中该组件的顶点,按alt+鼠标左键脱出下一段轨迹
在蓝图中,这样让物体沿轨迹运动
14、DestructiableComponent的Overlap事件和StaticMeshComponent的OverLap事件效果不太一样,
DestructiableComponent的Overlap事件:
人撞到物体触发
物体撞到人不触发
StaticMeshComponent的OverLap事件:
人撞到物体触发
物体撞到人触发
解决方法:在DestructiableComponent下面再放个StaticMeshComponent,触发事件全部调用StaticMeshComponent里的Overlap
15、对于可破碎物体,如果破碎后禁掉碰撞,记得一定在fracture回调函数中禁用碰撞,不能用自己函数多播禁
16、对于粒子特效,在播放时候调用SetActive函数
记得在粒子特效蓝图中取消勾选Auto Active,否则,特效一开始进入就播放
17、用vs编译热加载ue编辑器有时会出现一些bug,需要用vs启动
18、有时打开一个c++文件,显示已被删除或被remame,无法打开
解决方法:项目重新generate
19、检测墙后面的玩家不受到爆炸伤害
做法:
使用LineTraceForObejct方法,检测两点之间(人物和爆炸桶)做一条射线是否有物体ObjectType为某个枚举,比如墙的ObjectType设为WorldStatic,就会返回true,输出的hit保存射线碰撞点的相关信息,可以获取到碰撞到碰撞到的actor,component等相关信息。
Obeject Type在这里设:
20、将碰撞通道转换为ObjectType:
TArray <TEnumAsByte<EObjectTypeQuery>> WorldStaticObjectTypes;
WorldStaticObjectTypes.Add(UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_WorldStatic));
也即这句话
UEngineTypes::ConvertToObjectType(ECollisionChannel::ECC_WorldStatic
21、测试时绘制的线一般是在服务端绘制
22、使用TimeLine时候
输出的新建轨迹0,在c++中是float DeltaTime,输出的DeltaTime是曲线轨迹的值
而这个值根据Tick()函数传入的float DeltaTime来计算,在编辑器中控制台通过t.MaxFPS 来控制想要的FPS,如果FPS很低,那么TimeLine采样频率就少,如果特别低,那么就会只采曲线的初值和终值,如果FPS很高,那么会取曲线中的许多点,输出的
曲线轨迹的值也就更平滑。
23、子父类component的碰撞可能发生矛盾,建议不要一个block,一个overlap,否则有时会屏蔽一个
可以两个都设为一样的,不同的在代码里面设
//让可破碎component对地面产生碰撞,有破碎效果
DestructibleComponent->SetCollisionResponseToChannel(ECC_WorldStatic, ECollisionResponse::ECR_Block);
DestructibleComponent->SetCollisionResponseToChannel(ECC_WorldDynamic, ECollisionResponse::ECR_Block);
24、通过replicated movement可以让客户端物体复制服务端的位置,但是通过childActor方式的actor,不能通过replicated movement可以让客户端物体复制服务端的位置,只能多播,服务端和客户端同时set Location
25、IsValidLowLevel():
指针不为空,但内容出现NULL或者异常。(此情况一般是,对这个对象的多处指针引用,但是在A处销毁了,之后在B处继续调用)。牵扯到内存管理,如果此对象的类是属于UE4的,那可以 yourObject!=nullptr&&_yourObject->IsValidLowLevel()。
参见UE4UObjectBase这个底层基类
bool UObjectBase::IsValidLowLevel() const;
bool UObjectBase::IsValidLowLevelFast(bool bRecursive /*= true*/) const
26、在Subclass类中有一个函数
/** Implicit conversion to UClass */
FORCEINLINE operator UClass* () const
{
return **this;
}
这里*this表示当前类本身,又加了一个*表示调用了该类里面的重载*操作符的函数,该函数是这样定义的:
/** Dereference back into a UClass, does runtime type checking */
FORCEINLINE UClass* operator*() const
{
if (!Class || !Class->IsChildOf(TClass::StaticClass()))
{
return nullptr;
}
return Class;
}
这里第二个函数Operator作用是重载运算符,第一个函数Operator作用是隐式转换,自动将Subclass类转换成UClass类。
27、beginplay里面判断ROLE_AutonomousProxy,是有可能会出现错误,
应重写actor的PostNetReceive虚函数,把判断ROLE_AutonomousProxy的逻辑放到该函数里面
28、在beginPlay写逻辑,一定要延迟一段时间,这样客户端才能保证同步,否则有些逻辑在服务端执行了,没时间通过rpc传到客户端
29、
http://www.cppblog.com/Herbert/archive/2009/01/08/70479.html
void AGOASmelterLava::TakeDamageToPlayerEverySecond()
{
for (int i = 0; i < AllOverLapPlayer.Num();)
{
//UKismetSystemLibrary::PrintString(this, TEXT("受伤"));
if (IsValid(AllOverLapPlayer[i]))
{
FHitResult hitResult;
IGOATakeDamageInterface::Execute_TakeDamage(AllOverLapPlayer[i], EGOADamageType::OrganMelee, DamageDataEverySecond, hitResult, CurrentInstigateActor, this, CurrentInstigatePlayerController);
if (AllOverLapPlayer[i]->IsDead())
{
AllOverLapPlayer.Remove(Cast<AGladiator>(AllOverLapPlayer[i]));
continue;
}
}
i++;
}
}