有GIF需載入
現在使用的攻擊判定是跟著 Frame Rate 生成的,所以如果幀數突然變低,就會直接穿過去打不到敵人。
所以今天要製作的進階判定是,提取 前一幀 跟 目前幀 的 Socket,用 中間幀 來做判斷。
可以看到,即使只有 20 FPS 也還是可以打到敵人。
command(~) t.MaxFPS 20 設置最高 FPS 為 20
iThomeAnimNotifyState_DoAttackTrace.h
(這是 Day 11|為攻擊增加判定 的東西)
protected:
// If Trace Amount = -1, Use Sphere Trace.
UPROPERTY(EditAnywhere, Category="Attack")
int TraceAmount=-1;
UPROPERTY(EditAnywhere, Category="Attack")
float TraceThickness=5.f;
public:
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override;
iThomeAnimNotifyState_DoAttackTrace.cpp
void UiThomeAnimNotifyState_DoAttackTrace::NotifyTick(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float FrameDeltaTime, const FAnimNotifyEventReference& EventReference)
{
if (AiThome30daysCharacter* Character = Cast<AiThome30daysCharacter>(MeshComp->GetOwner()))
{
// 加了 TraceThickness, TraceAmount
Character->DoAttackTrace(TraceSocketStart, TraceSocketEnd, Radius, TraceThickness, TraceAmount, CollideObjectType);
}
}
void UiThomeAnimNotifyState_DoAttackTrace::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference)
{
if (AiThome30daysCharacter* Character = Cast<AiThome30daysCharacter>(MeshComp->GetOwner()))
{
Character->DoAttackStart(TraceSocketStart, TraceSocketEnd);
}
}
iThome30daysCharacter.h
public:
//! Debug
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Debug")
bool Debug=true;
FVector LastStartSocketPos={0,0,0};
FVector LastEndSocketPos={0,0,0};
iThome30daysCharacter.cpp
void AiThome30daysCharacter::DoAttackStart(FName TraceSocketStart, FName TraceSocketEnd)
{
if (CurrentWeapon)
{
LastStartSocketPos = CurrentWeapon->WeaponMesh->GetSocketLocation(TraceSocketStart);
LastEndSocketPos = CurrentWeapon->WeaponMesh->GetSocketLocation(TraceSocketEnd);
}else
{
LastStartSocketPos = GetMesh()->GetSocketLocation(TraceSocketStart);
LastEndSocketPos = (TraceSocketEnd!=NAME_None) ? GetMesh()->GetSocketLocation(TraceSocketEnd) : LastStartSocketPos;
}
}
寬度
。長度
。厚度
為自己設的 Thickness
void AiThome30daysCharacter::DoAttackTrace(FName TraceSocketStart, FName TraceSocketEnd, float Radius, float Thickness, int TraceAmount, TArray<TEnumAsByte<EObjectTypeQuery>> CollideObjectType)
{
TArray<AActor*> IgnoreActors;
IgnoreActors.Add(this);
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(this);
FHitResult HitResult;
bool bHit=false;
if (TraceAmount>=0) for (int i=0; i<TraceAmount+2; i++)
{
float Alpha = (float)i / (TraceAmount+2 - 1);
FVector hStart = FMath::Lerp(LastStartSocketPos, LastEndSocketPos, Alpha);
FVector hEnd = FMath::Lerp(Start, End, Alpha);
if (hStart==hEnd) continue;
float Length = (hStart-hEnd).Size()*0.5f;
if (Length < KINDA_SMALL_NUMBER) Length = 0.1f;
float Size = (Start-End).Size()*0.5f/(TraceAmount+2);
if (Size < KINDA_SMALL_NUMBER) Size = 0.1f;
bHit = GetWorld()->SweepSingleByObjectType(
HitResult,
hStart,
hEnd,
FQuat::Identity,
CollideObjectType,
FCollisionShape::MakeBox(FVector3f(Length,Size,Thickness)),
QueryParams
);
if (Debug)
{
FVector Center = (hStart + hEnd) * 0.5f;
// 計算旋轉
FVector SweepDir = (hEnd - hStart);
SweepDir.Normalize();
// Up 向量可以沿劍面方向或使用 World Up
FVector Up = FVector::UpVector;
Up = (Up - SweepDir * FVector::DotProduct(Up, SweepDir)).GetSafeNormal();
FQuat Rotation = FRotationMatrix::MakeFromXZ(SweepDir, Up).ToQuat();
DrawDebugBox(GetWorld(), Center, FVector(Length, Size, Thickness), Rotation, FColor::Red, false, 2.f, 0, 1.f);
}
if (bHit)
{
if (Debug) DrawDebugSphere(
GetWorld(),
HitResult.ImpactPoint,
10.0f,
12,
FColor::Green,
false,
2.0f // 顯示秒數
);
break;
}
}
LastStartSocketPos = Start;
LastEndSocketPos = End;
if (TraceAmount==-1)
{
bHit = UKismetSystemLibrary::SphereTraceSingleForObjects(
GetWorld(),
Start,
End,
Radius,
CollideObjectType,
false,
IgnoreActors,
Debug ? EDrawDebugTrace::ForDuration : EDrawDebugTrace::None,
HitResult,
true,
FColor::Red,
FColor::Green,
2.f
);
}
就完成了 :D