前言
这是近段时间开发遇到的一些比较典型的问题
实例
1. 在UE5里需要Spawn一个对象,怎么能让其Spawn的位置一定合理
例如:上坐骑的问题,周边地形生成actor的策略,很可能生成出来的位置是一个非法的位置
解决方案:引擎World.h中提供了一个FindTeleportSpot方法
2. 使用GAS时如果出现了LogAbilitySystem: Warning: Can't activate LocalOnly or LocalPredicted ability %s when not local!这样的警告消息,意味着ASC没有在Client端完成OwnerActor和AvatarActor的初始设定。
可以在PossessdBy中进行初始化
void AnMountBase::PossessedBy(AController* NewController)
{
Super::PossessedBy(NewController);
if(AbilitySystemComponent)
{
AbilitySystemComponent->InitAbilityActorInfo(this, this);
}
SetOwner(NewController);
InitializeAttributes();
AddStartupEffects();
AddMountAbilities();
}
参考:https://stonelzp.github.io/ue4-gameplay-ability-system/
3. GAS的一些痛点,统一计算规则
详细见GEEC,MMC的封装和使用文章
4. UE在Possess的时候,产生瞬移的情况
答:是因为在服务器上创建的Character还未复制到客户端,但是Possess中RPC又已经提前到客户端了。虽然最终也能possess正确,其实是因为引擎在Tick中来确保客户端执行正确。
例如,网络游戏中,服务器会创建初始玩家角色,同时也会在服务器上进行Possess操作
5. UE5的反射应用
怎么通过UE5的反射动态添加字段或查询字段复制
- 遍历一个类的所有属性
for (TFieldIterator<FProperty> It(UCaAttributeSetBase::StaticClass()); It; ++It)
{
FGameplayAttribute GameplayAttribute = FGameplayAttribute(*It);
if(GameplayAttribute.IsValid())
{ RelevantAttributesToCapture.Add(FGameplayEffectAttributeCaptureDefinition(GameplayAttribute,EGameplayEffectAttributeCaptureSource::Source, true));
RelevantAttributesToCapture.Add(FGameplayEffectAttributeCaptureDefinition(GameplayAttribute,EGameplayEffectAttributeCaptureSource::Target, true));
}
UE_LOG(LogTemp,Log,TEXT("AttrbuteName:%s"), *It->GetName());
}
-
遍历一个类的所有方法
可以参考UObject中的 CallFunctionByNameWithArguments -
遍历一个方法的所有参数
可以参考UObject中的 CallFunctionByNameWithArguments -
怎么通过UE的反射动态添加字段或查询字段复制
如何动态获取CPP的属性,并设置其值呢(反射)
6. UE5的元数据
- 无侵入代码的修改部分枚举元数据
#if WITH_EDITOR
UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EGameplayEffectAttributeCaptureSource"), true);
if(EnumPtr)
{
EnumPtr->SetMetaData(TEXT("DisplayName"), TEXT("攻击者"), 0);
EnumPtr->SetMetaData(TEXT("DisplayName"), TEXT("受击者"), 1);
}
#endif
参考:https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/GameplayArchitecture/Metadata/
7. UE5的加载(重点)
LoadObject
LoadClass
StaticLoadObject
StaticLoadClass
AssetManager
LoadAsset_Blocking
LoadClassAsset_Blocking
AsyncLoadAsset
AsyncLoadClassAsset
8. UE5中的WorldContext(上下文)
为什么蓝图中创建的BlueprintFunctionLibrary和Cpp创建的有区别,蓝图的多了WorldContext参数。
因此导致,在不正确的WorldContext下,没法调用蓝图创建的BlueprintFunctionLibrary,而Cpp的没问题
9.蓝图的蜜糖陷阱
这两种写法会导致完全不一样的结果
小心Blueprintpure函数, 其实每次都会去产生新的值,导致之前的修改值无效化
而如果是非blueprint,对返回值可以做修改。
区别在于,非BlueprintPure,只会在流程中依次执行,且只是执行一次,返回值会作为一个临时变量。
而BlueprintPure可能会执行多次,每次都是一个新的值,之前做的修改会丢失,除非将其存在一个临时变量中,对临时变量做修改。
10. GAS中GA怎么触发press相关的task,即怎么把GameplayAbility和InputId bind起来?
11. UObject如果集成Tick?
- 方案一: 继承FTickableGameObject
UCLASS(Blueprintable,BlueprintType)
class TICKOBJECT_API UTickTestObject : public UObject, public FTickableGameObject
{
GENERATED_BODY()
public:
virtual void Tick(float DeltaTime) override;
virtual TStatId GetStatId() const override;
};
#include "TickTestObject.h"
void UTickTestObject::Tick(float DeltaTime)
{
UE_LOG(LogTemp, Log, TEXT("UTickTestObject: Do"));
}
TStatId UTickTestObject::GetStatId() const
{
return Super::GetStatID();
}
- 方案二: AddTicker
UCLASS(Blueprintable, BlueprintType)
class TICKOBJECT_API UTickTestObject2 : public UObject
{
GENERATED_BODY()
virtual void BeginDestroy() override;
public:
UFUNCTION(BlueprintCallable)
void StartTick();
UFUNCTION(BlueprintCallable)
void EndTick();
UFUNCTION(BlueprintNativeEvent)
bool Tick(float DeltaTime);
private:
FTSTicker::FDelegateHandle TickHandler;
};
#include "TickTestObject2.h"
void UTickTestObject2::StartTick()
{
TickHandler = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &UTickTestObject2::Tick));
}
void UTickTestObject2::EndTick()
{
if(TickHandler.IsValid())
{
FTSTicker::GetCoreTicker().RemoveTicker(TickHandler);
}
}
bool UTickTestObject2::Tick_Implementation(float DeltaTime)
{
UE_LOG(LogTemp, Log, TEXT("UTickTestObject2, Tick"));
return true;
}
void UTickTestObject2::BeginDestroy()
{
UObject::BeginDestroy();
EndTick();
}
动画位移,如何把人物的碰撞也跟随动画一起移动?
- 方案一:根运动
若需要修改位移距离:- 美术修改动画资源重新导入
- 使用AdditiveLayerTracks对骨骼进行简单调整
- 方案二:非根运动
如需要产生位移距离:- 程序设置移动Actor