Map

Modules PulseMap (data: maps of levels, map sources, map coordinates) and PulseMapUI (minimap, world map and the map view widget; depends on PulseHUD). The editor tool that renders the picture of a level is in PulseEditor. A level gets a map from a picture of it seen from above; the minimap and the world map show that picture with the player and the markers of the Pulse HUD.

Overview

Type What it is
UPulseMapDefinition Data asset Map (Primary Asset Type PulseMap): where the picture comes from (source), background color, zoom limits
FPulseMapSource Source of the picture, an instanced struct. FPulseMapSource_Image (Image): one texture of a known world area; sources without one picture draw tiles (e.g. Pulse World of the bridge PulseWorldCoreBridge)
APulseMapActor Pulse Map Actor: says which map its level uses; its box is the area the map image tool renders
UPulseMapSubsystem World subsystem: the map of the world (Set Map, map actor, Level Maps, provided map, Default Map), OnMapChanged, the tiles of maps without one picture
UPulseMapViewWidget Pulse Map View: draws picture, markers and player arrow for any view (center, size, rotation)
UPulseMinimapWidget Pulse Minimap: the map around the player, turning with the view or north up
UPulseWorldMapWidget Pulse World Map: a menu with the whole map, drag and zoom
UPulseMapUISubsystem Local player subsystem: puts the minimap on the HUD, opens and closes the world map
UPulseMapSettings Project Settings > Pulse > Core > Map: map per level, default map, limits of the tiles
UPulseMapUISettings Project Settings > Pulse > Core > Map UI: minimap on the HUD, world map class, key, stack
UPulseMapPlayerSettings Player settings in the HUD section: minimap on or off, rotation, zoom

Setup

  1. Turn on the Pulse HUD (bEnableHUD, see HUD system). The minimap and the world map are parts of it.
  2. Place a Pulse Map Actor in the level (Place Actors panel, search “Pulse Map”). Scale its box over the playable part of the level; things above the top of the box are left out of the picture (roofs, clouds), the turn of the actor is ignored.
  3. Press Render Map in the details of the map actor (or run Pulse.Map.Render in the editor console). The tool renders the area from above and creates T_<Level>_Map (texture) and DA_<Level>_Map (map) in a folder Map next to the level, sets the map on the actor and saves both assets. Save the level afterwards. Rendering again updates both in place.
  4. Play: the minimap is in the top right corner, M opens the world map.

Without a map actor the tool renders the bounds of the visible levels (ALevelBounds, actors with bRelevantForLevelBounds) and places a map actor there. A level can also get its map from the settings instead of a map actor: Level Maps (one map per level) or Default Map (every other level).

Pulse.Map.Render [Resolution] [Unlit] [/Game/Folder]: the resolution is the longer side of the picture (default 2048, the other side follows the area), Unlit renders the base colors without light and shadow, a folder overrides the place of the assets. The picture is rendered twice as large and scaled down; fog, clouds, atmosphere, motion blur and depth of field are off.

The map asset

Property What it does
DisplayName Title of the world map.
Source Where the picture comes from. Image: Image (north up) and WorldBounds (Min = south west corner, Max = north east corner).
BackgroundColor Around the picture and on maps without one.
MinimapViewSize World units across the minimap (the zoom of the minimap; the player setting scales it).
MinViewSize / MaxViewSize Closest and farthest zoom of the world map. MaxViewSize 0: the whole map, 100 km for a map without bounds.

The check (Pulse Check, Data Validation) reports a map without a source, an Image source with an empty area and zoom limits in the wrong order.

Map sources

FPulseMapSource is an extension point: another plugin or a bridge adds a source of its own as a child struct, and the map asset picks it in Source.

Function What a source returns
GetWorldBounds(OutBounds) The world area of the map, false for a map without bounds (an endless world).
GetImage(OutImageBounds) One picture of the whole map and the area it shows, null if the source has none.
GetTileLayout(OutLayout) How the source cuts the world into tiles (FPulseMapTileLayout: tile size of level 0, pixels, coarsest level, border); false if it draws no tiles.
GetTileContentKey(WorldContext) What the tiles show right now; when it changes (a new world), every tile is thrown away. 0: nothing to draw yet.
RequestTile(WorldContext, Request, Done) Draws one tile (FPulseMapTileRequest: key, world area, pixels, border); may work on other threads and calls Done once, from any thread, with (Pixels + 2 Border) squared colors (row 0 north, column 0 west, sRGB) or an empty array. False if it cannot draw now (asked again later).

