| Home Page | Recent Changes | Preferences

Ultimate FlashLight 2k3

A really nice flashlight for UT2003. [Watch it in action]

Summary

I spent a few days trying to create a fairly realistic flashlight using projectors, then switched to dynamic lights after i didn't get the specific look i wanted, at the same time discovering they had a random darkening effect when projected onto certain terrain. This worked out better for the terrain issue, but i felt it looked too drab. What i ended up deciding on was a dynamic projector to get a focal point with adjustable FOV, and then keeping the light for ambience. To follow the FOV adjustment of the projector, i'm manually adjusting the LightRadius of the dynamic light, which is dependant on a trace done every tick. The resulting effect was quite stunning, and i decided it was too good to go unshared.

On a side note, i made my projective texture light-affected, which seems to partially cure the darkening effect on terrain.

What You Need

  1. A texture for the projector. I use a 256x256, DXT1 compressed bitmap. The full info is commented below.

Effect_TacLightProjector.uc

Here is where everything takes place. bHasLight is toggled from an outside control. All you need to do is call spawn(class'Effect_TacLightProjector',Owner); where "Owner" is the pawn that will be owning the light. Then set its bHasLight variable, and the actor will take care of the rest.

//=============================================================================
//  2003 Matt 'SquirrelZero' Farber
//=============================================================================
// "UT2k3 Ultimate Flashlight"
// I wrote this using _both_ a dynamic projector and a dynamic light, with the 
// dynamic projector as the focal point and lightsource providing general 
// ambient illumination.  Either one by itself is pretty dull, but combine the 
// two and you've got yourself a really nice effect if done correctly.
//=============================================================================
class Effect_TacLightProjector extends DynamicProjector;

// add a light too, there seems to be a problem with the projector darkening terrain sometimes
var Effect_TacLightGlow TacLightGlow;

var Pawn LightPawn;
var Controller LightController;
var bool bHasLight;

replication
{
    // relevant variables needed by the client
    reliable if (Role == ROLE_Authority)
        LightPawn, LightController;

    // relevant variables needed by the client
    reliable if (Role == ROLE_Authority)
        bHasLight;
}

// setup the pawn and controller variables, spawn the dynamic light
simulated function PostBeginPlay()
{
    if( bProjectActor )
        SetCollision(True, False, False);
    if (Owner != None)
    {
        LightPawn = Pawn(Owner);
        LightController = LightPawn.Controller;
    }
    if (TacLightGlow == None && Role == ROLE_Authority)
    {
        TacLightGlow = spawn(class'Effect_TacLightGlow');
    }
}

// updates the taclight projector and dynamic light positions
simulated function Tick(float DeltaTime)
{
    local vector StartTrace,EndTrace,X,Y,Z,HitLocation,HitNormal;
    local material SurfaceMat;
    local float BeamLength;

    // we're changing its location and rotation, so detach it
    DetachProjector();

    // fallback
    if (LightPawn == None || LightController == None || !bHasLight)
    {
        if (TacLightGlow != None)
            TacLightGlow.bDynamicLight = false;
        return;
    }

    // ok, first we need to get a location directly in front of the player
    StartTrace = LightPawn.Location + LightPawn.EyePosition();
    GetAxes(LightController.GetViewRotation(),X,Y,Z);

    // not too far out, we don't want a flashlight that can shine across the map
    EndTrace = StartTrace + 1800*vector(LightController.GetViewRotation());
    Trace(HitLocation,HitNormal,EndTrace,StartTrace,true,,SurfaceMat);

    // find out how far the first hit was
    BeamLength = VSize(StartTrace-HitLocation);

    // clear the base, we're going to be doing some adjustments
    if (Base != None);
        SetBase(none);

    // this makes a neat focus effect when you get close to a wall
    if (BeamLength <= 90)
    {
        SetLocation(StartTrace+vector(LightController.GetViewRotation())*Max((0.2*BeamLength), 1));
        SetBase(LightPawn);
        SetRotation(LightController.GetViewRotation());
        SetDrawScale(FMax(0.02,(BeamLength/90))*Default.DrawScale);
    }
    // else we project it normally, positioning slightly in front of the player
    else 
    {
        SetLocation(StartTrace+vector(LightController.GetViewRotation())*80);
        SetBase(LightPawn);
        SetRotation(LightController.GetViewRotation());
        SetDrawScale(Default.DrawScale);
    }

    // reattach it
    AttachProjector();

    // turns the dynamic light on if it's off
    if (!TacLightGlow.bDynamicLight)
        TacLightGlow.bDynamicLight = true;

    // better than tracing for the actor, makes it more projector-like
    if (SurfaceMat != None)
    {
        // again, neat focus effect up close, starts earlier than the dynamic projector
        if (BeamLength <= 100)
        {
            TacLightGlow.LightBrightness = TacLightGlow.Default.LightBrightness * (1.0 + (1.0 - (BeamLength/100)));
            TaclightGlow.LightRadius = TacLightGlow.Default.LightRadius * FMax(0.3,(BeamLength/100));
        }
        // else we scale its radius and brightness depending on distance from the material
        else
        {
            // fades the lightsource out as it moves farther away
            if (BeamLength >= 1300)
                TacLightGlow.LightBrightness = TacLightGlow.Default.LightBrightness * ((1800-BeamLength)/500);
            // else normal brightness
            else
                TacLightGlow.LightBrightness = TacLightGlow.Default.LightBrightness;

            // this makes the light act more like a spotlight, resizing depending on distance
            TacLightGlow.LightRadius = TacLightGlow.Default.LightRadius + (4.5 * (BeamLength/1900));
        }
        TacLightGlow.SetLocation(HitLocation-vector(LightController.GetViewRotation())*64);
        TacLightGlow.SetRotation(rotator(HitLocation-StartTrace));

    }
    // else if the trace returned nothing, then we put the light right in front of the player, scale its brightness down,
    // and give it normal radius.  This makes it seem as if the light is casting a dull glow  onto the ground in front 
    // of the player, and is dim enough for players to not notice the transition.
    else
    {
        TacLightGlow.LightRadius = TacLightGlow.Default.LightRadius;
        TacLightGlow.LightBrightness = 50;
        TacLightGlow.SetLocation(StartTrace+vector(LightController.GetViewRotation())*100);
    }
}

