// Called every frame void ACart::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); auto World = GetWorld(); for (int32 i = 0; i < 4; i++) { FHitResult HitResult(ForceInit); FCollisionQueryParams Params = FCollisionQueryParams(FName(TEXT("Trace")), true, this); Params.bTraceComplex = true; //Params.bTraceAsyncScene = true; Params.bReturnPhysicalMaterial = true; const FName TraceTag("MyTraceTag"); World->DebugDrawTraceTag = TraceTag; Params.TraceTag = TraceTag; bool Hit = World->LineTraceSingleByChannel(HitResult, Arrows[i]->GetComponentLocation(), Arrows[i]->GetComponentLocation() + Arrows[i]->GetForwardVector() * (SpringLength + WheelRadius), ECC_PhysicsBody, Params); if (Hit) { FVector Force = HitResult.ImpactNormal * SpringStrength * BodyMesh->GetMass() * (1 - HitResult.Distance / (SpringLength + WheelRadius)); BodyMesh->AddForceAtLocation(Force, Arrows[i]->GetComponentLocation()); Wheels[i]->SetWorldLocation(HitResult.Location - (Arrows[i]->GetForwardVector() * WheelRadius)); } else { Wheels[i]->SetWorldLocation(Arrows[i]->GetComponentLocation() + Arrows[i]->GetForwardVector() * SpringLength); } } }
HitResult SphereObject::GetCollision( const Ray& ray ) const { HitResult Res = HitResult( ); // Calculate ray start's offset from the sphere center CPPEngine::Vector3 p = ray.Start - this->Center; float rSquared = this->Radius * this->Radius; float p_d = p.Dot( ray.Direction ); // The sphere is behind the start point. if ( p_d < 0 ) return Res; // The sphere contains the ray's start point. if( p.LengthSquared( ) < rSquared ) { Res.Hit = true; Res.Position = ray.Start; return Res; } // Flatten p into the plane passing through c perpendicular to the ray. // This gives the closest approach of the ray to the center. CPPEngine::Vector3 a = p - ray.Direction * p_d; float aSquared = a.LengthSquared( ); // Closest approach is outside the sphere. if ( aSquared > rSquared ) return Res; // Calculate distance from plane where ray enters/exits the sphere. float h = sqrt( rSquared - aSquared ); // Calculate intersection point relative to sphere center. CPPEngine::Vector3 i = a - h * ray.Direction; CPPEngine::Vector3 intersection = this->Center + i; CPPEngine::Vector3 normal = i / this->Radius; // We've taken a shortcut here to avoid a second square root. // Note numerical errors can make the normal have length slightly different from 1. // If you need higher precision, you may need to perform a conventional normalization. Res.Position = intersection; Res.Normal = normal; Res.DistanceSquared = ( intersection - ray.Start ).LengthSquared( ); Res.Object = this; Res.Hit = true; return Res; }
void ATraceWeapon::Attack() { Super::Attack(); FVector Start = GetActorLocation(); FVector Direction = Instigator->GetActorRotation().Vector(); FVector End = Start + Direction * Range; FHitResult Impact = WeaponTrace(Start, End); HitResult(Impact, Start, Direction); }
/** * Read actor method * Sets up structure, which contains information about a trace hit. Point of impact and surface normal at that point. * Runs the ray trace method, firing a ray out infront of the character upon keypress of "read". * * @params N/A * @return N/A **/ void APlayerCharacter::Read() { FHitResult HitResult(EForceInit::ForceInit); bool bSuccessTrace = TraceFromSelf(HitResult, 150.0f, ECollisionChannel::ECC_EngineTraceChannel1); // Fire ray trace. if (bSuccessTrace) { AActor* const HitActor = HitResult.GetActor(); if (HitActor) { ASign* SignActor = Cast<class ASign>(HitActor); // Get actor collided with. if (SignActor) { FString const SignMessage = SignActor->GetMessage(); // Take message from Sig GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Yellow, SignMessage); // Display as debug message. } } } }
////////////////////////////////////////////////////////////////////////// // lineCast BcBool ScnPhysicsWorldComponent::lineCast( const MaVec3d& A, const MaVec3d& B, MaVec3d& Intersection, MaVec3d& Normal ) { btCollisionWorld::ClosestRayResultCallback HitResult( btVector3( A.x(), A.y(), A.z() ), btVector3( B.x(), B.y(), B.z() ) ); DynamicsWorld_->rayTest( btVector3( A.x(), A.y(), A.z() ), btVector3( B.x(), B.y(), B.z() ), HitResult ); if( HitResult.hasHit() ) { Intersection = MaVec3d( HitResult.m_hitPointWorld.x(), HitResult.m_hitPointWorld.y(), HitResult.m_hitPointWorld.z() ); Normal = MaVec3d( HitResult.m_hitNormalWorld.x(), HitResult.m_hitNormalWorld.y(), HitResult.m_hitNormalWorld.z() ); return BcTrue; } return BcFalse; }
void UFlareAsteroidComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); float CollisionSize = GetCollisionShape().GetExtent().Size(); EffectsUpdateTimer += DeltaTime; // Get player ship AFlareGame* Game = Cast<AFlareGame>(GetWorld()->GetAuthGameMode()); FCHECK(Game); AFlarePlayerController* PC = Game->GetPC(); FCHECK(PC); AFlareSpacecraft* ShipPawn = PC->GetShipPawn(); // Update if close to player and visible if (ShipPawn && (ShipPawn->GetActorLocation() - GetComponentLocation()).Size() < 500000 && (GetWorld()->TimeSeconds - LastRenderTime) < 0.5) { if (EffectsUpdateTimer > EffectsUpdatePeriod) { // World data FVector AsteroidLocation = GetComponentLocation(); FVector SunDirection = Game->GetPlanetarium()->GetSunDirection(); SunDirection.Normalize(); // Compute new FX locations for (int32 Index = 0; Index < Effects.Num(); Index++) { FVector RandomDirection = FVector::CrossProduct(SunDirection, EffectsKernels[Index]); RandomDirection.Normalize(); FVector StartPoint = AsteroidLocation + RandomDirection * CollisionSize; // Trace params FHitResult HitResult(ForceInit); FCollisionQueryParams TraceParams(FName(TEXT("Asteroid Trace")), false, NULL); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; ECollisionChannel CollisionChannel = ECollisionChannel::ECC_WorldDynamic; // Trace bool FoundHit = GetWorld()->LineTraceSingleByChannel(HitResult, StartPoint, AsteroidLocation, CollisionChannel, TraceParams); if (FoundHit && HitResult.Component == this && Effects[Index]) { FVector EffectLocation = HitResult.Location; if (!Effects[Index]->IsActive()) { Effects[Index]->Activate(); } Effects[Index]->SetWorldLocation(EffectLocation); Effects[Index]->SetWorldRotation(SunDirection.Rotation()); } else { Effects[Index]->Deactivate(); } } EffectsUpdateTimer = 0; } } // Disable all else { for (int32 Index = 0; Index < Effects.Num(); Index++) { Effects[Index]->Deactivate(); } } }
void UFlareSpacecraftDamageSystem::OnCollision(class AActor* Other, FVector HitLocation, FVector NormalImpulse) { // If receive hit from over actor, like a ship we must apply collision damages. // The applied damage energy is 0.2% of the kinetic energy of the other actor. The kinetic // energy is calculated from the relative speed between the 2 actors, and only with the relative // speed projected in the axis of the collision normal: if 2 very fast ship only slightly touch, // only few energy will be decipated by the impact. // // The damages are applied only to the current actor, the ReceiveHit method of the other actor // will also call an it will apply its collision damages itself. // If the other actor is a projectile, specific weapon damage code is done in the projectile hit // handler: in this case we ignore the collision AFlareShell* OtherProjectile = Cast<AFlareShell>(Other); if (OtherProjectile) { return; } // No primitive component, ignore UPrimitiveComponent* OtherRoot = Cast<UPrimitiveComponent>(Other->GetRootComponent()); if (!OtherRoot) { return; } // Ignore debris AStaticMeshActor* OtherActor = Cast<AStaticMeshActor>(Other); if (OtherActor) { if (OtherActor->GetName().StartsWith("Debris")) { return; } } // Relative velocity FVector DeltaVelocity = ((OtherRoot->GetPhysicsLinearVelocity() - Spacecraft->Airframe->GetPhysicsLinearVelocity()) / 100); // Compute the relative velocity in the impact axis then compute kinetic energy /*float ImpactSpeed = DeltaVelocity.Size(); float ImpactMass = FMath::Min(Spacecraft->GetSpacecraftMass(), OtherRoot->GetMass()); */ //200 m /s -> 6301.873047 * 20000 -> 300 / 2 damage float ImpactSpeed = 0; float ImpactEnergy = 0; float ImpactMass = Spacecraft->GetSpacecraftMass(); // Check if the mass was set and is valid if (ImpactMass > KINDA_SMALL_NUMBER) { ImpactSpeed = NormalImpulse.Size() / (ImpactMass * 100.f); ImpactEnergy = ImpactMass * ImpactSpeed / 8402.f; } float Radius = 0.2 + FMath::Sqrt(ImpactEnergy) * 0.11; //FLOGV("OnCollision %s", *Spacecraft->GetImmatriculation().ToString()); //FLOGV(" OtherRoot->GetPhysicsLinearVelocity()=%s", *OtherRoot->GetPhysicsLinearVelocity().ToString()); //FLOGV(" OtherRoot->GetPhysicsLinearVelocity().Size()=%f", OtherRoot->GetPhysicsLinearVelocity().Size()); //FLOGV(" Spacecraft->Airframe->GetPhysicsLinearVelocity()=%s", *Spacecraft->Airframe->GetPhysicsLinearVelocity().ToString()); //FLOGV(" Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()=%f", Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()); //FLOGV(" dot=%f", FVector::DotProduct(DeltaVelocity.GetUnsafeNormal(), HitNormal.GetUnsafeNormal())); /*FLOGV(" DeltaVelocity=%s", *DeltaVelocity.ToString()); FLOGV(" ImpactSpeed=%f", ImpactSpeed); FLOGV(" ImpactMass=%f", ImpactMass); FLOGV(" ImpactEnergy=%f", ImpactEnergy); FLOGV(" Radius=%f", Radius);*/ bool HasHit = false; FHitResult BestHitResult; float BestHitDistance = 0; for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]); if (Component) { FHitResult HitResult(ForceInit); FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; Component->LineTraceComponent(HitResult, HitLocation, HitLocation + Spacecraft->GetLinearVelocity().GetUnsafeNormal() * 10000, TraceParams); if (HitResult.Actor.IsValid()){ float HitDistance = (HitResult.Location - HitLocation).Size(); if (!HasHit || HitDistance < BestHitDistance) { BestHitDistance = HitDistance; BestHitResult = HitResult; } //FLOGV("Collide hit %s at a distance=%f", *Component->GetReadableName(), HitDistance); HasHit = true; } } } if (HasHit) { //DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Magenta, true); } else { int32 BestComponentIndex = -1; BestHitDistance = 0; for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]); if (Component) { float ComponentDistance = (Component->GetComponentLocation() - HitLocation).Size(); if (BestComponentIndex == -1 || BestHitDistance > ComponentDistance) { BestComponentIndex = ComponentIndex; BestHitDistance = ComponentDistance; } } } UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[BestComponentIndex]); FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; Component->LineTraceComponent(BestHitResult, HitLocation, Component->GetComponentLocation(), TraceParams); //DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Yellow, true); } AFlareSpacecraft* OtherSpacecraft = Cast<AFlareSpacecraft>(Other); UFlareCompany* DamageSource = NULL; if (OtherSpacecraft) { DamageSource = OtherSpacecraft->GetParent()->GetCompany(); LastDamageCauser = OtherSpacecraft; } else { LastDamageCauser = NULL; } ApplyDamage(ImpactEnergy, Radius, BestHitResult.Location, EFlareDamage::DAM_Collision, DamageSource); }