Skip to content

Pickups System — Integration Surfaces

Where new logic belongs

This page explains where new gameplay logic should integrate with the RevFramework Pickups system.

If you're adding new behaviour, start here.

The Pickups architecture is intentionally layered so that gameplay code can extend the system without modifying its internals. Each extension point exists for a specific responsibility.

Choosing the correct surface keeps systems predictable, maintainable, and compatible with the rest of RevFramework.


Integration Surfaces Overview

Goal Extension Surface Example
Add a new pickup behaviour PickupEffect teleport, spawn actor, apply buff
Wrap behaviour around an effect PickupEffectDecorator play VFX/SFX, conditional gate
Create a new pickup asset type PickupEffectDefinitionBase custom ScriptableObject definition
Customize decorator creation IPickupDecoratorCreator custom decorator pipeline
Replace decorator resolution IPickupDecoratorRegistry alternate decorator registry
Control multiplayer authority IPickupAuthority server authority, owner checks
Add success feedback IPickupFeedback play sound, spawn particles
Add failure feedback IPickupFailFeedback flash, deny sound
Create new world pickup behaviour TriggerPickup trigger-based pickup
Create custom interactable pickups InteractablePickupBase press / hold interaction
Forward physics callbacks to a pickup TriggerRelay2D / TriggerRelay3D one receiver, resolved on Awake
Integrate other gameplay systems Effects / decorators health, inventory, economy

Add a New Pickup Behaviour

Use:

PickupEffect

This is the core extension surface when the behaviour belongs to the payload of the pickup.

Examples:

  • teleport player
  • grant currency
  • apply status
  • spawn actor
  • trigger animation
PickupDefinition
→ builds PickupEffect
→ effect runs when pickup is consumed

Wrap Behaviour Around an Effect

Use:

PickupEffectDecorator

Decorators wrap another effect and run before or after it.

Examples:

  • spawn VFX
  • play sound
  • add random chance
  • add conditional gates
Decorator
→ runs before effect
→ runs effect
→ runs after effect

Decorators allow behaviour to be added without modifying the core effect.


Create a New Pickup Asset Type

Use:

PickupEffectDefinitionBase

Definitions are authoring assets used by designers and tools.

They describe:

  • what effect to build
  • what decorators to apply
  • configuration data

Definitions should not contain runtime logic.


Customize Decorator Creation

Use:

IPickupDecoratorCreator

This allows custom decorator construction logic.

Typical uses:

  • custom decorator categories
  • decorator pipelines
  • specialized effect wrapping

Decorators are resolved through the registry used by the factory.


Add Multiplayer Authority Rules

Use:

IPickupAuthority

This interface determines whether a pickup may be consumed by a given actor.

Typical implementations:

  • server-authoritative gameplay
  • ownership checks
  • permission systems

The system resolves authority through:

PickupAuthority.Resolve(...)

If no authority is present, pickups proceed without authority gating.

Resolution is cached per scene, and a scene where nothing was found is remembered for the rest of that frame — the search ends in a scan of every active MonoBehaviour, and without that memo it ran once per pickup collection. An authority spawned or enabled mid-frame is therefore picked up on the next frame rather than immediately. Call PickupAuthority.Invalidate() if you need it to take effect at once.


Add Feedback

Feedback should not modify gameplay logic.

Instead use:

IPickupFeedback
IPickupFailFeedback

Examples:

Success feedback:

  • play sound
  • spawn particles
  • UI confirmation

Failure feedback:

  • flash red
  • play error sound
  • UI message

Create New World Pickup Behaviour

Use:

TriggerPickup

This component handles:

  • trigger entry (enter only — stay and exit are ignored)
  • actor validation: layer mask, optional required tag, and the effect's null-damageable policy
  • optional authority checks
  • effect application through PickupEffectRunner.TryApply(...)
  • destroy on use, or re-arm on the next physics step when destroyOnUse is off

TriggerPickup has no respawn behaviour. destroyOnUse is a bool: the pickup is either destroyed after a delivered payload or left in place and re-armed. Timed respawn (hide, wait, reappear) lives on InteractablePickupBase instead.

A refused payload is not consumed. TriggerPickup reads the bool from TryApply and leaves itself in the world when the effect reported a refusal, so an actor can come back for it.

Extend or compose around it if you need custom world pickup logic.


Create Custom Interactable Pickups

Use:

InteractablePickupBase

This provides a richer interaction flow:

  • auto pickup
  • press-to-pickup
  • hold-to-pickup
  • prompt UI
  • facing checks
  • respawn behaviour (respawn / respawnDelay — hide, wait, reappear)

Override DoPickup() to implement custom consumption logic.

This base enforces authority, since 1.3.0

Like TriggerPickup, it consults PickupAuthority — in TryPickup, before your DoPickup(...) is called, so a denied actor never reaches your delivery code. You do not need to write the check.

When no authority is in reach, Resolve returns null and the pickup proceeds, so this is a no-op for projects that do not use pickup authority. Before 1.3.0 the base enforced nothing and the gate was each subclass's job.


Wire the Trigger Relay

Use:

TriggerRelay2D / TriggerRelay3D

A relay forwards Unity's OnTriggerEnter/Stay/Exit into the IPickupTriggerReceiver contract, and forces its attached collider to isTrigger on Awake.

One receiver, resolved once, in Awake

A relay calls GetComponent<IPickupTriggerReceiver>() once, in Awake, and caches the single component it finds on the same GameObject.

Awake has already run by the time Instantiate returns. A pickup component added after spawning is never wired to the relay — it sits on the object receiving nothing, with no error and no warning.

So the receiver (TriggerPickup, or your InteractablePickupBase subclass) must be authored on the prefab, beside the relay and the collider. Adding a relay late is fine; adding the receiver late is not.

One receiver, not many: if a GameObject carries two, only the first is forwarded to. Put a second pickup behaviour on a child object with its own collider and relay.


Integrate Other Systems

Pickups does not depend on other gameplay systems directly.

Instead, integrations are typically implemented through:

  • custom PickupEffect implementations
  • decorators that wrap effects
  • external systems invoking PickupEffectRunner

Examples:

  • applying health changes
  • granting inventory items
  • awarding currency
  • applying status effects

Typical pattern:

PickupEffectDefinition
→ factory builds effect chain
→ effect or decorator calls into external system

This keeps the Pickups system modular and dependency-free.


What Not To Extend

Avoid modifying

  • internal decorator implementations
  • effect runner internals
  • cooldown systems
  • internal utility MonoBehaviours
  • reflection bridges or internal helpers

If behaviour does not fit any integration surface listed here, it likely belongs in another system.


Rule of Thumb

When adding behaviour ask:

Question Correct Surface
What happens when consumed? PickupEffect
What wraps that behaviour? PickupEffectDecorator
How is the pickup authored? PickupEffectDefinitionBase
What triggers the pickup? TriggerPickup / InteractablePickupBase
Who is allowed to consume it? IPickupAuthority
What UX surrounds the event? IPickupFeedback

Tip

If you follow this mapping, the Pickups system stays clean and composable.