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.
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();
Back to TOC
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
}
Back to TOC
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)
}
Back to TOC
PLTextActorInterface.cpp
// ------------------------------------------------------------------------------------------------
// ___ __ ___ ___ ®
// Copyright © 2009-2010, All rights reserved __)| /\ \__|| / \ / | /
// Playlogic games factory, Breda | |___/__\ __||___\__/ \__| | \___
//
// ------------------------------------------------------------------------------------------------
#include "PLBase.h"
IMPLEMENT_CLASS( UPLTextActorInterface );
Back to TOC
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 );
Back to TOC
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 );
}
}
}
Back to TOC
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
}
Back to TOC
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());
}
Back to TOC
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
Back to TOC