Return one variable that can have different data types

Hi, I’m wanting to return a variable that can different data types, float or int.
So “damage” returns int, “attackSpeed” returns float.

I’ve tried converting the float to int, but how do I go back to use the float again?

damage = unitStats.combatStats(coll.gameObject.tag, “damage”); // Find out the opponents damage

public int combatStats (string type, string field) {
if (type == “humanTank”)
{
health = 100;
armour = 5;
damage = 10;
attackSpeed = 0.03f;
moveSpeed = 0.02f;
unitType = “heavy”;
alignment = “human”;
}

switch (field) {
// these are dynamic based on the the feed
case “damage”:
return damage;
case “attackSpeed”:
return attackSpeed;

default: return 0;
}
}

If the function is defined as:

public int Func_name(){}

then what the function will return is going to be int. If you want more precision (float), you will have to return float and convert the float that the function returned into a int outside of the function.

public float Func_name(){ return 0.1f;}
int myInt = (int)Func_name();                  //Search: c# casting float to int / int to float

Is there a reason you’re using string comparisons for something like this? C# is a highly typed language - you can, however, cast any object in C# as an object and return it…

E.G.:

public object GetType(string WhatKind, float value)
{
    if(WhatKind = "int")
    {
        return (object)((int)value);
    }
    if(WhatKind = "double")
    {
        return (object)((double)value);
    }
    else if(WhatKind = "ParisHilton")
    {
        return (object)(new RottenChickenFermentedInTheDeadSeaIHopeYouDontForgetWhatTypesYourFunctionsMightReturnBecauseCastingEverythingToAnObjectCanGetReallyHardToSortOutOnTheOtherSide() );
    }
    
    return (object)(new SeeTheCompilerDoesntCareAtAllWhatICastAsObjectAndPassBackSoBugsAreHardToFind((object)WhatKind));

}

However, I wouldn’t do this kind of thing at all… In general you want to have one function return one type each time no matter what.

You look like you might benefit from learning about interfaces which allow you to operate on an indefinite number of objects generically by creating a “contract” that defines the kinds of methods that every kind of object derived from that interface will employ.

Random ramblings, apparently my head is off tonight.

You could just pass around objects and use reflection to determine their type, then act on them. But reflection is like fight club and static variables. Its one of those things that is only for use by those who already understand it.

You could also use a generic method. Generic methods can return values based on the type of value passed in.

You could just make everything a float

But I ultimately think you will do far better off by using a strong type, rather then all this string manipulation. Lazy variables are useful in some situations, but slow and error prone. There is a reason they are not included in the language by default. One typo in the string for the calling method, or the variable set up, and you have introduced a hard to spot bug. You also fry your autocomplete.

1 Like

Thanks for all your inputs guys.
The main purpose was to reduce repetition in code. I tried using objects " return(object)((int)value); " but unfortunately it broke other things.
I think I will have to make 2 functions to return ints and floats.

damage = unitStats.combatStatsInt(coll.gameObject.tag, "damage"); // Find out the opponents damage
        attackSpeed = unitStats.combatStatsFloat(coll.gameObject.tag, "attackSpeed"); // Find out the opponents attack speed

Just have your int version call the float version, and round the result.

1 Like

This seems like really poor design that’s going to lead to a debugging nightmare when you make a typo.

Why not create a component script (UnitAttributes) and attach it to your prefabs? That way you can edit the values in the editor, instead of searching a giant switch block statement, and finding all the information for any unit is as simple as GetComponent()?

1 Like

I’m going to agree with GroZZleR on this… (although I already did above)… You shouldn’t use tags for something like this. Instead you should attach a script to everything that needs its collisions examined and use “GetComponent” on the colliding object. After you do that, you can communicate freely with the colliding object directly between scripts. It will save the overhead of string comparisons which are a nasty type of comparison (switch implementation for a string is sometimes even more processing time than a whole bunch of “if … else if” blocks). Regardless, you want to avoid, in any programming you do, string comparisons unless it’s absolutely necessary - they have a lot of overhead and most of the time (with the exception of this tag method you’re using) an enum will suffice and be much faster.

I know what it’s like to be on the “don’t ever do what you’re doing” side of a programming forum post - so no offense intended, just trying to push some more proper technique on you.

I appreciate any input and want to do whats best, I understand where your coming from. Will I need a unitStats script for every type of unit?
So " if tag is alienWalker GetComponent "
" if tag is humanTank GetComponent "