Tiles

A map whose source has no single picture but a tile layout (an endless world) is drawn in square tiles. Level 0 is the finest; each level doubles the tile size. UPulseMapSubsystem keeps the tiles of the map of its world for every view on that machine:

  • Which tiles: every frame each view (minimap, world map) takes the finest level whose pixels are at least as large as a screen pixel, and the tiles that cover it (a turned minimap takes the tiles around its circle, so the set does not change while it turns). Views that could touch more than Max Tiles Per View take coarser tiles; the count does not depend on where the view is, so the level does not change while the map is dragged. The tiles two levels coarser are asked first: a few of them fill the whole view while the fine ones come.
  • Drawing: a tile without its own picture is drawn with the matching part of the nearest coarser picture; drawn tiles never overlap. Each picture has a border of one pixel, so tiles blend into each other without seams.
  • Requests: at most Max Tile Requests at once, nearest to the middle first. A source that cannot draw yet (no world) is asked again after Tile Retry Seconds, a request without an answer is given up after Tile Timeout Seconds.
  • Changes: InvalidateArea(Area) marks the tiles of an area as old: they keep their picture until the new one arrives, also when the change came while a request ran. A new content key (another world) throws every tile away.
  • Memory: beyond Max Tile Textures (default 192 tiles of 258 x 258 pixels, about 50 MB) the tiles no view used in the last frames are dropped; their textures are reused for the next tiles (up to half the limit stay free), so memory does not wait for the garbage collector.

The rules are pure functions in PulseMapTiles.h (Pulse::Map::Tiles), tested by the specs PulseMap.Tiles.

Provided maps

A system that knows the map of its world gives it with SetProvidedMap (the bridge PulseWorldCoreBridge does this for a Pulse World). Set Map, a map actor and the Level Maps of the settings win over a provided map; the Default Map does not.

Coordinates

Maps are north up: north is +X of the world, east is +Y. The top of a picture is north, its right edge east. The functions are pure (PulseMapTypes.h, namespace Pulse::Map) and tested by the specs PulseMap.Coordinates:

Function What it does
WorldToUV / UVToWorld World point to the place in a picture (0, 0 top left) and back.
WorldToWidget / WidgetToWorld World point to the place in a map widget for a view (FPulseMapView: center, view size across the shorter side, rotation, widget size) and back.
GetIconAngle Angle of an arrow for a yaw on a view (clockwise from up).
ClampViewSize View size within zoom limits.
GetVisibleArea World area a view shows (its turned corners).

A view with a rotation puts that yaw up: the minimap that turns with the player uses the yaw of the camera.

Minimap

UPulseMinimapWidget follows the pawn of its player (the camera without a pawn) every frame. It turns with the view or keeps north up (player setting Minimap rotation), zooms by Minimap zoom and hides itself when the level has no map or the player turned it off. The default layout is a frame (style role Pulse.UI.Panel.HUD) with a map view that keeps markers on its edge, hides markers beyond their MaxDistance and shows “N” in the direction of north.

With Add Minimap To HUD (Map UI settings, on by default) UPulseMapUISubsystem adds the minimap to the gameplay part of every new HUD layout (UPulseHUDLayout::AddGameplayWidget: into GameplayRoot, at Minimap Anchor with Minimap Offset and Minimap Size), so it hides with the HUD and while menus are open. A layout that contains a Pulse Minimap already keeps it; a layout without a GameplayRoot panel gets none (the log says so).

Your own minimap: a Widget Blueprint child of Pulse Minimap with a Pulse Map View named MapView and a border named Frame, set as Minimap Class.

World map

UPulseWorldMapWidget is an activatable widget on a HUD stack (World Map Stack, default Pulse.HUD.Stack.Main). It opens with the whole map and the player in the middle (as far as the map allows).

Input What it does
Drag (any mouse button) Moves the map.
Mouse wheel Zooms around the cursor (ZoomStep per notch).
W A S D, arrow keys, left stick Moves the map (PanSpeed).
Q / E, + / -, Page Down / Up, right stick Zooms (KeyZoomSpeed).
Space, C, top face button, right stick press Back to the player.
Back action (Escape, B) or the map key again Closes the map.

Hovering a marker shows it larger with its name. While it is open the menu follows a new map of the world, and it closes itself when the world loses its map. The map key closes the map through a Slate input processor, because the game gets no input while the map is open; the input context of the menu (cursor, UI only) comes from the Pulse player input manager (World Map Input Context).

