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)
Related
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.
I really need some help with desereliazing a JSON.
Here is my JSON : https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD
Here is the code I have so far :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Newtonsoft.Json;
public class StockManager : MonoBehaviour {
private string webString;
private CurrencyContainer container;
[SerializeField]private int currenciesToLoad;
void Start()
{
StartCoroutine(GetText());
}
void Update()
{
if (container != null)
{
Debug.Log (container.Arr);
}
else
{
Debug.Log ("null");
}
}
IEnumerator GetText()
{
using (WWW www = new WWW("https://min-api.cryptocompare.com/data/histoday?fsym=BTC&tsym=USD"))
{
yield return www;
if (www.error != null)
{
Debug.Log("Error is : " + www.error);
}
else
{
webString = "{ \"Arr\":" + www.text + "}";
container = JsonConvert.DeserializeObject<CurrencyContainer> (webString);
}
}
}
[System.Serializable]
public class Datum
{
public int time;
public double close;
public double high;
public double low;
public double open;
public double volumefrom;
public double volumeto;
}
[System.Serializable]
public class ConversionType
{
public string type;
public string conversionSymbol;
}
[System.Serializable]
public class Example
{
public string Response;
public int Type;
public bool Aggregated;
public IList<Datum> Data;
public int TimeTo;
public int TimeFrom;
public bool FirstValueInArray;
public ConversionType ConversionType;
}
[System.Serializable]
public class CurrencyContainer
{
public Example[] Arr;
}
}
The error I get is : JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'StockManager+Example[]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
I have no idea how to fix and any help is really appreciated.
Thanks a lot.
You have "one level to much" in your object structure, as the given JSON is only on "item" of your type Example. Try the following:
var item = JsonConvert.DeserializeObject<Example>(www.text);
See it HERE in action.
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);
}
}
}
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.
I've been working on these classes for some time now:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
namespace KinectFysioterapi
{
public class BoneVector
{
public Vector3D vector;
private double x;
private double y;
private double z;
#region Constructor
public BoneVector(Joint startJoint, Joint endJoint)
{
if (!(startJoint.Equals(endJoint)))
{
x = endJoint.Position.X - startJoint.Position.X;
y = endJoint.Position.Y - startJoint.Position.Y;
z = endJoint.Position.Z - startJoint.Position.Z;
vector = new Vector3D(x, y, z);
}
}
#endregion
}
public class SkeletonVectorCollection : Skeleton
{
public SkeletonVectorCollection(Skeleton input)
{
foreach (BoneOrientation orientation in input.BoneOrientations)
{
this[orientation.EndJoint] = new BoneVector(input.Joints[orientation.StartJoint], input.Joints[orientation.EndJoint]);
}
}
//Not sure how to do this correctly
public BoneVector this[JointType jointType]
{
get
{
return this[jointType];
}
protected set
{
this[jointType] = value;
}
}
}
}
I having huge problems getting the last part running without problems.
What i'm looking for is to input a kinect skeleton and get out a new skeleton with additional information about some defined vectors between the joints.
My goal is to be able to do the following:
SkeletonVectorCollection collection = new SkeletonVectorCollection(skeleton);
skeleton[Jointtype.Head].vector.x.ToString();
Im very unsure how to use this[JointType jointType] correctly.
As defined your SkeletonVectorCollection is recursive as your this[JointType] implementation has no backing property and is trying to store to itself. If you don't need/want to inherit from Skeleton you can switch to a Dictionary and get the implementation for free.
public class SkeletonVectorCollection : Dictionary<JointType, BoneVector>
{
public SkeletonVectorCollection(Skeleton input)
{
foreach (BoneOrientation orientation in input.BoneOrientations)
{
this[orientation.EndJoint] = new BoneVector(input.Joints[orientation.StartJoint], input.Joints[orientation.EndJoint]);
}
}
}
If you must inherit from Skeleton then you need to provide an implementation with some sort of storage behind it here is a version that uses a dictionary internally.
public class SkeletonVectorCollection : Skeleton
{
private Dictionary<JointType, BoneVector> _boneVectors = new Dictionary<JointType, BoneVector>();
public SkeletonVectorCollection(Skeleton input)
{
foreach (BoneOrientation orientation in input.BoneOrientations)
{
this[orientation.EndJoint] = new BoneVector(input.Joints[orientation.StartJoint], input.Joints[orientation.EndJoint]);
}
}
public BoneVector this[JointType jointType]
{
get
{
return _boneVectors[jointType];
}
protected set
{
_boneVectors[jointType] = value;
}
}
}