void APawnWithCamera::DoTrace() { FVector Loc = CameraOne->GetActorLocation(); UE_LOG(LogClass, Error, TEXT("Loc is %s"), *Loc.ToString()); FRotator Rot = CameraOne->GetActorRotation(); UE_LOG(LogClass, Error, TEXT("Rot is %s"), *Rot.ToString()); FVector Start = Loc; UE_LOG(LogClass, Error, TEXT("Start is %s"), *Start.ToString()); FVector End = Loc + (Rot.Vector() * Distance); UE_LOG(LogClass, Error, TEXT("End is %s"), *End.ToString()); TempActor->SetActorLocation(End); FCollisionQueryParams TraceParam = FCollisionQueryParams(FName(TEXT("Trace")), true, this); TraceParam.bTraceComplex = true; TraceParam.bTraceAsyncScene = true; TraceParam.bReturnPhysicalMaterial = false; TraceParam.AddIgnoredActor(this); FHitResult Hit(ForceInit); GetWorld()->LineTraceSingle(Hit, Start, End, ECC_Pawn, TraceParam); DrawDebugLine(GetWorld(), Start, End, FColor(255, 0, 0), false, -1, 0, 12.33f); if (Hit.bBlockingHit) { UE_LOG(LogClass, Error, TEXT("Hit Something")); } else { UE_LOG(LogClass, Error, TEXT("No Hit")); } }
void UFlareSpacecraftNavigationSystem::PushCommandRotation(const FVector& RotationTarget, const FVector& LocalShipAxis) { if (RotationTarget.IsNearlyZero() || LocalShipAxis.IsNearlyZero()) { return; } FFlareShipCommandData Command; Command.Type = EFlareCommandDataType::CDT_Rotation; Command.RotationTarget = RotationTarget; Command.LocalShipAxis = LocalShipAxis; FLOGV("UFlareSpacecraftNavigationSystem::PushCommandRotation RotationTarget '%s'", *RotationTarget.ToString()); FLOGV("UFlareSpacecraftNavigationSystem::PushCommandRotation LocalShipAxis '%s'", *LocalShipAxis.ToString()); PushCommand(Command); }
void UTankMovementComponent::RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed) { //No need to call super() as we're replacing the functionality auto VelocityString = MoveVelocity.ToString(); auto Name = GetOwner()->GetName(); UE_LOG(LogTemp, Warning, TEXT("%s Requesting move: %s"), *Name,*VelocityString); }
FQuat FAnimNode_RotationMultiplier::MultiplyQuatBasedOnSourceIndex(const FTransform& RefPoseTransform, const FTransform& LocalBoneTransform, const EBoneAxis Axis, float InMultiplier, const FQuat& ReferenceQuat) { // Find delta angle for source bone. FQuat DeltaQuat = ExtractAngle(RefPoseTransform, LocalBoneTransform, Axis); // Turn to Axis and Angle FVector RotationAxis; float RotationAngle; DeltaQuat.ToAxisAndAngle(RotationAxis, RotationAngle); const FVector DefaultAxis = GetAxisVector(Axis); // See if we need to invert angle - shortest path if( (RotationAxis | DefaultAxis) < 0.f ) { RotationAxis = -RotationAxis; RotationAngle = -RotationAngle; } // Make sure it is the shortest angle. RotationAngle = FMath::UnwindRadians(RotationAngle); // New bone rotation FQuat OutQuat = ReferenceQuat * FQuat(RotationAxis, RotationAngle* InMultiplier); // Normalize resulting quaternion. OutQuat.Normalize(); #if 0 //DEBUG_TWISTBONECONTROLLER UE_LOG(LogSkeletalControl, Log, TEXT("\t RefQuat: %s, Rot: %s"), *ReferenceQuat.ToString(), *ReferenceQuat.Rotator().ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t NewQuat: %s, Rot: %s"), *OutQuat.ToString(), *OutQuat.Rotator().ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t RollAxis: %s, RollAngle: %f"), *RotationAxis.ToString(), RotationAngle ); #endif return OutQuat; }
void UCheatManager::BugItStringCreator( FVector ViewLocation, FRotator ViewRotation, FString& GoString, FString& LocString ) { GoString = FString::Printf(TEXT("BugItGo %f %f %f %f %f %f"), ViewLocation.X, ViewLocation.Y, ViewLocation.Z, ViewRotation.Pitch, ViewRotation.Yaw, ViewRotation.Roll); UE_LOG(LogCheatManager, Log, TEXT("%s"), *GoString); LocString = FString::Printf(TEXT("?BugLoc=%s?BugRot=%s"), *ViewLocation.ToString(), *ViewRotation.ToString()); UE_LOG(LogCheatManager, Log, TEXT("%s"), *LocString); }
void UAURSmoothingFilterKalman::Measurement(FTransform const & MeasuredTransform) { try { FVector translation = MeasuredTransform.GetTranslation(); FString msg = "In: " + translation.ToString(); //FQuat rotation = MeasuredTransform.GetRotation(); TransformAsMat.at<float>(0) = translation.X; TransformAsMat.at<float>(1) = translation.Y; TransformAsMat.at<float>(2) = translation.Z; //TransformAsMat.at<float>(3) = rotation.X * rotation.W; //TransformAsMat.at<float>(4) = rotation.Y * rotation.W; //TransformAsMat.at<float>(5) = rotation.Z * rotation.W; cv::Mat FilterResult = Filter.predict(); Filter.correct(TransformAsMat); translation.Set(FilterResult.at<float>(0), FilterResult.at<float>(1), FilterResult.at<float>(2)); msg += "\nOut: " + translation.ToString(); UE_LOG(LogAUR, Log, TEXT("%s"), *msg); //FVector rot_axis(FilterResult.at<float>(3), FilterResult.at<float>(4), FilterResult.at<float>(5)); //float rot_magnitude = rot_axis.Size(); //rot_axis.Normalize(); //rotation.X = rot_axis.X; //rotation.Y = rot_axis.Y; //rotation.Z = rot_axis.Z; //rotation.W = rot_magnitude; CurrentTransform.SetComponents(MeasuredTransform.GetRotation(), translation, FVector(1, 1, 1)); } catch (cv::Exception& e) { FString exception_msg(e.what()); UE_LOG(LogAUR, Error, TEXT("Kalman: %s"), *exception_msg) CurrentTransform = MeasuredTransform; } }
// Called when the game starts void UPositionReport::BeginPlay() { Super::BeginPlay(); FString ObjectName = GetOwner()->GetName(); FVector ObjectPos = GetOwner()->GetActorLocation(); UE_LOG(LogTemp, Warning, TEXT("%s is at %s"), *ObjectName, *ObjectPos.ToString()); // ... }
// Called when the game starts or when spawned void AMazeWalls::BeginPlay() { Super::BeginPlay(); // Spawn a wall. (Same idea as in the puzzle code!) const FVector WallLocation = FVector(10.f, 10.f, 0.f) + GetActorLocation(); AWall* NewWall = GetWorld()->SpawnActor<AWall>(WallLocation, FRotator(0, 0, 0)); // Spits out the wall's position to the screen. if (GEngine) GEngine->AddOnScreenDebugMessage(1, 5.f, FColor::Red, "Start Point" + WallLocation.ToString()); }
void AHeatmapDataCollector::CollectData() { //if (!FPlatformFileManager::Get().GetPlatformFile().DirectoryExists(*SaveDir)) // Dir exist? //{ // FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*SaveDir); // create if it not exist // if (!FPlatformFileManager::Get().GetPlatformFile().DirectoryExists(*SaveDir)) //still could not make directory? // { // return false; //Could not make the specified directory // } // auto FileToSave = SaveDir + "/" + FileName; // if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*FileToSave)) return false; //} if (bCollectData) { if (CharCurrentPtr) { FVector CALoc = CharCurrentPtr->GetActorLocation(); /*if (SplineDataSwitcher == ESplineDataSwitcher::ES_ArrayData) { auto FileToSaveArr = SaveDir + "/" + "ArrayLocationLogs.txt"; static TArray<int16> ArrayToFile; ArrayToFile.Add(CALoc.X); ArrayToFile.Add(CALoc.Y); ArrayToFile.Add(CALoc.Z); SaveArrayToFile(ArrayToFile, *FileToSaveArr); return true; }*/ if (SplineDataSwitcher == ESplineDataSwitcher::ES_StringData) { auto FileToSaveArr = SaveDirectoryPath + "/" + LogFileName + "_" + FString::FromInt(CharNumberInWorld) + ".txt"; const FString& loc = CALoc.ToString(); const TCHAR* StrPtr = *loc; StringOfCoords.Append(" line"); StringOfCoords.Append(FString::FromInt(it)); StringOfCoords.Append(" "); StringOfCoords.Append(StrPtr); StringOfCoords.Append("\n"); it++; FFileHelper::SaveStringToFile(*StringOfCoords, *FileToSaveArr);//FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM } } } if (CharCurrentPtr) { GetWorldTimerManager().SetTimer(ChekTimeHandler, this, &AHeatmapDataCollector::CollectData, UpdateTimeBetweenChek, true); } }
FString UKismetStringLibrary::BuildString_Vector(const FString& AppendTo, const FString& Prefix, FVector InVector, const FString& Suffix) { // faster, preallocating method FString const VecStr = InVector.ToString(); FString StringResult; StringResult.Empty(AppendTo.Len()+Prefix.Len()+VecStr.Len()+Suffix.Len()+1); // adding one for the string terminator StringResult += AppendTo; StringResult += Prefix; StringResult += VecStr; StringResult += Suffix; return StringResult; }
void UCheatManager::BugItWorker( FVector TheLocation, FRotator TheRotation ) { UE_LOG(LogCheatManager, Log, TEXT("BugItGo to: %s %s"), *TheLocation.ToString(), *TheRotation.ToString() ); Ghost(); APlayerController* const MyPlayerController = GetOuterAPlayerController(); if (MyPlayerController->GetPawn()) { MyPlayerController->GetPawn()->TeleportTo( TheLocation, TheRotation ); MyPlayerController->GetPawn()->FaceRotation( TheRotation, 0.0f ); } MyPlayerController->SetControlRotation(TheRotation); }
FQuat FAnimNode_RotationMultiplier::ExtractAngle(const TArray<FTransform> & RefPoseTransforms, FA2CSPose & MeshBases, const EBoneAxis Axis, int32 SourceBoneIndex) { // local bone transform const FTransform & LocalBoneTransform = MeshBases.GetLocalSpaceTransform(SourceBoneIndex); // local bone transform with reference rotation FTransform ReferenceBoneTransform = RefPoseTransforms[SourceBoneIndex]; ReferenceBoneTransform.SetTranslation(LocalBoneTransform.GetTranslation()); // find delta angle between the two quaternions X Axis. const FVector RotationAxis = GetAxisVector(Axis); const FVector LocalRotationVector = LocalBoneTransform.GetRotation().RotateVector(RotationAxis); const FVector ReferenceRotationVector = ReferenceBoneTransform.GetRotation().RotateVector(RotationAxis); const FQuat LocalToRefQuat = FQuat::FindBetween(LocalRotationVector, ReferenceRotationVector); checkSlow( LocalToRefQuat.IsNormalized() ); // Rotate parent bone atom from position in local space to reference skeleton // Since our rotation rotates both vectors with shortest arc // we're essentially left with a quaternion that has angle difference with reference skeleton version const FQuat BoneQuatAligned = LocalToRefQuat * LocalBoneTransform.GetRotation(); checkSlow( BoneQuatAligned.IsNormalized() ); // Find that delta angle const FQuat DeltaQuat = (ReferenceBoneTransform.GetRotation().Inverse()) * BoneQuatAligned; checkSlow( DeltaQuat.IsNormalized() ); #if 0 //DEBUG_TWISTBONECONTROLLER UE_LOG(LogSkeletalControl, Log, TEXT("\t ExtractAngle, Bone: %s (%d)"), *SourceBone.BoneName.ToString(), SourceBoneIndex); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t Bone Quat: %s, Rot: %s, AxisX: %s"), *LocalBoneTransform.GetRotation().ToString(), *LocalBoneTransform.GetRotation().Rotator().ToString(), *LocalRotationVector.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneRef Quat: %s, Rot: %s, AxisX: %s"), *ReferenceBoneTransform.GetRotation().ToString(), *ReferenceBoneTransform.GetRotation().Rotator().ToString(), *ReferenceRotationVector.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t LocalToRefQuat Quat: %s, Rot: %s"), *LocalToRefQuat.ToString(), *LocalToRefQuat.Rotator().ToString() ); const FVector BoneQuatAlignedX = LocalBoneTransform.GetRotation().RotateVector(RotationAxis); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneQuatAligned Quat: %s, Rot: %s, AxisX: %s"), *BoneQuatAligned.ToString(), *BoneQuatAligned.Rotator().ToString(), *BoneQuatAlignedX.ToString() ); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuat Quat: %s, Rot: %s"), *DeltaQuat.ToString(), *DeltaQuat.Rotator().ToString() ); FTransform BoneAtomAligned(BoneQuatAligned, ReferenceBoneTransform.GetTranslation()); const FQuat DeltaQuatAligned = FQuat::FindBetween(BoneAtomAligned.GetScaledAxis( EAxis::X ), ReferenceBoneTransform.GetScaledAxis( EAxis::X )); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuatAligned Quat: %s, Rot: %s"), *DeltaQuatAligned.ToString(), *DeltaQuatAligned.Rotator().ToString() ); FVector DeltaAxis; float DeltaAngle; DeltaQuat.ToAxisAndAngle(DeltaAxis, DeltaAngle); UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaAxis: %s, DeltaAngle: %f"), *DeltaAxis.ToString(), DeltaAngle ); #endif return DeltaQuat; }
static FVector FindConvexMeshOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal) { if (IsInvalidFaceIndex(PHit.faceIndex)) { return InNormal; } PxConvexMeshGeometry PConvexMeshGeom; bool bSuccess = PHit.shape->getConvexMeshGeometry(PConvexMeshGeom); check(bSuccess); //should only call this function when we have a convex mesh if (PConvexMeshGeom.convexMesh) { check(PHit.faceIndex < PConvexMeshGeom.convexMesh->getNbPolygons()); const PxU32 PolyIndex = PHit.faceIndex; PxHullPolygon PPoly; bool bSuccessData = PConvexMeshGeom.convexMesh->getPolygonData(PolyIndex, PPoly); if (bSuccessData) { // Account for non-uniform scale in local space normal. const PxVec3 PPlaneNormal(PPoly.mPlane[0], PPoly.mPlane[1], PPoly.mPlane[2]); const PxVec3 PLocalPolyNormal = TransformNormalToShapeSpace(PConvexMeshGeom.scale, PPlaneNormal.getNormalized()); // Convert to world space const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor); const PxVec3 PWorldPolyNormal = PShapeWorldPose.rotate(PLocalPolyNormal); const FVector OutNormal = P2UVector(PWorldPolyNormal); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!OutNormal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is ConvexMesh): %s (LocalPolyNormal:%s)"), *OutNormal.ToString(), *P2UVector(PLocalPolyNormal).ToString()); UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString()); } #endif return OutNormal; } } return InNormal; }
void CheckVector( FVector ResultVector, FVector ExpectedVector, FString TestName, FString ParameterName, int32 TestIndex, float Tolerance = KINDA_SMALL_NUMBER ) { const FVector Delta = ExpectedVector - ResultVector; if (Delta.SizeSquared() > FMath::Square(Tolerance)) { //UE_LOG(CollisionAutomationTestLog, Log, TEXT("%d:HitResult=(%s)"), iTest+1, *OutHits[iHits].ToString()); TestBase->AddError(FString::Printf(TEXT("Test %d:%s %s mismatch. Should be %s but is actually %s."), TestIndex, *TestName, *ParameterName, *ExpectedVector.ToString(), *ResultVector.ToString())); } }
FString UKismetStringLibrary::Conv_VectorToString(FVector InVec) { return InVec.ToString(); }
/** Util to find the normal of the face that we hit */ static bool FindGeomOpposingNormal(const PxLocationHit& PHit, FVector& OutNormal, FVector& OutPointOnGeom) { PxTriangleMeshGeometry PTriMeshGeom; PxConvexMeshGeometry PConvexMeshGeom; if( PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && PTriMeshGeom.triangleMesh != NULL && PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() ) { const int32 TriIndex = PHit.faceIndex; const void* Triangles = PTriMeshGeom.triangleMesh->getTriangles(); // Grab triangle indices that we hit int32 I0, I1, I2; if(PTriMeshGeom.triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES) { PxU16* P16BitIndices = (PxU16*)Triangles; I0 = P16BitIndices[(TriIndex*3)+0]; I1 = P16BitIndices[(TriIndex*3)+1]; I2 = P16BitIndices[(TriIndex*3)+2]; } else { PxU32* P32BitIndices = (PxU32*)Triangles; I0 = P32BitIndices[(TriIndex*3)+0]; I1 = P32BitIndices[(TriIndex*3)+1]; I2 = P32BitIndices[(TriIndex*3)+2]; } // Get verts we hit (local space) const PxVec3* PVerts = PTriMeshGeom.triangleMesh->getVertices(); const PxVec3 V0 = PVerts[I0]; const PxVec3 V1 = PVerts[I1]; const PxVec3 V2 = PVerts[I2]; // Find normal of triangle, and convert into world space PxVec3 PLocalTriNormal = ((V1 - V0).cross(V2 - V0)).getNormalized(); TransformNormalToShapeSpace(PTriMeshGeom.scale, PLocalTriNormal, PLocalTriNormal); const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor); const PxVec3 PWorldTriNormal = PShapeWorldPose.rotate(PLocalTriNormal); OutNormal = P2UVector(PWorldTriNormal); if (PTriMeshGeom.scale.isIdentity()) { OutPointOnGeom = P2UVector(PShapeWorldPose.transform(V0)); } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!OutNormal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is TriangleMesh): %s (V0:%s, V1:%s, V2:%s)"), *OutNormal.ToString(), *P2UVector(V0).ToString(), *P2UVector(V1).ToString(), *P2UVector(V2).ToString()); UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString()); } #endif return true; } else if(PHit.shape->getConvexMeshGeometry(PConvexMeshGeom) && PConvexMeshGeom.convexMesh != NULL && PHit.faceIndex < PConvexMeshGeom.convexMesh->getNbPolygons() ) { const int32 PolyIndex = PHit.faceIndex; PxHullPolygon PPoly; bool bSuccess = PConvexMeshGeom.convexMesh->getPolygonData(PolyIndex, PPoly); if(bSuccess) { PxVec3 PPlaneNormal(PPoly.mPlane[0], PPoly.mPlane[1], PPoly.mPlane[2]); // Convert to local space, taking scale into account. // TODO: can we just change the hit normal within the physx code where we choose the most opposing normal, and avoid doing this here again? PxVec3 PLocalPolyNormal; TransformNormalToShapeSpace(PConvexMeshGeom.scale, PPlaneNormal.getNormalized(), PLocalPolyNormal); const PxTransform PShapeWorldPose = PHit.actor->getGlobalPose() * PHit.shape->getLocalPose(); const PxVec3 PWorldPolyNormal = PShapeWorldPose.rotate(PLocalPolyNormal); OutNormal = P2UVector(PWorldPolyNormal); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!OutNormal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is ConvexMesh): %s (LocalPolyNormal:%s)"), *OutNormal.ToString(), *P2UVector(PLocalPolyNormal).ToString()); UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString()); } #endif return true; } } return false; }
bool UMovementComponent::ResolvePenetrationImpl(const FVector& ProposedAdjustment, const FHitResult& Hit, const FQuat& NewRotationQuat) { // SceneComponent can't be in penetration, so this function really only applies to PrimitiveComponent. const FVector Adjustment = ConstrainDirectionToPlane(ProposedAdjustment); if (!Adjustment.IsZero() && UpdatedPrimitive) { // See if we can fit at the adjusted location without overlapping anything. AActor* ActorOwner = UpdatedComponent->GetOwner(); if (!ActorOwner) { return false; } UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration: %s.%s at location %s inside %s.%s at location %s by %.3f (netmode: %d)"), *ActorOwner->GetName(), *UpdatedComponent->GetName(), *UpdatedComponent->GetComponentLocation().ToString(), *GetNameSafe(Hit.GetActor()), *GetNameSafe(Hit.GetComponent()), Hit.Component.IsValid() ? *Hit.GetComponent()->GetComponentLocation().ToString() : TEXT("<unknown>"), Hit.PenetrationDepth, (uint32)GetNetMode()); // We really want to make sure that precision differences or differences between the overlap test and sweep tests don't put us into another overlap, // so make the overlap test a bit more restrictive. const float OverlapInflation = CVarPenetrationOverlapCheckInflation.GetValueOnGameThread(); bool bEncroached = OverlapTest(Hit.TraceStart + Adjustment, NewRotationQuat, UpdatedPrimitive->GetCollisionObjectType(), UpdatedPrimitive->GetCollisionShape(OverlapInflation), ActorOwner); if (!bEncroached) { // Move without sweeping. MoveUpdatedComponent(Adjustment, NewRotationQuat, false, nullptr, ETeleportType::TeleportPhysics); UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration: teleport by %s"), *Adjustment.ToString()); return true; } else { // Disable MOVECOMP_NeverIgnoreBlockingOverlaps if it is enabled, otherwise we wouldn't be able to sweep out of the object to fix the penetration. TGuardValue<EMoveComponentFlags> ScopedFlagRestore(MoveComponentFlags, EMoveComponentFlags(MoveComponentFlags & (~MOVECOMP_NeverIgnoreBlockingOverlaps))); // Try sweeping as far as possible... FHitResult SweepOutHit(1.f); bool bMoved = MoveUpdatedComponent(Adjustment, NewRotationQuat, true, &SweepOutHit, ETeleportType::TeleportPhysics); UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration: sweep by %s (success = %d)"), *Adjustment.ToString(), bMoved); // Still stuck? if (!bMoved && SweepOutHit.bStartPenetrating) { // Combine two MTD results to get a new direction that gets out of multiple surfaces. const FVector SecondMTD = GetPenetrationAdjustment(SweepOutHit); const FVector CombinedMTD = Adjustment + SecondMTD; if (SecondMTD != Adjustment && !CombinedMTD.IsZero()) { bMoved = MoveUpdatedComponent(CombinedMTD, NewRotationQuat, true, nullptr, ETeleportType::TeleportPhysics); UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration: sweep by %s (MTD combo success = %d)"), *CombinedMTD.ToString(), bMoved); } } // Still stuck? if (!bMoved) { // Try moving the proposed adjustment plus the attempted move direction. This can sometimes get out of penetrations with multiple objects const FVector MoveDelta = ConstrainDirectionToPlane(Hit.TraceEnd - Hit.TraceStart); if (!MoveDelta.IsZero()) { bMoved = MoveUpdatedComponent(Adjustment + MoveDelta, NewRotationQuat, true, nullptr, ETeleportType::TeleportPhysics); UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration: sweep by %s (adjusted attempt success = %d)"), *(Adjustment + MoveDelta).ToString(), bMoved); } } return bMoved; } } return false; }
void UInterpToMovementComponent::AddControlPointPosition(FVector Pos) { UE_LOG(LogInterpToMovementComponent, Warning, TEXT("Pos:%s"),*Pos.ToString()); ControlPoints.Add( FInterpControlPoint(Pos)); }
void UFlareSpacecraftNavigationSystem::PhysicSubTick(float DeltaSeconds) { TArray<UActorComponent*> Engines = Spacecraft->GetComponentsByClass(UFlareEngine::StaticClass()); if (Spacecraft->GetDamageSystem()->IsPowered()) { // Linear physics FVector DeltaV = LinearTargetVelocity - Spacecraft->GetLinearVelocity(); FVector DeltaVAxis = DeltaV; DeltaVAxis.Normalize(); bool Log = false; if (Spacecraft->GetParent()->GetCompany() == Spacecraft->GetGame()->GetPC()->GetCompany()) { //Log = true; } bool HasUsedOrbitalBoost = false; float LinearMasterAlpha = 0.f; float LinearMasterBoostAlpha = 0.f; if(Log) { FLOGV("PhysicSubTick DeltaSeconds=%f", DeltaSeconds); FLOGV("PhysicSubTick LinearTargetVelocity=%s", *LinearTargetVelocity.ToString()); FLOGV("PhysicSubTick Spacecraft->GetLinearVelocity()=%s", *Spacecraft->GetLinearVelocity().ToString()); FLOGV("PhysicSubTick DeltaV=%s", *DeltaV.ToString()); } if (!DeltaV.IsNearlyZero()) { // First, try without using the boost FVector Acceleration = DeltaVAxis * GetTotalMaxThrustInAxis(Engines, -DeltaVAxis, false).Size() / Spacecraft->GetSpacecraftMass(); float AccelerationDeltaV = Acceleration.Size() * DeltaSeconds; LinearMasterAlpha = FMath::Clamp(DeltaV.Size()/ AccelerationDeltaV, 0.0f, 1.0f); // Second, if the not enought trust check with the boost if (UseOrbitalBoost && AccelerationDeltaV < DeltaV.Size() ) { FVector AccelerationWithBoost = DeltaVAxis * GetTotalMaxThrustInAxis(Engines, -DeltaVAxis, true).Size() / Spacecraft->GetSpacecraftMass(); if (AccelerationWithBoost.Size() > Acceleration.Size()) { HasUsedOrbitalBoost = true; float BoostDeltaV = (AccelerationWithBoost.Size() - Acceleration.Size()) * DeltaSeconds; float DeltaVAfterClassicalAcceleration = DeltaV.Size() - AccelerationDeltaV; LinearMasterBoostAlpha = FMath::Clamp(DeltaVAfterClassicalAcceleration/ BoostDeltaV, 0.0f, 1.0f); Acceleration = AccelerationWithBoost; } } FVector ClampedAcceleration = Acceleration.GetClampedToMaxSize(DeltaV.Size() / DeltaSeconds); Spacecraft->Airframe->SetPhysicsLinearVelocity(ClampedAcceleration * DeltaSeconds * 100, true); // Multiply by 100 because UE4 works in cm } // Angular physics FVector DeltaAngularV = AngularTargetVelocity - Spacecraft->Airframe->GetPhysicsAngularVelocity(); FVector DeltaAngularVAxis = DeltaAngularV; DeltaAngularVAxis.Normalize(); if (!DeltaAngularV.IsNearlyZero()) { FVector SimpleAcceleration = DeltaAngularVAxis * AngularAccelerationRate; // Scale with damages float TotalMaxTorqueInAxis = GetTotalMaxTorqueInAxis(Engines, DeltaAngularVAxis, false); if (!FMath::IsNearlyZero(TotalMaxTorqueInAxis)) { float DamageRatio = GetTotalMaxTorqueInAxis(Engines, DeltaAngularVAxis, true) / TotalMaxTorqueInAxis; FVector DamagedSimpleAcceleration = SimpleAcceleration * DamageRatio; FVector ClampedSimplifiedAcceleration = DamagedSimpleAcceleration.GetClampedToMaxSize(DeltaAngularV.Size() / DeltaSeconds); Spacecraft->Airframe->SetPhysicsAngularVelocity(ClampedSimplifiedAcceleration * DeltaSeconds, true); } } // Update engine alpha for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++) { UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]); FVector ThrustAxis = Engine->GetThrustAxis(); float LinearAlpha = 0; float AngularAlpha = 0; if (Spacecraft->IsPresentationMode()) { LinearAlpha = true; } else if (!DeltaV.IsNearlyZero() || !DeltaAngularV.IsNearlyZero()) { if(Engine->IsA(UFlareOrbitalEngine::StaticClass())) { if(HasUsedOrbitalBoost) { LinearAlpha = (-FVector::DotProduct(ThrustAxis, DeltaVAxis) + 0.2) * LinearMasterBoostAlpha; } AngularAlpha = 0; } else { LinearAlpha = -FVector::DotProduct(ThrustAxis, DeltaVAxis) * LinearMasterAlpha; FVector EngineOffset = (Engine->GetComponentLocation() - COM) / 100; FVector TorqueDirection = FVector::CrossProduct(EngineOffset, ThrustAxis); TorqueDirection.Normalize(); if (!DeltaAngularV.IsNearlyZero() && !Engine->IsA(UFlareOrbitalEngine::StaticClass())) { AngularAlpha = -FVector::DotProduct(TorqueDirection, DeltaAngularVAxis); } } } Engine->SetAlpha(FMath::Clamp(LinearAlpha + AngularAlpha, 0.0f, 1.0f)); } } else { // Shutdown engines for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++) { UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]); Engine->SetAlpha(0); } } }
void UBlendSpaceBase::TickAssetPlayerInstance(const FAnimTickRecord& Instance, class UAnimInstance* InstanceOwner, FAnimAssetTickContext& Context) const { const float DeltaTime = Context.GetDeltaTime(); float MoveDelta = Instance.PlayRateMultiplier * DeltaTime; // this happens even if MoveDelta == 0.f. This still should happen if it is being interpolated // since we allow setting position of blendspace, we can't ignore MoveDelta == 0.f // also now we don't have to worry about not following if DeltaTime = 0.f { // first filter input using blend filter const FVector BlendInput = FilterInput(Instance.BlendFilter, Instance.BlendSpacePosition, DeltaTime); EBlendSpaceAxis AxisToScale = GetAxisToScale(); if (AxisToScale != BSA_None) { float FilterMultiplier = 1.f; // first use multiplier using new blendinput // new filtered input is going to be used for sampling animation // so we'll need to change playrate if you'd like to not slide foot if ( !Instance.BlendSpacePosition.Equals(BlendInput) ) { // apply speed change if you want, if (AxisToScale == BSA_X) { if (BlendInput.X != 0.f) { FilterMultiplier = Instance.BlendSpacePosition.X / BlendInput.X; } } else if (AxisToScale == BSA_Y) { if (BlendInput.Y != 0.f) { FilterMultiplier = Instance.BlendSpacePosition.Y / BlendInput.Y; } } } // now find if clamped input is different // if different, then apply scale to fit in FVector ClampedInput = ClampBlendInput(BlendInput); if ( !ClampedInput.Equals(BlendInput) ) { // apply speed change if you want, if (AxisToScale == BSA_X) { if (ClampedInput.X != 0.f) { FilterMultiplier *= BlendInput.X / ClampedInput.X; } } else if (AxisToScale == BSA_Y) { if (ClampedInput.Y != 0.f) { FilterMultiplier *= BlendInput.Y / ClampedInput.Y; } } } MoveDelta *= FilterMultiplier; UE_LOG(LogAnimation, Log, TEXT("BlendSpace(%s) - BlendInput(%s) : FilteredBlendInput(%s), FilterMultiplier(%0.2f)"), *GetName(), *Instance.BlendSpacePosition.ToString(), *BlendInput.ToString(), FilterMultiplier ); } check(Instance.BlendSampleDataCache); // For Target weight interpolation, we'll need to save old data, and interpolate to new data static TArray<FBlendSampleData> OldSampleDataList; static TArray<FBlendSampleData> NewSampleDataList; check(IsInGameThread() && !OldSampleDataList.Num() && !NewSampleDataList.Num()); // this must be called non-recursively on the game thread OldSampleDataList.Append(*Instance.BlendSampleDataCache); // get sample data based on new input // consolidate all samples and sort them, so that we can handle from biggest weight to smallest Instance.BlendSampleDataCache->Reset(); // new sample data that will be used for evaluation TArray<FBlendSampleData> & SampleDataList = *Instance.BlendSampleDataCache; // get sample data from blendspace if (GetSamplesFromBlendInput(BlendInput, NewSampleDataList)) { float NewAnimLength=0; // if target weight interpolation is set if (TargetWeightInterpolationSpeedPerSec > 0.f) { UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Target Samples ")); // recalculate AnimLength based on weight of target animations - this is used for scaling animation later (change speed) float PreInterpAnimLength = GetAnimationLengthFromSampleData(NewSampleDataList); UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : PreAnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), PreInterpAnimLength); // target weight interpolation if (InterpolateWeightOfSampleData(DeltaTime, OldSampleDataList, NewSampleDataList, SampleDataList)) { // now I need to normalize NormalizeSampleDataWeight(SampleDataList); } else { // if interpolation failed, just copy new sample data tto sample data SampleDataList = NewSampleDataList; } // recalculate AnimLength based on weight of animations UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Interp Samples ")); NewAnimLength = GetAnimationLengthFromSampleData(SampleDataList); // now scale the animation if (NewAnimLength > 0.f) { MoveDelta *= PreInterpAnimLength/NewAnimLength; } } else { // when there is no target weight interpolation, just copy new to target SampleDataList.Append(NewSampleDataList); NewAnimLength = GetAnimationLengthFromSampleData(SampleDataList); } float& NormalizedCurrentTime = *(Instance.TimeAccumulator); const float NormalizedPreviousTime = NormalizedCurrentTime; if (Context.IsLeader()) { // advance current time - blend spaces hold normalized time as when dealing with changing anim length it would be possible to go backwards UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : AnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), NewAnimLength); // Advance time using current/new anim length float CurrentTime = NormalizedCurrentTime * NewAnimLength; FAnimationRuntime::AdvanceTime(Instance.bLooping, MoveDelta, /*inout*/ CurrentTime, NewAnimLength); NormalizedCurrentTime = NewAnimLength ? (CurrentTime / NewAnimLength) : 0.0f; Context.SetSyncPoint(NormalizedCurrentTime); } else { NormalizedCurrentTime = Context.GetSyncPoint(); } // generate notifies and sets time { TArray<const FAnimNotifyEvent*> Notifies; // now calculate time for each samples const float ClampedNormalizedPreviousTime = FMath::Clamp<float>(NormalizedPreviousTime, 0.f, 1.f); const float ClampedNormalizedCurrentTime = FMath::Clamp<float>(NormalizedCurrentTime, 0.f, 1.f); const bool bGenerateNotifies = Context.ShouldGenerateNotifies() && (NormalizedCurrentTime != NormalizedPreviousTime) && NotifyTriggerMode != ENotifyTriggerMode::None; int32 HighestWeightIndex = 0; // Get the index of the highest weight, assuming that the first is the highest until we find otherwise bool bTriggerNotifyHighestWeightedAnim = NotifyTriggerMode == ENotifyTriggerMode::HighestWeightedAnimation && SampleDataList.Num() > 0; if(bGenerateNotifies && bTriggerNotifyHighestWeightedAnim) { float HighestWeight = SampleDataList[HighestWeightIndex].GetWeight(); for(int32 I = 1 ; I < SampleDataList.Num(); I++) { if(SampleDataList[I].GetWeight() > HighestWeight) { HighestWeightIndex = I; HighestWeight = SampleDataList[I].GetWeight(); } } } for (int32 I = 0; I < SampleDataList.Num(); ++I) { FBlendSampleData& SampleEntry = SampleDataList[I]; const int32 SampleDataIndex = SampleEntry.SampleDataIndex; // Skip SamplesPoints that has no relevant weight if( SampleData.IsValidIndex(SampleDataIndex) && (SampleEntry.TotalWeight > ZERO_ANIMWEIGHT_THRESH) ) { const FBlendSample& Sample = SampleData[SampleDataIndex]; if( Sample.Animation ) { const float SampleNormalizedPreviousTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedPreviousTime : 1.f - ClampedNormalizedPreviousTime; const float SampleNormalizedCurrentTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedCurrentTime : 1.f - ClampedNormalizedCurrentTime; const float PrevSampleDataTime = SampleNormalizedPreviousTime * Sample.Animation->SequenceLength; float& CurrentSampleDataTime = SampleEntry.Time; CurrentSampleDataTime = SampleNormalizedCurrentTime * Sample.Animation->SequenceLength; // Figure out delta time float DeltaTimePosition = CurrentSampleDataTime - PrevSampleDataTime; const float SampleMoveDelta = MoveDelta * Sample.Animation->RateScale; // if we went against play rate, then loop around. if ((SampleMoveDelta * DeltaTimePosition) < 0.f) { DeltaTimePosition += FMath::Sign<float>(SampleMoveDelta) * Sample.Animation->SequenceLength; } if( bGenerateNotifies && (!bTriggerNotifyHighestWeightedAnim || (I == HighestWeightIndex))) { // Harvest and record notifies Sample.Animation->GetAnimNotifies(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping, Notifies); } if (Context.RootMotionMode == ERootMotionMode::RootMotionFromEverything && Sample.Animation->bEnableRootMotion) { Context.RootMotionMovementParams.AccumulateWithBlend(Sample.Animation->ExtractRootMotion(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping), SampleEntry.GetWeight()); } UE_LOG(LogAnimation, Verbose, TEXT("%d. Blending animation(%s) with %f weight at time %0.2f"), I+1, *Sample.Animation->GetName(), SampleEntry.GetWeight(), CurrentSampleDataTime); } } } if (bGenerateNotifies && Notifies.Num() > 0) { InstanceOwner->AddAnimNotifies(Notifies, Instance.EffectiveBlendWeight); } } } OldSampleDataList.Reset(); NewSampleDataList.Reset(); } }
void UBlendSpaceBase::TickAssetPlayer(FAnimTickRecord& Instance, struct FAnimNotifyQueue& NotifyQueue, FAnimAssetTickContext& Context) const { const float DeltaTime = Context.GetDeltaTime(); float MoveDelta = Instance.PlayRateMultiplier * DeltaTime; // this happens even if MoveDelta == 0.f. This still should happen if it is being interpolated // since we allow setting position of blendspace, we can't ignore MoveDelta == 0.f // also now we don't have to worry about not following if DeltaTime = 0.f { // first filter input using blend filter const FVector BlendSpacePosition(Instance.BlendSpace.BlendSpacePositionX, Instance.BlendSpace.BlendSpacePositionY, 0.f); const FVector BlendInput = FilterInput(Instance.BlendSpace.BlendFilter, BlendSpacePosition, DeltaTime); EBlendSpaceAxis AxisToScale = GetAxisToScale(); if (AxisToScale != BSA_None) { float FilterMultiplier = 1.f; // first use multiplier using new blendinput // new filtered input is going to be used for sampling animation // so we'll need to change playrate if you'd like to not slide foot if ( !BlendSpacePosition.Equals(BlendInput) ) { // apply speed change if you want, if (AxisToScale == BSA_X) { if (BlendInput.X != 0.f) { FilterMultiplier = BlendSpacePosition.X / BlendInput.X; } } else if (AxisToScale == BSA_Y) { if (BlendInput.Y != 0.f) { FilterMultiplier = BlendSpacePosition.Y / BlendInput.Y; } } } // now find if clamped input is different // if different, then apply scale to fit in FVector ClampedInput = ClampBlendInput(BlendInput); if ( !ClampedInput.Equals(BlendInput) ) { // apply speed change if you want, if (AxisToScale == BSA_X) { if (ClampedInput.X != 0.f) { FilterMultiplier *= BlendInput.X / ClampedInput.X; } } else if (AxisToScale == BSA_Y) { if (ClampedInput.Y != 0.f) { FilterMultiplier *= BlendInput.Y / ClampedInput.Y; } } } MoveDelta *= FilterMultiplier; UE_LOG(LogAnimation, Log, TEXT("BlendSpace(%s) - BlendInput(%s) : FilteredBlendInput(%s), FilterMultiplier(%0.2f)"), *GetName(), *BlendSpacePosition.ToString(), *BlendInput.ToString(), FilterMultiplier ); } check(Instance.BlendSpace.BlendSampleDataCache); // For Target weight interpolation, we'll need to save old data, and interpolate to new data TArray<FBlendSampleData>& OldSampleDataList = FBlendSpaceScratchData::Get().OldSampleDataList; TArray<FBlendSampleData>& NewSampleDataList = FBlendSpaceScratchData::Get().NewSampleDataList; check(!OldSampleDataList.Num() && !NewSampleDataList.Num()); // this must be called non-recursively OldSampleDataList.Append(*Instance.BlendSpace.BlendSampleDataCache); // get sample data based on new input // consolidate all samples and sort them, so that we can handle from biggest weight to smallest Instance.BlendSpace.BlendSampleDataCache->Reset(); // new sample data that will be used for evaluation TArray<FBlendSampleData> & SampleDataList = *Instance.BlendSpace.BlendSampleDataCache; // get sample data from blendspace if (GetSamplesFromBlendInput(BlendInput, NewSampleDataList)) { float NewAnimLength=0.f; float PreInterpAnimLength = 0.f; // if target weight interpolation is set if (TargetWeightInterpolationSpeedPerSec > 0.f) { UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Target Samples ")); // recalculate AnimLength based on weight of target animations - this is used for scaling animation later (change speed) PreInterpAnimLength = GetAnimationLengthFromSampleData(NewSampleDataList); UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : PreAnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), PreInterpAnimLength); // target weight interpolation if (InterpolateWeightOfSampleData(DeltaTime, OldSampleDataList, NewSampleDataList, SampleDataList)) { // now I need to normalize FBlendSampleData::NormalizeDataWeight(SampleDataList); } else { // if interpolation failed, just copy new sample data tto sample data SampleDataList = NewSampleDataList; } // recalculate AnimLength based on weight of animations UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Interp Samples ")); } else { // when there is no target weight interpolation, just copy new to target SampleDataList.Append(NewSampleDataList); } bool bCanDoMarkerSync = (SampleIndexWithMarkers != INDEX_NONE) && (Context.IsSingleAnimationContext() || (Instance.bCanUseMarkerSync && Context.CanUseMarkerPosition())); if (bCanDoMarkerSync) { //Copy previous frame marker data to current frame for (FBlendSampleData& PrevBlendSampleItem : OldSampleDataList) { for (FBlendSampleData& CurrentBlendSampleItem : SampleDataList) { // it only can have one animation in the sample, make sure to copy Time if (PrevBlendSampleItem.Animation && PrevBlendSampleItem.Animation == CurrentBlendSampleItem.Animation) { CurrentBlendSampleItem.Time = PrevBlendSampleItem.Time; CurrentBlendSampleItem.PreviousTime = PrevBlendSampleItem.PreviousTime; CurrentBlendSampleItem.MarkerTickRecord = PrevBlendSampleItem.MarkerTickRecord; } } } } NewAnimLength = GetAnimationLengthFromSampleData(SampleDataList); if (PreInterpAnimLength > 0.f && NewAnimLength > 0.f) { MoveDelta *= PreInterpAnimLength / NewAnimLength; } float& NormalizedCurrentTime = *(Instance.TimeAccumulator); const float NormalizedPreviousTime = NormalizedCurrentTime; // @note for sync group vs non sync group // in blendspace, it will still sync even if only one node in sync group // so you're never non-sync group unless you have situation where some markers are relevant to one sync group but not all the time // here we save NormalizedCurrentTime as Highest weighted samples' position in sync group // if you're not in sync group, NormalizedCurrentTime is based on normalized length by sample weights // if you move between sync to non sync within blendspace, you're going to see pop because we'll have to jump // for now, our rule is to keep normalized time as highest weighted sample position within its own length // also MoveDelta doesn't work if you're in sync group. It will move according to sync group position // @todo consider using MoveDelta when this is leader, but that can be scary because it's not matching with DeltaTime any more. // if you have interpolation delay, that value can be applied, but the output might be unpredictable. // // to fix this better in the future, we should use marker sync position from last tick // but that still doesn't fix if you just join sync group, you're going to see pop since your animation doesn't fix if (Context.IsLeader()) { // advance current time - blend spaces hold normalized time as when dealing with changing anim length it would be possible to go backwards UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : AnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), NewAnimLength); const int32 HighestMarkerSyncWeightIndex = bCanDoMarkerSync ? GetHighestWeightMarkerSyncSample(SampleDataList, SampleData) : -1; if (HighestMarkerSyncWeightIndex == -1) { bCanDoMarkerSync = false; } if (bCanDoMarkerSync) { FBlendSampleData& SampleDataItem = SampleDataList[HighestMarkerSyncWeightIndex]; const FBlendSample& Sample = SampleData[SampleDataItem.SampleDataIndex]; bool bResetMarkerDataOnFollowers = false; if (!Instance.MarkerTickRecord->IsValid()) { SampleDataItem.MarkerTickRecord.Reset(); bResetMarkerDataOnFollowers = true; } else if (!SampleDataItem.MarkerTickRecord.IsValid() && Context.MarkerTickContext.GetMarkerSyncStartPosition().IsValid()) { Sample.Animation->GetMarkerIndicesForPosition(Context.MarkerTickContext.GetMarkerSyncStartPosition(), true, SampleDataItem.MarkerTickRecord.PreviousMarker, SampleDataItem.MarkerTickRecord.NextMarker, SampleDataItem.Time); } const float NewDeltaTime = Context.GetDeltaTime() * Instance.PlayRateMultiplier; if (!FMath::IsNearlyZero(NewDeltaTime)) { Context.SetLeaderDelta(NewDeltaTime); Sample.Animation->TickByMarkerAsLeader(SampleDataItem.MarkerTickRecord, Context.MarkerTickContext, SampleDataItem.Time, SampleDataItem.PreviousTime, NewDeltaTime, true); check(Context.MarkerTickContext.IsMarkerSyncStartValid()); TickFollowerSamples(SampleDataList, HighestMarkerSyncWeightIndex, Context, bResetMarkerDataOnFollowers); } NormalizedCurrentTime = SampleDataItem.Time / Sample.Animation->SequenceLength; *Instance.MarkerTickRecord = SampleDataItem.MarkerTickRecord; } else { // Advance time using current/new anim length float CurrentTime = NormalizedCurrentTime * NewAnimLength; FAnimationRuntime::AdvanceTime(Instance.bLooping, MoveDelta, /*inout*/ CurrentTime, NewAnimLength); NormalizedCurrentTime = NewAnimLength ? (CurrentTime / NewAnimLength) : 0.0f; UE_LOG(LogAnimMarkerSync, Log, TEXT("Leader (%s) (normal advance) - PreviousTime (%0.2f), CurrentTime (%0.2f), MoveDelta (%0.2f) "), *GetName(), NormalizedPreviousTime, NormalizedCurrentTime, MoveDelta); } Context.SetAnimationPositionRatio(NormalizedCurrentTime); } else { if(!Context.MarkerTickContext.IsMarkerSyncStartValid()) { bCanDoMarkerSync = false; } if (bCanDoMarkerSync) { const int32 HighestWeightIndex = GetHighestWeightSample(SampleDataList); FBlendSampleData& SampleDataItem = SampleDataList[HighestWeightIndex]; const FBlendSample& Sample = SampleData[SampleDataItem.SampleDataIndex]; if (Context.GetDeltaTime() != 0.f) { if(!Instance.MarkerTickRecord->IsValid()) { SampleDataItem.Time = NormalizedCurrentTime * Sample.Animation->SequenceLength; } TickFollowerSamples(SampleDataList, -1, Context, false); } *Instance.MarkerTickRecord = SampleDataItem.MarkerTickRecord; NormalizedCurrentTime = SampleDataItem.Time / Sample.Animation->SequenceLength; } else { NormalizedCurrentTime = Context.GetAnimationPositionRatio(); UE_LOG(LogAnimMarkerSync, Log, TEXT("Leader (%s) (normal advance) - PreviousTime (%0.2f), CurrentTime (%0.2f), MoveDelta (%0.2f) "), *GetName(), NormalizedPreviousTime, NormalizedCurrentTime, MoveDelta); } } // generate notifies and sets time { TArray<const FAnimNotifyEvent*> Notifies; const float ClampedNormalizedPreviousTime = FMath::Clamp<float>(NormalizedPreviousTime, 0.f, 1.f); const float ClampedNormalizedCurrentTime = FMath::Clamp<float>(NormalizedCurrentTime, 0.f, 1.f); const bool bGenerateNotifies = Context.ShouldGenerateNotifies() && (NormalizedCurrentTime != NormalizedPreviousTime) && NotifyTriggerMode != ENotifyTriggerMode::None; // Get the index of the highest weight, assuming that the first is the highest until we find otherwise const bool bTriggerNotifyHighestWeightedAnim = NotifyTriggerMode == ENotifyTriggerMode::HighestWeightedAnimation && SampleDataList.Num() > 0; const int32 HighestWeightIndex = (bGenerateNotifies && bTriggerNotifyHighestWeightedAnim) ? GetHighestWeightSample(SampleDataList) : -1; for (int32 I = 0; I < SampleDataList.Num(); ++I) { FBlendSampleData& SampleEntry = SampleDataList[I]; const int32 SampleDataIndex = SampleEntry.SampleDataIndex; // Skip SamplesPoints that has no relevant weight if( SampleData.IsValidIndex(SampleDataIndex) && (SampleEntry.TotalWeight > ZERO_ANIMWEIGHT_THRESH) ) { const FBlendSample& Sample = SampleData[SampleDataIndex]; if( Sample.Animation ) { float PrevSampleDataTime; float& CurrentSampleDataTime = SampleEntry.Time; if (!bCanDoMarkerSync || Sample.Animation->AuthoredSyncMarkers.Num() == 0) //Have already updated time if we are doing marker sync { const float SampleNormalizedPreviousTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedPreviousTime : 1.f - ClampedNormalizedPreviousTime; const float SampleNormalizedCurrentTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedCurrentTime : 1.f - ClampedNormalizedCurrentTime; PrevSampleDataTime = SampleNormalizedPreviousTime * Sample.Animation->SequenceLength; CurrentSampleDataTime = SampleNormalizedCurrentTime * Sample.Animation->SequenceLength; } else { PrevSampleDataTime = SampleEntry.PreviousTime; } // Figure out delta time float DeltaTimePosition = CurrentSampleDataTime - PrevSampleDataTime; const float SampleMoveDelta = MoveDelta * Sample.Animation->RateScale; // if we went against play rate, then loop around. if ((SampleMoveDelta * DeltaTimePosition) < 0.f) { DeltaTimePosition += FMath::Sign<float>(SampleMoveDelta) * Sample.Animation->SequenceLength; } if( bGenerateNotifies && (!bTriggerNotifyHighestWeightedAnim || (I == HighestWeightIndex))) { // Harvest and record notifies Sample.Animation->GetAnimNotifies(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping, Notifies); } if (Context.RootMotionMode == ERootMotionMode::RootMotionFromEverything && Sample.Animation->bEnableRootMotion) { Context.RootMotionMovementParams.AccumulateWithBlend(Sample.Animation->ExtractRootMotion(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping), SampleEntry.GetWeight()); } UE_LOG(LogAnimation, Verbose, TEXT("%d. Blending animation(%s) with %f weight at time %0.2f"), I+1, *Sample.Animation->GetName(), SampleEntry.GetWeight(), CurrentSampleDataTime); } } } if (bGenerateNotifies && Notifies.Num() > 0) { NotifyQueue.AddAnimNotifies(Notifies, Instance.EffectiveBlendWeight); } } } OldSampleDataList.Reset(); NewSampleDataList.Reset(); } }
EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass) { SCOPE_CYCLE_COUNTER(STAT_MoveToLocation); EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed; bool bCanRequestMove = true; UE_VLOG(this, LogNavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"), *Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); // Check input is valid if( Dest.ContainsNaN() ) { UE_VLOG(this, LogNavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"), *Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); ensure( !Dest.ContainsNaN() ); bCanRequestMove = false; } FVector GoalLocation = Dest; // fail if projection to navigation is required but it either failed or there's not navigation component if (bCanRequestMove && bProjectDestinationToNavigation && (NavComponent == NULL || !NavComponent->ProjectPointToNavigation(Dest, GoalLocation))) { UE_VLOG_LOCATION(this, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh")); bCanRequestMove = false; } if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap)) { UE_VLOG(this, LogNavigation, Log, TEXT("MoveToLocation: already at goal!")); PathFollowingComponent->SetLastMoveAtGoal(true); OnMoveCompleted(0, EPathFollowingResult::Success); Result = EPathFollowingRequestResult::AlreadyAtGoal; bCanRequestMove = false; } if (bCanRequestMove) { FNavPathSharedPtr Path = FindPath(GoalLocation, bUsePathfinding, UNavigationQueryFilter::GetQueryFilter(FilterClass)); if (Path.IsValid()) { bAllowStrafe = bCanStrafe; const uint32 RequestID = RequestMove(Path, NULL, AcceptanceRadius, bStopOnOverlap); Result = (RequestID != INVALID_MOVEREQUESTID) ? EPathFollowingRequestResult::RequestSuccessful : EPathFollowingRequestResult::Failed; } } if (Result == EPathFollowingRequestResult::Failed) { if (PathFollowingComponent) { PathFollowingComponent->SetLastMoveAtGoal(false); } OnMoveCompleted(INVALID_MOVEREQUESTID, EPathFollowingResult::Invalid); } return Result; }
static FVector FindTriMeshOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal) { if (IsInvalidFaceIndex(PHit.faceIndex)) { return InNormal; } PxTriangleMeshGeometry PTriMeshGeom; bool bSuccess = PHit.shape->getTriangleMeshGeometry(PTriMeshGeom); check(bSuccess); //this function should only be called when we have a trimesh if (PTriMeshGeom.triangleMesh) { check(PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles()); const PxU32 TriIndex = PHit.faceIndex; const void* Triangles = PTriMeshGeom.triangleMesh->getTriangles(); // Grab triangle indices that we hit int32 I0, I1, I2; if (PTriMeshGeom.triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES) { PxU16* P16BitIndices = (PxU16*)Triangles; I0 = P16BitIndices[(TriIndex * 3) + 0]; I1 = P16BitIndices[(TriIndex * 3) + 1]; I2 = P16BitIndices[(TriIndex * 3) + 2]; } else { PxU32* P32BitIndices = (PxU32*)Triangles; I0 = P32BitIndices[(TriIndex * 3) + 0]; I1 = P32BitIndices[(TriIndex * 3) + 1]; I2 = P32BitIndices[(TriIndex * 3) + 2]; } // Get verts we hit (local space) const PxVec3* PVerts = PTriMeshGeom.triangleMesh->getVertices(); const PxVec3 V0 = PVerts[I0]; const PxVec3 V1 = PVerts[I1]; const PxVec3 V2 = PVerts[I2]; // Find normal of triangle (local space), and account for non-uniform scale const PxVec3 PTempNormal = ((V1 - V0).cross(V2 - V0)).getNormalized(); const PxVec3 PLocalTriNormal = TransformNormalToShapeSpace(PTriMeshGeom.scale, PTempNormal); // Convert to world space const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor); const PxVec3 PWorldTriNormal = PShapeWorldPose.rotate(PLocalTriNormal); FVector OutNormal = P2UVector(PWorldTriNormal); if (PTriMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED) { //double sided mesh so we need to consider direction of query const float sign = FVector::DotProduct(OutNormal, TraceDirectionDenorm) > 0.f ? -1.f : 1.f; OutNormal *= sign; } #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (!OutNormal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is TriangleMesh): %s (V0:%s, V1:%s, V2:%s)"), *OutNormal.ToString(), *P2UVector(V0).ToString(), *P2UVector(V1).ToString(), *P2UVector(V2).ToString()); UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString()); } #endif return OutNormal; } return InNormal; }
inline FString convert(FVector x) { return x.ToString(); }
/* Validate Normal of OutResult. We're on hunt for invalid normal */ static void CheckHitResultNormal(const FHitResult& OutResult, const TCHAR* Message, const FVector& Start=FVector::ZeroVector, const FVector& End = FVector::ZeroVector, const PxGeometry* const Geom=NULL) { if(!OutResult.bStartPenetrating && !OutResult.Normal.IsNormalized()) { UE_LOG(LogPhysics, Warning, TEXT("(%s) Non-normalized OutResult.Normal from hit conversion: %s (Component- %s)"), Message, *OutResult.Normal.ToString(), *GetNameSafe(OutResult.Component.Get())); UE_LOG(LogPhysics, Warning, TEXT("Start Loc(%s), End Loc(%s), Hit Loc(%s), ImpactNormal(%s)"), *Start.ToString(), *End.ToString(), *OutResult.Location.ToString(), *OutResult.ImpactNormal.ToString() ); if (Geom != NULL) { if (Geom->getType() == PxGeometryType::eCAPSULE) { const PxCapsuleGeometry * Capsule = (PxCapsuleGeometry*)Geom; UE_LOG(LogPhysics, Warning, TEXT("Capsule radius (%f), Capsule Halfheight (%f)"), Capsule->radius, Capsule->halfHeight); } } ensure(OutResult.Normal.IsNormalized()); } }
void UTankAimingComponent::AimAt(FVector HitLocation) { auto TankName = GetOwner()->GetName(); auto BarrelLocation = Barrel->GetComponentLocation().ToString(); UE_LOG(LogTemp, Warning, TEXT("%s is aiming at %s from %s"), *TankName, *HitLocation.ToString(), *BarrelLocation); }
// Called every frame void APawnWithCamera::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); Count += 1; FVector testLoc = GetActorLocation(); UE_LOG(LogClass, Warning, TEXT("Test LOC %s"), *testLoc.ToString()); if (bZoomingin) { ZoomFactor += DeltaTime / 0.5f; } else { ZoomFactor -= DeltaTime / 0.25f; } ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f); OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor); OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor); { FRotator NewRotation = GetActorRotation(); NewRotation.Yaw += CameraInput.X; SetActorRotation(NewRotation); } { FRotator NewRotation = OurCameraSpringArm->GetComponentRotation(); NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f); OurCameraSpringArm->SetWorldRotation(NewRotation); } { if (!MovementInput.IsZero()) { //Scale our movement input axis values by 100 units per second MovementInput = MovementInput.SafeNormal() * 100.0f; FVector NewLocation = GetActorLocation(); //Print Actor Location. //UE_LOG(LogClass, Error , TEXT("This is a testing statement. %s"), *NewLocation.ToString()); //UE_LOG(LogClass, Warning, TEXT("Delta Time %f"), DeltaTime); NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime; NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime; this->SetActorLocation(NewLocation); } } //FHitResult HitCall(ForceInit); //FCollisionQueryParams ParamCall = FCollisionQueryParams(FName(TEXT("Trace")), true, this); //DoTrace(&HitCall, &ParamCall); //DoTrace(); //Get keyboard input if (GetWorld()->GetFirstPlayerController()->WasInputKeyJustPressed(EKeys::X)) { bShowLog = true; } else if (GetWorld()->GetFirstPlayerController()->WasInputKeyJustReleased(EKeys::X)) { bShowLog = false; } }
AActor* UWorld::SpawnActor( UClass* Class, FTransform const* Transform, const FActorSpawnParameters& SpawnParameters ) { SCOPE_CYCLE_COUNTER(STAT_SpawnActorTime); check( CurrentLevel ); check(GIsEditor || (CurrentLevel == PersistentLevel)); // Make sure this class is spawnable. if( !Class ) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because no class was specified") ); return NULL; } if( Class->HasAnyClassFlags(CLASS_Deprecated) ) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is deprecated"), *Class->GetName() ); return NULL; } if( Class->HasAnyClassFlags(CLASS_Abstract) ) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is abstract"), *Class->GetName() ); return NULL; } else if( !Class->IsChildOf(AActor::StaticClass()) ) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because %s is not an actor class"), *Class->GetName() ); return NULL; } else if (SpawnParameters.Template != NULL && SpawnParameters.Template->GetClass() != Class) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because template class (%s) does not match spawn class (%s)"), *SpawnParameters.Template->GetClass()->GetName(), *Class->GetName()); if (!SpawnParameters.bNoFail) { return NULL; } } else if (bIsRunningConstructionScript && !SpawnParameters.bAllowDuringConstructionScript) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because we are running a ConstructionScript (%s)"), *Class->GetName() ); return NULL; } else if (bIsTearingDown) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because we are in the process of tearing down the world")); return NULL; } else if (Transform && Transform->ContainsNaN()) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because the given transform (%s) is invalid"), *Transform->ToString()); return NULL; } ULevel* LevelToSpawnIn = SpawnParameters.OverrideLevel; if (LevelToSpawnIn == NULL) { // Spawn in the same level as the owner if we have one. @warning: this relies on the outer of an actor being the level. LevelToSpawnIn = (SpawnParameters.Owner != NULL) ? CastChecked<ULevel>(SpawnParameters.Owner->GetOuter()) : CurrentLevel; } FName NewActorName = SpawnParameters.Name; AActor* Template = SpawnParameters.Template; // Use class's default actor as a template. if( !Template ) { Template = Class->GetDefaultObject<AActor>(); } else if (NewActorName.IsNone() && !Template->HasAnyFlags(RF_ClassDefaultObject)) { NewActorName = MakeUniqueObjectName(LevelToSpawnIn, Template->GetClass(), *Template->GetFName().GetPlainNameString()); } check(Template!=NULL); // See if we can spawn on ded.server/client only etc (check NeedsLoadForClient & NeedsLoadForServer) if(!CanCreateInCurrentContext(Template)) { UE_LOG(LogSpawn, Warning, TEXT("Unable to spawn class '%s' due to client/server context."), *Class->GetName() ); return NULL; } FVector NewLocation = Transform ? Transform->GetLocation() : (Template->GetRootComponent() ? Template->GetRootComponent()->RelativeLocation : FVector::ZeroVector); FRotator NewRotation = Transform ? Transform->GetRotation().Rotator() : (Template->GetRootComponent() ? Template->GetRootComponent()->RelativeRotation : FRotator::ZeroRotator); FVector NewScale = Transform ? Transform->GetScale3D() : (Template->GetRootComponent() ? Template->GetRootComponent()->RelativeScale3D : FVector(1.f) ); PRAGMA_DISABLE_DEPRECATION_WARNINGS; // handle existing (but deprecated) uses of bNoCollisionFail where user set it to true ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = SpawnParameters.SpawnCollisionHandlingOverride; if ((CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::Undefined) && SpawnParameters.bNoCollisionFail) { CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; } PRAGMA_ENABLE_DEPRECATION_WARNINGS; // "no fail" take preedence over collision handling settings that include fails if (SpawnParameters.bNoFail) { // maybe upgrade to disallow fail if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding) { CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; } else if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding) { CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; } } // use override if set, else fall back to actor's preference ESpawnActorCollisionHandlingMethod const CollisionHandlingMethod = (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::Undefined) ? Template->SpawnCollisionHandlingMethod : CollisionHandlingOverride; // see if we can avoid spawning altogether by checking native components // note: we can't handle all cases here, since we don't know the full component hierarchy until after the actor is spawned if (CollisionHandlingMethod == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding) { if (EncroachingBlockingGeometry(Template, NewLocation, NewRotation)) { // a native component is colliding, that's enough to reject spawning UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *NewLocation.ToString(), *Class->GetName()); return nullptr; } } // actually make the actor object AActor* const Actor = NewObject<AActor>(LevelToSpawnIn, Class, NewActorName, SpawnParameters.ObjectFlags, Template); check(Actor); #if WITH_EDITOR Actor->ClearActorLabel(); // Clear label on newly spawned actors #endif // WITH_EDITOR if ( GUndo ) { ModifyLevel( LevelToSpawnIn ); } LevelToSpawnIn->Actors.Add( Actor ); // Add this newly spawned actor to the network actor list AddNetworkActor( Actor ); #if PERF_SHOW_MULTI_PAWN_SPAWN_FRAMES if( Cast<APawn>(Actor) ) { FString PawnName = FString::Printf(TEXT("%d: %s"), ThisFramePawnSpawns.Num(), *Actor->GetPathName()); ThisFramePawnSpawns.Add(PawnName); } #endif // tell the actor what method to use, in case it was overridden Actor->SpawnCollisionHandlingMethod = CollisionHandlingMethod; Actor->PostSpawnInitialize(FTransform(NewRotation, NewLocation, NewScale), SpawnParameters.Owner, SpawnParameters.Instigator, SpawnParameters.bRemoteOwned, SpawnParameters.bNoFail, SpawnParameters.bDeferConstruction); if (Actor->IsPendingKill() && !SpawnParameters.bNoFail) { UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because the spawned actor IsPendingKill")); return NULL; } // // // actor should have all of its components now, do any collision checking and handling that we need to do // switch (CollisionHandlingMethod) // { // case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn: // // Try to find a spawn position // { // FVector AdjustedLocation = NewLocation; // FRotator AdjustedRotation = NewRotation; // if (FindTeleportSpot(Actor, AdjustedLocation, AdjustedRotation)) // { // Actor->SetActorLocationAndRotation(AdjustedLocation, AdjustedRotation); // } // } // break; // case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding: // // Try to find a spawn position // { // FVector AdjustedLocation = NewLocation; // FRotator AdjustedRotation = NewRotation; // if (FindTeleportSpot(Actor, AdjustedLocation, AdjustedRotation)) // { // Actor->SetActorLocationAndRotation(AdjustedLocation, AdjustedRotation); // } // else // { // UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *NewLocation.ToString(), *Class->GetName()); // DestroyActor(Actor); // return nullptr; // } // } // break; // case ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding: // // #todo: don't recheck components checked above? // if (EncroachingBlockingGeometry(Actor, NewLocation, NewRotation)) // { // UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *NewLocation.ToString(), *Class->GetName()); // DestroyActor(Actor); // return nullptr; // } // break; // // note we use "always spawn" as default, so treat undefined as that // case ESpawnActorCollisionHandlingMethod::Undefined: // case ESpawnActorCollisionHandlingMethod::AlwaysSpawn: // default: // // nothing to do, just proceed as normal // break; // } Actor->CheckDefaultSubobjects(); // Broadcast notification of spawn OnActorSpawned.Broadcast(Actor); #if WITH_EDITOR if (GIsEditor) { GEngine->BroadcastLevelActorAdded(Actor); } #endif return Actor; }