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.
- Add the plugin
PulseFoundationto the project. PulseCore lists it as a dependency, so it is enabled with PulseCore; projects whose own modules usePulseGameCoreshould also enable it in their.uproject. - 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, orCancel. The back action triggers the action with roleCancel. - 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.SetCompassVisibilityisSetCompassVisible(hiding works now). Marker widgets don’t tick; Blueprint marker classes that usedEvent Tickmust move that code toOnMarkerDataChangedor the position overrides. - Input manager: equal priorities keep their push order (newest wins).
RemoveInputContextinvalidates 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:
- HUD widget: reparent to
UPulseHUDLayout, remove thePulse HUD Interface, name the compass panelCompassSlot. - Simple/confirm modals: reparent to
UPulseHUDModal(redirect does it on load), rename the content panel toContentSlot, replace theAddConfirmButton/AddCancelButtonevents by a dynamic entry boxActionListwhose entry class is aUPulseHUDModalActionButton. - Blueprints that called
PushConfirmModal/PushSimpleModalor the metadata nodes: use Show Modal And Wait with Make Confirm Modal Request (or a request withContentClass). - 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,CancelButtonStyleandNeutralButtonStyleare 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
bHideGameplayHUDWhileMenuOpento keep it. - Distances of markers show their unit (“120 m”);
bShowDistanceUnitturns 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, replacesW_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. UPulseCombatHealthBardefaults: trail colorPulse.UI.Color.BarLag, shieldPulse.UI.Color.Accent; critical hit numbers usePulse.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:
IPulseInteractableInterfacehas two new functions with defaults,GetInteractionInfoandOnInteractionCompleted.- 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;OnInteractionStartedstill fires when the input goes down. UPulseInteractableComponent::CanBeInteractedWithalso checks the newConditionsandCosts(Pulse conditions and costs of PulseFoundation); the costs are paid when the interaction completes.UPulseRepInteractAbilitysendsServerStartInteraction/ServerEndInteractionto 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 withbReplicateInteractionEventsget their events on the server for client players.- The query task finds interactables that are already in range when the ability activates.
PulseAbilitydepends onEnhancedInput(forFPulseInteractionInfo::InputAction).GetInteractionTimeandGetHoldProgress(alsoGetHoldProgressof 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;
OnInventoryChangedfires 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:
SpawnFailedandRateLimitedmean 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:
Unequipof a stack that only partly fits leaves it equipped and returnsNoSpace(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.RepairItempays 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 aPrimaryAssetIdRedirectsentry. Remove ownPrimaryAssetTypesToScanentries forPulseItemDefinition,PulseRecipeandPulseRecipeBook, or keepPulseRecipe/PulseRecipeBookwith 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
FindItemskips a stack whose replicated removal is in progress: insideOnItemRemoveduse the event’s item;GetItems()still lists the stack (IsItemPendingRemovaltells). - Drops plan their removal right after the checks: user code that changes the inventory during the drop (
GetDropTransform, the pickup spawn) makes it fail withInvalidIteminstead of duplicating items.OnItemDroppedfires right after the removal events of the same drop. - Item definitions have the native virtual
CanAddToInventoryViewfor rules that must hold for planned states;CanAddToInventoryonly 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);UnlockedRecipesis 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), withGetNodeState/SetNodeStateand versionedSaveToBytes/LoadFromBytesthat keep the regrow wait. Regrowing follows the Pulse game clock (rate and pause); with the default clock nothing changes.SetRemainingHarvestsstill starts a fresh wait when none runs. - Crafting stations keep their values in one property, Station Settings (
FPulseCraftingStationSettings). The five propertiesStationTags,SpeedMultiplier,UseRange,bUseStationInventoryandbOutputToStationInventoryare 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
UPulseCombatComponentis 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).EPulseCombatStatemoved toComponents/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
OnInventoryChangedper applied change;StartCraftingfires one per source inventory instead of one per removed stack), then the queue events, then oneOnQueueChangedper operation (also forCancelAllJobs). Calls from listeners fire their events after the outer ones, so anOnJobCancelledlistener 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 withMARK_PROPERTY_DIRTY_FROM_NAME; otherwise clients do not see the change where push model is on. Attribute sets: anInit<Attribute>call after the first replication needs a mark (InitFromMetaDataTableand 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.