Unreal Engine 4 杂项笔记

@vrqq  February 4, 2017

1、SweepMultiByChannel碰撞范围

void AActOutside::BeginPlay()
{
   Super::BeginPlay();
    TArray<FHitResult> HitResults;
   FVector StartLocation(0.0f,-100.0f,80.0f);
   FVector EndLocation(0.0f,0.0f,80.0f);
   FCollisionShape CS;
   CS.ShapeType = ECollisionShape::Sphere;
   CS.SetSphere(50);
   bool hitresult = GetWorld()->SweepMultiByChannel(HitResults, StartLocation, EndLocation, FQuat::FQuat(), ECollisionChannel::ECC_WorldStatic, CS);
   if (hitresult) {
      for (auto it = HitResults.CreateIterator(); it; it++)
         GLog->Log("[Sphere] Hit: "+(*it).Actor->GetName());
   }else {
      GLog->Log("[SPhere] Not Hit");
   }
   FVector CenterS = (EndLocation - StartLocation)/2 + StartLocation;
    DrawDebugSphere(GetWorld(), StartLocation, CS.GetSphereRadius(), 50, FColor::Red, true);
    DrawDebugSphere(GetWorld(), CenterS, CS.GetSphereRadius(), 50, FColor::Green, true);
    DrawDebugSphere(GetWorld(), EndLocation, CS.GetSphereRadius(), 50, FColor::Blue, true);
}

6632010047165655425.jpeg
实际运行图,可以看到SweepMultiByChannel函数撞了一个球和一个方块。
碰撞范围:以StartLocation(图中红球)为起点向EndLocation(图中蓝球)结束,所经过的一个范围,这个范围再刨除StartLocation所在的球(红)。

  • 换一种说法:碰撞范围就是从起点抛球到终点,图中红->绿->蓝,然后扫过的体积,减去红球(起点球)的体积。这其中经过的所有的东西都算hit。
  • 如果图中不透明球和红球重合,不算hit。
  • 实践发现,点碰点不算hit(球和球刚好相撞),点碰面算hit(球刚好撞方块的一个面)。
    图中碰撞轨迹中心高80,碰撞球半径50,所以离地了并没有和floor相撞。

2. 推一个东西跑

地图上面看到的那些个球啊,方块啊,其实都叫component,他们属于某个Actor,但是Actor是无形的。
比如我从左边Basic->Sphere拽出来个默认Actor,也不过是,一个Actor里面只有一个SphereComponent,然后那个SphereComponent显形。
使用如下方法推一个Component运动:

UStaticMeshComponent* smc = Cast<UStaticMeshComponent>(this->GetRootComponent());
if (smc) {
    GLog->Log("[Force]Ready to force. mass="+FString::SanitizeFloat(smc->GetMass()));
    FVector force(0, 3000, 0);
    smc->AddForce(force * smc->GetMass());
    smc->AddImpulse(force * smc->GetMass());
}

如何处理一个爆炸场景呢,见此:

smc->AddRadialImpulse(FVector(-400.0f,300.0f, 30.0f), 200, 1000, ERadialImpulseFalloff::RIF_Linear, true);

3.触发事件 Fire Event

Fire真是万能的,啥啥啥都叫fire。
按照 http://orfeasel.com/generating-hit-events/ 写了个Hit事件,让球自由落体,发现怎么也触发不了OnHit.
发现运行偶尔log会有红字的提示

