Code Samples - Unreal

Here are some things I worked on at Playlogic. The files here display an example targeting interface used in the Fairytale Fights Spinoff as well as a rendered text actor. The text actors are meant for prototyping purposes, and allow designers and decorators to easily put readable text in the world.

Content:

PLTargetable.uc

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2010, All rights reserved                      __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// PLTargetable.uc: Interface for targetable objects                                               
// ------------------------------------------------------------------------------------------------


interface PLTargetable extends Interface;

// Get the actor that we target
simulated function Actor Targetable_GetActor();

// Get the location we target (can be different from that of the target actor)
simulated function Vector Targetable_GetLocation();

// The radius of the target, so we can adjust for range
simulated function float Targetable_GetRadius();

// Get the type of target, ex: Enemy, Player, Device, TargetPoint
simulated function name Targetable_GetType();

// Whether the target is active at this time
simulated function bool Targetable_CanBeTargeted();

PLTargetManager.uc

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2010, All rights reserved                      __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// PLTargetManager.uc: Manages all targetable objects in the                                       
// game. Using the PLTargetable interface.                                                         
// ------------------------------------------------------------------------------------------------


class PLTargetManager extends PLManager;

var array<PLTargetable> Targets;
var float LastCleanupTime;
var const float TimeBetweenCleanups;

function RegisterTargetable(PLTargetable Target)
{
	assert( Target != none );

	if ( Targets.Find(Target) == INDEX_NONE )
	{
		Targets.AddItem(Target);
	}
}

function UnregisterTargetable(PLTargetable Target)
{
	CleanTargets();
	Targets.RemoveItem(Target);
}

// Return a score for the target. Higher is better. Note: 0 means can not be targeted.
delegate float ScoreTarget( PLTargetable Target, Vector SearchOrigin, vector SearchDirection, PLTargetable PreviousTarget )
{
	return 1;
}

function PLTargetable FindTarget( Vector SearchOrigin, Vector SearchDirection, PLTargetable PreviousTarget, delegate<ScoreTarget> ScoringFunction, optional Actor InSourceActor)
{
	local PLTargetable Target;
	local PLTargetable BestTarget;
	local float BestScore;
	local float Score;

	CleanTargets();

	foreach Targets(Target)
	{
		if ( Target != none && Target.Targetable_GetActor() != InSourceActor && Target.Targetable_CanBeTargeted() )
		{
			Score = ScoringFunction( Target, SearchOrigin, SearchDirection, PreviousTarget );
			//INTENDED: Targets with score 0 can not be targeted. BestScore starts at 0.
			if ( Score > BestScore )
			{
				BestTarget = Target;
				BestScore = Score;
			}
		}
	}

	return BestTarget;
}

function array<PLTargetable> FindTargets( Vector SearchOrigin, Vector SearchDirection, PLTargetable PreviousTarget, delegate<ScoreTarget> ScoringFunction, optional Actor InSourceActor, optional out Array<float> OutScores)
{
	local PLTargetable Target;
	local array<PLTargetable> BestTargets;
	local array<float> Scores;
	local float Score;
	local int i;

	CleanTargets();

	foreach Targets(Target)
	{
		if ( Target != none && Target.Targetable_GetActor() != InSourceActor && Target.Targetable_CanBeTargeted() )
		{
			Score = ScoringFunction( Target, SearchOrigin, SearchDirection, PreviousTarget );
			if (Score > 0)
			{
				for ( i = 0; i < Scores.Length; ++i)
				{
					if ( Scores[i] < Score )
					{
						Scores.InsertItem(i, Score);
						BestTargets.InsertItem(i, Target);
						break;
					}
				}
				if ( i == Scores.Length )
				{
					Scores.AddItem(Score);
					BestTargets.AddItem(Target);
				}
			}
		}
	}
	OutScores = Scores;
	return BestTargets;
}

protected function CleanTargets(optional bool bForce)
{
	if ( WorldInfo.TimeSeconds - LastCleanupTime > TimeBetweenCleanups || bForce )
	{
		Targets.RemoveItem(none);
		LastCleanupTime = WorldInfo.TimeSeconds;
	}
}

defaultproperties
{
	TimeBetweenCleanups=30
}

PLTargetPoint.uc

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2010, All rights reserved                      __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// PLTargetPoint.uc: Target point for designers to place in levels                                 
// ------------------------------------------------------------------------------------------------


class PLTargetPoint extends TargetPoint
	implements(PLTargetable);

