endlesstravel / Love2dCS

C# Wrapper for LÖVE, a 2d game engine
MIT License
166 stars 14 forks source link

Set custom data on Love.Physics.Fixture #78

Closed Shylie closed 4 years ago

Shylie commented 5 years ago

See Fixture:setUserData for more information.

Perhaps the implementation could use generic types like so:

using System.Collections.Generic.Dictionary<System.Type, object> to store the data?

Shylie commented 5 years ago

Possible implementation as follows:

using System;
using System.Collections.Generic;

public class UserData
{
    public void SetData<T>(T data)
    {
        _data[typeof(T)] = data;
    }
    public bool HasData<T>()
    {
        return _data.ContainsKey(typeof(T));
    }
    public T GetData<T>()
    {
        return _data.ContainsKey(typeof(T)) ? (T)_data[typeof(T)] : default;
    }
    private Dictionary<Type, object> _data = new Dictionary<Type, object>();
}

and have Fixture implement UserData in one of the following ways:

public class Fixture : LoveObject
{
    .... // current implementation

    public readonly UserData CustomData = new UserData();
}

or

public class Fixture : LoveObject
{
    .... // current implementation

    public T GetData<T>()
    {
        return _data.GetData<T>();
    }
    public bool HasData<T>()
    {
        return _data.HasData<T>();
    }
    public void SetData<T>(T data)
    {
        _data.SetData(data);
    }
    protected readonly UserData _data = new UserData();
}

Alternate implementation for UserData could replace HasData<T> and GetData<T> with


bool GetData<T>(out T data)
{
    if (_data.ContainsKey(typeof(T)))
    {
        data = _data[typeof(T)];
        return true;
    }
    else
    {
        data = default;
        return false;
    }
}
endlesstravel commented 5 years ago

Thanks for your code! And i want to keep the function name consistent with the original, i want change the SetData to SetUserData.

And i think it's necessary to provide more complete functions, so i want provide the GetUserDataAll RemoveUserData and ClearUserData.

Body:setUserData and Joint:setUserData have same function, so i will also add them at their class.

And there a bug on them, beacuse love.physics.newFixture , Fixture:getBody and Body:getFixtureList is create(get) object from difference place. so it will be a bug. so, in my oponion, if set/getUserData implement on C# layer, it will need a center.

so, finally i will implement by this:

    class UserData
    {
        public void SetData<T>(T data)
        {
            _data[typeof(T)] = data;
        }
        public bool HasData<T>()
        {
            return _data.ContainsKey(typeof(T));
        }
        public bool GetData<T>(out T data)
        {
            if (_data.TryGetValue(typeof(T), out var objData) == false)
            {
                data = default(T);
                return false;
            }

            data = (T)objData;
            return true;
        }
        public bool RemoveData<T>()
        {
            return _data.Remove(typeof(T));
        }
        public IEnumerator<KeyValuePair<Type, object>> GetAll()
        {
            return _data.GetEnumerator();
        }
        public void Clear()
        {
            _data.Clear();
        }
        private Dictionary<Type, object> _data = new Dictionary<Type, object>();
    }

    class UserDataCenter
    {
        public static UserData Get(LoveObject lob)
        {
            if (_data.TryGetValue(lob.p, out var ud) == false)
            {
                ud = new UserData();
                _data[lob.p] = ud;
            }

            return ud;
        }

        private static readonly Dictionary<IntPtr, UserData> _data = new Dictionary<IntPtr, UserData>();
    }

    public class Body/Fixture/Joint : xxxx
    {
        // .... other code .....

        #region UserData
        /// <summary>
        /// Get data by type. Each type can has one data.
        /// </summary>
        /// <typeparam name="T">the key</typeparam>
        /// <param name="data">The data to get.</param>
        /// <returns></returns>
        public bool GetUserData<T>(out T data) => _userData.GetData<T>(out data);

        /// <summary>
        /// Query exist data by type. Each type can has one data.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool HasUserData<T>() => _userData.HasData<T>();

        /// <summary>
        /// Set user data by type, each type can has one data.
        /// </summary>
        /// <typeparam name="T">the key</typeparam>
        /// <param name="data">The data to set.</param>
        public void SetUserData<T>(T data) => _userData.SetData(data);

        /// <summary>
        /// Remove user data by type, each type can has one data.
        /// </summary>
        /// <typeparam name="T">the key</typeparam>
        public bool RemoveUserData<T>() => _userData.RemoveData<T>();

        /// <summary>
        /// Clear all user data.
        /// </summary>
        public void ClearUserData() => _userData.Clear();

        /// <summary>
        /// Get all exists data.
        /// </summary>
        /// <returns></returns>
        public IEnumerator<KeyValuePair<Type, object>> GetUserDataAll() => _userData.GetAll();
        private UserData _userData
        {
            get
            {
                if(_userDataCache == null)
                    _userDataCache = UserDataCenter.Get(this);

                return _userDataCache;
            }
        }
        private UserData _userDataCache;
        #endregion
    }

is this is feasible or not ?

Shylie commented 5 years ago

Looks good! Thanks!

Might want to add

class UserDataCenter
{
    // existing code

    Remove(LoveObject lob)
    {
        if (_data.ContainsKey(lob.p))
        {
            _data.Remove(lob.p);
        }
    }
}

Side note: Naming convention for properties is private UserData UserData, rather than _userData, but that's not really important.

endlesstravel commented 5 years ago

UserData available from 11.0.34

Shylie commented 5 years ago

Thanks!

endlesstravel commented 4 years ago

closed because it finished .