/** * Issues level streaming load/unload requests based on whether * players are inside/outside level streaming volumes. */ void UWorld::ProcessLevelStreamingVolumes(FVector* OverrideViewLocation) { // if we are delaying using streaming volumes, return now if( StreamingVolumeUpdateDelay > 0 ) { StreamingVolumeUpdateDelay--; return; } // Option to skip indefinitely. else if( StreamingVolumeUpdateDelay == INDEX_NONE ) { return; } SCOPE_CYCLE_COUNTER( STAT_VolumeStreamingTickTime ); // Begin by assembling a list of kismet streaming objects that have non-EditorPreVisOnly volumes associated with them. // @todo DB: Cache this, e.g. level startup. TArray<ULevelStreaming*> LevelStreamingObjectsWithVolumes; TMap<ULevelStreaming*,bool> LevelStreamingObjectsWithVolumesOtherThanBlockingLoad; for( int32 LevelIndex = 0 ; LevelIndex < StreamingLevels.Num() ; ++LevelIndex ) { ULevelStreaming* LevelStreamingObject = StreamingLevels[LevelIndex]; if( LevelStreamingObject ) { for ( int32 i = 0 ; i < LevelStreamingObject->EditorStreamingVolumes.Num() ; ++i ) { ALevelStreamingVolume* StreamingVolume = LevelStreamingObject->EditorStreamingVolumes[i]; if( StreamingVolume && !StreamingVolume->bEditorPreVisOnly && !StreamingVolume->bDisabled ) { LevelStreamingObjectsWithVolumes.Add( LevelStreamingObject ); if( StreamingVolume->StreamingUsage != SVB_BlockingOnLoad ) { LevelStreamingObjectsWithVolumesOtherThanBlockingLoad.Add( LevelStreamingObject, true ); } break; } } } } // The set of levels with volumes whose volumes current contain player viewpoints. TMap<ULevelStreaming*,FVisibleLevelStreamingSettings> VisibleLevelStreamingObjects; // Iterate over all players and build a list of level streaming objects with // volumes that contain player viewpoints. bool bStreamingVolumesAreRelevant = false; for( FConstPlayerControllerIterator Iterator = GetPlayerControllerIterator(); Iterator; ++Iterator ) { APlayerController* PlayerActor = *Iterator; if (PlayerActor->bIsUsingStreamingVolumes) { bStreamingVolumesAreRelevant = true; FVector ViewLocation(0,0,0); // let the caller override the location to check for volumes if (OverrideViewLocation) { ViewLocation = *OverrideViewLocation; } else { FRotator ViewRotation(0,0,0); PlayerActor->GetPlayerViewPoint( ViewLocation, ViewRotation ); } TMap<AVolume*,bool> VolumeMap; // Iterate over streaming levels with volumes and compute whether the // player's ViewLocation is in any of their volumes. for( int32 LevelIndex = 0 ; LevelIndex < LevelStreamingObjectsWithVolumes.Num() ; ++LevelIndex ) { ULevelStreaming* LevelStreamingObject = LevelStreamingObjectsWithVolumes[ LevelIndex ]; // StreamingSettings is an OR of all level streaming settings of volumes containing player viewpoints. FVisibleLevelStreamingSettings StreamingSettings; // See if level streaming settings were computed for other players. FVisibleLevelStreamingSettings* ExistingStreamingSettings = VisibleLevelStreamingObjects.Find( LevelStreamingObject ); if ( ExistingStreamingSettings ) { // Stop looking for viewpoint-containing volumes once all streaming settings have been enabled for the level. if ( ExistingStreamingSettings->AllSettingsEnabled() ) { continue; } // Initialize the level's streaming settings with settings that were computed for other players. StreamingSettings = *ExistingStreamingSettings; } // For each streaming volume associated with this level . . . for ( int32 i = 0 ; i < LevelStreamingObject->EditorStreamingVolumes.Num() ; ++i ) { ALevelStreamingVolume* StreamingVolume = LevelStreamingObject->EditorStreamingVolumes[i]; if ( StreamingVolume && !StreamingVolume->bEditorPreVisOnly && !StreamingVolume->bDisabled ) { bool bViewpointInVolume; bool* bResult = VolumeMap.Find(StreamingVolume); if ( bResult ) { // This volume has already been considered for another level. bViewpointInVolume = *bResult; } else { // Compute whether the viewpoint is inside the volume and cache the result. bViewpointInVolume = StreamingVolume->EncompassesPoint( ViewLocation ); VolumeMap.Add( StreamingVolume, bViewpointInVolume ); INC_DWORD_STAT( STAT_VolumeStreamingChecks ); } if ( bViewpointInVolume ) { // Copy off the streaming settings for this volume. StreamingSettings |= FVisibleLevelStreamingSettings( (EStreamingVolumeUsage) StreamingVolume->StreamingUsage ); // Update the streaming settings for the level. // This also marks the level as "should be loaded". VisibleLevelStreamingObjects.Add( LevelStreamingObject, StreamingSettings ); // Stop looking for viewpoint-containing volumes once all streaming settings have been enabled. if ( StreamingSettings.AllSettingsEnabled() ) { break; } } } } } // for each streaming level } // bIsUsingStreamingVolumes } // for each PlayerController // do nothing if no players are using streaming volumes if (bStreamingVolumesAreRelevant) { // Iterate over all streaming levels and set the level's loading status based // on whether it was found to be visible by a level streaming volume. for( int32 LevelIndex = 0 ; LevelIndex < LevelStreamingObjectsWithVolumes.Num() ; ++LevelIndex ) { ULevelStreaming* LevelStreamingObject = LevelStreamingObjectsWithVolumes[LevelIndex]; // Figure out whether level should be loaded and keep track of original state for notifications on change. FVisibleLevelStreamingSettings* NewStreamingSettings= VisibleLevelStreamingObjects.Find( LevelStreamingObject ); bool bShouldAffectLoading = LevelStreamingObjectsWithVolumesOtherThanBlockingLoad.Find( LevelStreamingObject ) != NULL; bool bShouldBeLoaded = (NewStreamingSettings != NULL); bool bOriginalShouldBeLoaded = LevelStreamingObject->bShouldBeLoaded; bool bOriginalShouldBeVisible = LevelStreamingObject->bShouldBeVisible; bool bOriginalShouldBlockOnLoad = LevelStreamingObject->bShouldBlockOnLoad; int32 bOriginalLODIndex = LevelStreamingObject->LevelLODIndex; if( bShouldBeLoaded || bShouldAffectLoading ) { if( bShouldBeLoaded ) { // Loading. LevelStreamingObject->bShouldBeLoaded = true; LevelStreamingObject->bShouldBeVisible = NewStreamingSettings->ShouldBeVisible( bOriginalShouldBeVisible ); LevelStreamingObject->bShouldBlockOnLoad = NewStreamingSettings->ShouldBlockOnLoad(); } // Prevent unload request flood. The additional check ensures that unload requests can still be issued in the first UnloadCooldownTime seconds of play. else if( TimeSeconds - LevelStreamingObject->LastVolumeUnloadRequestTime > LevelStreamingObject->MinTimeBetweenVolumeUnloadRequests || LevelStreamingObject->LastVolumeUnloadRequestTime < 0.1f ) { //UE_LOG(LogLevel, Warning, TEXT("Unloading") ); if( GetPlayerControllerIterator() ) { LevelStreamingObject->LastVolumeUnloadRequestTime = TimeSeconds; LevelStreamingObject->bShouldBeLoaded = false; LevelStreamingObject->bShouldBeVisible = false; } } // Notify players of the change. if( bOriginalShouldBeLoaded != LevelStreamingObject->bShouldBeLoaded || bOriginalShouldBeVisible != LevelStreamingObject->bShouldBeVisible || bOriginalShouldBlockOnLoad != LevelStreamingObject->bShouldBlockOnLoad || bOriginalLODIndex != LevelStreamingObject->LevelLODIndex) { for( FConstPlayerControllerIterator Iterator = GetPlayerControllerIterator(); Iterator; ++Iterator ) { APlayerController* PlayerController = *Iterator; PlayerController->LevelStreamingStatusChanged( LevelStreamingObject, LevelStreamingObject->bShouldBeLoaded, LevelStreamingObject->bShouldBeVisible, LevelStreamingObject->bShouldBlockOnLoad, LevelStreamingObject->LevelLODIndex); } } } } } }
// // ARobotCameraActor::Tick // void ARobotCameraActor::Tick( float DeltaSeconds ) { auto World = GetWorld(); if( World != nullptr ) { // Get each player's pawn and add to the cumulative position. We'll look at the average of // this. uint8 PositionCount = 0; FVector MinLocation = FVector::ZeroVector; FVector MaxLocation = FVector::ZeroVector; for( auto Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator ) { auto RobotPlayer = Cast<ARobotPlayerController>( *Iterator ); if( RobotPlayer == nullptr ) { continue; } if( !RobotPlayer->GetIsIn() ) { continue; } auto Pawn = RobotPlayer->GetPawn(); if( Pawn == nullptr ) { continue; } auto PawnLocation = Pawn->GetActorLocation(); ++PositionCount; if( PositionCount == 1 ) { MinLocation = PawnLocation; MaxLocation = PawnLocation; continue; } // Find bounds. if( PawnLocation.X < MinLocation.X ) { MinLocation.X = PawnLocation.X; } if( PawnLocation.X > MaxLocation.X ) { MaxLocation.X = PawnLocation.X; } if( PawnLocation.Y < MinLocation.Y ) { MinLocation.Y = PawnLocation.Y; } if( PawnLocation.Y > MaxLocation.Y ) { MaxLocation.Y = PawnLocation.Y; } // Make sure we look at the top of the box so we can see the entire thing. if( PawnLocation.Z > MaxLocation.Z ) { MaxLocation.Z = PawnLocation.Z; } } // Create a box outlining the bounds of each player character. Scale the bounds so characters aren't on the very edge of the screen. FVector Size = FVector( FMath::Abs( MaxLocation.X - MinLocation.X ), FMath::Abs( MaxLocation.Y - MinLocation.Y ), 0.0f ); FVector Center = FVector( MinLocation.X + ( Size.X / 2.0f ), MinLocation.Y + ( Size.Y / 2.0f ), 0.0f ); FVector ScaledMax = MaxLocation * BoundsScale; FVector ScaledMin = MinLocation * BoundsScale; FVector ScaledSize = Size * BoundsScale; // How far do we need to zoom to make sure everyone fits? float MaxHorizontal = ScaledSize.Y / ( 2 * FMath::Tan( FMath::DegreesToRadians( CameraComponent->FieldOfView / 2 ) ) ); float MaxVertical = ScaledSize.X / ( 2 * FMath::Tan( FMath::DegreesToRadians( ( CameraComponent->FieldOfView / CameraComponent->AspectRatio ) / 2 ) ) ); float WantedArmLength = FMath::Max<float>( MaxHorizontal, MaxVertical ); NextArmLength = FMath::Clamp<float>( WantedArmLength, MinArmLength, MaxArmLength ); // Look at center of the box. if( PositionCount != 0 ) { FVector LastLocation = GetActorLocation(); NextPosition = FVector( MaxVertical <= MaxArmLength ? Center.X : LastLocation.X, MaxHorizontal <= MaxArmLength ? Center.Y : LastLocation.Y, MaxLocation.Z ); } } // Extrapolate to next length/location. SetActorLocation( FMath::Lerp( GetActorLocation(), NextPosition, FMath::Min( DeltaSeconds * ExtrapolateSpeed, 1.0f ) ) ); SpringArmComponent->TargetArmLength = FMath::Lerp( SpringArmComponent->TargetArmLength, NextArmLength, FMath::Min( DeltaSeconds * ExtrapolateSpeed, 1.0f ) ); }