Unit prefab can have its own stats but will need to know how much damage to take when colliding with different unit types.

Why isn’t there a ‘comboStatsDamage’ and a ‘comboStatsAttackSpeed’ methods that return the appropriate types for each, and you don’t have to pass in some magic string for it to work…

I don’t even understand the design here. Why are you passing in the ‘field’ and the ‘type’???

Have functions for each field!

And what is ‘type’ even for? Why would you ever call this method with anything other than “humanTank”?

With future units to be added. I was just using it as a tester / example

if (type == "humanTank")
{
health = 100;
armour = 5;
damage = 10;
attackSpeed = 0.03f;
moveSpeed = 0.02f;
unitType = "heavy";
alignment = "human";
}
if (type == "alienWalker")
{
health = 80;
armour = 1;
damage = 2;
attackSpeed = 0.02f;
moveSpeed = 0.03f;
unitType = "light";
alignment = "alien";
}

You can use one kind of script that holds an interface perhaps…

I’m not sure exactly what you’re doing… but this would be my approach to what I think you’re doing…

eg:

public class DamageStats
{
    public float attack;
    public float armor;
    public float ...
}

public interface ICollidable
{
       DamageStats Stats { get; set; }

       float DamageAmount();
}

public class HumanTank : ICollidable
{
      private DamageStats stats;

      public DamageStats Stats { get { return stats; } set { stats = value; } }

      public float DamageAmount()
      {
          ... Unique calculations here ...

      }
}

public class AlienWalker : ICollidable
{
      private DamageStats stats;

      public DamageStats Stats { get { return stats; } set { stats = value; } }

      public float DamageAmount()
      {
          ... Unique calculations here ...

      }
}

public class TagScript : MonoBehavior
{
     public ICollidable MyType; 
     public int typenumber; // set in inspector

     Start()
     {  
          switch(typenumber)
          {
               case 0:
                   MyType = new HumanTank();
                   break;
               case 1:
                   MyType = new AlienWalker();
                   break;
                default:
                    throw new UniverseImpodesException(); 
          }
     }
}

Unfortunate what you put up is beyond me.

How does each unit get unique sets of stats from damageStats if they get it all from public class DamageStats?

What I was trying to do was this;

  1. Game object “humanTank” hits “alienWalker”
  2. “humanTank” finds out alienWalkers damage and attackSpeed and gets damaged.
  3. “alienWalker” finds out humanTanks damage and attackSpeed and gets damaged.
  4. One will be victorious.

One script stores all stats for all units.
The script attached to each game object are identical so 1 script can handle all game objects damage taken when they collide.

Hope that makes sense

You’re trying to design a class that does WAY TOO MUCH. Not just a class… but a function that does way too much.

Why does this function both set the default values as well as return some field? Why?

Why couldn’t you have a ‘SetCombatStats’, and then after you call it, you retrieve the value for the field you desire?

Why is this function even called ‘combatStats’, what is it supposed to do?

Wait… do you only have one instance of this ‘combatStats’ class that you call to get the stats for any type of unit?

You only need one script here with public variables that you can set in the inspector. Then it doesn’t matter what it bumps into , it will read the values and act upon them.

Ah, even if the unit is created by script? As long as it creates a prefab with those set stats?

I just meant, make a simple script like this:

using UnityEngine;
using System.Collections;

public class UnitAttributes : MonoBehaviour
{
    public int health = 100;
    public int armour = 0;
    public int damage = 1;
    public float attackSpeed = 0.03f;
    public float moveSpeed = 0.02f;
}

Then add the script to your prefab so you can edit in the inspector like this:

Then access the data from the appropriate game object with:
UnitAttributes attributes = tank.GetComponent();
// attributes.health, attributes.armour, etc

Drag a prefab onto the scene, add the script to it. Edit the values in the inspector then drag the object off the heirarchy and back onto the prefab and now it will always spawn with those values, which can be edited at runtime.

Hah… guess I was over complicating it just a tad. Thanks all. :wink:
Guess then I can set its level when creating the game object? As in there can be harder units without duplicating prefabs for each unit?

Then damage = attributes.damage + (attributes.level * 0.623) or something?

yes, well create the obj then set its stuff:

GameObject go = Instantiate(MyPrefab, overHere, thisRotation) as GameObject;
Stats stats = go.GetComponent<Stats>();
stats.health = "whatever"
etc.
etc.