Mapping & SDK

KF SDK: Zones, Fog, Sky and Lighting

These are two lessons by mapper K-2 from the killingfloor.ru forum (2011-2012): "KF SDK: Zones" and "KF SDK: Lighting". Editor basics are covered in map editor basics and the mapping guide, and tying fog to the cull distance is explained in CullDistance and distance fog.

What zones are

Zones are the parts of the playable space that the map is split into at compile time. In the editor you can give each zone its own settings.

To see zones, switch an editor viewport to Zone/Portal mode (Alt+2): each zone is shown in its own color.

How zones form:

  1. Any subtracted space forms a zone.
  2. If one Non-solid space touches or overlaps another, they form a single zone.
  3. If Non-solid spaces do not touch each other, they are separate zones.

Splitting a zone with a portal

You can also cut a zone manually:

  1. Create a flat brush with the Sheet button. Size does not matter, as long as it fully covers the zone at the chosen spot.
  2. Place it where the boundary should be.
  3. Click Add Special Brush and in the dialog tick Portal, Invisible and Two Sided, so the divider is non-colliding, invisible and two-sided. In the editor such a brush has a green outline.
  4. Compile the map: the zone is now split.

If the dividers get in the way in the editor, enable Realtime Preview (P key) in the viewport and they become invisible.

