Migration from 1.x to 2.0

PulseCore 2.0 rebuilds the HUD, moves shared code into the new free base plugin PulseFoundation, gives all widgets a shared look (UI themes) and adds new modules: a settings menu (PulseSettings), world markers, tooltips and overlays in the HUD, interaction prompts with holding, conditions and costs (PulseInteractUI) and a combat system on GAS (PulseCombat). The interaction module keeps its API; it only gained functions. The inventory modules are new in 2.0 (see Inventory changes since early 2.0 previews for projects that used a preview).

New required plugin: PulseFoundation

The helper module PulseGameCore moved into PulseFoundation (1.0 or newer), the shared base of all Pulse plugins. The module keeps its name, classes and paths, so includes, Build.cs dependencies on PulseGameCore and assets referencing /Script/PulseGameCore keep working.

  1. Add the plugin PulseFoundation to the project. PulseCore lists it as a dependency, so it is enabled with PulseCore; projects whose own modules use PulseGameCore should also enable it in their .uproject.
  2. Remove a stale Binaries/<Platform>/*PulseGameCore* from the PulseCore folder of an installed plugin if the editor reports a module that is not part of PulseCore anymore.

The inventory save data of early 2.0 previews still loads (version 1); see the inventory section below.

HUD

The HUD module was rebuilt.

Removed

1.x 2.0
IPulseHUDManagerInterface, UPulseGlobalHUDManager, UPulsePlayerHUDManager UPulseHUDSubsystem (one HUD per local player, class redirect from UPulsePlayerHUDManager). There is no global (game instance) HUD anymore: it had no owning player, so CommonUI could not route its input. Show menus and modals on the HUD of the (first) local player.
IPulseHUDInterface (OnHUDManagerSet, AddStackContainer, InitializeCompass) UPulseHUDLayout base class (On HUD Initialized, AddCompass, OnHUDHiddenChanged, CompassSlot, GameplayRoot).
UPulseHUDMetadata, UPulseHUDModalMetadata, UPulseHUDButtonMetadata, UPulseHUDCompassMarkerMetadata, FPulseHUDMetadataValue, metadata pool, K2Node_CreateHUDMetadata, K2Node_PulseHUDMetadataValue, module PulseUncookedOnly FInstancedStruct payloads (FPulseHUDModalRequest::Payload, FPulseHUDMarkerData::CustomData, FPulseHUDNotification::Payload). Use the engine nodes Make Instanced Struct / Get Instanced Struct Value.
UPulseHUDSimpleModalBase, UPulseHUDConfirmModalBase UPulseHUDModal (class redirects). Simple, confirm and N-button modals are different FPulseHUDModalRequests.
UPulseHUDConfirmModalButton UPulseHUDModalActionButton (class redirect, ButtonTextBlock stays).
PushModal(FPulseHUDModalPushParams), PushSimpleModal, PushConfirmModal, FPulseHUDModalPushBase, FPulseHUDConfirmModalInfo, FPulseHUDConfirmModalPushInfo, FPulseHUDPushParams PushModal(FPulseHUDModalRequest), MakeConfirmModalRequest, MakeMessageModalRequest.
UPulseHUDWaitConfirmModalAction (Wait For Confirm Modal, Push And Wait For Confirm Modal) UPulseHUDShowModalAction (Show Modal And Wait, Wait For Modal Closed).
Tokens (UPulseHUDTokenBase, UPulseHUDTokenContainer, UPulseHUDToken_Button, UPulseHUDTokenModal) Modal actions (FPulseHUDModalAction, ActionList).
Pulse.HUD.Stack.Notification Notification areas (Pulse.HUD.Notification.Default).
IPulseHUD module interface, Pulse::HUD::GetSettingsFast Pulse::HUD::GetSettings().
FPulseHUDStyle getters (Get...OrCore), HUDStyleCustomization Plain FPulseHUDStyle fields in UPulseHUDSettings::Style.

Settings ([/Script/PulseHUD.PulseHUDSettings])

1.x key 2.0 key
bEnablePerPlayerHUDManager bEnableHUD
PerPlayerHUDManagerParams.HUDWidgetClass HUDLayoutClass
PerPlayerHUDManagerParams.HUDWidgetZOrder HUDZOrder
bEnableGlobalHUDManager, GlobalHUDManagerParams removed
CoreStyle (DefaultModalStyle, DefaultModalContainerClass, DefaultSimpleModalClass, DefaultConfirmModalClass, DefaultConfirmModalButtonClass, DefaultConfirmModalButtonStyle, DefaultCancelModalButtonStyle, DefaultCompassClass, DefaultCompassMarkerClass) Style (ModalStyleClass, ModalContainerClass, ModalClass, ModalActionButtonClass, ConfirmButtonStyle, CancelButtonStyle, NeutralButtonStyle, CompassClass, CompassMarkerClass)
— MaxVisibleNotifications, MaxQueuedNotifications, DefaultNotificationDuration

Config keys are not redirected; update DefaultGame.ini by hand.

Changed behavior

  • Stacks register themselves with the HUD of their owning player instead of being found once in the layout. They work in nested and later created widgets.
  • The HUD is rebuilt when the player controller changes (e.g. map travel). Keep no references to old HUD widgets across travels; listen to OnHUDCreated.
  • Modals close with a result (FName): the id of the action, or Cancel. The back action triggers the action with role Cancel.
  • Modals add an input context (bManageInput, default cursor + UI only) when the player input manager is enabled.
  • Markers are created from FPulseHUDMarkerData (compass and world markers share it, AddMarker) and addressed by handle. SetCompassVisibility is SetCompassVisible (hiding works now). Marker widgets don’t tick; Blueprint marker classes that used Event Tick must move that code to OnMarkerDataChanged or the position overrides.
  • Input manager: equal priorities keep their push order (newest wins). RemoveInputContext invalidates the handle in Blueprint. New: AddInputContextForWidget.
  • Settings are a UDeveloperSettings (same section and class name).

Assets

Widget Blueprints based on removed classes must be reparented:

  1. HUD widget: reparent to UPulseHUDLayout, remove the Pulse HUD Interface, name the compass panel CompassSlot.
  2. Simple/confirm modals: reparent to UPulseHUDModal (redirect does it on load), rename the content panel to ContentSlot, replace the AddConfirmButton / AddCancelButton events by a dynamic entry box ActionList whose entry class is a UPulseHUDModalActionButton.
  3. Blueprints that called PushConfirmModal / PushSimpleModal or the metadata nodes: use Show Modal And Wait with Make Confirm Modal Request (or a request with ContentClass).
  4. Compass markers created with metadata: use Add Marker (or Add Compass Marker) with FPulseHUDMarkerData.

Look and UI themes

All widgets take their look from the UI themes of PulseFoundation (see HUD system).

  • FPulseHUDStyle::ConfirmButtonStyle, CancelButtonStyle and NeutralButtonStyle are empty by default; empty means the theme’s button roles. Projects that set them keep their styles.
  • The default widgets are built with theme roles. Blueprints derived from them keep working; widgets that bind the same names work as before.
  • While the main stack shows a menu the gameplay HUD is hidden. Turn off bHideGameplayHUDWhileMenuOpen to keep it.
  • Distances of markers show their unit (“120 m”); bShowDistanceUnit turns it off.
  • The default theme of PulseFoundation follows the “Pulse HUD Basisstil”: new colors, Roboto Condensed for numbers and labels, smaller UMG font sizes (body 13), upper case labels and buttons. Widgets that use theme roles change their look; check own widgets that sit next to them.
  • The default HUD layout changed: fixed 640 x 46 compass at the top (160° visible), notifications top right, player vitals (W_DefaultPlayerVitals, replaces W_DefaultPlayerHealthBar), hotbar, crosshair, interaction prompt next to the crosshair with a hold ring. Modal containers center their modal inside their area instead of stretching it.
  • UPulseCombatHealthBar defaults: trail color Pulse.UI.Color.BarLag, shield Pulse.UI.Color.Accent; critical hit numbers use Pulse.UI.Color.Highlight.

Interaction

Interactables can describe themselves for prompts, require holding the input, be locked by conditions and cost something (see Interaction system). Existing interactables keep working:

  • IPulseInteractableInterface has two new functions with defaults, GetInteractionInfo and OnInteractionCompleted.
  • An interaction now completes: right when it starts (hold time 0, the default) or after the hold time. Effects that should respect holding, conditions or costs belong into OnInteractionCompleted; OnInteractionStarted still fires when the input goes down.
  • UPulseInteractableComponent::CanBeInteractedWith also checks the new Conditions and Costs (Pulse conditions and costs of PulseFoundation); the costs are paid when the interaction completes.
  • UPulseRepInteractAbility sends ServerStartInteraction / ServerEndInteraction to the server (in 1.x a client ran them locally), and the server no longer fails RPC validation when client and server disagree for a moment. Interactables with bReplicateInteractionEvents get their events on the server for client players.
  • The query task finds interactables that are already in range when the ability activates.
  • PulseAbility depends on EnhancedInput (for FPulseInteractionInfo::InputAction).
  • GetInteractionTime and GetHoldProgress (also GetHoldProgress of the view model) count world seconds instead of real seconds: they stop while the game is paused and follow slow motion, like the completion timer. The hold state of the ability is one value, FPulseInteractHoldState (GetHoldState).

New modules

Nothing has to change for them; enable what you need:

Module Content
PulseInventory Grid inventory with equipment, hotbar, pickups, loot and item costs (Inventory system)
PulseInventoryInteract Pickups that work with the interaction system
PulseSettings Settings menu for the player settings of PulseFoundation (Settings menu)
PulseInteractUI Interaction prompt widget
PulseAttributes Health, shield and stamina sets, regeneration, attribute view models (Attributes)
PulseInventoryEffects Consumables and stats of equipped items on GAS (Item effects)
PulseCrafting Recipes, crafting stations and a crafting queue per player (Crafting)
PulseCombat Combat on the Gameplay Ability System (Combat)
PulseCombatMelee Melee combos (Melee)
PulseCombatRanged Projectiles and hitscan (Ranged)
PulseCombatInventory Weapons, armor, ammunition, durability, loot on death (Combat and inventory)
PulseCombatHUD Damage numbers, health and boss bars, hit feedback (Combat HUD); W_DefaultHUDWidget contains its widgets
PulseInventoryUI Hotbar and item tooltips (Inventory UI); W_DefaultHUDWidget contains the hotbar

Early 2.0 previews called the marker data FPulseHUDCompassMarkerData; it is redirected to FPulseHUDMarkerData (Config/DefaultPulseCore.ini) and kept as a C++ alias.

Inventory changes since early 2.0 previews

The inventory modules are new in 2.0. Projects that used an early 2.0 preview:

  • Custom inventory traits override GetMaxAcceptedCount(const FPulseInventoryView& InInventory, const FPulseInventoryAddContext& InContext, EPulseInventoryResult& OutBlockReason). Read the inventory from the view (InInventory.Items, Pulse::Inventory::GetTotalWeight(InInventory)), not from a component: the view can be a planned state.
  • Inventory events fire after the whole operation, with a snapshot of each stack; OnInventoryChanged fires once per operation. Changes made inside a listener are applied at once and fire their events after the running ones.
  • Drops change everything or nothing: SpawnFailed and RateLimited mean nothing changed (no items given back elsewhere, no budget used); the drop budget belongs to the player (unique net id), not to the controller.
  • A swap between two inventories keeps the item ids of both stacks where they are free (before, both got new ids).
  • Equip, unequip and item costs change everything or nothing: Unequip of a stack that only partly fits leaves it equipped and returns NoSpace (before, the rest stayed equipped and the part that fit moved).
  • Item traits have new virtual functions (AddEquippedStats, GetStatsOnlyWhileActive): rebuild modules that derive item traits. RepairItem pays its repair items from the given inventory as one Items cost and never takes lent stacks.
  • Item definitions have the primary asset type PulseItem (PulseItem:<asset name>), registered in code. Item asset names must be unique (duplicates: editor warning, ensure in packaged non-shipping builds); renamed items that are in saves need a PrimaryAssetIdRedirects entry. Remove own PrimaryAssetTypesToScan entries for PulseItemDefinition, PulseRecipe and PulseRecipeBook, or keep PulseRecipe / PulseRecipeBook with exactly the base class /Script/PulseCrafting.PulseRecipe / .PulseRecipeBook, bHasBlueprintClasses=False, bIsEditorOnly=False (other settings are reported and the plugin then does not register the type).
  • Save data version 2 (inventory, equipment, crafting) stores items and recipes by id next to the path; version 1 data still loads.
  • On clients FindItem skips a stack whose replicated removal is in progress: inside OnItemRemoved use the event’s item; GetItems() still lists the stack (IsItemPendingRemoval tells).
  • Drops plan their removal right after the checks: user code that changes the inventory during the drop (GetDropTransform, the pickup spawn) makes it fail with InvalidItem instead of duplicating items. OnItemDropped fires right after the removal events of the same drop.
  • Item definitions have the native virtual CanAddToInventoryView for rules that must hold for planned states; CanAddToInventory only sees the live component.
  • Equipment events of an equip, unequip or item cost fire after its inventory events, also when it runs inside an inventory listener; equipment changes made by inventory listeners fire after them. An equipment import reports a saved active slot that is no hotbar slot.
  • Repairs read their data instance first (Pulse::Inventory::FindRepairRules, MakeRepairCosts): an instance durability with its own repair item pays that item, one without falls back to the definition. Stats count a definition trait once when the instance has a trait of a base or derived type.
  • The inventory view model (MVVMPulseInventory) keeps the item model of an id when a swap or an import keeps that id; on clients it leaves out stacks whose removal is in progress.
  • Saved items and recipes are loaded by their id first, the path is the fallback. Recipe and recipe book asset names must be unique within their type (data validation, setup step). Crafting save data version 2 stores unlocked recipes as Recipes (id and path per entry); UnlockedRecipes is read for version 1 data only.

Crafting, gathering and combat changes since early 2.0 previews

Crafting, gathering and combat are new in 2.0. Projects that used an early 2.0 preview:

  • Resource nodes keep their state as one value, FPulseResourceNodeState (harvests left, hits, regrow stamp), with GetNodeState / SetNodeState and versioned SaveToBytes / LoadFromBytes that keep the regrow wait. Regrowing follows the Pulse game clock (rate and pause); with the default clock nothing changes. SetRemainingHarvests still starts a fresh wait when none runs.
  • Crafting stations keep their values in one property, Station Settings (FPulseCraftingStationSettings). The five properties StationTags, SpeedMultiplier, UseRange, bUseStationInventory and bOutputToStationInventory are gone: set the values again under Station Settings in your stations (values saved under the old names are not read). Blueprint: read Station Settings, write it with Set Station Settings. C++: GetStationSettings(), SetStationSettings(...), GetStationTags(), GetSpeedMultiplier().
  • The life state of UPulseCombatComponent is one replicated value, FPulseLifeState (GetLifeState), with the killer as ids (FPulseKillerIds: player id for players only, actor key for placed or keyed actors, team, class; GetKillerIds, GetKillerPlayerId). EPulseCombatState moved to Components/PulseLifeState.h (included by the component header). Death and revive events are the same as before.
  • Crafting events fire after the whole change: the crafter’s own inventory changes first (one OnInventoryChanged per applied change; StartCrafting fires one per source inventory instead of one per removed stack), then the queue events, then one OnQueueChanged per operation (also for CancelAllJobs). Calls from listeners fire their events after the outer ones, so an OnJobCancelled listener already sees the next job started. A round whose outputs all failed their chance counts at once.
  • Every replicated property of PulseCore uses the push model (bIsPushBased, marked where it is written). Game subclasses must change replicated state through the setters (SetStationSettings, SetRemainingHarvests, SetNodeState, Kill / Revive, the inventory functions), or mark the property themselves with MARK_PROPERTY_DIRTY_FROM_NAME; otherwise clients do not see the change where push model is on. Attribute sets: an Init<Attribute> call after the first replication needs a mark (InitFromMetaDataTable and ability system writes mark by themselves).
  • Crafting save data version 3 stores the station id of every job (FPulseCraftingSavedJob::StationId); versions 1 and 2 load without station as before. Stations placed in a level get an id without setup, stations spawned while playing need a Key in their Save Identity.

PulseCore plugin documentation.

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