Chose GameObject from SettingCollection - unity3d

I'm using Zenject framework and I want instansiate gameObject for class created by factory. For this I'm using something like this in GameInstaller
Container.BindFactory<int,int,Hex, Hex.Factory().
FromComponentInNewPrefab(_settings.PlainHexPrefab[0]).
WithGameObjectName("Hex").
UnderTransformGroup("Zenject");`
It's work fine, but in my case in _settings.PlainHexPrefab I have collection GameObjects and I need choose one of them dependency by properties from Hex object. How could I do it?

Here's three ways that might work for you:
FromMethod
public class Hex : MonoBehaviour
{
public class Factory : PlaceholderFactory<int, int, Hex>
{
}
}
public class TestInstaller : MonoInstaller<TestInstaller>
{
public GameObject[] HexPrefabs;
public override void InstallBindings()
{
Container.BindFactory<int, int, Hex, Hex.Factory>().FromMethod(CreateHex);
}
Hex CreateHex(DiContainer _, int p1, int p2)
{
var prefab = HexPrefabs[Random.RandomRange(0, HexPrefabs.Count() -1)];
return Container.InstantiatePrefabForComponent<Hex>(prefab);
}
}
Custom Factory
public class HexFactory : IFactory<int, int, Hex>
{
readonly DiContainer _container;
readonly GameObject[] _prefabs;
public HexFactory(
GameObject[] prefabs,
DiContainer container)
{
_container = container;
_prefabs = prefabs;
}
public Hex Create(int p1, int p2)
{
var prefab = _prefabs[Random.RandomRange(0, _prefabs.Count() -1)];
return _container.InstantiatePrefabForComponent<Hex>(prefab);
}
}
public class TestInstaller : MonoInstaller<TestInstaller>
{
public GameObject[] HexPrefabs;
public override void InstallBindings()
{
Container.BindFactory<int, int, Hex, Hex.Factory>()
.FromIFactory(b => b.To<HexFactory>().AsSingle().WithArguments(HexPrefabs));
}
}
Sub factories in custom factory. This also has the advantage that each prefab factory should be 'validated'
public class CustomHexFactory : IFactory<int, Hex>
{
readonly List<Hex.Factory> _subFactories;
public CustomHexFactory(List<Hex.Factory> subFactories)
{
_subFactories = subFactories;
}
public Hex Create(int p1)
{
return _subFactories[Random.RandomRange(0, _subFactories.Count() -1)].Create(p1);
}
}
public class TestInstaller : MonoInstaller<TestInstaller>
{
public GameObject[] HexPrefabs;
public override void InstallBindings()
{
foreach (var prefab in HexPrefabs)
{
Container.BindFactory<int, Hex, Hex.Factory>().FromComponentInNewPrefab(prefab)
.WhenInjectedInto<CustomHexFactory>();
}
Container.BindFactory<int, Hex, Hex.Factory>().FromFactory<CustomHexFactory>();
}
}

Related

Make a gameObject follow another gameObject's behavior?

I am controlling a text GameObject. I am performing operations such as SetActive() and changing the text of the gameObject. Now I am duplicating the same object and I want the duplicated object to follow the behaviors of its main gameObject. That is ObjA is parent and ObjB is a clone. If I change the text UI through code of objA, I want objB to automatically change its component. How do I achieve this behavior?
There are multiple ways to do that, but it will always be by code, there is no "authomatism" to do that.
So by code you can make a relation child to parent or parent to child.
One way could be that one:
public class Parent : MonoBehaviour
{
public Child child = null;
public void DoSomething()
{
this.gameObject.SetActive(true);
child.DoSomething();
}
}
public class Child : MonoBehaviour
{
public void DoSomething()
{
this.gameObject.SetActive(true);
}
}
Another fancy way to do that is using delegates, or Actions:
public class Parent : MonoBehaviour
{
public Action OnDoSomething = null;
public Action OnDoSomethingElse = null;
public void DoSomething()
{
this.gameObject.SetActive(false);
OnDoSomething();
}
public void DoSomethingElse()
{
this.gameObject.SetActive(true);
OnDoSomethingElse();
}
}
public class Child : MonoBehaviour
{
public Parent parent = null;
public void Awake()
{
parent.OnDoSomething += ChildDoSometing;
parent.OnDoSomethingElse += ChildDoSometingElse;
}
public void OnDestroy()
{
parent.OnDoSomething -= ChildDoSometing;
parent.OnDoSomethingElse -= ChildDoSometingElse;
}
public void ChildDoSometing()
{
this.gameObject.SetActive(false);
}
public void ChildDoSometingElse()
{
this.gameObject.SetActive(false);
}
}
You can be tempted to pass your own method as Action parameter like Action<Action> but remember that child will call parent method, won't operate on his own. So in this case if you do something like:
public class Parent : MonoBehaviour
{
public Action<Action> OnDoSomething = null;
[ContextMenu("A")]
public void DoSomething()
{
this.gameObject.SetActive(true);
OnDoSomething(this.DoSomething);
}
public void DoSomethingElse()
{
print("Hello");
}
}
public class Child : MonoBehaviour
{
public Parent parent = null;
public void Awake()
{
parent.OnDoSomething += RepeatedAction;
}
public void OnDestroy()
{
parent.OnDoSomething -= RepeatedAction;
}
public void RepeatedAction(Action actionToRepeat)
{
actionToRepeat?.Invoke();
}
}
Will result on StackOverflow exception, cause child will call parent, who calls child, who calls again parent...you can see the problem.
Anyway would be nice to declare an abstract base class that have all the methods, and let both classes inherit from that class, and implement those methods.

How to spawn units using DOTS?

I am following this talk https://www.youtube.com/watch?v=BNMrevfB6Q0 and try to understand how to spawn units as I click my mouse (for testing purposes).
For this in general, I created a UnitBase etc. which implements IConvertGameObjectToEntity::Convert to make sure all units can be converted to a Entity objects and looks like this:
namespace Units
{
public abstract class UnitBase : MonoBehaviour, IConvertGameObjectToEntity
{
public float health;
public bool selected;
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
dstManager.AddComponentData(entity, new Translation {Value = transform.position});
dstManager.AddComponentData(entity, new Rotation {Value = transform.rotation});
dstManager.AddComponentData(entity, new Selected {Value = selected});
dstManager.AddComponentData(entity, new Health {Value = health});
}
}
public abstract class MobileUnitBase : UnitBase
{
public float speed;
public new void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
base.Convert(entity, dstManager, conversionSystem);
dstManager.AddComponentData(entity, new Speed {Value = speed});
}
}
}
These are the main components:
namespace ECS.Components
{
public class Health : IComponentData
{
public float Value;
}
public class Speed : IComponentData
{
public float Value;
}
public class Selected : IComponentData
{
public bool Value;
}
}
Now, in my Test script I have this:
public class Test : MonoBehaviour
{
public GameObject unitPrefab;
private EntityManager _entityManager;
private Entity _unitEntityPrefab;
// Start is called before the first frame update
void Start()
{
_entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
_unitEntityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(unitPrefab, World.Active);
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
SpawnUnit(15, 15);
}
}
private void SpawnUnit(float x, float z)
{
Debug.Log($"Spawning unit at x:{x} y:{z}");
var unitEntity = this._entityManager.Instantiate(this._unitEntityPrefab);
_entityManager.SetComponentData(unitEntity, new Translation {Value = new Vector3(x, 1, z)});
_entityManager.SetComponentData(unitEntity, new Rotation {Value = Quaternion.Euler(0, 0, 0)});
_entityManager.SetComponentData(unitEntity, new Health {Value = 100f});
_entityManager.SetComponentData(unitEntity, new Selected {Value = false});
}
}
Where unitPrefab is just a simple GameObject that has a Capsule on it and a, basically empty, script which is empty for now:
public class UnitScript : UnitBase
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
The thing is, as I click, the entities get created but the unit is not rendered.
Obviously I am missing something but I don't see what it is.
Any help would be much appreciated!

Unity How to Serialize Map Data

use 2019.1.8f1 ver
I've been referring to a lot of information, but an unknown error bothers me.
Error
SerializationException: Type 'UnityEngine.GameObject' in Assembly 'UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
[System.Serializable]
public class Map
{
public Node[,] nodes;
}
[System.Serializable]
public class Node
{
public GameObject tile;
public bool walkable;
public Vector3 worldPosition;
public int gridX;
public int gridY;
public int gCost;
public int hCost;
public int FCost { get { return gCost + hCost; } }
public Node(bool _walkable, Vector3 _worldPosition, int _gridX, int _gridY)
{
walkable = _walkable;
worldPosition = _worldPosition;
gridX = _gridX;
gridY = _gridY;
}
}
[System.Serializable]
public class Grid : MonoBehaviour
{
public Node[,] grid;
public void SaveMapData()
{
Debug.Log("Save");
FileStream stream = File.Create(Application.persistentDataPath + path);
BinaryFormatter bf = new BinaryFormatter();
Map map = new Map();
map.nodes = grid.grid;
bf.Serialize(stream, map);
stream.Close();
}
Your Node class references the GameObject class here:
public GameObject tile;
The best solution would be to get rid of that reference. This would also improve the decoupling of your model from the view.
Or try the [NonSerialized] attribute as a quick fix:
[NonSerialized] public GameObject tile;

Unity Json.Net: Error setting value

I'm trying to create a convenient serialized reference to asset objects. It serialized correctly, but deserialization fails with exception
"Error setting value to 'textAssetRef' on 'TestData'."
I've prepared a small test component for Unity to illustrate this:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
public class Test : MonoBehaviour
{
public TestData testData;
public void Awake()
{
try
{
var sData = JsonConvert.SerializeObject(testData, Formatting.Indented);
UnityEngine.Debug.Log(sData);
testData = JsonConvert.DeserializeObject<TestData>(sData);
UnityEngine.Debug.Log("Done.");
}
catch (Exception x)
{
UnityEngine.Debug.LogError(x.Message);
}
}
}
[Serializable]
public class TestData
{
public TextAssetRef textAssetRef;
}
[Serializable]
[JsonConverter(typeof(Serialization))]
public class TextAssetRef : ObjectRef<TextAsset>
{
public TextAssetRef() { }
public TextAssetRef(TextAssetRef other) : base(other) { }
public TextAssetRef(TextAsset ta) : base(ta) { }
}
[Serializable]
public class ObjectRef<T> where T : UnityEngine.Object
{
public T obj;
public ObjectRef() { }
public ObjectRef(ObjectRef<T> other) { obj = other.obj; }
public ObjectRef(T obj) { this.obj = obj; }
public class Serialization : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var objRef = (ObjectRef<T>)value;
var jObject = new JObject { { "path", AssetDatabase.GetAssetPath(objRef.obj) } };
serializer.Serialize(writer, jObject);
}
public override bool CanConvert(Type objectType) { return objectType == typeof(ObjectRef<T>); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var assetPath = jObject.GetValue("path").Value<string>();
return new ObjectRef<T> { obj = AssetDatabase.LoadAssetAtPath<T>(assetPath) };
}
}
}
To reproduce the error just create a new project with JSON.Net asset package in Unity, create empty object in scene, put this Test component on it and press Play.
What am I to do to get it deserialized correctly?

Using Ninject custom instance providers to bind successfully using factory method argument to resolve

I've been studying this accepted answer to a similar question in which what I believe is a concrete factory returns an implementation based on a string argument on the factory method matching a named binding on the concrete implementation.
I'm struggling to get a slightly more complex example to work properly when the factory is an abstract factory, and I wish to use Ninject convention-based binding. Consider the following test:
[Fact]
public void VehicleBuilderFactory_Creates_Correct_Builder_For_Specified_Client()
{
// arrange
StandardKernel kernel = new StandardKernel();
kernel.Bind(typeof (IVehicleBuilderFactory<,>))
.ToFactory(() => new UseFirstArgumentAsNameInstanceProvider())
.InSingletonScope();
kernel.Bind(scanner => scanner
.FromThisAssembly()
.SelectAllClasses()
.WhichAreNotGeneric()
.InheritedFrom(typeof(IVehicleBuilder<>))
.BindAllInterfaces());
var bicycleBuilderFactory =
kernel.Get<IVehicleBuilderFactory<IVehicleBuilder<BlueBicycle>, BlueBicycle>>();
string country = "Germany";
string localizedColor = "blau";
// act
var builder = bicycleBuilderFactory.Create<IVehicleBuilder<BlueBicycle>>(country);
Bicycle Bicycle = builder.Build(localizedColor);
// assert
Assert.IsType<BlueBicycleBuilder_Germany>(builder);
Assert.IsType<BlueBicycle>(Bicycle);
Assert.Equal(localizedColor, Bicycle.Color);
}
Here's where I try juggling with torches & knives 'cause I saw it on the internet once:
public class UseFirstArgumentAsNameInstanceProvider : StandardInstanceProvider
{
protected override string GetName(MethodInfo methodInfo, object[] arguments) {
return methodInfo.GetGenericArguments()[0].Name + "Builder_" + (string)arguments[0];
// ex: Germany -> 'BlueBicycle' + 'Builder_' + 'Germany' = 'BlueBicyleBuilder_Germany'
}
protected override ConstructorArgument[] GetConstructorArguments(MethodInfo methodInfo, object[] arguments) {
return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
}
}
I get stabbed and set ablaze when I try to assign bicycleBuilderFactory with this error:
System.InvalidCastException was unhandled by user code
Message=Unable to cast object of type 'Castle.Proxies.ObjectProxy' to type 'Ninject.Extensions.Conventions.Tests.IVehicleBuilderFactory`2[Ninject.Extensions.Conventions.Tests.IVehicleBuilder`1[Ninject.Extensions.Conventions.Tests.BlueBicycle],Ninject.Extensions.Conventions.Tests.BlueBicycle]'.
Source=System.Core
StackTrace:
at System.Linq.Enumerable.<CastIterator>d__b1`1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at Ninject.ResolutionExtensions.Get[T](IResolutionRoot root, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:line 37
at Ninject.Extensions.Conventions.Tests.NinjectFactoryConventionsTests.VehicleBuilderFactory_Creates_Correct_Builder_For_Specified_Client() in C:\Programming\Ninject.Extensions.Conventions.Tests\NinjectFactoryConventionsTests.cs:line 40
InnerException:
Is it possible to bind using the ToFactory() method and custom provider, using the factory method argument ("Germany") along with the generic type argument (IVehicleBiulder<BlueBicycle>, BlueBicycle) to resolve the type?
Here's the rest of the code for the test, as compact and readable as I could make it.
public interface IVehicleBuilderFactory<T, TVehicle>
where T : IVehicleBuilder<TVehicle> where TVehicle : IVehicle
{
T Create<T>(string country);
}
VehicleBuilder implementations
public interface IVehicleBuilder<T> where T : IVehicle { T Build(string localizedColor); }
abstract class BicycleBuilder<T> : IVehicleBuilder<T> where T : Bicycle
{
public abstract T Build(string localizedColor);
}
public abstract class RedBicycleBuilder : IVehicleBuilder<RedBicycle>
{
private readonly RedBicycle _Bicycle;
public RedBicycleBuilder(RedBicycle Bicycle) { _Bicycle = Bicycle; }
public RedBicycle Build(string localizedColor)
{
_Bicycle.Color = localizedColor;
return _Bicycle;
}
}
public abstract class GreenBicycleBuilder : IVehicleBuilder<GreenBicycle>
{
private readonly GreenBicycle _Bicycle;
public GreenBicycleBuilder(GreenBicycle Bicycle) { _Bicycle = Bicycle; }
public GreenBicycle Build(string localizedColor)
{
_Bicycle.Color = localizedColor;
return _Bicycle;
}
}
public abstract class BlueBicycleBuilder : IVehicleBuilder<BlueBicycle>
{
private readonly BlueBicycle _Bicycle;
public BlueBicycleBuilder(BlueBicycle Bicycle) { _Bicycle = Bicycle; }
public BlueBicycle Build(string localizedColor)
{
_Bicycle.Color = localizedColor;
return _Bicycle;
}
}
public class RedBicycleBuilder_USA : RedBicycleBuilder {
public RedBicycleBuilder_USA(RedBicycle Bicycle) : base(Bicycle) { }
}
public class RedBicycleBuilder_Germany : RedBicycleBuilder {
public RedBicycleBuilder_Germany(RedBicycle Bicycle) : base(Bicycle) { }
}
public class RedBicycleBuilder_France : RedBicycleBuilder {
public RedBicycleBuilder_France(RedBicycle Bicycle) : base(Bicycle) { }
}
public class RedBicycleBuilder_Default : RedBicycleBuilder {
public RedBicycleBuilder_Default(RedBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_USA : GreenBicycleBuilder {
public GreenBicycleBuilder_USA(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_Germany : GreenBicycleBuilder {
public GreenBicycleBuilder_Germany(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_France : GreenBicycleBuilder {
public GreenBicycleBuilder_France(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class GreenBicycleBuilder_Default : GreenBicycleBuilder {
public GreenBicycleBuilder_Default(GreenBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_USA : BlueBicycleBuilder
{
public BlueBicycleBuilder_USA(BlueBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_Germany : BlueBicycleBuilder {
public BlueBicycleBuilder_Germany(BlueBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_France : BlueBicycleBuilder
{
public BlueBicycleBuilder_France(BlueBicycle Bicycle) : base(Bicycle) { }
}
public class BlueBicycleBuilder_Default : BlueBicycleBuilder
{
public BlueBicycleBuilder_Default(BlueBicycle Bicycle) : base(Bicycle) { }
}
Vehicle implementations:
public interface IVehicle { string Color { get; set; } }
public abstract class Vehicle : IVehicle { public string Color { get; set; } }
public abstract class Bicycle : Vehicle { }
public class RedBicycle : Bicycle { }
public class GreenBicycle : Bicycle { }
public class BlueBicycle : Bicycle { }
Based on comments from #LukeN, I've refactored the Bicycle class, so that its color is set through constructor injection with an IColorSetter. The IColorSetter implementation has a generic Color type, and each of the Color implementations are 'localized' by way of constructor injection with an IColorLocalizer<T>.
This way, no class seems to have knowledge of anything beyond what is logically its responsibility (I think).
However, I'll need to think about this more to see how the refactored classes shown below can be used to show how to use a Ninject custom instance provider could be used to pick the property IColorLocalizer<T> now, since it's the only class that will know about colors and languages; the color coming from its generic type, and the language coming from the name of the implementation itself.
Since asking the original post, I've moved away from using an IoC container to make choices like this, choosing instead to programmatically put in code a switch for picking an implementation, with a default implementation selected for any unhandled outlier cases. But I'm not sure if it's mainly to get beyond something that's stumped me, or because it's a poor choice to lean on an IoC container in this way.
I'll need to update this answer more as I think about it.
Vehicles
public abstract class Vehicle {
public abstract string Color { get; internal set; }
public abstract string Move();
}
public class Bicycle : Vehicle {
public Bicycle(IColorSetter colorSetter) { colorSetter.SetColor(this); }
public override string Color { get; internal set; }
public override string Move() { return "Pedaling!"; }
}
Color setters
public interface IColorSetter { void SetColor(Vehicle vehicle); }
public class ColorSetter<T> : IColorSetter where T : Color
{
private readonly T _color;
public ColorSetter(T color) { _color = color; }
public void SetColor(Vehicle vehicle) { vehicle.Color = _color.Name; }
}
Color localizers
public interface IColorLocalizer<in T> where T : Color {
void LocalizeColor(T color);
}
public class GermanBlueLocalizer : IColorLocalizer<Blue> {
public void LocalizeColor(Blue color) { color.Name = "blau"; }
}
public class EnglishBlueLocalizer : IColorLocalizer<Blue> {
public void LocalizeColor(Blue color) { color.Name = "blue"; }
}
Colors
public abstract class Color { public string Name { get; internal set; } }
public class Red : Color {
public Red(IColorLocalizer<Red> colorLocalizer) {
colorLocalizer.LocalizeColor(this); }
}
public class Green : Color {
public Green(IColorLocalizer<Green> colorLocalizer) {
colorLocalizer.LocalizeColor(this); }
}
public class Blue : Color {
public Blue(IColorLocalizer<Blue> colorLocalizer) {
colorLocalizer.LocalizeColor(this); }
}