My program doesn't have bugs. It just develops random features.

What happens at map startup

From Unreal Wiki, The Unreal Engine Documentation Site
Revision as of 04:03, 12 February 2010 by Wormbo (Talk | contribs) (started page, will add verbal list of events later)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The information in this article are based on a snippet of native UT2004 code posted by Steve Polge on Epic Game's ut2003mods mailing list. The code can be found as UnGame.cpp in the Engine\Src directory of the UT2004 UnrealScript source code download.

The actual UnGame.cpp snippet

The following is a copy of the code snippet this article is based on. This particular piece of code is executed after a new map has been loaded and before the local player enters the game or any replication kicks in. Keep in mind that much of the initialization also happens at the UnrealScript level, so for a complete picture you should have the UT2004 sources ready and browse to the relevant UnrealScript functions.

Hints for reading the code

A few things that will help understanding the code even if you're not into C++:

  • The -> operator does the same as the dot does in UnrealScript - it accessed variables or functions of an object.
  • GLevel is a Level object. In UnrealScript this object is referenced by the XLevel property of any Actor.
  • GLevel->GetLevelInfo() returns the actual LevelInfo object. The Info property also points to the LevelInfo.
  • Variables in UnrealScript objects have the same name in C++. For example Info->bBegunPlay refers to a variable you'd access as Level.bBegunPlay in UnrealScript code.
  • The LevelInfo's bBegunPlay acts as general gate to UnrealScript execution. During map load it is 0 (False), so no UnrealScript code will be executed until it is set to 1 (True) in this code snippet.
  • UnrealScript functions declared with the event keyword are called from C++ via eventNameOfUScriptFunction(parameters), for example eventPreBeginPlay calls the PreBeginPlay() function.
  • Dynamic arrays have a slightly different syntax in native code. They are declared as TArray<type>, their length is returned via array.Num() and individual elements are accessed via round parentheses instead of square brackets. array.Empty() does what aray.Length = 0; would do in UnrealScript.
  • GLevel->IsServer() basically returns the same as (Level.NetMode != NM_Client) in UnrealScript. Similarly GIsClient basically returns the same as Level.NetMode != NM_DedicatedServer in UnrealScript.
  • The GLevel->Actors array is the same list that the AllActors iterator traverses in UnrealScript.
  • The native class name of Actor subclasses is prefixed with an A, the native name of non-Actor classes with a U and the native name of structs with an F, for example APhysicsVolume, UKarmaParams or FVector.
  • Name literals do not exist. Instead, the values of all natively-used names are hard-coded as NAME_name, for example NAME_None for 'None'.
  • Class literals do exist, but look very different from UnrealScript. They are expressed as nativeClassName::StaticClass().
  • bScriptInitialized is an Actor property that is set to True in the actually points to an object, similar to the UnrealScript code if (someObjectReference' != None).
  • Values for bool variables are 0 and 1, not False and True.
  • GetClass()->GetDefaultActor() corresponds to UnrealScript's .default. syntax for accessing the default variable values of a class.

General notes about this code snippet

  • When running as a (listen and dedicated) server, actors mentioned in the [Engine.GameEngine] ServerActors list in the main INI file (e.g. UnrealTournament.ini, UT2004.ini or UTEngine.ini) are created before this code is executed. UnrealScript execution is not yet enabled at that point, so these actors are initialized like any other actor already in the map.
  • Players do not exist while this code is executed. Any (local or remote) players join "much" later.

Notes for Unreal Engine 1

  • The bScriptInitialized variable does not exist in Unreal Engine 1, so unlike in later engine generations actors spawned during initialization may receive *BeginPlay() calls twice.
  • Unreal Engine 1 does not have volumes or Karma physics, so these parts of the code here do not apply.

Notes for Unreal Engine 3

  • The Actor's Level variable and the LevelInfo class are both called WorldInfo in Unreal Engine 3.
  • The BeginPlay() and PostNetBeginPlay() functions have been removed, with PostBeginPlay() now taking PostNetBeginPlay()'s role.
  • Unreal Engine 3 does not use zones, so the related parts of the code here do not apply.
  • Initialization of levels streamed in at runtime is not covered by this article.
  • Kismet initialization is not covered in this article. (More research is required on this.)

The code

/*=============================================================================
	UnGame.cpp: Unreal game engine.
	Copyright 1997-2003 Epic Games, Inc. All Rights Reserved.
 
=============================================================================*/
 
	GLevel->iFirstDynamicActor = 0;
	if( !Info->bBegunPlay )
	{
        appResetTimer(); // sjs
 
		// fix up level problems
		FixUpLevel();
 
		// Update draw distance.
		if (GIsClient)
		{
			GLevel->GetLevelInfo()->InitDistanceFogLOD();
			GLevel->GetLevelInfo()->UpdateDistanceFogLOD( Client->DrawDistanceLOD );
		}
 
		// Lock the level.
		debugf( NAME_Log, TEXT("Bringing %s up for play (%i) appSeconds: %f..."), GLevel->GetFullName(), appRound(GetMaxTickRate()), appSeconds() ); // sjs
		GLevel->FinishedPrecaching = 0;
		GLevel->TimeSeconds = 0;
		GLevel->GetLevelInfo()->TimeSeconds = 0;
		GLevel->GetLevelInfo()->GetDefaultPhysicsVolume()->bNoDelete = true;
 
		// Kill off actors that aren't interesting to the client.
		if( !GLevel->IsServer() )
		{
			for( INT i=0; i<GLevel->Actors.Num(); i++ )
			{
				AActor* Actor = GLevel->Actors(i);
				if( Actor )
				{
					if( Actor->bStatic || Actor->bNoDelete || Actor->IsA(AxEmitter::StaticClass()) ) 
					{
						if ( !Actor->bClientAuthoritative )
							Exchange( Actor->Role, Actor->RemoteRole );
					}
					else
						GLevel->DestroyActor( Actor );
				}
			}
		}
 
		// Init touching actors & clear LastRenderTime
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) )
			{
				GLevel->Actors(i)->LastRenderTime = 0.f;
				GLevel->Actors(i)->Touching.Empty();
				GLevel->Actors(i)->PhysicsVolume = GLevel->GetLevelInfo()->GetDefaultPhysicsVolume();
			}
 
 
		// Init scripting.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) )
				GLevel->Actors(i)->InitExecution();
 
		// Enable actor script calls.
		Info->bBegunPlay = 1;
		Info->bStartup = 1;
		Info->TimeDilation = ((ALevelInfo *)(Info->GetClass()->GetDefaultActor()))->TimeDilation;
 
