void UBuoyancyForceComponent::InitializeComponent() { Super::InitializeComponent(); //UE_LOG(LogTemp, Warning, TEXT("We're initializing...")); //Store the world ref. World = GetWorld(); // If no OceanManager is defined, auto-detect if (!OceanManager) { for (TActorIterator<AOceanManager> ActorItr(World); ActorItr; ++ActorItr) { OceanManager = Cast<AOceanManager>(*ActorItr); break; } } TestPointRadius = FMath::Abs(TestPointRadius); UPrimitiveComponent* BasePrimComp = Cast<UPrimitiveComponent>(GetAttachParent()); if (BasePrimComp) { ApplyUprightConstraint(BasePrimComp); //Store the initial damping values. _baseLinearDamping = BasePrimComp->GetLinearDamping(); _baseAngularDamping = BasePrimComp->GetAngularDamping(); } }
void UBuoyancyComponent::InitializeComponent() { Super::InitializeComponent(); // If no OceanManager is defined auto-detect if (!OceanManager) { for (TActorIterator<AOceanManager> ActorItr(GetWorld()); ActorItr; ++ActorItr) { OceanManager = Cast<AOceanManager>(*ActorItr); break; } } ApplyUprightConstraint(); TestPointRadius = FMath::Abs(TestPointRadius); //Signed based on gravity, just in case we need an upside down world _SignedRadius = FMath::Sign(GetGravityZ()) * TestPointRadius; if (UpdatedPrimitive->IsValidLowLevel()) { _baseLinearDamping = UpdatedPrimitive->GetLinearDamping(); _baseAngularDamping = UpdatedPrimitive->GetAngularDamping(); } }
void UBuoyancyComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { if (!OceanManager || !UpdatedComponent || !UpdatedPrimitive) return; if (!UpdatedComponent->IsSimulatingPhysics()) { FVector waveHeight = OceanManager->GetWaveHeightValue(UpdatedComponent->GetComponentLocation()); UpdatedPrimitive->SetWorldLocation(FVector(UpdatedComponent->GetComponentLocation().X, UpdatedComponent->GetComponentLocation().Y, waveHeight.Z), true); return; } //ApplyUprightConstraint is apparently needed again at first tick for BP-updated components. //TODO: there has to be a better way than this(?), PostInitialize(?) if (!_hasTicked) { _hasTicked = true; ApplyUprightConstraint(); } 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 = UpdatedComponent->GetComponentTransform().TransformPosition(testPoint); float waveHeight = OceanManager->GetWaveHeightValue(worldTestPoint).Z; //If test point radius is touching water add buoyancy force if (waveHeight > (worldTestPoint.Z + _SignedRadius)) { PointsUnderWater++; isUnderwater = true; float DepthMultiplier = (waveHeight - (worldTestPoint.Z + _SignedRadius)) / (TestPointRadius * 2); DepthMultiplier = FMath::Clamp(DepthMultiplier, 0.f, 1.f); //If we have a point density override, use the overriden value insted 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 = UpdatedPrimitive->GetMass() / PointDensity * FluidDensity * -GetGravityZ() / TotalPoints * DepthMultiplier; //Experimental velocity damping using GetUnrealWorldVelocityAtPoint! FVector DampingForce = -GetVelocityAtPoint(UpdatedPrimitive, worldTestPoint) * VelocityDamper * UpdatedPrimitive->GetMass() * DepthMultiplier; //Wave push force if (EnableWaveForces) { float waveVelocity = FMath::Clamp(GetVelocityAtPoint(UpdatedPrimitive, worldTestPoint).Z, -20.f, 150.f) * (1 - DepthMultiplier); DampingForce += FVector(OceanManager->GlobalWaveDirection.X, OceanManager->GlobalWaveDirection.Y, 0) * UpdatedPrimitive->GetMass() * waveVelocity * WaveForceMultiplier / TotalPoints; } //Add force for this test point UpdatedPrimitive->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(GetWorld(), worldTestPoint, TestPointRadius, 8, DebugColor); } } //Clamp the velocity to MaxUnderwaterVelocity if there is any point underwater if (ClampMaxVelocity && PointsUnderWater > 0 && UpdatedPrimitive->GetPhysicsLinearVelocity().Size() > MaxUnderwaterVelocity) { FVector Velocity = UpdatedPrimitive->GetPhysicsLinearVelocity().GetSafeNormal() * MaxUnderwaterVelocity; UpdatedPrimitive->SetPhysicsLinearVelocity(Velocity); } //Update damping based on number of underwater test points UpdatedPrimitive->SetLinearDamping(_baseLinearDamping + FluidLinearDamping / TotalPoints * PointsUnderWater); UpdatedPrimitive->SetAngularDamping(_baseAngularDamping + FluidAngularDamping / TotalPoints * PointsUnderWater); }