var() name TargetType;
var() float TargetRadius;
var() bool bCanBeTargeted;

replication
{
	if (bNetDirty)
		bCanBeTargeted;
}

// PLTargetable Implementation = START =

// Get the actor that we target
simulated function Actor Targetable_GetActor()
{
	return self;
}

// Get the location we target (can be different from that of the target actor)
simulated function Vector Targetable_GetLocation()
{
	return Location;
}

// The radius of the target, so we can adjust for range
simulated function float Targetable_GetRadius()
{
	return TargetRadius;
}

// Get the type of target, ex: Enemy, Player, Device, TargetPoint
simulated function name Targetable_GetType()
{
	return TargetType;
}

// Whether the target is active at this time
simulated function bool Targetable_CanBeTargeted()
{
	return bCanBeTargeted;
}

// PLTargetable Implementation = END =

event PostBeginPlay()
{
	Super.PostBeginPlay();

	WorldInfo.GetManager(class'PLTargetManager').RegisterTargetable(self);
}

simulated event Destroyed()
{
	super.Destroyed();

	WorldInfo.GetManager(class'PLTargetManager').UnregisterTargetable(self);
}

simulated function OnToggle(SeqAct_Toggle Action)
{
	if (Action.InputLinks[0].bHasImpulse)
	{
		bCanBeTargeted = true;
	}
	else if (Action.InputLinks[1].bHasImpulse)
	{
		bCanBeTargeted = false;
	}
	else
	{
		bCanBeTargeted = !bCanBeTargeted;
	}

	ForceNetRelevant();
	if (RemoteRole != ROLE_None)
	{
		SetForcedInitialReplicatedProperty(Property'PLBase.PLTargetPoint.bCanBeTargeted', (bCanBeTargeted == default.bCanBeTargeted));
	}
}

defaultproperties
{
	bCanBeTargeted=true
	TargetType=TargetPoint
	TargetRadius=0.0

	Begin Object Class=SpriteComponent Name=MySprite
		Sprite=Texture2D'EditorMaterials.TargetIcon'
		AlwaysLoadOnClient=False
		AlwaysLoadOnServer=False
		Scale=0.35
		HiddenEditor=True
	End Object
	Components.Add(MySprite)
}

PLTextActorInterface.cpp

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2009-2010, All rights reserved                 __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// ------------------------------------------------------------------------------------------------

#include "PLBase.h"

IMPLEMENT_CLASS( UPLTextActorInterface );

PLTextActorInterface.uc

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2010, All rights reserved                      __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// Common interface for actors using the text texture                                             
// ------------------------------------------------------------------------------------------------

interface PLTextActorInterface extends interface
	native;

cpptext
{
	/**
	 * Call-back when the text size has changed, sending the new text size and the actual UV scale
	 */
	virtual void TextSizeChanged( UObject* Sender, FVector2D const & NewTextSize, FVector2D const & NewUVs ) = 0;
}

/**
 * Set the text for this actor dynamically
 */
simulated event SetText( array<string> TextLines );

PLTextMesh.cpp

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2009-2010, All rights reserved                 __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// ------------------------------------------------------------------------------------------------

#include "PLBase.h"

IMPLEMENT_CLASS( APLTextMesh );

void APLTextMesh::InitTextTexture()
{
	// Ensure we have a texture
	if ( TextTexture == NULL )
	{
		TextTexture = ConstructObject< UPLTextTexture >( UPLTextTexture::StaticClass(), this );
	}

	// Ensure that this class is in the interfaces list
	new( TextTexture->TextActors ) TScriptInterface<class IPLTextActorInterface>( this );

	// Initialize the texture
	TextTexture->UpdateText();
}

void APLTextMesh::PostLoad()
{
	Super::PostLoad();

	// Ensure we have a properly initialized texture
	if ( GIsEditor && TextTexture == NULL )
	{
		InitTextTexture();
	}

	if ( GIsEditor && StaticMeshComponent != NULL )
	{
		// Ensure we have our own MIC, non-recurring
		UMaterialInstanceConstant* Material = Cast< UMaterialInstanceConstant >( StaticMeshComponent->GetMaterial( 0 ) );
		if ( Material != NULL && Material->IsIn( this ) && Material->Parent != NULL && Material->Parent->IsInA( StaticClass() ) )
		{
			UMaterialInterface* Parent = Material->Parent;
			while ( Parent != NULL && Parent->IsA( UMaterialInstanceConstant::StaticClass() ) && Parent->IsInA( StaticClass() ) )
			{
				UMaterialInstanceConstant* Current = CastChecked< UMaterialInstanceConstant >( Parent );
				Parent = Current->Parent;
			}
			Material->SetParent( Parent );
		}
	}
}