#ifdef WITH_KARMA
		if(!GIsEditor && !GLevel->GetLevelInfo()->bKNoInit)
		{
			KInitLevelKarma(GLevel);
 
			for( INT i=0; i<GLevel->Actors.Num(); i++ )
				if( GLevel->Actors(i) )
					KInitActorKarma( GLevel->Actors(i) );
		}
#endif
 
		// Init the game.
		if( Info->Game )
		{		
			Info->Game->eventInitGame( Options, Error );
			Info->Game->eventSetGrammar();
		}
 
		// Send PreBeginPlay.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
				GLevel->Actors(i)->eventPreBeginPlay();
 
		// Set BeginPlay.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
				GLevel->Actors(i)->eventBeginPlay();
 
		// Set zones && gather volumes.
		TArray<AVolume*> LevelVolumes;
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
		{
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
				GLevel->Actors(i)->SetZone( 1, 1 );
 
			AVolume* Volume = Cast<AVolume>(GLevel->Actors(i));
			if( Volume )
				LevelVolumes.AddItem(Volume);
		}
 
		// Set appropriate volumes for each actor.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
				GLevel->Actors(i)->SetVolumes( LevelVolumes );
 
		// Post begin play.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
			{
				GLevel->Actors(i)->eventPostBeginPlay();
 
				if(GLevel->Actors(i))
					GLevel->Actors(i)->PostBeginPlay();
			}
 
		// Post net begin play.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
				GLevel->Actors(i)->eventPostNetBeginPlay();
 
		// Begin scripting.
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
			if( GLevel->Actors(i) && !GLevel->Actors(i)->bScriptInitialized )
				GLevel->Actors(i)->eventSetInitialState();
 
		// Find bases
		for( INT i=0; i<GLevel->Actors.Num(); i++ )
		{
			if( GLevel->Actors(i) ) 
			{
				if ( GLevel->Actors(i)->AttachTag != NAME_None )
				{
					//find actor to attach self onto
					for( INT j=0; j<GLevel->Actors.Num(); j++ )
					{
						if( GLevel->Actors(j) 
							&& ((GLevel->Actors(j)->Tag == GLevel->Actors(i)->AttachTag) || (GLevel->Actors(j)->GetFName() == GLevel->Actors(i)->AttachTag))  )
						{
							GLevel->Actors(i)->SetBase(GLevel->Actors(j), FVector(0,0,1), 0);
							break;
						}
					}
				}
				else if( GLevel->Actors(i)->bCollideWorld && GLevel->Actors(i)->bShouldBaseAtStartup
				 &&	((GLevel->Actors(i)->Physics == PHYS_None) || (GLevel->Actors(i)->Physics == PHYS_Rotating)) )
				{
					 GLevel->Actors(i)->FindBase();
				}
			}
		}
 
		for( INT i=0; i<GLevel->Actors.Num(); i++ ) 
		{
			if(GLevel->Actors(i))
			{
				if( GLevel->Actors(i)->IsA(AProjector::StaticClass())) // sjs - why is this needed?!!
				{
					GLevel->Actors(i)->PostEditChange();
				}
 
#ifdef WITH_KARMA
				AActor* actor = GLevel->Actors(i);
 
				if(actor->Physics != PHYS_Karma || !actor->KParams || !actor->KParams->IsA(UKarmaParams::StaticClass()))
					continue;
 
				UKarmaParams* kparams = Cast<UKarmaParams>(actor->KParams);
 
				// If running below HighDetailPhysics, turn off karma dynamics for actors with bHighDetailOnly set true.
				if(	GLevel->GetLevelInfo()->PhysicsDetailLevel < PDL_High && kparams->bHighDetailOnly )
					KTermActorDynamics(actor);
 
				// If dedicated server, turn off karma for actors with bHighDetailOnly or bClientsOnly
				if(	GLevel->GetLevelInfo()->NetMode == NM_DedicatedServer && (kparams->bHighDetailOnly || kparams->bClientOnly) )					
					KTermActorDynamics(actor);
#endif
			}
		}
 
		Info->bStartup = 0;
	}