To delete a portal, select the brush that makes it rather than the surface: for a Zone/Portal that is usually the green wireframe in the 2D views. Then the Delete key removes it (K-2's clarification from the comments).

Zone settings: ZoneInfo

A zone's properties are set with the ZoneInfo actor (Actors → Info → ZoneInfo): place it inside the zone and edit its properties. One ZoneInfo per zone: if you put several in the same zone, the settings may not work correctly. ZoneInfo lets you add fog, fully light the zone and give it a name.

ZoneInfo tab:

PropertyWhat it does
bClearToFogColorWith fog enabled, the space hidden by the fog takes on the fog color
bDistanceFogTrue enables fog (fog improves performance)
bSoftKillZEnables the KillZ effect: a player dies after falling or rising a set vertical distance
bTerrainZoneRequired for terrain to work in the zone
KillZThe KillZ distance itself
LocationNameLocation name (in KF it only shows in team chat)
ZoneTagZone tag used with ZoneTriggers (in KF neither this option nor ZoneTriggers are relevant)

ZoneLight tab:

PropertyWhat it does
AmbientBrightness / AmbientHue / AmbientSaturationExtra static lighting for the whole zone. Does not look great and is rarely used
bNewKFColorCorrectionEnables color correction when fog is on; the color is set in KFOverlayColor (used on KF-Wyre, for example). Tripwire Interactive added this in one of the patches, and it sometimes works incorrectly or not at all
bNoKFColorCorrectionDo not use color correction
DistanceFogBlendTimeHow many seconds after entering the zone the player sees the fog
DistanceFogColorFog color
DistanceFogEndDistance from the player beyond which nothing is visible in the fog
DistanceFogEndMinAn alternative to DistanceFogEnd for games with a fog visibility setting in their options (KF is not one of them); used for performance
DistanceFogStartDistance from the player where the fog starts. A negative value makes the fog thicker

The remaining options (except KFOverlayColor) are best left at their defaults. How to pick the fog start and end to match CullDistance is covered in CullDistance and distance fog.

Sky: the SkyZoneInfo zone

  1. Create a separate zone for the sky, usually a cube subtracted outside the playable map.
  2. Place a SkyZoneInfo inside it (Actors → Info → ZoneInfo → SkyZoneInfo). It acts as a fixed "camera" through which the player sees the sky. Its properties are the same as ZoneInfo, but unlike ZoneInfo a SkyZoneInfo has a direction (shown by an arrow).
  3. So the sky is not black, set an AmbientBrightness in the zone properties or enable Unlit on the surroundings.
  4. Build the skybox. The simplest way is to apply sky textures to the cube's walls. You can go further: almost anything can go into a skybox (most often static meshes) and it will all be visible. A sky dome (Skydome) with decorations is common, and animated textures can give moving clouds or lightning. The skyboxes of the official maps KF-Suburbia, KF-Departed and KF-Wyre are good examples.
  5. Choose which surfaces show the sky. For example, if the map is built inside a huge cube, select the faces that should become see-through and tick Fake Backdrop in their surface properties.
  6. Check the result by enabling backdrop display in the editor window's View settings (K key).

If some spots of the map show smeared texture "trails" in game, you are seeing the space behind the backdrop with no skybox: check that Fake Backdrop is not ticked on surfaces where there should be no sky. For the thread author the cause was an open seam in the geometry; once the hole was closed the effect went away (K-2 and Mysterion, "Map bug with textures" thread, killingfloor.ru forum).

Where to get sky textures and what size to make the cube is covered in ambient sounds, snow, teleporters and sky textures.

Lighting: getting started

Lighting is one of the most important parts of map design. With good lighting, modest detailing goes almost unnoticed, while great geometry is easily ruined by bad lights or Ambient brightness.

  • For lighting work there are the Lighting Only (Alt+0) and Dynamic Light (Alt+5) viewport modes. The first shows lighting "clean" and is the main one; the second shows light over the geometry and object textures and is for fine-tuning and a full preview.
  • After adding or changing lights, the lighting must be compiled. Besides a full map build there are two separate buttons: Build Lighting rebuilds all lighting, Build Changed Lighting rebuilds only the lights changed since the last compile. The second saves time but ignores things like changes to lightmap size and format.
  • Uncompiled lighting is treated as dynamic and usually does not match the final result: light passes through objects without shadows, and angles and neighboring lights are not taken into account.

K-2 splits lighting into three rough categories:

CategoryHow it is builtWhat it is for
FillLight with no visible source: groups of ordinary Lights of the same or similar hue and saturation, medium radius (12-32), spread almost evenly over the areaEvenly lighting an area with no natural sources, or "extending" their effect to imitate scattered light. Overlapping similar sources produces many shades of different brightness and looks much better than zone ambient brightness
NaturalLight the player associates with a visible source; any type, including combined ones, with a radius that depends on the map and the sourceLight from lamps, headlights, fire; sunlight or moonlight matching the skybox also belongs here
AccentMostly Lights with a small radius (2-8) and, optionally, high brightnessUsed with other lights to light details and spots other light cannot reach, or to emphasize particular features

Light properties

Light is the ordinary template "bulb" and the root of all other light types. Add it with right-click → Add Light Here; in the actor browser it is under Actor → Light.

Light Color tab:

PropertyWhat it does
LightBrightness / LightHue / LightSaturationBrightness, hue and saturation (they also change the icon color)

Clicking the Light Color tab shows two extra buttons: pick a color with an eyedropper or choose it from a palette. Warning: if several lights with different settings are selected at once, opening the palette crashes the editor.

Lighting tab:

PropertyWhat it does
bActorShadowsWhether actors cast shadows (in practice it has no effect)
bAttenByLifeIn theory, light fading over its lifetime
bCoronaEnables a corona (see below)
bDirectionalCoronaMakes corona size depend on the viewing angle: largest when looked at head-on, "gone" at 90 degrees
bDynamicLightMakes the light dynamic: it passes through objects without casting shadows (often looks unnatural), but needs no compiling and can be used for some effects
bLightingVisibilityWhether lighting visibility from this light is calculated when checking the map
bSpecialLitWhen True, this light only shows on BSP brush surfaces that have Special Lit ticked
LightConeLight cone angle (only with LE_Spotlight); the maximum of 255 equals roughly 160 degrees
LightEffectA special lighting effect (not all of them work correctly)
LightPeriod / LightPhaseIn theory these drive effect animation, but K-2 found no effect in the stock set that they influence
LightRadiusLight radius in its own units: one unit is about 27 UU. The real bounds show up if you set actor display to Radii View
LightTypeLight type (an extra effect); works together with LightEffect or on its own; most types need bDynamicLight = True

If the map is too bright, lower LightBrightness or LightRadius on the lights (UltraKill's tip from the comments).

Light source types

Spotlight (Actor → Light → Spotlight). A directional light that shines in a cone. It is essentially an ordinary Light with LightEffect = LE_Spotlight, but it has a direction arrow, which makes it easier to set up.

Sunlight (Actors → Light → Sunlight). Sunlight. It differs from an ordinary Light by its icon, its direction arrow and LE_Sunlight set by default. It covers the whole area of the map it can reach. Wherever the Sunlight sits, the light source is always the space behind the backdrop, so a "sky" is required.

Unreal Engine 2 has no light scattering as such: sunlight here is like on the Moon, and if a Sunlight is the only light on the map, everything in shadow is black. Extra lights partly solve this, including extra Sunlights: a main Sunlight plus several weaker, untinted ones with the same overall ray angle gives fairly realistic sunlight in open areas. Their number and settings depend on the map, and it takes some fiddling with the Movement tab, precise Rotation on all three axes and some math. The alternative is fill lighting for shaded spots.

TriggerLight (Actor → Light → TriggerLight). A light that turns on and off through Event → Tag: on the light's Events tab you enter a tag in the Tag field, and the trigger gets the same tag in its Event field. It is a dynamic light by default. It needs InitialState set up to work correctly:

InitialStateBehavior
TriggerTurnsOnActivation turns the light on and it stays on
TriggerTurnsOffActivation turns the light off and it stays off
TriggerToggleEach activation turns the light on or off, any number of times
TriggerControlOnly with ordinary Triggers: the light is on while the Trigger is active, and vice versa
TriggerPoundIn theory the light should fade on and off over RemainOnTime; in practice it works as a one-time on/off

Other TriggerLight properties:

PropertyWhat it does
bDelayFullOnMakes turning on and off smooth, ignoring ChangeTime
bInitiallyOnWhether the light starts on
ChangeTimeHow long brightness takes to rise from 0 to the set value when turning on (and back when turning off)
RemainOnTimeIn theory only a TriggerPound parameter; in practice it has no visible effect

KFMovableLight (Actor → Light → KFMovableLight). A light that can be attached to a mover so it moves with it:

  1. On the Lighting tab, set bDynamicLight = True manually.
  2. On the Movement tab, enter a tag in the AttachTag field.
  3. Give the mover you are attaching to the same tag in its Tag field (Events tab).

When the mover moves, the light repeats the same movement relative to its starting position.

K-2 skipped Effect_TacLightGlow and HeadlightCorona as not relevant.

Coronas

Light coronas are one of the simplest yet prettiest map decorations. They are essentially visible "skins" for lights: sprites with a (usually) translucent texture.

  1. In the light's properties, on the Lighting tab, set bCorona = True.
  2. On the Display tab, set Skin: select a texture in the browser and click Use, or type the path manually.
PropertyWhat it does
CoronaRotationCorona rotation factor depending on the distance from the player to the light
CoronaRotationOffsetInitial corona rotation
MaxCoronaSize / MinCoronaSizeMaximum and minimum corona size
UseOwnFinalBlendPresumably: whether a complex FinalBlend texture is used

K-2 recommends making separate lights for coronas, with zero brightness and a large radius.

Shadows and lightmap resolution

Shadow quality depends first of all on lightmap resolution, but this applies only to BSP geometry.

  1. Select the BSP brush surfaces you need and open their properties.
  2. Change the Light map value: from 256 to 1, default 16.0. The lower the value, the higher the resolution (maximum is 1.0).

Higher resolution increases map size and compile time and costs a little performance, so 1.0-2.0 only makes sense where it is most noticeable. The exception is small maps with little content: on those you can press Shift+S right away and set maximum quality.

Light falls differently on polygonal models and terrain (vertex lit): the more polygons a model has and the smaller they are, the better the lighting and shadows (the same goes for the terrain grid). Still, it is far from the best BSP shadow quality. Individual shadows can be made with Projectors (Actor → Projector), and they look right on any object. The main difficulty is creating the texture, since a projector simply projects a texture.

Lightmap format

The lighting format is set in Build Options. There are three choices:

FormatCompression
DXT1Maximum
DXT3Medium (the default)
RGB8None

With RGB8 the lighting is much better quality and softer, and the "colored blotches" effect goes away. The uncompressed format increases map size and costs performance, but not as much as high lightmap resolution.

Comparing the combinations (DXT3 or RGB8, Light map 16.0 or 1.0-2.0), K-2 concluded that at low resolution RGB8 looks much better than DXT3, while at high resolution the difference becomes very small. Compression artifacts are far more visible with colored light than with white. Pick a combination based on the map's performance.

Map brightness and tint

A common problem: in game, the map's brightness and overall gamma do not match what the editor shows, and usually the map turns out too bright. Color correction can be set in a ZoneInfo's properties for one zone, or in Level Properties (F6) for the whole map:

  1. On the ZoneInfo tab, set bDistanceFog = True.
  2. On the ZoneLight tab, set bNewKFColorCorrection = True.
  3. Adjust the gamma in KFOverlayColor. Lowering all three channels B, G and R (blue, green, red) evenly reduces the map's overall brightness.

Fog is required for color correction to work. If you do not want fog, give it an extremely large radius.

Police lights and headlights: a breakdown from KF-MountainPass

From K-2's answer in the "Вопросы, вопросы и еще раз вопросы." (Questions, questions and more questions) thread on the killingfloor.ru forum. The flashing lights and headlights of the police car on KF-MountainPass are built from many small components, and the best way to study them is to open the map and look.

  • Flashing lights: 4 ordinary Lights that give constant light; 2 TriggerLights with LightType=LT_Blink and bInitiallyOn=True (bInitiallyOn is the second line of the lower TriggerLight tab); 2 sprite Emitters generating the flash particles.
  • Headlights: Lights with zero brightness and coronas; a Light with a small radius and high brightness lighting the static mesh where the headlights are; ordinary Lights with moderate radius and brightness lighting the surroundings.

If the light in one room only lights the walls while other rooms are fine, K-2 advised checking whether the ceiling is lit, what radius the lights have, whether the floor surfaces have Special Lit ticked, and whether the floor uses a shader. In the "Проблема с освещением" (Lighting problem) thread it turned out to be a bug, fixed by a light radius of 16 to 32 or by moving the lights slightly.