void APLTextMesh::PostEditImport()
{
	Super::PostEditImport();

	// Ensure we have our own MIC
	UMaterialInstanceConstant* Material = Cast< UMaterialInstanceConstant >( StaticMeshComponent->GetMaterial( 0 ) );

	if ( Material != NULL && Material->GetOuter() != NULL && Material->IsInA( StaticClass() ) )
	{
		StaticMeshComponent->SetMaterial( 0, Material->Parent );
	}
	TextTexture->TextActors.Reset();
	InitTextTexture();
}

void APLTextMesh::Spawned()
{
	Super::Spawned();

	// Just spawned, initialize texture
	InitTextTexture();
}

void APLTextMesh::TextSizeChanged( UObject* Sender, FVector2D const & NewTextSize, FVector2D const & NewUVs )
{
	FComponentReattachContext ReattachContext( StaticMeshComponent );

	// Update mesh width and height
	if ( StaticMeshComponent->StaticMesh != NULL )
	{
		StaticMeshComponent->Scale3D = FVector( 1.0f, NewTextSize.X / Max< FLOAT >( SMALL_NUMBER, StaticMeshComponent->StaticMesh->Bounds.BoxExtent.Y ), NewTextSize.Y / Max< FLOAT >( SMALL_NUMBER, StaticMeshComponent->StaticMesh->Bounds.BoxExtent.Z ) );
	}
	else
	{
		StaticMeshComponent->Scale3D = FVector( NewTextSize.X, 0.0f, NewTextSize.Y );
	}

	// Ensure we have our own MIC
	UMaterialInstanceConstant* Material = Cast< UMaterialInstanceConstant >( StaticMeshComponent->GetMaterial( 0 ) );

	if ( Material == NULL || !Material->IsIn( this ) )
	{
		Material = ConstructObject< UMaterialInstanceConstant >( UMaterialInstanceConstant::StaticClass(), this );
		check( Material );

		UMaterialInterface* ParentMat = StaticMeshComponent->GetMaterial( 0 );
		Material->SetParent( ParentMat );

		StaticMeshComponent->SetMaterial( 0, Material );
	}

	// Update parameters
	Material->SetTextureParameterValue( TextureParameterName, TextTexture );
	Material->SetVectorParameterValue( UVParameterName, FLinearColor( NewUVs.X, NewUVs.Y, 0.0, 0.0 ) );
}

void APLTextMesh::CheckForErrors()
{
	Super::CheckForErrors();

	// Ensure we have our own MIC
	UMaterialInstanceConstant* Material = Cast< UMaterialInstanceConstant >( StaticMeshComponent->GetMaterial( 0 ) );
	if ( Material != NULL && Material->IsIn( this ) && Material->Parent != NULL )
	{
		if ( Material->Parent->IsIn( this ) )
		{
			GWarn->MapCheck_Add( MCTYPE_ERROR, this, *FString::Printf( TEXT( "%s: Multiple Recursive Material, please recreate!" ), *GetName() ), MCACTION_DELETE );
		}
		else if ( Material->Parent->IsInA( StaticClass() ) )
		{
			GWarn->MapCheck_Add( MCTYPE_ERROR, this, *FString::Printf( TEXT( "%s: Cross-actor material reference, please recreate!" ), *GetName() ), MCACTION_DELETE );
		}
	}
}

PLTextMesh.uc

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2010, All rights reserved                      __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// Decal that uses a text texture to show text                                                     
// ------------------------------------------------------------------------------------------------

class PLTextMesh extends StaticMeshActor
	implements(PLTextActorInterface)
	placeable
	native;

// Name of the texture parameter in the material to put the text on
var() name TextureParameterName;

// Name of the UV parameter in the material to send the UVs to
var() name UVParameterName;

// The actual dynamic texture
var() editinline editconst instanced PLTextTexture TextTexture;

cpptext
{
	// AActor interface
	virtual void Spawned();
	virtual void CheckForErrors();
	
	// UObject interface
	virtual void PostLoad();
	virtual void PostEditImport();

	// PLTextActorInterface
	virtual void TextSizeChanged( UObject* Sender, FVector2D const & NewTextSize, FVector2D const & NewUVs );
}

