I’d like fire attacks to do more damage vs earth and less vs water.
Same for earth and water.
Is storing the element as a string or integer and using if/then/else the only way or is there a better way?
public enum Element { Fire, Earth, Water }
public float CalculateDamage(Element source, Element target)
{
if (source == Element.Fire)
{
if (target == Element.Fire)
return 0.2f;
else if (target == Element.Earth)
return 0.3f;
else if (target == Element.Water)
return 0.1f;
}
else if (source == Element.Earth)
{
// same
}
else if (source == Element.Water)
{
// same
}
}
Thanks a lot!
You could also make a 2D array such as float[SrcElement, TargetElement] and put in the corresponding values. I’m pretty sure it would be a little faster if you’re using it extensively, and at the very least you get more elegant code… (but i guess this is subjective :p)
Yea I’ll be using it a lot. But what would I do with my 2D array?
You could do something like this :
float[,] elementToElementDamageRatios = new float[3,3];
void Start()
{
//Init Fire to X
elementToElementDamageRatios[(int)Element.Fire, (int)Element.Fire] = 0.2f;
elementToElementDamageRatios[(int)Element.Fire, (int)Element.Earth] = 0.3f;
elementToElementDamageRatios[(int)Element.Fire, (int)Element.Water] = 0.1f;
//Init Earth to X
elementToElementDamageRatios[(int)Element.Earth, (int)Element.Fire] = __;
elementToElementDamageRatios[(int)Element.Earth, (int)Element.Earth] = __;
elementToElementDamageRatios[(int)Element.Earth, (int)Element.Water] = __;
//Init Water to X
elementToElementDamageRatios[(int)Element.Water, (int)Element.Fire] = __;
elementToElementDamageRatios[(int)Element.Water, (int)Element.Earth] = __;
elementToElementDamageRatios[(int)Element.Water, (int)Element.Water] = __;
}
public float CalculateDamage(Element source, Element target)
{
return (elementToElementDamageRatios[(int)source, (int)target]);
}
You could make the variable public and have a nice editor script to show the names of the elements if you’re intending to work a lot on thoose values but that’s not mandatory. At least the lookup of the value is as fast as it gets and you can have millions of units happily firing at each other without lag (at least coming from this :p)
That’s really smart. Thanks!
Hate to “zombie” a thread, but I really think this will help with an issue that I’m currently having. I just have to ask one more question @_Daniel and @_met44 . Will this still work if the “target” has multiple elements, and those elements have different weaknesses/resistances/immunity to the “source” element?
Here’s an excerpt from my thread on this:
So to put a face on this. For example:
-Creature attacks player with Toxic.
-Toxic’s elements type = POISON.
-Player’s element types = GROUND & ROCK.
-Both GROUND and ROCK take half damage from POISON element.
-This would result in the player taking 25% of the total damage.
Then Type1 = 0.5 (POISON vs GROUND) …and… Type2 = 0.5 (POISON vs ROCK) which makes the attacks total effectiveness (which is what I use the chart to solve in the first place) 0.25 (because 0.5 * 0.5 = 0.25).
To solve: Modifier = STAB * (Type1 * Type2) * Critcal * other * Random.Range(0.85, 1);
So far I’ve been trying to use an Excel spreadsheet, but if I can use @_met44 's method it would be awesome.
@DRRosen3 , you could make the enum an Array and then use a for each element loop and calculate the value that way.
I’m not home right now so I can’t post any code but I think it’d work.
Okay. I think I know what you’re saying. It’d pretty much look exactly the same as the example @_met44 gave right? Except the return would be different because my players/creatures have two types.
-
- public float CalculateDamage(Element source, Element target1, Element target2)
- {
- return (elementToElementDamageRatios[(int)source, (int)target1, (int)target2]);
- }
Is that right?
EDIT - ACTUALLY…for me I think it’d be more like this:
I have to solve: Modifier = STAB * (Type1 * Type2) * Critcal * other * Random.Range(0.85, 1);
- public float CalculateType1(Element source, Element target1)
- {
- return (elementToElementDamageRatios[(int)source, (int)target1]);
- }
- public float CalculateType2(Element source, Element target2);
- {
- return (elementToElementDamageRations[(int)source, (int)target2]);
- }
And then set the Type1 variable to be equal to CalculateType1 and the Type2 variable to be equal to CalculateType2.
Not exactly.
First off, I created a public enum Element to let the user select the object’s element.
For you, you could make it like this:
public enum Element = {
Fire,
Ice,
Earth
}
public Element element;
private float damageFactor;
Then you’d have a loop
foreach (Element e in Element) {
damageFactor += (elementToElementDamageRatios[(int)source, (int)target]);
}
Hmmm. Keep in I have two elements on each object.
Well that’s why you use an array and a loop.
when you do “public Element element;” you can specify how many and what elements the object will have. 1, 2, 3 or 10.
Then your loop will loop through every element, whether it’s once or ten times, if the object has 10 different elements.
That’s almost it except he need to multiply instead of adding.
But if you have only 2 elements forever, go simple with just using the same method twice and multiply the result, no need to create other methods, you just pass in the right parameters :).
so you simply end up with:
float type1 = CalculateDamage(elem, target1);
float type2 = CalculateDamage(elem, target2);
Modifier = STAB * (type1 * type2 ) * Critcal * other * Random.Range(0.85, 1);
more complex but more flexiable way is to just make your damage and armour there own data types with classes, which contain all the information about why they are strong and weak against, with some built in methods for comparing armor types to damage types
That’s exactly what I have done. I’m just working on, right now, how to access the arguments for the method. The CalculateDamage method is written inside my CalculateTotalDamage script, but the 2D array is in its own script.
EDIT - Sorry, I just realized what I said. The CalculateDamage(); method is written in the same script as the 2D array. I then call the method in my CalculateTotalDamage script, but, the information needed for the arguements that CalculateDamage(); needs is in a TOTALLY DIFFERENT script.
Well, first, they’re element types and not armor, but I get what you mean. However, I think it’s a bit extreme, as I have 19 different element types, so that’s a LOT of scrips. Granted when compiled the scripts are very tiny, I still think that’s a bit excessive.
Just to add another alternative to this discussion for future reference, I would personally likely opt for the following code. It’s a bit more readable imho, though at a small runtime cost in relation to a 2D array.
enum Element {Fire, Earth, Water, Air};
Dictionary<Element, Element[]> doubleDamage = new Dictionary<Element, Element[]>();
Dictionary<Element, Element[]> halfDamage = new Dictionary<Element, Element[]>();
doubleDamage.Add(Element.Fire, new Element[]{Element.Earth});
doubleDamage.Add(Element.Air, new Element[]{Element.Fire, Element.Earth});
halfDamage.Add(Element.Water, new Element[]{Element.Earth, Element.Air});
void CalculateDamage(Element sourceElement, Element[] targetElements){
float damageMultiplier = 1f;
foreach(Element elem in targetElements){
if(doubleDamage[sourceElement].Contains(elem)){
damageMultiplier *= 2;
}
else if(halfDamage[sourceElement].Contains(elem)){
damageMultiplier *= 0.5f;
}
}
return damageMultiplier; //+ STAB + ....
}
This also means you don’t need to store every possible combination; just the ones you actually need/ use.
Just my $0.02. =)
Easy, use a reference to that component, either by making it a public parameter or using GetComponent on the object that holds it.
You could also use the singleton pattern if the component holding the data is doing it for your whole game.