How to automate unit testing of object state? - c#

I have this serializable class which is my class for persisting game data.
[Serializable]
class GameData
{
public float experience = Helper.DEFAULT_EXPERIENCE;
public float score = Helper.DEFAULT_SCORE;
public float winPercent = Helper.DEFAULT_WIN_PERCENT;
public int tasksSolved = Helper.DEFAULT_NUM_OF_TASKS_SOLVED;
public int correct = Helper.DEFAULT_NUM_OF_CORRECT;
public int additions = Helper.DEFAULT_NUM_OF_ADDITIONS;
public int subtractions = Helper.DEFAULT_NUM_OF_SUBTRACTIONS;
public bool useAddition = Helper.DEFAULT_USE_ADDITION;
public bool useSubtraction = Helper.DEFAULT_USE_SUBTRACTION;
public bool useIncrementalRange = Helper.DEFAULT_USE_INCREMENTAL_RANGE;
public bool gameStateDirty = Helper.DEFAULT_GAME_STATE_DIRTY;
public bool gameIsNormal = Helper.DEFAULT_GAME_IS_NORMAL;
public bool operandsSign = Helper.DEFAULT_OPERANDS_SIGN;
}
The class that utilizes this serializable class looks like this:
using UnityEngine;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class SaveLoadGameData : MonoBehaviour
{
public static SaveLoadGameData gameState;
public float experience = Helper.DEFAULT_EXPERIENCE;
public float score = Helper.DEFAULT_SCORE;
public float winPercent = Helper.DEFAULT_WIN_PERCENT;
public int tasksSolved = Helper.DEFAULT_NUM_OF_TASKS_SOLVED;
public int correct = Helper.DEFAULT_NUM_OF_CORRECT;
public int additions = Helper.DEFAULT_NUM_OF_ADDITIONS;
public int subtractions = Helper.DEFAULT_NUM_OF_SUBTRACTIONS;
public bool useAddition = Helper.DEFAULT_USE_ADDITION;
public bool useSubtraction = Helper.DEFAULT_USE_SUBTRACTION;
public bool useIncrementalRange = Helper.DEFAULT_USE_INCREMENTAL_RANGE;
public bool gameStateDirty = Helper.DEFAULT_GAME_STATE_DIRTY;
public bool gameIsNormal = Helper.DEFAULT_GAME_IS_NORMAL;
public bool operandsSign = Helper.DEFAULT_OPERANDS_SIGN;
void Awake () {}
public void init ()
{
if (gameState == null)
{
DontDestroyOnLoad(gameObject);
gameState = this;
}
else if (gameState != this)
{
Destroy(gameObject);
}
}
public void SaveForWeb ()
{
UpdateGameState();
try
{
PlayerPrefs.SetFloat(Helper.EXP_KEY, experience);
PlayerPrefs.SetFloat(Helper.SCORE_KEY, score);
PlayerPrefs.SetFloat(Helper.WIN_PERCENT_KEY, winPercent);
PlayerPrefs.SetInt(Helper.TASKS_SOLVED_KEY, tasksSolved);
PlayerPrefs.SetInt(Helper.CORRECT_ANSWERS_KEY, correct);
PlayerPrefs.SetInt(Helper.ADDITIONS_KEY, additions);
PlayerPrefs.SetInt(Helper.SUBTRACTIONS_KEY, subtractions);
PlayerPrefs.SetInt(Helper.USE_ADDITION, Helper.BoolToInt(useAddition));
PlayerPrefs.SetInt(Helper.USE_SUBTRACTION, Helper.BoolToInt(useSubtraction));
PlayerPrefs.SetInt(Helper.USE_INCREMENTAL_RANGE, Helper.BoolToInt(useIncrementalRange));
PlayerPrefs.SetInt(Helper.GAME_STATE_DIRTY, Helper.BoolToInt(gameStateDirty));
PlayerPrefs.SetInt(Helper.OPERANDS_SIGN, Helper.BoolToInt(operandsSign));
PlayerPrefs.Save();
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
public void SaveForX86 () {}
public void Load () {}
public void UpdateGameState () {}
public void ResetGameState () {}
}
Note: GameData is inside the same file with SaveLoadGameData class.
As you can see GameData class has ton of stuff and creating test for each function inside SaveLoadGameData class is long and boring process. I have to create a fake object for each property inside GameData and test the functionality of the functions in SaveLoadGameData do they do what they are supposed to do.
Note: This is Unity3D 5 code and testing MonoBehaviors with stubs and mocks is almost immposible. Therefore I created helper function that creates fake object:
SaveLoadGameData saveLoadObject;
GameObject gameStateObject;
SaveLoadGameData CreateFakeSaveLoadObject ()
{
gameStateObject = new GameObject();
saveLoadObject = gameStateObject.AddComponent<SaveLoadGameData>();
saveLoadObject.init();
saveLoadObject.experience = Arg.Is<float>(x => x > 0);
saveLoadObject.score = Arg.Is<float>(x => x > 0);
saveLoadObject.winPercent = 75;
saveLoadObject.tasksSolved = 40;
saveLoadObject.correct = 30;
saveLoadObject.additions = 10;
saveLoadObject.subtractions = 10;
saveLoadObject.useAddition = false;
saveLoadObject.useSubtraction = false;
saveLoadObject.useIncrementalRange = true;
saveLoadObject.gameStateDirty = true;
saveLoadObject.gameIsNormal = false;
saveLoadObject.operandsSign = true;
return saveLoadObject;
}
How would you automate this process?
Yes two asserts inside one test is a bad practice but what would you do instead?
Example test for SaveForWeb()
[Test]
public void SaveForWebTest_CreateFakeGameStateObjectRunTheFunctionAndCheckIfLongestChainKeyExists_PassesIfLongestChainKeyExistsInPlayerPrefs()
{
// arrange
saveLoadObject = CreateFakeSaveLoadObject();
// act
saveLoadObject.SaveForWeb();
// assert
Assert.True(PlayerPrefs.HasKey(Helper.LONGEST_CHAIN_KEY));
Assert.AreEqual(saveLoadObject.longestChain, PlayerPrefs.GetInt(Helper.LONGEST_CHAIN_KEY, Helper.DEFAULT_LONGEST_CHAIN));
GameObject.DestroyImmediate(gameStateObject);
}

Since Helper is static class containing only public constants I had to use BindingFlags.Static and BindingFlags.Public to iterate over its members, so I used this code snippet to automate asserting over several fields of different type:
FieldInfo[] helperFields = typeof(SaveLoadGameData).GetFields();
FieldInfo[] defaults = typeof(Helper).GetFields(BindingFlags.Static | BindingFlags.Public);
for(int i = 0; i < defaults.Length; i += 1)
{
Debug.Log(helperFields[i].Name + ", " + helperFields[i].GetValue(saveLoadObject) + ", " + defaults[i].GetValue(null));
Assert.AreEqual(helperFields[i].GetValue(saveLoadObject), defaults[i].GetValue(null));
}
Note: defaults and helperFields have the same length as I am checking if helperFields have the default values after using ResetGameState().
Though this answer is about ResetGameState() instead of SaveForWeb() function, this code can be applied wherever possible.

Related

Script in Unity responding incorrect values

I have been trying to use weatherbit.io API to access AQI information in my android application. The script AqiInfoScript is used to access the API and the Update AQI script is used to print the value out.
AqiInfoScript:
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using SimpleJSON;
public class AqiInfoScript : MonoBehaviour
{
private float timer;
public float minutesBetweenUpdate;
private float latitude;
private float longitude;
private bool locationInitialized;
public static string cityName;
public static double currentAqi;
private readonly string baseWeatherbitURL = "https://api.weatherbit.io/v2.0/current/airquality?";
private readonly string key = "*********************";
public void Begin()
{
latitude = GPS.latitude;
longitude = GPS.longitude;
locationInitialized = true;
}
void Update()
{
if (locationInitialized)
{
if (timer <= 0)
{
StartCoroutine(GetAqi());
timer = minutesBetweenUpdate * 60;
}
else
{
timer -= Time.deltaTime;
}
}
}
private IEnumerator GetAqi()
{
string weatherbitURL = baseWeatherbitURL + "lat=" + latitude + "&lon=" + longitude + "&key="
+ key;
UnityWebRequest aqiInfoRequest = UnityWebRequest.Get(weatherbitURL);
yield return aqiInfoRequest.SendWebRequest();
//error
if (aqiInfoRequest.isNetworkError || aqiInfoRequest.isHttpError)
{
Debug.LogError(aqiInfoRequest.error);
yield break;
}
JSONNode aqiInfo = JSON.Parse(aqiInfoRequest.downloadHandler.text);
cityName = aqiInfo["city_name"];
currentAqi = aqiInfo["data"]["aqi"];
}
}
UpdateAQI Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UpdateAQI : MonoBehaviour
{
public Text airquality;
//public Text coordinates;
private void Update()
{
airquality.text = "Current Aqi: " + AqiInfoScript.currentAqi.ToString();
}
}
Current Output : Current AQI: 0
Desired Output : Current AQI: 129.0000
Issue
As I see it the API returns for your request e.g. for lat=42, lon=-7 the following JSON
{
"data":[
{
"mold_level":1,
"aqi":54,
"pm10":7.71189,
"co":298.738,
"o3":115.871,
"predominant_pollen_type":"Molds",
"so2":0.952743,
"pollen_level_tree":1,
"pollen_level_weed":1,
"no2":0.233282,
"pm25":6.7908,
"pollen_level_grass":1
}
],
"city_name":"Pías",
"lon":-7,
"timezone":"Europe\/Madrid",
"lat":42,
"country_code":"ES",
"state_code":"55"
}
As you can see "data" is actually an array. You are treating it as a single value.
Behind the Scenes
Unfortunately the SmipleJson library you are using is quite error prone because it doesn't throw any exceptions for typos but rather silently fails and uses a default value.
You can see this e.g. in
public override JSONNode this[string aKey]
{
get
{
if (m_Dict.ContainsKey(aKey))
return m_Dict[aKey];
else
return new JSONLazyCreator(this, aKey);
}
....
}
and
public static implicit operator double(JSONNode d)
{
return (d == null) ? 0 : d.AsDouble;
}
Solution
It should rather be
cityName = aqiInfo["city_name"];
currentAqi = aqiInfo["data"][0]["aqi"].AsDouble;
Debug.Log($"cityName = {cityName}\ncurrentAqi = {currentAqi}");
In general I would suggest to use JsonUtility.FromJson and rather implement the required data structure in c# e.g.:
[Serializable]
public class Data
{
public int mold_level;
public double aqi;
public double pm10;
public double co;
public double o3;
public string predominant_pollen_type;
public double so2;
public int pollen_level_tree;
public int pollen_level_weed;
public double no2;
public double pm25;
public int pollen_level_grass;
}
[Serializable]
public class Root
{
public List<Data> data;
public string city_name;
public int lon;
public string timezone;
public int lat;
public string country_code;
public string state_code;
}
and then you would use
var aqiInfo = JsonUtility.FromJson<Root>(aqiInfoRequest.downloadHandler.text);
cityName = aqiInfo.city_name;
currentAqi = aqiInfo.data[0].aqi;
Debug.Log($"cityName = {cityName}\ncurrentAqi = {currentAqi}");
For me as said with the dummy values lat=42, lon=-7 both times it prints out as expected
cityName = Pías
currentAqi = 54
UnityEngine.Debug:Log(Object)
<GetAqi>d__18:MoveNext() (at Assets/Example.cs:109)
UnityEngine.SetupCoroutine:InvokeMoveNext(IEnumerator, IntPtr)

Problem with serialization of object in unity

This is the class I want to serialize:
System.Serializable]
public class Save
{
public int turn, player;
public int build, attack, action;
public double resource, resourceProd;
public List<ObjectData> objectsData = new List<ObjectData>();
public Save(GameObject[] saveBuilding, GameObject[] saveUnit, int turn, int player, int build, int attack, int action, double resource, double resourceProd)
{
foreach(GameObject building in saveBuilding)
{
ObjectData data = new ObjectData();
data.position = building.transform.position;
data.building = building.GetComponent<Building>();
data.path = data.building.type[0] + data.building.level + data.building.branch;
objectsData.Add(data);
}
foreach(GameObject unit in saveUnit)
{
ObjectData data = new ObjectData();
data.position = unit.transform.position;
data.unit = unit.GetComponent<Unit>();
data.path = data.unit.type[0] + data.unit.level + data.unit.branch;
objectsData.Add(data);
}
this.turn = turn;
this.player = player;
this.build = build;
this.attack = attack;
this.action = action;
this.resource = resource;
this.resourceProd = resourceProd;
Debug.Log(objectsData.Count);
}
}
public class ObjectData
{
public Vector3 position;
public Building building;
public Unit unit;
public string path;
}
When I creat a instance of it with the method below, there are two element in objectsData according toDebug.Log(objectsData.Count);.
public static void SaveTurn()
{
turnSave = new Save(GameObject.FindGameObjectsWithTag("building"), GameObject.FindGameObjectsWithTag("unit"), turnCourt, Players.player, BuildDisplay.maxBuildAction, Attacking.maxAttackAction, Building.actions, Currency.resource, Building.totalprod);
solution.Add(JsonUtility.ToJson(turnSave));
Debug.Log(JsonUtility.ToJson(turnSave));
}
When I try the load the save Debug.Log(save.objectsData); returns null.
Why does this happens?
public static void LoadSave(string saves)
{
Save save = JsonUtility.FromJson<Save>(saves);
Currency.resource = save.resource;
Building.totalprod = save.resourceProd;
BuildDisplay.maxBuildAction = save.build;
Attacking.maxAttackAction = save.attack;
Building.actions = save.action;
Debug.Log(save.objectsData);
LoadObjects(save.objectsData);
RemoveObjects(GameObject.FindGameObjectsWithTag("building"));
RemoveObjects(GameObject.FindGameObjectsWithTag("unit"));
Turn.refresh();
}
public static void LoadObjects(List<ObjectData> gameObjects)
{
Debug.Log(gameObjects.Count);
foreach(ObjectData gameObject in gameObjects)
{
GameObject Prefab = Resources.Load(gameObject.path) as GameObject;
if(gameObject.building != null)
{
Building building = Prefab.GetComponent<Building>();
building = gameObject.building;
}
if(gameObject.unit != null)
{
Unit unit = Prefab.GetComponent<Unit>();
unit = gameObject.unit;
}
Instantiate(Prefab);
}
}
After making all class involved serializable Debug.Log(save.objectsData); no longer returns null and Debug.Log(gameObjects.Count); returns 2, but there is a null exception at Building building = Prefab.GetComponent<Building>(); even though there is a Building script on the Prefab.
First of all all classes you want to serialize need to have the attribute [Serializable] so also the ObjetData
[Serializable]
public class ObjectData
{
...
}
Then another "issue" here is that Unit and Building are both MonoBehaviour which derives from UnityEngine.Object. All types deriving from UnityEngine.Object are always only (de)serialized as instance references, never actually containing there according field values.
There are two similar ways around this.
Option A
Instead of serializing the Unit and Building directly you could rather serialize some dedicated data class that basically reflects the components field values but in a serializable container.
E.g. let's say your classes look like
public class Unit : MonoBehaviour
{
[SerializeField] private string _name;
[SerializeField] private int _id;
...
}
Then you could create a container like
[Serializable]
public class UnitData
{
public string Name;
public int ID;
}
And then in order to maintain capsulation and keep your fields private I would add according methods to your types themselves like e.g.
public class Unit : MonoBehaviour
{
[SerializeField] private string _name;
[SerializeField] private int _id;
public UnitContainer GetData()
{
return new UnitContainer()
{
Name = _name,
ID = _id;
}
}
public void SetData(UnitData data)
{
_name = data.Name;
_id = data.ID;
// ... Probably react to changed data
}
}
This way you could also store values that are usually not serialized like properties or private fields.
Same for the Building.
Then you could rather store these in your data like
[Serializable]
public class ObjectData
{
public Vector3 position;
public BuildingData building;
public UnitData unit;
public string path;
}
Then in your Save class you would make sure to create these data classes accordingly
foreach(var building in saveBuilding)
{
var data = new ObjectData()
{
position = building.transform.position,
building = building.GetComponent<Building>().GetData(),
path = data.building.type[0] + data.building.level + data.building.branch
};
objectsData.Add(data);
}
foreach(var unit in saveUnit)
{
var data = new ObjectData()
{
position = unit.transform.position,
unit = unit.GetComponent<Unit>().GetData(),
path = data.unit.type[0] + data.unit.level + data.unit.branch
};
objectsData.Add(data);
}
And for loading accordingly
foreach(var data in objectDatas)
{
var prefab = (GameObject) Resources.Load(data.path);
if(data.building != null)
{
var building = Instantiate (prefab).GetComponent<Building>();
building.SetData(data.building);
}
else if(data.unit != null)
{
var unit = Instantiate(prefab).GetComponent<Unit>();
unit.SetData(data.unit);
}
}
Option B
Actually quite similar but without the need for an extra data container class you could rather again (de)serialize your components directly using JSON like e.g.
public class Unit : MonoBehaviour
{
...
public string GetData()
{
return JsonUtility.ToJson(this);
}
public void SetData(string json)
{
JsonUtility.FromJsonOverwrite(json, this);
// ... React to changed data
}
}
This automatically includes all fields that can be serialized (are either public or tagged [SerializeField] and have a serializable type).
And then rather store these json strings like
[Serializable]
public class ObjectData
{
public Vector3 position;
public string building;
public string unit;
public string path;
}
The methods for save and load would be the same as for Option A above.

Iterate a dictionary of interfaces as specific type

I have a dictionary of string/interface class. I cannot include the fields I need in the interface child-classes, but each of them have the same public variables I need to change.
I want to loop through the dictionary and change them to values within the looping class. I cannot do so, as the interface does not contain those variables.
How should I go about this?
public class MakeAbility : MonoBehaviour
{
public BlockScriptableObject block;
public IDictionary<string, IAbility> abilities_parts = new Dictionary<string, IAbility>();
public Targeting target_manager;
public AbAttack attack;
public AbCast cast;
public AbDefend defend;
public AbDefendOther defend_other;
public AbPotion potion;
private void Start()
{
abilities_parts.Add("attack", attack);
abilities_parts.Add("cast", cast);
abilities_parts.Add("defend", defend);
abilities_parts.Add("defend_other", defend_other);
abilities_parts.Add("potion", potion);
}
public void trigger_button()
{
foreach (var i in abilities_parts.Values)
{
i.block_attack_damage = block.attack_damage;
i.targeting_for_attack = target_manager;
}
public interface IAbility
{
void Use();
void Enact();
}
public class AbPotion : MonoBehaviour, IAbility
{
public Targeting targeting_for_attack;
public int block_attack_damage = 10;
public void Use()
{
}
public void Enact()
{
}
}
Your properties are NOT properties of IAbility. They are properties of the AbPotion class. You will need an if else statement on the types to set them individually. The thing is, they should have been set before adding to the Dictionary and probably thru the constructor.
public void trigger_button()
{
foreach (var i in abilities_parts.Values)
{
if(i is AbPotion)
{
var potion = i as AbPotion;
potion.block_attack_damage = block.attack_damage;
potion.targeting_for_attack = target_manager;
}
else if(i is AbAttack)
{
var attack = i as AbAttack;
attack.Property1= value1;
attack.Property2 = value2;
}
}
}

Pass variable to class implemented in a script loaded with Roslyn

I'm trying to implement a script evaluator to evaluate some classes that my users will implement that will have a fixed entry point accepting a variable that will be given from the main application.
To do this I've declared a namespace that contains the type of the object that will be passed to the entry point. This is the whole code that declares the custom type and executes the scripts:
namespace MyNamespace
{
public class Vertex
{
public float X { get; } = 0.0f;
public float Y { get; } = 0.0f;
public float Z { get; } = 0.0f;
public Vertex(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return string.Format("[{0}, {1}, {2}]", X, Y, Z);
}
}
}
public class Globals
{
public Vertex testVertex = new Vertex(0, 0, 0);
public int testValue = 100;
}
class Program
{
static void Main(string[] args)
{
try
{
ScriptOptions options = ScriptOptions.Default;
// Add assemblies
options = options.AddReferences(
typeof(System.Object).GetTypeInfo().Assembly,
typeof(System.Linq.Enumerable).GetTypeInfo().Assembly,
typeof(MyNamespace.Vertex).GetTypeInfo().Assembly
);
// Add usings
options = options.AddImports("System");
options = options.AddImports("System.Linq");
options = options.AddImports("System.Collections.Generic");
options = options.AddImports("MyNamespace");
var script = <added below>;
Globals globals = new Globals();
ScriptState<object> scriptState = CSharpScript.RunAsync(script, options: options, globals: globals).Result;
scriptState = scriptState.ContinueWithAsync(#"Figure fig = new Figure();").Result;
scriptState = scriptState.ContinueWithAsync(#"fig.Draw(testVertex);").Result;
}
catch (CompilationErrorException e)
{
Console.WriteLine(string.Join(Environment.NewLine, e.Diagnostics));
}
}
}
And this is an example of a script that I'm trying to load:
public class Figure
{
public void Draw(Vertex v)
{
System.Console.WriteLine(v.ToString());
}
public void Draw(int i)
{
System.Console.WriteLine(i);
}
}
Now, the problem is that when I call the method Draw of the class Figure inside the script, it throws me an exception saying
error CS1503: Argument 1: cannot convert from 'MyNamespace.Vertex [~/Projects/ScriptTest/ScriptTest/bin/Debug/netcoreapp1.0/ScriptTest.dll]' to 'MyNamespace.Vertex [ScriptTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]'
When I do
scriptState = scriptState.ContinueWithAsync(#"fig.Draw(testValue);").Result; // Using testValue instead of testVertex
everything works fine.
It think the problem is that the script is referring to a different namespace than MyNamespace but I don't know how to solve this.

Unity3D: Return value with a Delegate

I want to be able to get the return values from all the methods in my delegate. This is the code I have written in c#.
using UnityEngine;
using System.Collections;
public static class DelagetsAndEvents {
public delegate int UnitEventHandler(string _unit);
public static event UnitEventHandler unitSpawn;
public static int UnitSpawn(string _unit)
{
if(unitSpawn != null)
{
unitSpawn(_unit);
}
// here I want to return 1 + 2 from Planet/UnitSpawn and SolarSystem/UnitSpawn
// is it possible to run a foreach on every method in the delegate and add their returns?
return (method 1's return value) + (method 2's return value) (Or both seperately, that would be even better)
}
}
public class Planet {
public Planet()
{
DelagetsAndEvents.unitSpawn += UnitSpawn;
}
int UnitSpawn(string _unit)
{
Debug.Log("yo");
return 1;
}
}
public class SolarSystem{
public SolarSystem()
{
DelagetsAndEvents.unitSpawn += UnitSpawn;
}
int UnitSpawn(string _unit)
{
Debug.Log("bla");
return 2;
}
}
As you can see, the delegate has a return type of int. Then the methods I put into my delegate also have the return type of int. One of them return 1 and the other one return 2. Is there a way to get those results to the location where I execute my delegate? That will be here:
using UnityEngine;
using System.Collections;
public class TestDelagets : MonoBehaviour {
void Start () {
SolarSystem s = new SolarSystem();
Planet p = new Planet();
string g = "";
int i = DelagetsAndEvents.UnitSpawn(g);
Debug.Log(i);
}
}
Well, in the "regular" .NET framework, you could use Delegate.GetInvocationList. For example, to combine that with LINQ:
// Note: do all of this after checking that unitSpawn is non-null...
var results = unitSpawn.GetInvocationList()
.Cast<UnitEventHandler>()
.Select(d => d(_unit))
.ToList();
I don't know offhand whether that will work with Unity, but I'd hope it would...
If the LINQ part doesn't work, you could use:
var invocations = unitSpawn.GetInvocationList();
var results = new int[invocations.Length];
for (int i = 0; i < invocations.Length; i++)
{
results[i] = ((UnitEventHandler)invocations[i]).Invoke(_unit);
}
As you mention that you would need to get the added value or the two separate values, I would choose a different approach.
You could use Linq but Unity recommends to avoid it. Most likely due to the process of serialization between C++ and C# and GC.
You could store your methods in an array of actions. Then you can either get the full amount, or one by one with a basic foreach loop.
public class DelegateContainer : IDisposable{
private IList<Func<string, int>> container = null;
public DelegateContainer(){
this.container = new List<Func<string,int>>();
}
public void Dispose(){
this.container.Clear();
this.container = null;
}
public bool AddMethod(Func<string, int> func){
if(func != null && this.container.Contains(func) == false){
this.container.Add(func);
return true;
}
return false;
}
public bool RemoveMethod(Func<string, int>func){
if(func != null && this.container.Contains(func) == true){
this.container.Remove(func);
return true;
}
return false;
}
public int GetFullValue(){
int total = 0;
foreach(var meth in this.container){
if(meth != null) { total += meth(""); }
}
return total;
}
public IEnumerable<int> GetAllValues(){
IList <int> list = new List<int>();
foreach(var meth in this.container){
if(meth != null) { list.Add(meth("");); }
}
return list as IEnumerable<int>;
}
}
Thanks guys! It helped alot. I solved it with the folowing code:
using UnityEngine;
using System.Collections;
public static class DelagetsAndEvents {
public delegate int UnitEventHandler(string _unit);
public static event UnitEventHandler unitSpawn;
public static int[] UnitSpawn(string _unit)
{
if(unitSpawn != null)
{
unitSpawn(_unit);
}
System.Delegate[] funcs = unitSpawn.GetInvocationList();
int[] TIntArray = new int[funcs.Length];
for (int i = 0; i < funcs.Length; ++i)
{
TIntArray[i] = (int) funcs[i].DynamicInvoke(_unit);
}
return TIntArray;
}
}
public class Planet {
public Planet()
{
DelagetsAndEvents.unitSpawn += UnitSpawn;
}
int UnitSpawn(string _unit)
{
Debug.Log("yo");
return 1;
}
}
public class SolarSystem{
public SolarSystem()
{
DelagetsAndEvents.unitSpawn += UnitSpawn;
}
int UnitSpawn(string _unit)
{
Debug.Log("bla");
return 2;
}
}
and:
using UnityEngine;
using System.Collections;
using System.Collections;
public class TestDelagets : MonoBehaviour {
void Start () {
SolarSystem s = new SolarSystem();
Planet p = new Planet();
string g = "";
int[] i = DelagetsAndEvents.UnitSpawn(g);
foreach(int f in i)
{
Debug.Log(f);
}
}
}

Categories