LogOutputDevice:Error: === Handled ensure: ===
LogOutputDevice:Error: Ensure condition failed: this->IsBound() [File:Runtime/Core/Public/Delegates/DelegateSignatureImpl.inl] [Line: 1043]
LogOutputDevice:Error: Unable to bind delegate to 'onHitEvt' (function might not be marked as a UFUNCTION)
LogOutputDevice:Error: Stack: 
LogOutputDevice:Error: FDebug::OptionallyLogFormattedEnsureMessageReturningFalse(bool, char const*, char const*, int, wchar_t const*, ...) Address = 0x790cd1f  (filename not found) [in UE4Editor-Core.dylib]
LogOutputDevice:Error: void TBaseDynamicDelegate<FWeakObjectPtr, void, UPrimitiveComponent*, AActor*, UPrimitiveComponent*, FVector, FHitResult const&>::__Internal_BindDynamic<AEvtActor>(AEvtActor*, TBaseDynamicDelegate<FWeakObjectPtr, void, UPrimitiveComponent*, AActor*, UPrimitiveComponent*, FVector, FHitResult const&>::TMethodPtrResolver<AEvtActor>::FMeth
odPtr, FName) Address = 0x92062969 (filename not found) [in UE4Editor-MyProject-3963.dylib]
LogOutputDevice:Error: void TBaseDynamicMulticastDelegate<FWeakObjectPtr, void, UPrimitiveComponent*, AActor*, UPrimitiveComponent*, FVector, FHitResult const&>::__Internal_AddDynamic<AEvtActor>(AEvtActor*, TBaseDynamicDelegate<FWeakObjectPtr, void, UPrimitiveComponent*, AActor*, UPrimitiveComponent*, FVector, FHitResult const&>::TMethodPtrResolver<AEvtActor
>::FMethodPtr, FName) Address = 0x920626f1 (filename not found) [in UE4Editor-MyProject-3963.dylib]
LogOutputDevice:Error: AEvtActor::BeginPlay() Address = 0x920624c5 (filename not found) [in UE4Editor-MyProject-3963.dylib]
LogOutputDevice:Error: AWorldSettings::NotifyBeginPlay() Address = 0xa438980  (filename not found) [in UE4Editor-Engine.dylib]
LogOutputDevice:Error: AGameStateBase::HandleBeginPlay() Address = 0x94867e1  (filename not found) [in UE4Editor-Engine.dylib]
LogOutputDevice:Error: UWorld::BeginPlay() Address = 0xa4178ee  (filename not found) [in UE4Editor-Engine.dylib]
LogOutputDevice:Error: UGameInstance::StartPIEGameInstance(ULocalPlayer*, bool, bool, bool) Address = 0x943cda3  (filename not found) [in UE4Editor-Engine.dylib]
LogOutputDevice:Error: UEditorEngine::CreatePIEGameInstance(int, bool, bool, bool, bool, float) Address = 0xf8f9d76  (filename not found) [in UE4Editor-UnrealEd.dylib]
LogOutputDevice:Error: UEditorEngine::PlayInEditor(UWorld*, bool) Address = 0xf8ef305  (filename not found) [in UE4Editor-UnrealEd.dylib]
LogOutputDevice:Error: UEditorEngine::StartQueuedPlayMapRequest() Address = 0xf8d71ee  (filename not found) [in UE4Editor-UnrealEd.dylib]
LogOutputDevice:Error: UEditorEngine::Tick(float, bool) Address = 0xf104163  (filename not found) [in UE4Editor-UnrealEd.dylib]
LogOutputDevice:Error: UUnrealEdEngine::Tick(float, bool) Address = 0xfd433fc  (filename not found) [in UE4Editor-UnrealEd.dylib]
LogOutputDevice:Error: FEngineLoop::Tick() Address = 0x7611c79  (filename not found) [in UE4Editor]
LogOutputDevice:Error: GuardedMain(wchar_t const*) Address = 0x7618b72  (filename not found) [in UE4Editor]
LogOutputDevice:Error: -[UE4AppDelegate runGameThread:] Address = 0x762616c  (filename not found) [in UE4Editor]
LogOutputDevice:Error: -[FCocoaGameThread main] Address = 0x783f526  (filename not found) [in UE4Editor-Core.dylib]
LogOutputDevice:Error: __NSThread__start__() Address = 0xd3620c6d (filename not found) [in Foundation]
LogOutputDevice:Error: _pthread_body() Address = 0xe73f1aab (filename not found) [in libsystem_pthread.dylib]
LogOutputDevice:Error: _pthread_body() Address = 0xe73f19f7 (filename not found) [in libsystem_pthread.dylib]
LogOutputDevice:Error: thread_start() Address = 0xe73f11fd (filename not found) [in libsystem_pthread.dylib]

发现我的函数定义里面没写UFUNCTION(),以为没啥用。。结果真有用。。
网上找了好多还有报bug的我还怕赶上了bug,最后发现自己偷懒不打全了。。
顺便找到了一个 动态启用禁用hit event 的贴 https://answers.unrealengine.com/questions/127168/simulation-generate-hit-events-boolean-in-c.html

4.AI自动寻找路径

根据这篇教程:http://orfeasel.com/creating-a-basic-patrol-ai/
教程里面少了一步就是往地图上面放BotTargetPoint,整体看过来有点混乱,做完还crash了几次,不过还好最后还算捋顺了。。寻找路径需要用到Blueprint_BehaviorTree,其实这个建议用Blueprint,简明易懂一目了然,其中具体某一步的实现方法,可以用c++来写。整个程序的目标:Character随机走到某个TargetPoint上面。

  • 第一步先放一个略显强大的Character Class,说白了就是个放在地图上的小物件,表示,这个小物件就是我们要控制的东西。
  • 第二步为了控制,创建一个AIController,然后把Character拽到地图上,在属性里指定让这个新建的Controller控制就好了。在init里面创建了BehaviorTreeComponent和BlackboardComponent,为了衔接创建的两个BluePrint。在function Possess()里面,程序首先引入了两个Blueprint的指针,然后UGameplayStatics::GetAllActorsOfClass读在地图上放的所有TargetPoint 然后存进 BotTargetPoints数组 里,然后开始按照BP_BehaviorTree执行AI策略。
  • 第三步制作BehaviorTree,打开行为树,教程里面的行为树是依次执行找点->走过去->停留。其中用c++完成了一个class叫BotTargetPointSelection 就是找到下一个目的TargetPoint(需要手工拖进地图上的),并且和当前这个不一样,就是不能从点A走到点A(原地不动)。
  • 然后run就可以了。

解释一下BehaviorTree,就是AI的策略,是走过去,还是跳一下,还是停留,还是抬胳膊,或者执行自定义函数(自己写个class继承UBTTaskNode就可以,blueprint系统可以自动识别)。。
然后Blackboard就是黑板,就是记录一些数据的,行为树只能同时link一个黑板。黑板的作用举例:AI按照行为树走到了一个点,发现地下写了个数字9,然后此时用c++写:Blackboard->SetValueAsInt("地下的数字", 9); ,然后走到下一个点,有人问他,刚才地下写的多少啊,然后使用c++读:Blackboard->GetValueAsInt("地下的数字"); ,然后就返回了9。

另附此章基础:http://www.cnblogs.com/fjz13/p/6061655.html


添加新评论