Disclaimer: This is a solo project. All functionality you see here is of my creation.
The keys are pretty basic. They're all instances of the same actor, BP_Pickup. You can change the color per-instance in the editor and the functionality will work exactly as intended.
When you pick up a key, the player state gets updated on that, which in turn updates the HUD (bottom right). Since you can only carry one of each key at a time, the game will inform you with a low buzz when you can't pick up a key.
Key possession and behavior is pretty simple (the possession order gets a little bit muddy), but the timing behavior is the complicated bit.
It looks pretty simple on this end. Those BPI events get called from the Timer Component. Instead of destroying the actor like I normally would, I hide it while the timer ticks down. We'll then check its state only when the timer ends. If we've set it to kill (which only happens when the key is put into a receptacle), then when the timer ends, we'll destroy the actor. If not, we reset everything and take the key from the player.
That timer is also called to end if we put the key into a receptacle, so we don't hear the ticking if we don't need to.
I talked a bit about my custom component here. To recap very quickly, the component serves to keep track of the state of targeted actors, ensure they're all 'checked in', and then run the timer. All of this behavior takes place inside of this component.
Whenever the target actor checks in, we set their state on a map and try to run a timer. The timer will only run if all of the values of the map read "true". If you look at Figure 1B, you'll note that we call the check-in as soon as the timer starts. Since our key is the only 'target' that we pass in to the timer component, the timer starts ticking right away.
Back to the keys. We need to know whether or not we should strip the player of the key or destroy that actor we hid away.
Our player state has an event delegate for announcing when possession changes (which applies to any and all keys). I don't want to bind this on every instance, though; only when we activate timer behavior.
So on those timer-active instances, this KillActor event is called. If our condition is true, we 'set to kill' (evaluated in Figure 1B) and then we call the Timer to reset. That reset shuts off the ticking sound and also calls Timer End in Figure 1B.
Our evaluation goes a bit like this:
Go get our card possessions from the player state.
Do we NOT HAVE the card that matches the card color of this instance?
If we DO NOT HAVE that card, and this current instance has been set as 'picked up' (Figure 1A), then set Set To Kill true.
Run Timer Reset Event
The idea is that if we don't have the card anymore but we did pick it up, there's only one thing that could have happened to it; we used it. If we still have the card, then this evaluates to false and we get a reset.