1. 游戏开始的按3次键才能把门一点点打开,实现是Sequence或者Matinee完整地把门打开的全部过程Key好了,只不过增加了几个Event点,Event响应就是暂停Sequence或者Matinee的播放,然后按键后继续播放,再触发事件再次暂停播放。
2.通过Delay的办法解决动画瞬移的问题。角色播放了一个蒙太奇动画并且同时进行显示操作,如果两个操作之间不加入delay操作的话,那么由于角色出生位置与蒙太奇初始帧的位置不同,可以明显看到有位置瞬移的问题,如果操作之间加入Delay的话,执行的流程就是播放动画N秒后再开始显示,那么角色的位置就是当前动画的那个位置,可以解决瞬移问题。
3. FHitResult.Normal 碰撞点的法线方向,也可以理解成碰撞点相对于面的垂直方向,例如奔跑碰到了方块,那么Normal的方向就是碰撞点指向你的方向。FHitResult.ImpactPoint就是碰撞点的世界坐标。
4.slomo操作可以起到整个游戏加快或者减慢的效果,比如游戏中动作慢镜头效果。参数为1表示正常,小于1表示减慢,大于1表示加快。如果是想在sequence中进行设置的话,有个“播放速率轨迹”可以设置。如果想在蓝图中动态设置的话,可以下面这样:
如果在C++代码中设置的话,可以如下调用:
/**
* Sets the global time dilation.
* @param TimeDilation value to set the global time dilation to
*/
UFUNCTION(BlueprintCallable, Category="Utilities|Time", meta=(WorldContext="WorldContextObject") )
static void SetGlobalTimeDilation(const UObject* WorldContextObject, float TimeDilation);
如果是调试阶段,可以命令行输入“slomo 0.1”即可。
5. 某些库函数声明中会出现meta=(WorldContext="WorldContextObject")和名字为WorldContext的参数,比如:
/** Returns the player controller at the specified player index */
UFUNCTION(BlueprintPure, Category="Game", meta=(WorldContext="WorldContextObject", UnsafeDuringActorConstruction="true"))
static class APlayerController* GetPlayerController(const UObject* WorldContextObject, int32 PlayerIndex);
在蓝图中调用该函数的时候并不用传入WorldCotextObject的参数,系统会自动帮你传入,这对于调用方和实现方都是很方便的。通过WorldCotextObject可以拿到World的指针,然后就可以处理你的逻辑了。
6. ActionGame过场动画播放之后可以看到镜头是从下往上升过去的,蓝图和c++源码中并没有明显的设置啊,这是如何执行的呢?通过调试发现,原因在APlayerCameraManager::UpdateViewTarget中找到了。
void APlayerCameraManager::UpdateViewTarget(FTViewTarget& OutVT, float DeltaTime)
{
// Don't update outgoing viewtarget during an interpolation
if ((PendingViewTarget.Target != NULL) && BlendParams.bLockOutgoing && OutVT.Equal(ViewTarget))
{
return;
}
// store previous POV, in case we need it later
FMinimalViewInfo OrigPOV = OutVT.POV;
//@TODO: CAMERA: Should probably reset the view target POV fully here
OutVT.POV.FOV = DefaultFOV;
OutVT.POV.OrthoWidth = DefaultOrthoWidth;
OutVT.POV.AspectRatio = DefaultAspectRatio;
OutVT.POV.bConstrainAspectRatio = bDefaultConstrainAspectRatio;
OutVT.POV.bUseFieldOfViewForLOD = true;
OutVT.POV.ProjectionMode = bIsOrthographic ? ECameraProjectionMode::Orthographic : ECameraProjectionMode::Perspective;
OutVT.POV.PostProcessSettings.SetBaseValues();
OutVT.POV.PostProcessBlendWeight = 1.0f;
bool bDoNotApplyModifiers = false;
if (ACameraActor* CamActor = Cast(OutVT.Target))
{
// Viewing through a camera actor.
CamActor->GetCameraComponent()->GetCameraView(DeltaTime, OutVT.POV);
}
else
{
static const FName NAME_Fixed = FName(TEXT("Fixed"));
static const FName NAME_ThirdPerson = FName(TEXT("ThirdPerson"));
static const FName NAME_FreeCam = FName(TEXT("FreeCam"));
static const FName NAME_FreeCam_Default = FName(TEXT("FreeCam_Default"));
static const FName NAME_FirstPerson = FName(TEXT("FirstPerson"));
if (CameraStyle == NAME_Fixed)
{
// do not update, keep previous camera position by restoring
// saved POV, in case CalcCamera changes it but still returns false
OutVT.POV = OrigPOV;
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}
else if (CameraStyle == NAME_ThirdPerson || CameraStyle == NAME_FreeCam || CameraStyle == NAME_FreeCam_Default)
{
// Simple third person view implementation
FVector Loc = OutVT.Target->GetActorLocation();
FRotator Rotator = OutVT.Target->GetActorRotation();
if (OutVT.Target == PCOwner)
{
Loc = PCOwner->GetFocalLocation();
}
// Take into account Mesh Translation so it takes into account the PostProcessing we do there.
// @fixme, can crash in certain BP cases where default mesh is null
// APawn* TPawn = Cast(OutVT.Target);
// if ((TPawn != NULL) && (TPawn->Mesh != NULL))
// {
// Loc += FQuatRotationMatrix(OutVT.Target->GetActorQuat()).TransformVector(TPawn->Mesh->RelativeLocation - GetDefault(TPawn->GetClass())->Mesh->RelativeLocation);
// }
//OutVT.Target.GetActorEyesViewPoint(Loc, Rot);
if( CameraStyle == NAME_FreeCam || CameraStyle == NAME_FreeCam_Default )
{
Rotator = PCOwner->GetControlRotation();
}
FVector Pos = Loc + ViewTargetOffset + FRotationMatrix(Rotator).TransformVector(FreeCamOffset) - Rotator.Vector() * FreeCamDistance;
FCollisionQueryParams BoxParams(SCENE_QUERY_STAT(FreeCam), false, this);
BoxParams.AddIgnoredActor(OutVT.Target);
FHitResult Result;
GetWorld()->SweepSingleByChannel(Result, Loc, Pos, FQuat::Identity, ECC_Camera, FCollisionShape::MakeBox(FVector(12.f)), BoxParams);
OutVT.POV.Location = !Result.bBlockingHit ? Pos : Result.Location;
OutVT.POV.Rotation = Rotator;
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}
else if (CameraStyle == NAME_FirstPerson)
{
// Simple first person, view through viewtarget's 'eyes'
OutVT.Target->GetActorEyesViewPoint(OutVT.POV.Location, OutVT.POV.Rotation);
// don't apply modifiers when using this debug camera mode
bDoNotApplyModifiers = true;
}
else
{
UpdateViewTargetInternal(OutVT, DeltaTime);
}
}
if (!bDoNotApplyModifiers || bAlwaysApplyModifiers)
{
// Apply camera modifiers at the end (view shakes for example)
ApplyCameraModifiers(DeltaTime, OutVT.POV);
}
// Synchronize the actor with the view target results
SetActorLocationAndRotation(OutVT.POV.Location, OutVT.POV.Rotation, false);
UpdateCameraLensEffects(OutVT);
}
可以看到,如果当前的OutVT.Target是CameraActor的话,执行的是CameraActor组件的GetCameraView函数而不是APlatformerPlayerCameraManager::UpdateViewTargetInternal,因为过场动画使用的镜头就是CameraActor,所以刚开始播放过场动画的时候,APlatformerPlayerCameraManager::UpdateViewTargetInternal并没有执行。当过场动画播放完毕,镜头需要回归到人物时,APlatformerPlayerCameraManager::UpdateViewTargetInternal开始执行,APlatformerPlayerCameraManager::CalcCameraOffsetZ则描述了Z插值的相关东西。