void ALeapMotionHandActor::UpdateBones(float DeltaSeconds) { if (BoneActors.Num() == 0) { return; } float CombinedScale = GetCombinedScale(); FLeapMotionDevice* Device = FLeapMotionControllerPlugin::GetLeapDeviceSafe(); if (Device && Device->IsConnected()) { int BoneArrayIndex = 0; for (ELeapBone LeapBone = bShowArm ? ELeapBone::Forearm : ELeapBone::Palm; LeapBone <= ELeapBone::Finger4Tip; ((int8&)LeapBone)++) { FVector TargetPosition; FRotator TargetOrientation; bool Success = Device->GetBonePostionAndOrientation(HandId, LeapBone, TargetPosition, TargetOrientation); if (Success) { // Offset target position & rotation by the SpawnReference actor's transform FQuat RefQuat = GetRootComponent()->GetComponentRotation().Quaternion(); TargetPosition = RefQuat * TargetPosition * CombinedScale + GetRootComponent()->GetComponentLocation(); TargetOrientation = (RefQuat * TargetOrientation.Quaternion()).Rotator(); // Get current position & rotation ALeapMotionBoneActor* BoneActor = BoneActors[BoneArrayIndex++]; UPrimitiveComponent* PrimitiveComponent = Cast<UPrimitiveComponent>(BoneActor->GetRootComponent()); if (PrimitiveComponent && PrimitiveComponent->IsSimulatingPhysics()) { FVector CurrentPositon = PrimitiveComponent->GetComponentLocation(); FRotator CurrentRotation = PrimitiveComponent->GetComponentRotation(); // Compute linear velocity FVector LinearVelocity = (TargetPosition - CurrentPositon) / DeltaSeconds; // Compute angular velocity FVector Axis; float Angle; ConvertDeltaRotationsToAxisAngle(CurrentRotation, TargetOrientation, Axis, Angle); if (Angle > PI) { Angle -= 2 * PI; } FVector AngularVelcity = Axis * (Angle / DeltaSeconds); // Apply velocities PrimitiveComponent->SetPhysicsLinearVelocity(LinearVelocity); PrimitiveComponent->SetAllPhysicsAngularVelocity(AngularVelcity * 180.0f / PI); } } } } }
/** Kicks this ball to a location */ void AMagicBattleSoccerBall::KickToLocation(const FVector& Location, float AngleInDegrees) { if (nullptr == Possessor) { // Safety check. The possessor must be valid. } else { // Reset the possessor SetPossessor(nullptr); // Calculate the angle required to hit the coordinate (x,y) on the plane containing // the origin and ground point. The x coordinate is the distance from the ball // to where the user clicked, and the y coordinate should be zero. // http://en.wikipedia.org/wiki/Trajectory_of_a_projectile FVector Origin = GetActorLocation(); float x = FVector::Dist(Location, Origin); float y = 0.f; float g = -980.f; // Standard gravity float r = AngleInDegrees * 3.14159f / 180.f; float p = std::tan(r); float v = std::sqrt((p*p + 1.f)*(g*x*x) / (2.f * (y - x*p))); float forceMag = v; FVector forward = (Location - Origin); forward.Z = 0; // We only support kicking to locations that are on the ball's Z plane forward.Normalize(); FVector cross = FVector::CrossProduct(forward, FVector::UpVector); FVector kick = forward * FMath::Cos(r) * forceMag; kick.Z = FMath::Sin(r) * forceMag; UPrimitiveComponent *Root = Cast<UPrimitiveComponent>(GetRootComponent()); Root->SetPhysicsLinearVelocity(kick); Root->SetPhysicsAngularVelocity(cross * forceMag * -4.f); } }
void UBuoyancyForceComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); // If disabled or we are not attached to a parent component, return. if (!bIsActive || !GetAttachParent()) return; if (!OceanManager) return; UPrimitiveComponent* BasePrimComp = Cast<UPrimitiveComponent>(GetAttachParent()); if (!BasePrimComp) return; if (!BasePrimComp->IsSimulatingPhysics()) { if (!SnapToSurfaceIfNoPhysics) return; UE_LOG(LogTemp, Warning, TEXT("Running in no physics mode..")); float waveHeight = OceanManager->GetWaveHeightValue(BasePrimComp->GetComponentLocation(), World, true, TwoGerstnerIterations).Z; BasePrimComp->SetWorldLocation(FVector(BasePrimComp->GetComponentLocation().X, BasePrimComp->GetComponentLocation().Y, waveHeight)); return; } //Get gravity float Gravity = BasePrimComp->GetPhysicsVolume()->GetGravityZ(); //--------------- If Skeletal --------------- USkeletalMeshComponent* SkeletalComp = Cast<USkeletalMeshComponent>(GetAttachParent()); if (SkeletalComp && ApplyForceToBones) { TArray<FName> BoneNames; SkeletalComp->GetBoneNames(BoneNames); for (int32 Itr = 0; Itr < BoneNames.Num(); Itr++) { FBodyInstance* BI = SkeletalComp->GetBodyInstance(BoneNames[Itr], false); if (BI && BI->IsValidBodyInstance() && BI->bEnableGravity) //Buoyancy doesn't exist without gravity { bool isUnderwater = false; //FVector worldBoneLoc = SkeletalComp->GetBoneLocation(BoneNames[Itr]); FVector worldBoneLoc = BI->GetCOMPosition(); //Use center of mass of the bone's physics body instead of bone's location FVector waveHeight = OceanManager->GetWaveHeightValue(worldBoneLoc, World, true, TwoGerstnerIterations); float BoneDensity = MeshDensity; float BoneTestRadius = FMath::Abs(TestPointRadius); float SignedBoneRadius = FMath::Sign(Gravity) * TestPointRadius; //Direction of radius (test radius is actually a Z offset, should probably rename it!). Just in case we need an upside down world. //Get density & radius from the override array, if available. for (int pointIndex = 0; pointIndex < BoneOverride.Num(); pointIndex++) { FStructBoneOverride Override = BoneOverride[pointIndex]; if (Override.BoneName.IsEqual(BoneNames[Itr])) { BoneDensity = Override.Density; BoneTestRadius = FMath::Abs(Override.TestRadius); SignedBoneRadius = FMath::Sign(Gravity) * BoneTestRadius; } } //If test point radius is below water surface, add buoyancy force. if (waveHeight.Z > (worldBoneLoc.Z + SignedBoneRadius)) { isUnderwater = true; float DepthMultiplier = (waveHeight.Z - (worldBoneLoc.Z + SignedBoneRadius)) / (BoneTestRadius * 2); DepthMultiplier = FMath::Clamp(DepthMultiplier, 0.f, 1.f); float Mass = SkeletalComp->CalculateMass(BoneNames[Itr]); //Mass of this specific bone's physics body /** * -------- * Buoyancy force formula: (Volume(Mass / Density) * Fluid Density * -Gravity) / Total Points * Depth Multiplier * -------- */ float BuoyancyForceZ = Mass / BoneDensity * FluidDensity * -Gravity * DepthMultiplier; //Velocity damping. FVector DampingForce = -BI->GetUnrealWorldVelocity() * VelocityDamper * Mass * DepthMultiplier; //Experimental xy wave force if (EnableWaveForces) { float waveVelocity = FMath::Clamp(BI->GetUnrealWorldVelocity().Z, -20.f, 150.f) * (1 - DepthMultiplier); DampingForce += FVector(OceanManager->GlobalWaveDirection.X, OceanManager->GlobalWaveDirection.Y, 0) * Mass * waveVelocity * WaveForceMultiplier; } //Add force to this bone BI->AddForce(FVector(DampingForce.X, DampingForce.Y, DampingForce.Z + BuoyancyForceZ)); //BasePrimComp->AddForceAtLocation(FVector(DampingForce.X, DampingForce.Y, DampingForce.Z + BuoyancyForceZ), worldBoneLoc, BoneNames[Itr]); } //Apply fluid damping & clamp velocity if (isUnderwater) { BI->SetLinearVelocity(-BI->GetUnrealWorldVelocity() * (FluidLinearDamping / 10), true); BI->SetAngularVelocity(-BI->GetUnrealWorldAngularVelocity() * (FluidAngularDamping / 10), true); //Clamp the velocity to MaxUnderwaterVelocity if (ClampMaxVelocity && BI->GetUnrealWorldVelocity().Size() > MaxUnderwaterVelocity) { FVector Velocity = BI->GetUnrealWorldVelocity().GetSafeNormal() * MaxUnderwaterVelocity; BI->SetLinearVelocity(Velocity, false); } } if (DrawDebugPoints) { FColor DebugColor = FLinearColor(0.8, 0.7, 0.2, 0.8).ToRGBE(); if (isUnderwater) { DebugColor = FLinearColor(0, 0.2, 0.7, 0.8).ToRGBE(); } //Blue color underwater, yellow out of watter DrawDebugSphere(World, worldBoneLoc, BoneTestRadius, 8, DebugColor); } } } return; } //-------------------------------------------------------- float TotalPoints = TestPoints.Num(); if (TotalPoints < 1) return; int PointsUnderWater = 0; for (int pointIndex = 0; pointIndex < TotalPoints; pointIndex++) { if (!TestPoints.IsValidIndex(pointIndex)) return; //Array size changed during runtime bool isUnderwater = false; FVector testPoint = TestPoints[pointIndex]; FVector worldTestPoint = BasePrimComp->GetComponentTransform().TransformPosition(testPoint); FVector waveHeight = OceanManager->GetWaveHeightValue(worldTestPoint, World, !EnableWaveForces, TwoGerstnerIterations); //Direction of radius (test radius is actually a Z offset, should probably rename it!). Just in case we need an upside down world. float SignedRadius = FMath::Sign(BasePrimComp->GetPhysicsVolume()->GetGravityZ()) * TestPointRadius; //If test point radius is below water surface, add buoyancy force. if (waveHeight.Z > (worldTestPoint.Z + SignedRadius) && BasePrimComp->IsGravityEnabled()) //Buoyancy doesn't exist without gravity { PointsUnderWater++; isUnderwater = true; float DepthMultiplier = (waveHeight.Z - (worldTestPoint.Z + SignedRadius)) / (TestPointRadius * 2); DepthMultiplier = FMath::Clamp(DepthMultiplier, 0.f, 1.f); //If we have a point density override, use the overridden value instead of MeshDensity float PointDensity = PointDensityOverride.IsValidIndex(pointIndex) ? PointDensityOverride[pointIndex] : MeshDensity; /** * -------- * Buoyancy force formula: (Volume(Mass / Density) * Fluid Density * -Gravity) / Total Points * Depth Multiplier * -------- */ float BuoyancyForceZ = BasePrimComp->GetMass() / PointDensity * FluidDensity * -Gravity / TotalPoints * DepthMultiplier; //Experimental velocity damping using VelocityAtPoint. FVector DampingForce = -GetUnrealVelocityAtPoint(BasePrimComp, worldTestPoint) * VelocityDamper * BasePrimComp->GetMass() * DepthMultiplier; //Experimental xy wave force if (EnableWaveForces) { DampingForce += BasePrimComp->GetMass() * FVector2D(waveHeight.X, waveHeight.Y).Size() * FVector(OceanManager->GlobalWaveDirection.X, OceanManager->GlobalWaveDirection.Y, 0) * WaveForceMultiplier / TotalPoints; //float waveVelocity = FMath::Clamp(GetUnrealVelocityAtPoint(BasePrimComp, worldTestPoint).Z, -20.f, 150.f) * (1 - DepthMultiplier); //DampingForce += OceanManager->GlobalWaveDirection * BasePrimComp->GetMass() * waveVelocity * WaveForceMultiplier / TotalPoints; } //Add force for this test point BasePrimComp->AddForceAtLocation(FVector(DampingForce.X, DampingForce.Y, DampingForce.Z + BuoyancyForceZ), worldTestPoint); } if (DrawDebugPoints) { FColor DebugColor = FLinearColor(0.8, 0.7, 0.2, 0.8).ToRGBE(); if (isUnderwater) { DebugColor = FLinearColor(0, 0.2, 0.7, 0.8).ToRGBE(); } //Blue color underwater, yellow out of watter DrawDebugSphere(World, worldTestPoint, TestPointRadius, 8, DebugColor); } } //Clamp the velocity to MaxUnderwaterVelocity if there is any point underwater if (ClampMaxVelocity && PointsUnderWater > 0 && BasePrimComp->GetPhysicsLinearVelocity().Size() > MaxUnderwaterVelocity) { FVector Velocity = BasePrimComp->GetPhysicsLinearVelocity().GetSafeNormal() * MaxUnderwaterVelocity; BasePrimComp->SetPhysicsLinearVelocity(Velocity); } //Update damping based on number of underwater test points BasePrimComp->SetLinearDamping(_baseLinearDamping + FluidLinearDamping / TotalPoints * PointsUnderWater); BasePrimComp->SetAngularDamping(_baseAngularDamping + FluidAngularDamping / TotalPoints * PointsUnderWater); }
AFlareSpacecraft* UFlareSector::LoadSpacecraft(UFlareSimulatedSpacecraft* ParentSpacecraft) { AFlareSpacecraft* Spacecraft = NULL; FLOGV("UFlareSector::LoadSpacecraft : Start loading ('%s')", *ParentSpacecraft->GetImmatriculation().ToString()); // Spawn parameters FActorSpawnParameters Params; Params.bNoFail = true; Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; // Create and configure the ship Spacecraft = GetGame()->GetWorld()->SpawnActor<AFlareSpacecraft>(ParentSpacecraft->GetDescription()->Template->GeneratedClass, ParentSpacecraft->GetData().Location, ParentSpacecraft->GetData().Rotation, Params); if (Spacecraft && !Spacecraft->IsPendingKillPending()) { Spacecraft->Load(ParentSpacecraft); UPrimitiveComponent* RootComponent = Cast<UPrimitiveComponent>(Spacecraft->GetRootComponent()); if (Spacecraft->IsStation()) { SectorStations.Add(Spacecraft); } else { SectorShips.Add(Spacecraft); } SectorSpacecrafts.Add(Spacecraft); FLOGV("%s spawn mode = %d", *Spacecraft->GetImmatriculation().ToString(), ParentSpacecraft->GetData().SpawnMode+0) switch (ParentSpacecraft->GetData().SpawnMode) { // Already known to be correct case EFlareSpawnMode::Safe: FLOGV("UFlareSector::LoadSpacecraft : Safe spawn '%s' at (%f,%f,%f)", *ParentSpacecraft->GetImmatriculation().ToString(), ParentSpacecraft->GetData().Location.X, ParentSpacecraft->GetData().Location.Y, ParentSpacecraft->GetData().Location.Z); RootComponent->SetPhysicsLinearVelocity(ParentSpacecraft->GetData().LinearVelocity, false); RootComponent->SetPhysicsAngularVelocity(ParentSpacecraft->GetData().AngularVelocity, false); break; // First spawn case EFlareSpawnMode::Spawn: PlaceSpacecraft(Spacecraft, ParentSpacecraft->GetData().Location); { FVector NewLocation = Spacecraft->GetActorLocation(); FLOGV("UFlareSector::LoadSpacecraft : Placing '%s' at (%f,%f,%f)", *ParentSpacecraft->GetImmatriculation().ToString(), NewLocation.X, NewLocation.Y, NewLocation.Z); } RootComponent->SetPhysicsLinearVelocity(FVector::ZeroVector, false); RootComponent->SetPhysicsAngularVelocity(FVector::ZeroVector, false); break; // Incoming in sector case EFlareSpawnMode::Travel: { FLOGV("UFlareSector::LoadSpacecraft : Travel '%s' at (%f, %f, %f)", *ParentSpacecraft->GetImmatriculation().ToString(), ParentSpacecraft->GetData().Location.X, ParentSpacecraft->GetData().Location.Y, ParentSpacecraft->GetData().Location.Z); FVector SpawnDirection; TArray<AFlareSpacecraft*> FriendlySpacecrafts = GetCompanySpacecrafts(Spacecraft->GetCompany()); FVector FriendlyShipLocationSum = FVector::ZeroVector; int FriendlyShipCount = 0; for (int SpacecraftIndex = 0 ; SpacecraftIndex < FriendlySpacecrafts.Num(); SpacecraftIndex++) { AFlareSpacecraft *SpacecraftCandidate = FriendlySpacecrafts[SpacecraftIndex]; if (!SpacecraftCandidate->IsStation() && SpacecraftCandidate != Spacecraft) { FriendlyShipLocationSum += SpacecraftCandidate->GetActorLocation(); FriendlyShipCount++; } } if (FriendlyShipCount == 0) { FVector NotFriendlyShipLocationSum = FVector::ZeroVector; int NotFriendlyShipCount = 0; for (int SpacecraftIndex = 0 ; SpacecraftIndex < SectorShips.Num(); SpacecraftIndex++) { AFlareSpacecraft *SpacecraftCandidate = SectorShips[SpacecraftIndex]; if (SpacecraftCandidate != Spacecraft && SpacecraftCandidate->GetCompany() != Spacecraft->GetCompany()) { NotFriendlyShipLocationSum += SpacecraftCandidate->GetActorLocation(); NotFriendlyShipCount++; } } if (NotFriendlyShipCount == 0) { SpawnDirection = FMath::VRand(); } else { FVector NotFriendlyShipLocationMean = NotFriendlyShipLocationSum / NotFriendlyShipCount; SpawnDirection = (GetSectorCenter() - NotFriendlyShipLocationMean).GetUnsafeNormal(); } } else { FVector FriendlyShipLocationMean = FriendlyShipLocationSum / FriendlyShipCount; SpawnDirection = (FriendlyShipLocationMean - GetSectorCenter()).GetUnsafeNormal() ; } float SpawnDistance = GetSectorRadius() + 1; if (GetSimulatedSector()->GetSectorBattleState(Spacecraft->GetCompany()) != EFlareSectorBattleState::NoBattle) { SpawnDistance += 500000; // 5 km } SpawnDistance = FMath::Min(SpawnDistance, GetSectorLimits()); FVector Location = GetSectorCenter() + SpawnDirection * SpawnDistance; FVector CenterDirection = (GetSectorCenter() - Location).GetUnsafeNormal(); Spacecraft->SetActorRotation(CenterDirection.Rotation()); PlaceSpacecraft(Spacecraft, Location); float SpawnVelocity = 0; if (GetSimulatedSector()->GetSectorBattleState(Spacecraft->GetCompany()) != EFlareSectorBattleState::NoBattle) { SpawnVelocity = 10000; } RootComponent->SetPhysicsLinearVelocity(CenterDirection * SpawnVelocity, false); RootComponent->SetPhysicsAngularVelocity(FVector::ZeroVector, false); } break; case EFlareSpawnMode::Exit: { float SpawnDistance = GetSectorLimits() * 0.9; float SpawnVelocity = ParentSpacecraft->GetData().LinearVelocity.Size() * 0.6; FVector SpawnDirection = ParentSpacecraft->GetData().Location.GetUnsafeNormal(); FVector Location = SpawnDirection * SpawnDistance; FVector CenterDirection = (GetSectorCenter() - Location).GetUnsafeNormal(); FLOGV("UFlareSector::LoadSpacecraft : Exit '%s' at (%f, %f, %f)", *ParentSpacecraft->GetImmatriculation().ToString(), Location.X, Location.Y, Location.Z); PlaceSpacecraft(Spacecraft, Location); Spacecraft->SetActorRotation(CenterDirection.Rotation()); RootComponent->SetPhysicsLinearVelocity(CenterDirection * SpawnVelocity, false); RootComponent->SetPhysicsAngularVelocity(FVector::ZeroVector, false); } break; } ParentSpacecraft->SetSpawnMode(EFlareSpawnMode::Safe); }