The key is the input action World Map Input Action (IA_PulseMap) of the mapping context World Map Mapping Context (IMC_PulseMap, key M), added for every local player. Leave the mapping context empty when the game maps the action in a context of its own. ToggleWorldMap, OpenWorldMap and CloseWorldMap of UPulseMapUISubsystem open it from Blueprint or C++.

Your own world map: a Widget Blueprint child of Pulse World Map with MapView, Backdrop, TitleText and HintText, set as World Map Class.

Markers

Maps show the markers of the Pulse HUD whose targets include Map (AddMarker, default target: compass, screen and maps; see Markers, tooltips and overlays). The map view reads the marker records of the HUD and follows OnMarkerChanged. A marker of a tracked actor follows the actor.

Marker field On maps
Icon The icon (texture or material, loaded when needed); empty: the diamond of the UI theme.
ColorRole Tint of the icon.
Name Shown while the marker is hovered on the world map.
bShowOnScreenEdge Stays on the edge of the minimap while it is outside.
MaxDistance The minimap hides the marker farther from the player (like the compass).

A marker only for maps: AddMarker(Data, Map).

The map view widget

Pulse Map View (UPulseMapViewWidget) draws a map into your own widgets: SetMap, SetView (center, view size, rotation), SetPlayer (location, yaw), RefreshMarkers (reads the HUD markers again; call it while you tick), FindMarkerAt and SetHighlightedMarker for hovering, LocalToWorld / WorldToLocal, UpdateTiles (maps in tiles: call it once per frame while the view shows) and ClearTiles (a view that stops showing lets go of its tiles). Appearance: player arrow (theme icon Pulse.UI.Icon.Arrow, PlayerIconRotation -90 because that arrow points right), marker size, edge markers, the “N”, text role and color role of the arrow.

Player settings

UPulseMapPlayerSettings registers three options in the HUD section of the Interface tab (with Register Player Settings of the Map UI settings): Minimap (on or off), Minimap rotation (turn with the view, north up) and Minimap zoom (50 to 200 %). Setting ids Pulse.Setting.Map.ShowMinimap, .MinimapRotation, .MinimapZoom.

Multiplayer

Maps are local: every machine finds the map of its world, every local player has its own minimap and world map, and the markers are the HUD markers of that player. Nothing is replicated. SetMap changes the map on the machine where it is called.

Messages

Channel Payload Origin Notes
Pulse.Message.Map.Opened FPulseMapMessage Client The world map opened. Player: the controller of the local player, Source: the map.
Pulse.Message.Map.Closed FPulseMapMessage Client The world map closed.

Blueprint and C++

Function What it does
UPulseMapSubsystem::GetMap / GetMapOrigin The map of the world and where it comes from (Set Map, map actor, Level Maps, provided, Default Map).
UPulseMapSubsystem::SetMap(Map) Sets the map of this machine at runtime; null goes back to the map actor and the settings.
UPulseMapSubsystem::SetProvidedMap(Map) / GetProvidedMap The map a system knows for its world (after Level Maps, before the Default Map).
UPulseMapSubsystem::InvalidateArea(Area) Draws the tiles of an area again (invalid box: everywhere).
UPulseMapSubsystem::GetTiles / GetTileTexture (C++) The tiles a view shows and their pictures; UPulseMapViewWidget::UpdateTiles uses them.
OnMapChanged / OnMapChangedNative The map of the world changed.
UPulseMapDefinition::GetWorldBounds / GetMaxViewSize Area and farthest zoom of a map.
APulseMapActor::GetAreaBounds The area of the map actor’s box.
UPulseMapUISubsystem::OpenWorldMap / CloseWorldMap / ToggleWorldMap / IsWorldMapOpen / GetMinimap The map widgets of a local player.

The specs PulseMap cover the coordinates (PulseMap.Coordinates), how the subsystem picks a map (PulseMap.Subsystem), the tiles (PulseMap.Tiles) and the edge and zoom helpers of the widgets (PulseMap.UI).

Example

LV_PulseCore_Example has a Pulse Map Actor over the whole level with the map DA_PulseCore_Example_Map (/PulseCore/Examples/Map, 40 x 40 m, rendered with the tool). Play it: the minimap shows the trees, rocks and the training dummy, M opens the world map, PulseHUDMarker and PulseMapMarker add markers.

Cheats

See Console.


PulseCore plugin documentation.

This site uses Just the Docs, a documentation theme for Jekyll.