Sniper's Paradise!
Night Vision Goggles
This tutorial outlines one means of achieving a night vision goggles effect, the code is complete, but you must create the necessary textures yourself.
Night vision goggles can be broken down into two parts, the first is providing better illumination and the second is flashy graphics. The essential part is devising a means of illuminating objects & scenery for one player's view only (thus most likely a client side only effect). The approach I've taken is to use two techniques for this. The first is to adjust the player's view glow to brighten their view (and colour it as part of the flashy effects). The second is to create an effect that encompasses other players making them more visible. To provide the nice graphic effect all we use is a 256x256 animated texture which we overlay onto the player's view.
To be able to draw onto the player's view, I've used a HUD mutator, which allows us to hook into PostRender() and draw our animated texture. This approach is used by the UT relics, so the relic code can be used as a basis to work from.
// HUD Mutator to handle drawing NVG overlay class NVGMutator extends Mutator;
var PlayerPawn PlayerOwner; var float LastChecked; var NVG aNVG;
// If the mutator hasn't been registered, register it simulated function Tick(float DeltaTime) { if (Level.NetMode == NM_DedicatedServer || bHUDMutator) Disable('Tick'); else RegisterHUDMutator(); }
// RegisterHUDMutator that keeps track of the HUD's owner simulated function RegisterHUDMutator() { local PlayerPawn P;
ForEach AllActors(class'PlayerPawn', P) if ( P.IsA('PlayerPawn') && (P.myHUD != None) ) { NextHUDMutator = P.myHud.HUDMutator; P.myHUD.HUDMutator = Self; bHUDMutator = True; PlayerOwner = P; } }
// If the player has NVG, draw overlay simulated function PostRender(canvas C) { local int i; local Inventory inv;
if ( PlayerOwner == None ) { Destroy(); if ( NextHUDMutator != None ) NextHUDMutator.PostRender( C ); return; } if ( PlayerOwner == ChallengeHUD(PlayerOwner.MyHUD).PawnOwner ) { // Delay checking of inventory if ( Level.TimeSeconds - LastChecked > 0.3 ) { LastChecked = Level.TimeSeconds; inv = PlayerOwner.Inventory; while (inv != None ) { if ( inv.IsA('NVG') ) { aNVG = NVG(inv); break; } inv = inv.inventory; i++; if ( i > 200 ) inv = none; } } // Draw overlay if NVG activated if ( aNVG != None && aNVG.bEnabled ) { C.SetPos(0, 0); C.Style = ERenderStyle.STY_Translucent; C.DrawIcon(Texture'Static_a00', FMax(C.ClipX, C.ClipY)/256.0); // Assumes 256x256 texture } }
if ( NextHUDMutator != None ) NextHUDMutator.PostRender( C ); }
defaultproperties { aNVG=None bAlwaysRelevant=True bNetTemporary=True RemoteRole=Role_SimulatedProxy }
The above HUD mutator periodically checks the player's inventory to see if they're carrying a an NVG object, if they are and it's activated then it overlays their display with an animated texture (a nice static effect like the guided redeemer has is ideal). It avoids checking the player's inventory too frequently to help reduce the performance hit when using the night vision goggles (more on this later). Note that as it's a HUD mutator most of the work goes on client side.
< p>Next comes the actual NVG inventory object, this ties everything together. It's job is to ensure that the player using it has the necessary HUD mutator in play and to implement the remaining functionality for the night vision goggles. It also provides an exec function for turning the night vision goggles on or off.// Night vision goggles class NVG extends TournamentPickup;
var float Range; // Illumination range of NVG var bool bEnabled; // NVG in use? var int tickCount;
replication { // Variables replicated from server to client reliable if (bNetOwner && ROLE == ROLE_Authority) bEnabled; // Functions called on the server reliable if (Role < ROLE_Authority) NVGToggle; }
// Toggle NVG exec function NVGToggle() { bEnabled = !bEnabled; Owner.PlaySound(Sound'UnrealShare.Pickups.FSHLITE1',,1.5*Pawn(Owner).SoundDampening); AdjustGlow(); }
// Ensure that NVG effects are disabled when NVG is destroyed or dropped
function Destroyed() { bEnabled = false; AdjustGlow();
Super.Destroyed(); }
function DropFrom(vector StartLocation) { Super.DropFrom(StartLocation);
bEnabled = false; AdjustGlow(); }
// Adjust view glow; brighter when NVG activated simulated function AdjustGlow() { local PlayerPawn PP;
PP = PlayerPawn(Owner); if (bEnabled) { // Brighten view if (PP != None) PP.ClientAdjustGlow(-0.15, vect(139.0,218.0,72.0)); } else { // Darken view if (PP != None) PP.ClientAdjustGlow(+0.15, vect(-139.0,-218.0,-72.0)); } }
// Ensure that the player has an NVGMutator simulated function PostBeginPlay() { Super.PostBeginPlay();
if ( Level.NetMode == NM_DedicatedServer ) return;
CheckForHUDMutator(); }
// Check for an NVGMutator, spawn one if necessary
simulated function CheckForHUDMutator()
{ local Mutator M; local NVGMutator HM; local PlayerPawn P; ForEach AllActors(class'PlayerPawn', P) if ( P.myHUD != None ) { // check if it already has a NVGMutator M = P.myHud.HUDMutator; while ( M != None ) { if ( M.IsA('NVGMutator') ) return; M = M.NextHUDMutator; } HM = Spawn(class'NVGMutator'); HM.RegisterHUDMutator(); if ( HM.bHUDMutator ) { if ( Role == ROLE_SimulatedProxy ) SetTimer(0.0, false); return; } else HM.Destroy(); }
if ( Role == ROLE_SimulatedProxy ) SetTimer(1.0, true); }
// Ensure that the player has an NVGMutator simulated function Timer() { Super.Timer(); if ( Role == ROLE_SimulatedProxy ) CheckForHUDMutator(); }
// Spawn and locate NVGIllum objects simulated function Tick(float DeltaTime) { local pawn P; local int illumCount; local int numPawns; local NVGIllum NVGi;
if (Owner == None) SetOwner(Instigator); // Bots have no use for NVGs if (Owner.IsA('Bot')) { Destroy(); return; }
// Client side only if (Level.NetMode == NM_DedicatedServer) return;
// Only update every third tick to reduce CPU usage tickCount++; if (tickCount <= 2) return; tickCount = 0;
// Count number of NVGIllum objects on client illumCount = 0; foreach AllActors(class'NVGIllum', NVGi) { illumCount++; NVGi.bHidden = true; // we use this to set the symbol as 'available' for assignment } if (bEnabled) { // Iterate through pawns and assign them an NVGIllum object numPawns = 0; foreach VisibleCollidingActors(class'Pawn', P, Range, Owner.Location) { // spawn another NVGIllum if necessary numPawns++; if (numPawns > illumCount) { NVGi = Spawn(class'NVGIllum', P); NVGi.bHidden = P.bHidden; NVGi.SetOwner(P); NVGi.Mesh = P.Mesh; NVGi.DrawScale = P.DrawScale; illumCount++; } // otherwise assign an NVGIllum to the Pawn else { foreach AllActors (class 'NVGIllum', NVGi) { if (NVGi.bHidden == True) { NVGi.bHidden = P.bHidden; NVGi.SetOwner(P); NVGi.Mesh = P.Mesh; NVGi.DrawScale = P.DrawScale; break; } } } } } }
defaultproperties { tickCount=0 Range=900.0 bEnabled=False PickupMessage="You got the NVG." ItemName="NVG" PickupViewMesh=LodMesh'Botpack.ThighPads' RemoteRole=ROLE_SimulatedProxy }
The brightening of the player's view is handled by the NVGToggle() and AdjustGlow() functions. Providing better illumination of other players is performed in Tick(). This part works pretty much like the team beacon HUD mutator (indeed the code is virtually the same) and is quite processor intensive, due to the repeated use of foreach iterators (not a good idea). To reduce to performance hit it only updates every other tick. In fact you could leave out Tick() and have a quite good night vision goggles effect but I think it makes the night vision goggles more useful if they provide a little better illumination of players. The player illumination is a bit a of a cheat, we simply iterate through all the players we can see and assign them an effect very similar to the UT_ShieldBeltEffect, except that its texture is a dark grey. Due to UT using additive alpha blending when doing transluceny, this has the effect of creating the appearence of a lighter "aura" around the player, making them more visible. To reduce the performance hit of this, there's a limit on how far away a player is to qualify for the effect. This adds realism in a way too, as all night vision goggles do have limited visibility. Most of this is executed client side (so no other player sees the aura effect around the other players).
Finally we come to the aura effect:
// Illumination effect for NVG class NVGIllum extends Effects;
var int FatnessOffset;
simulated function Destroyed() { if ( bHidden && (Owner != None) ) { if ( Level.NetMode == NM_Client ) Owner.Texture = Owner.Default.Texture; else Owner.SetDefaultDisplayProperties(); } Super.Destroyed(); }
simulated function Tick(float DeltaTime) { local int IdealFatness;
if ( bHidden || (Level.NetMode == NM_DedicatedServer) || (Owner == None) ) { Disable('Tick'); return; }
IdealFatness = Owner.Fatness; // Convert to int for safety. IdealFatness += FatnessOffset;
if ( Fatness > IdealFatness ) Fatness = Max(IdealFatness, Fatness - 130 * DeltaTime); else Fatness = Min(IdealFatness, 255); }
defaultproperties { FatnessOffset=24 bAnimByOwner=True bOwnerNoSee=True bNetTemporary=False bTrailerSameRotation=True Physics=PHYS_Trailer RemoteRole=ROLE_SimulatedProxy DrawType=DT_Mesh Style=STY_Translucent Texture=Texture'Illum' ScaleGlow=0.500000 Fatness=157 bUnlit=True }
Basically this simply mimics its Owner's location, rotation & fatness, creating a translucent shell around them. The colour of the texture should be chosen carefully (I used a dark grey) to create the right effect.
And now you should have working night vision goggles. The code could undoubtably be improved, mostly to reduce the performance hit (which is around 15fps on my PC); all those iterators in Tick() are not a good thing.
All logos and trademarks are properties of their respective owners.
Unreal™ is a registered trademark of Epic Games Inc.
Privacy Policy
Website by Softly
Powered by RUSH