// Ensure we have an initialized texture we can use
protected native function InitTextTexture();

// Set the text dynamicaly
reliable multicast event SetText( array< string > TextLines )
{
	if ( TextTexture == none )
	{
		InitTextTexture();
	}

	TextTexture.SetText( TextLines );
}

defaultproperties
{
	bStatic=true
	bMovable=false
	
	TextureParameterName=Text;
	UVParameterName=UV;

	begin object class=PLTextTexture Name=Tex
	end object
	TextTexture=Tex

	DrawScale=0.5

	bBlockActors=false;
	bBlocksNavigation=false;
	bCollideActors=false;

	Begin Object Name=StaticMeshComponent0
		bAcceptsStaticDecals=false
		bAcceptsDynamicDecals=false
		StaticMesh=StaticMesh'EditorMeshes.TexPropPlane'
		Materials(0)=Material'MAT_Text.TextMaterials.TwosidedTextMaterial'

		BlockActors=false;
		BlockRigidBody=false;
		BlockZeroExtent=false;
		BlockNonZeroExtent=false;
	End Object
}

PLTextTextureResource.cpp

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2009-2010, All rights reserved                 __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// ------------------------------------------------------------------------------------------------

#include "PLBase.h"
#include "PLTextTextureResource.h"

FPLTextTextureResource::FPLTextTextureResource( const class UPLTextTexture* InOwner )
	: FTextureRenderTarget2DResource( InOwner )
	, Owner( InOwner )
{
	if ( InOwner != NULL )
	{
		TextLines = InOwner->TextLines;
		TextSizeX = appTrunc( InOwner->TextSize.X );
		TextSizeY = appTrunc( InOwner->TextSize.Y );
		TextColor = FLinearColor( InOwner->TextColor );
		BackgroundColor = FLinearColor( InOwner->BackgroundColor );
		bMaskedText = InOwner->bMaskedText;
	}
}

void FPLTextTextureResource::UpdateResource()
{
	// clear the target surface
	RHISetRenderTarget(RenderTargetSurfaceRHI,FSurfaceRHIRef());
	RHISetViewport(0,0,0.0f,TextSizeX,TextSizeY,1.0f);
	RHIClear(TRUE,BackgroundColor,FALSE,0.f,FALSE,0);

	FCanvas Canvas( this, NULL );
	if ( Owner != NULL && Owner->TextFont != NULL )
	{
		// Render the text onto the canvas
		UFont* TextFont = Owner->TextFont;
		FLOAT Y = 0.0f;

		for ( TArray< FString >::TIterator Text( TextLines ); Text; ++Text )
		{
			INT Size = TextFont->GetStringSize( **Text );
			DrawString( &Canvas, TextSizeX * 0.5f - Size * 0.5f, Y, **Text, TextFont, TextColor, 1.0, 1.0, 0.0, NULL, bMaskedText ? SE_BLEND_Masked : SE_BLEND_Translucent );
			Y += TextFont->GetMaxCharHeight();
		}
	}
	// Flush the canvas
	Canvas.Flush();
	// copy surface to the texture for use
	RHICopyToResolveTarget(RenderTargetSurfaceRHI, TRUE, FResolveParams());
}

PLTextTextureResource.h

// ------------------------------------------------------------------------------------------------
//                                                           ___                  __   ___    ___ ®    
// Copyright © 2009-2010, All rights reserved                 __)|    /\ \__||   /  \ /    | /    
// Playlogic games factory, Breda                            |   |___/__\ __||___\__/ \__| | \___   
//
// Resource for the Text Texture                      
//
// ------------------------------------------------------------------------------------------------

#ifndef __PLTEXTTEXTURERESOURCE_H__
#define __PLTEXTTEXTURERESOURCE_H__

struct FPLTextTextureResource : public FTextureRenderTarget2DResource
{
public:
	/** 
	* Constructor
	* @param InOwner - 2d texture object to create a resource for
	*/
	FPLTextTextureResource( const class UPLTextTexture* InOwner );

	/**
	* Clear contents of the render target
	*/
	virtual void UpdateResource();

private:
	/** The UTextureRenderTarget2D which this resource represents. */
	const class UPLTextTexture* Owner;
	TArray< FString > TextLines;
	INT TextSizeX, TextSizeY;
	FLinearColor TextColor, BackgroundColor;
	UBOOL bMaskedText;
};

#endif