I have been experimenting with HUDs. I was able to to create some usual game play hud elements like, health-bar, proximity indicator…
But then I tried to do something a bit more complex. I will explain what I’ve been trying to achieve.
I have a basic demo with a couple of charcter classes, weapons, damage-types, and enemy types. The enmies emplu several protection mechanisms like Shield, Armour,… Some weapons are effective against Shileld, while others are effective against armor. There is also some damage modifiers based on the character class. Now when my character aims at a particular enemy, I need to draw a hud over this enemy showing some stats such as his health, protections… Plus I want to show how much damage the current weapon could do to this enemy.
I was able to do this, but my HUD blueprint became a mess. The issue is I have to take values from a number of entities to calculate the predicted damage. It involves the pawn, the weapon he is carrying and the controller.
So my question is how should I divide the job among these entities. I could think of 3 options right now:
- Create a function in the controller called ‘EstimateDamage’. This function calculates the damage by referring to various properties in Pawn, Active weapon and the Active enemy. Then call this function from HUD and draw the stats.
- Create a function in controller called ‘DrawEstimation’. THis function accepts a HUD object as its input. The function calculates damage like in option1 and then draws it using the HUD object passed into it. Later within the HUD’s draw event, call this function along with a reference to self.
- Do everything in HUD like now.
The problems I get with each method is below:
- Since stats must be displayed above the enemy, I must get a reference to it within the HUD. But in my current setup, this is not a publicaly visible property.
- Since the drawing is done within the controller, I have access to the Enemy. But putting draw calls inside controller does not feel right.
- Too much code in HUD and feels like tight coupling, meaning I will have to edit the HUD every time I change the Damage system.
So what is the ‘intended’ way of doing this?
Thanks in advance.
For start, you shoot line trace to get information about your target, from withing blueprint.
You want to display stats about target, That is simple part. Just create function in HUD blueprint that as argument will take at least Object of type of your target, you supply it from your trace. In function you just get properties of your target and draw them on screen.
As for damage prediction.
First you need to get object that will deal damage first. Weapon, ability, spell, whatever.
Then in C++ or in class blueprint you will need to create function, that will get at least one argument that is your Target Object, like:
In that function you perform simulation of possible damage. based on current target stats. Try to keep it simple, this function will be called on each tick, and executed completely as long as you supply valid target!
You also will probably need struct, to cotain all the data you want to display. Define it in separate file, add UPROPERTY(BlueprintReadOnly) to struct properties and make struct BlueprintType.
Then use this struct to aggregate data from your function and return it.
In blueprint HUD call the above function, and create another Draw function that will take as argument your struct.
I know how to calculate the estimated damage. But I wasn’t sure where to do it.
So in your opinion, that function should go inside the DamageCauser (weapon, spell…)?
Yes. You can make it virtual in base class of your weapon/ability and then let the each inherited class implement it.
I assume each weapon/ability will have slightly different formula for calculating damage output, so it’s good idea for each class to implement it’s own damage prediction function.
Actually its bit more complicated than that. Its not just the weapon. The active player class applies some modifiers (like more accuracy, more weapon damage…)
So I made two functions: One within the weapon/damage causer which will return a struct with the base values (accuracy,damage) it will do based on the current weapon state. Then within the character, I have another function which will apply the modifiers to this struct and then apply it against the enemy (this involves a call to enemy class function which will give me its resistance modifiers) The function on Character will use all these things to calculate the effective a damage and return it. I then call this function from HUD and use its output to draw the estimate on screen.
To improve performance I re-calculate the estimated damage only 3 times in a second.
Thanks for the help. Please let me know if you have any more suggestions.
HUD doesn’t really care from where data comes or how it come to it. It should only care about how to display it ;).
If you have properties on character that affect weapon firing, then it still shouldn’t really matter much.
Your weapon is Owned by something. In that case it will be Pawn that wields it.
So in your function that predict damage, just get stats from Owner and apply them in any way you see fit. I think doing everything in single place (in that case Weapon) is still better and you make clear dependency.
In your case now, you are calling back and forth which is not needed at all. All you need to is to get Owner of your weapon and apply stats to it from Owner.
That is, how I do it anyway. Nothing wrong with your approach, but if different weapon will want to make a little bit different usage of your character stats, you will have to implement some if/switches to figure out which formula to use for calculations.
You can of course still display final prediction trough function in character if that makes it easier for you.
Yes it makes sense. But on closer inspection, I find that the way my Charcter’s class affect the damage output is a bit complicated. On the other hand, all the weapons behave in the same way and only differ in their properties (ie their firing logic and damage logic is the same - but have different base damage, accuracy, projectile velocity…). So I think in my case the calculation should go with the Character.
Anyway I understand the advantages of your design. I will keep it mind.
I would prefer a unified approach solely by using blueprints. Do it all in the HUD but with functions and collapsed nodes so it doesn’t look like spaghetti. This way you wouldn’t lose track of what’s going on and can update the estimation mechanism by simply modifying a single blueprint.
Create duplicates of the variables in your damage mechanism within the HUD (such as Armour, Shield, barrier, WeaponClass, CharacterClass - the last two maybe enumerations to take advantage of the “Switch on Enum” node). Then create a function called “EstimateDamage”.
Create a blueprint interface shared among your character, enemies and the HUD. Use a line trace mechanism to get your enemy to tell the HUD about the Armour, Shield, Damage etc. via this interface. Then run the “EstimateDamage” function before you draw the information on the screen.
You can use World to Screen Space node to get the screen location of the enemy for a fancier look.