Need advice about PowerUp in Multiplayer


I’m basically trying to implement the Unreal Tournament’s UDamage, Adrenaline and Invisibility PowerUps that anyone in the level can take.

What I already have is a Pickup class which works for weapons, ammo, health and armor.

The rules for PowerUps are the following:

  • Only one player at a time can have the same PowerUp [HR][/HR]
  • The PowerUp has a global lifetime: once it is picked up, it lasts X seconds no matter how many owners it will encounter
  • [HR][/HR]The PowerUp’s lifetime is displayed to the owner player from a HUD. When the owner player dies or the PowerUp expires, the HUD must be removed

My idea was to:

  • Write 3 methods for the Character: SetDoubleDamage(bool), SetAdrenaline(bool), SetInvisibility(bool) - these methods would be called from the Pickup class [HR][/HR]
  • Keep track of the lifetime of each PowerUp in the GameMode with a float (PowerUpLifetime) [HR][/HR]
  • Hold a Timer in the GameState which determines the expiration of each (taken) PowerUp

But then, I though “what if I want to add a new PowerUp in the future? O maybe other 3 PowerUps?”

Then I will have to write X more methods for the Character, keep X more lifetime floats in the GameMode and hold X more timers in the GameState.

I don’t even know if it is correct to keep the lifetime floats in the GameMode and the Timers in the GameState.

I’m looking for advice about this. It’s mostly about “where to place the things” and “who should keep track of what”.

Thank you in advance.

bump i need some advice about how to implement it

Check out their newest demo project “Action RPG Game” and the gameplay abilities. Tom Looman also has a video he posted making some changes to it.

You should create a custom PowerUp class that keep all of this at the same place.
Create a new c++ class or bp that inherite from AActor and had only one of these in the persistant level, then do stuff in this BP, you’ll be able to access it from anywhere using getallActorOfCLass. If you made it in c++ you’ll be able to create a static ufunction that return the reference to it and then access it by this static function.
Never change any values from elsewhere than in this BP, or you’ll have problems later, but you can get values from anywhere. For Set values, just call a function in PowerUp like updatePlayerDead(Player).
Best way is to add events where you need, and listen for them in PowerUp that will do stuff itself…

There is a lots of way doing this but for keep your data in your case it seem maps are the variable type you need, my opinion is to make :

that is not a working code, just here for show a structure!!!

A base abstract class PowerUpDef that represent your powerUp, can be store in you pickup class:
lineartexture2D icon;
FName name;
float duration;
virtual StartBuff(Player player)
virtual StopBuff(Player player)

a struct PlayerPower that represent a powerUp durring usage:
PowerUpDef powerUpDef;

A map<Player,PlayerPower> aliveData that store playerPower that are alive;

then when your player pick up a powerup you can call a func

An update function that ckeck validity of timers


That will alow you to make different powerUpDef subclass implementing startBuff and StopBuff without doing any change to your PowerUp class.

ElFab has the right idea. You already have a custom pickup class that handles giving something to a pawn so you want to extend from there. Here is what I would do:

  1. Create a child of your pickup class called Powerup. This child is responsible for tracking it’s “powerup time” and doing whatever the powerup does. For time, I’d do this with a replicated variable and it’s up to you if you want to adjust the value server side in a timer (less accurate, less demanding) or tick (very accurate, very demanding). You control the # of powerups/etc available by either only placing 1 of each or only spawning 1 of each.

  2. Add a (replicated) reference to the currently held powerup in your pawn class. When you pickup the powerup, set that reference. When the pawn dies or they drop the power up or the powerup is dead, remove it. I’d also setup an activation/deactivation event chain in the powerup. You can hold a reference to the “active” powerups in your gamestate as well if you want.

  3. When you give the powerup to a actor, also set the powerup’s owner to that actor (and conversly remove the owner when dropped/over). This way you can restrict replicatioin to just the actor owning the powerup. Or dont and make the replication of the powerup time always relevant. It’s up to you.

  4. You have to determine how you want the powerup to add effects to the character. There are a lot of ways to do this and the “best” way is really determined by your game. For example, to do a damage powerup, you could have a damage multiplier in your pawn and when you add the powerup, it sets the modifier. When you remove it it removes the modifier. Or you can make your “cause damage” functions in your weapon/projectile/etc class to ask their instrigator for a damage modifier and that causes the actor to query any owned powerups, etc and etc. It’s really up to you.

Now as for having to add additional methods to your pawn to add different powerups, the answer is of course you might have to. But if you utilize the actor system this way you’re setting yourself up for the easier route.

Thanks for the detailed answer. I want to be sure my idea is somewhat similar to yours:

  1. Subclass the Pickup class calling it Pickup_PowerUp with one additional variable: **duration **to identify how much time the powerup lasts

  2. Subclass the Pickup_PowerUp calling it Pickup_PowerUp_DoubleDamage -> when this Pickup_PowerUp_DoubleDamage is picked up, call Character’s enableDoubleDamage()

The **enableDoubleDamage() **does 3 things

  • Sets the Character’s boolean **hasDoubleDamage **to **true **(and this boolean is used to check whether the damage multiplier shall be applied or not)
  • The HUD is shown to the Character showing a countdown for the effect of the double damage
  • Starts Character’s timer for the double damage. When the Timer expires, the **disableDoubleDamage() **function sets **hasDoubleDamage back to false **and hides the HUD

What if the Character dies?

In that case, I spawn a Pickup_PowerUp_DoubleDamage from the dead character’s position and at the same time I call disableDoubleDamage()

What is the problem?

The problems here are two:

  1. Who keeps track of the remaining time of a particular PowerUp? When the player with the PowerUp dies and the new player picks it up, the Timer has to start from where it had ended when the previous owner died…

  2. If I have 10 pickups, then I should create 10 booleans for my Character (hasXYZ), 10 timers and 10 enable/disable functions?

Thanks in advance.

The character shouldn’t care about the duration of the powerup. The character should only care/know that a powerup is being applied to it. The powerup class itself should be responsible for it’s timing and for telling the character to start and stop it’s effects. The logic is sort of like this:

  1. Character overlaps a powerup so Character->Pickup(Powerup) is called passing in the powerup that the character wants to pick up.
  2. Character adds the powerup to an array of powerups (or inventory or whatever).
  3. Character calls Powerup->PickedupBy(Holder) passing in a reference to the charater that holds it
  4. Powerup->PickedupBy() stores a refernece to the Holder
  5. Powerup does whatever initialization/reinitialzaiton needed
  6. Powerup calls Holder->EnableDoubleDamage()
  7. In Powerup->Tick() if there is a valid holder, the powerup decrements it’s Duration. If Duration <= 0.0 then it calls Powerup->Expired()
  8. Powerup->Expired() calls Holder->DropPickup(Powerup)
    8 Character->DropPickup() removes the Powerup for the inventory and calls Powerup->Drop()
  9. Powerup->Drop() calls Holder->DisableDoubleDamage()
  10. Powerup->Drop() resets the holder
  11. Powerup->Drop() enables physics, does any deactivation/etc.
  12. Powerup->Finished() calls Destory()

When the pawn dies, it iterates over it’s inventory and calls Powerup->Drop() on all of them. That will cause the powerups to turn off their function and drop in to the world.

Hope that helps.