defaultproperties
{
    // for my projtexture, i'm using a 256x256 DXT1 texture, created in the same way as the bulldog headlights --
    // just a bright white circle dead center on a black background.  Make sure the texture is set to TC_Clamp
    // or it'll repeat itself over the whole surface.
    ProjTexture=Texture'FOMisc.Projected.TacLight'
    MaterialBlendingOp=PB_Modulate
    FrameBufferBlendingOp=PB_Add
    MaxTraceDistance=1600
    bClipBSP=True
    bProjectOnUnlit=True
    bGradient=True
    bProjectOnAlpha=True
    bProjectOnParallelBSP=True
    bLightChanged=True
    RemoteRole=ROLE_SimulatedProxy
    DrawScale=0.21000
    FOV=5
}

Effect_TacLightGlow.uc

A dynamic light used by the Effect_TacLightProjector class to provide extra ambience.

//=============================================================================
//  2003 Matt 'SquirrelZero' Farber
//=============================================================================
class Effect_TacLightGlow extends Light;

function PostBeginPlay()
{
    SetTimer(1.0,True);
}

// makes bots "see" the light when it's on
function Timer()
{
    MakeNoise(0.3);
}

defaultproperties
{
    Texture=S_Light
    LightType=LT_Steady
    LightEffect=LE_None
    LightBrightness=180
    LightSaturation=255
    LightRadius=3.0
    LightPeriod=34
    CollisionRadius=+0005.000000
    CollisionHeight=+0005.000000
    bHidden=true
    bStatic=false
    bNoDelete=false
    bMovable=true
    bDynamicLight=false
    bDirectional=true
    RemoteRole=ROLE_SimulatedProxy
}

Applications

In my case, i created a specialized exec function in my PlayerController class for toggling the light on or off, named ToggleItem(). Since my light will only be used by specific weapons and not all the time, the exec then calls my custom function SpecialFire(), declared in my Weapon class, on the currently held weapon. By default SpecialFire() is a blank function, but on taclight weapons here is where i insert my trigger for the light. This then calls a client→server (Reliable if Role < ROLE_Authority) replicated function, and the server spawns the light if it doesn't exist, else toggles it on or off if it does (bHasLight = true; or bHasLight = false;). The Owner of the light is always the weapon's Instigator, since by default the Instigator is replicated in the WeaponClass. To see how to specify the Owner when spawning the projector, look at the spawn() function in the Actor class.

Final Note

That's all there is to it, the projector actor handles nearly everything on its own. If you intend to use this method, please contact me and let me know, only because i'd like to hear how you made use of it.

Comments

MythOpus: Wow, this looks good. I've been looking for a flshlight for awhile now. My search is over :D I have a question though... how did you get this ingame and how do you turn it on ?? I've compiled it.. what next?

SquirrelZero: Thanks =]. I added an 'applications' section, to explain how i put it to use.

MythOpus: I see... Way outta my league :) So this is for Face Off then ?

SquirrelZero: nah, different mod coming out soon. [Frag.Ops]

Foxpaw: Could this page be renamed to something simpler, like just Flashlight or Creating a flashlight or something to that effect? "Ultimate Flashlight 2K3" sound like "Xtreme Advertising" run amok.

SquirrelZero: what am i advertising? This a free script. I'm not forcing anyone to use it over another one. Ultimate FlashLight 2k3 is the name of the program, i made it up in exactly 5 seconds, because honestly i didn't care what it was called as long as it was useful for someone. I mean cmon, that's along the same lines as asking someone to rename their mutator or mod. I'm sorry that my choice doesn't agree with you, but i think you're a bit too "Xtreme" yourself.

Mosquito: IN BEFORE THE LOCK!! wait... this isn't a forum.... oh well. Hey? how did you record the video WITH sound?!

Dark Pulse: He probably dumped Pictures repeatedly (I know there's a console command for this) and then Did a Rip of Audio using total Recorder or something, then bundled them together.

(v)adOnion: Be glad he's sharing his code - a lot of people don't and you'll have to figure stuff out all on your own.

Bslayerw: SquirrelZero, FragOps looks awesome. Thanks for sharing the code. It's people like you that make the modding community a better place. Sharing code like this is very valuable.

RegularX: Ditto here SZ. I just started researching into some good flashlight code, had kinda wondered about a path like this, then found this page. Exactly what I was looking for. Thanks for sharing, this will shave off a lot of frustration I think :)

Category Custom Class

The Unreal Engine Documentation Site

Wiki Community

Topic Categories

Image Uploads

Random Page

Recent Changes

Offline Wiki

Unreal Engine

Console Commands

Terminology

Mapping Topics

Mapping Lessons

UnrealEd Interface

Questions&Answers

Scripting Topics

Scripting Lessons

Making Mods

Class Tree

Questions&Answers

Modeling Topics

Questions&Answers

Log In