When you create new class and mark it as [System.Serializable] your inspector will create and show its default object for property of new class' type in your MonoBehaviour component.
When creating custom PropertyDrawer though you need to create this default object on your own and put its reference into SerializedProperty.objectReferenceValue (as far as I understand).
But this field is of type UnityEngine.Object and my new class cant be assigned there. How to overcome it? Inheriting your class from UnityEngine.Object doesnt help as SerializedProperty.objectReferenceValue is still null, even after assigning in there the newly created object (which is actually of the same type – UnityEngine.Object).
I hope I understood your question correctly, taken from the Unity documentation:
using UnityEngine;
public enum IngredientUnit { Spoon, Cup, Bowl, Piece }
// Custom serializable class
[Serializable]
public class Ingredient
{
public string name;
public int amount = 1;
public IngredientUnit unit;
}
public class Recipe : MonoBehaviour
{
public Ingredient potionResult;
public Ingredient[] potionIngredients;
}
[CustomPropertyDrawer(typeof(Ingredient))]
public class IngredientDrawerUIE : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
// Create property container element.
var container = new VisualElement();
// Create property fields.
var amountField = new PropertyField(property.FindPropertyRelative("amount"));
var unitField = new PropertyField(property.FindPropertyRelative("unit"));
var nameField = new PropertyField(property.FindPropertyRelative("name"), "Fancy Name");
// Add fields to the container.
container.Add(amountField);
container.Add(unitField);
container.Add(nameField);
return container;
}
}
So when you view a GameObject with the Recipe component on it, Unity's inspector will show something like this:
So you do not need to inherit from anything, simply mark the class you want to create a property drawer as Serializable, and create a property drawer class for it (Make sure to place it in the Editor folder, or create a assembly definition file which targets the editor only if you are working with assembly definition files).
Related
I am not sure if this is possible with C#, but is it possible to store information in an attribute that is related to the instance of a class?
So, I have the following class with the field Initialized, as seen here:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class GameObjectAttribute : Attribute {
internal bool Initialized = false;
}
I then use this to add the attribute to the class:
[GameObject]
public class Player {
}
Now, in this class, is it possible to modify the data in the attribute for each individual instance of the class like in this pseudo code:
internal class Core {
async void Tick() {
while (isRunning) {
foreach (var gameObject in gameObjects) {
// Get attribute information
var refToAttribute = gameObject.... // Somehow get information
if (!refToAttribute.Initialized) {
// Do some stuff
refToAttribute.Initialized = true;
}
}
await Task.Delay(1);
}
}
}
Edit
When coming from a TypeScript world, I can return a new instance of a class when the class is created:
export function GameObject() {
return function (constructor: T) {
return class extends constructor {
initialized = false;
}
}
}
#GameObject
export class Player {
}
so now, in my loop I have access to instance, however Player does not have access.
Now, in this class, is it possible to modify the data in the attribute for each individual instance of the class
No, attributes are attached to the class, not to instances of said class. There is only a single instance of the attribute and that instance is attached to the class Player. You can access the attribute from the player instance, but only by looking at the attributes that are attached to the type. So it is really not anything that you could use to provide additional information for any particular player instance.
What you can do in a statically typed language is to wrap the value. Something like this would achieve what you are trying to do:
public class PlayerGameObject
{
public bool IsInitialized { get; set; }
public Player Player { get; set; }
}
// …
foreach (var gameObject in gameObjects)
{
if (!gameObject.IsInitialized)
{
var player = gameObject.Player;
// do some stuff
gameObject.IsInitialized = true;
}
}
is it possible to store information in an attribute that is related to the instance of a class?
No. Attributes are part of the definition of a class, not instances of it. It's impossible for the same reason that a method can't be public for one instance of a class but private for another instance of the same class.
Information about an instance of a class can only be stored in its fields and properties.
I suggest you use a base class instead.
//Your framework
abstract class GameObject
{
internal bool Initialized { get; set; } = false;
}
//Inside program that uses the framework
class Player : GameObject
{
}
Now the Player class has a property than only your code can access.
//Your framework
void Initialize(GameObject obj)
{
if (!obj.Initialized)
{
//Do something
obj.Initialized = true;
}
}
//Inside program that uses the framework
var player = new Player();
if (player.Initialized) //Compile-time error
I have a class similar to this one:
class Foo : MonoBehaviour {
public Car a;
[MyProperty]
public Trunk b;
}
I've implemented MyPropertyAttribute and a MyPropertyDrawer which inherits PropertyDrawer to create custom inspector for MyProperty decorated attributes.
Now, my issues is that I want to access somehow the actual object instance of the class Foo who owns that b instance.
In other words, I have:
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
this._property = property;
Car c = property.serializedObject.targetObject as Car;
Foo ownerOfCar = ...; // <<< Idk how to get this instance
}
I want to use the Foo reference to change other properties in the class, for example the b variable, which's a Trunk.
I've tried many properties and methods (like objectReferenceValue, tweaking the serializedObject values etc.) but none really worked :(.
Is it possible?
I'm getting confused with constants problems which I'm facing during trial of learning Selenium in C#.
First of all, each class which I'm creating inherits a class which sets a new driver (BaseClassTest):
public class BaseApplicationPage
{
protected IWebDriver Driver { get; set; }
public BaseApplicationPage(IWebDriver driver)
{
Driver = driver;
}
}
Next, one of my "main" class (HomePage) starts from inheriting elements from "BaseApplicationPage" and later creates constructor which (in most cases) has empty body. However in that case, inside the body there is a line which: creates a new "Slider" class.
internal class HomePage : BaseApplicationPage
{
public HomePage(IWebDriver driver) : base(driver)
{
Slider = new Slider(driver);
}
public Slider Slider { get; internal set; }
My questions:
Is it necessary to fill all new class with something like it (constructor + inheriting from BaseClass)?
Why in my case inside the body there is reference to slider class rather than leaving it empty and adding something like this:
public SliderSection Slider => new SliderSection(Driver);
Answer 1: Is it necessary to fill all new class with something like it (constructor + inheriting from BaseClass)? -- If you need driver object in any particular class like one you've defined(HomePage) , you need a constructor to initialise driver object. Then only you can use driver reference anywhere in that particular class.
Answer2 :
You can use both
public SliderSection Slider => new SliderSection(Driver);
and Slider = new Slider(driver); provided here, Slider type must be defined in this class or it's base class.
I have a game project built upon Cocos2D XNA and MonoGame. I wanted to add a little bit of custom logic into CCSprite class, so I created a class which inherits from CCSprite. I added a dummy auto property and tried to use this class, but for some reason sprites being created as instances of my custom sprite class are not shown on the layer, while sprites which are instances of CCSprite class - are completely OK.
Code looks like this:
public class Sprite: CCSprite {
public string SomeProp {get; set;}
}
...
line1: var mySprite1 = new Sprite("texture.png");
line2: var mySprite1 = new CCSprite("texture.png");
AddChild(mySprite1);
If I use line1 and comment out line 2, then mySprite 1 is not shown. Otherwise - if mySprite is an instance of CCSprite - it works well.
What could be the source of this problem?
You are not calling the constructor of the CCsprite with your own Sprite class.
Sprite:CCSprite{
public Sprite():base()
{
//blabla
}
}
the base() is calling the constructor of CCSprite the class you are inheriting
if you want to pass through parameters then do something like this:
Sprite:CCSprite{
public Sprite(string imgpath):base(imgpath)
{
//blabla
}
}
Now I've passed a string through the contructors.
This time I have problem with virtual fields.
I have core class for my game objects. This class contains a field with Model class object. Model's object contains values such as position etc.
Now - while drawing I need to read position of each object from it's model. The problem starts when instead of default model class I'm using derived. Example:
abstract class GenericGameObject { public DefaultGameObjectModel Model = new DefaultGameObjectModel(); }
class Missile : GenericGameObject { public new MissileModel Model = new MissileModel(); }
class DefaultGameObjectModel { public Vector2 Position = new Vector2(){X=0}; }
class MissileModel : DefaultGameObjectModel { }
Missile m = new Missile();
m.Model.Position.X = 10;
// NOT OK! ((GenericGameObject)m).Model.Position.X == 0
I tried to make Model defined as virtual property instead of field, but this fails because
derived properties have to be of same type as their base. Casting is futile because there will be many other model types. What can I do if I want to read a value from derived class, not from base?
I asked this question already but the answer didn't brought any solution. Explaination:
to use interface IGameObjectModel
Concept is good, but I have to enforce fields. Interfaces can't define fields so I have to define property. But then I can't do IGameObjectModel.Position.X=10 because Position is not a field.
to make GenericGameObject a generic type such as GenericGameObject and Missile a type derived from GenericGameObject
I couldn't then cast a missile to GenericGameObject and generally store those object on same list. Of course I could make main base type which those two could inherit from, but then I wouldn't have access to Model field.
to make model a property instead of field.
It is impossible to change property type in derived class.
Whad can I do?
In this case your best approach would be to assign the value of your parent field to be an instance of your derived class, then either cast it back to your derived class or hold on to a reference of your derived class (probably better).
Or you could go down this road, which I like the best...
abstract class GenericGameObject
{
public DefaultGameObjectModel Model
{
get { return ModelInternal; }
}
protected abstract DefaultGameObjectModel ModelInternal { get; }
}
class Missile : GenericGameObject
{
private MissileModel model = new MissileModel();
public override DefaultGameObjectModel ModelInternal
{
get { return model; }
}
public new MissileModel Model
{
get { return model; }
set { model = value; }
}
}
class DefaultGameObjectModel { public Vector2 Position = new Vector2(){X=0}; }
class MissileModel : DefaultGameObjectModel { }
Missile m = new Missile();
m.Model.Position.X = 10;
This solution gives you access to your base model instance from the context of the base class, while giving you access to your concrete model instance from the inherited class.
There's no such thing as 'virtual fields'. Only properties and methods can be virtual.
In your Missle class, you appear to be using the new keyword as a modifier to hide the inherited member named Model.
When you hide an inherited member this way, you don't get polymorphic behavior. This is bad because the code in your base class (if it references the Model field) may not work as you expect.
Best bet: Use a property. Cast or generalize (move members to base class) as necessary.
If you used an interface, I believe you'd still be able to call:
IGameObjectModel.Position.X = 10;
As long as the object type you used for Position has a read/write property called X. Your interface would look something like:
public interface IGameObjectModel
{
Vector2 Position
{
get;
// only add set if you need to set the Position object outside of your class
// set;
}
// ...other properties
}
You said that if you used an interface with a property that you "can't do IGameObjectModel.Position.X=10". I assume this is because Vector2 is a struct and therefore has value-type semantics. If this is correct, you should simply assign the Position property to a new Vector2 calculated from the original value. For example:
Missile m = new Missile();
m.Model.Position = new Vector2()
{
X = m.Model.Position.X + 10,
Y = m.Model.Position.Y
};
Did you try using generics? Using generics you can separate your game object model from your game object. You can then instantiate your game object with any game object model. The game object can communicate with the game object model thru standard interfaces.
interface IGameObjectModel {
void Shoot();
:
}
class GameObject<TModel> where TModel:IGameObjectModel {
public TModel Model;
public GameObject(TModel model) {
Model = model;
}
public void Shoot() {
Model.Shoot();
}
:
}
class MissleModel : IGameObjectModel {
public void Shoot() {
:
}
}
With the above, you can then instantiate your game object with the missle model :-
MissleModel model = new MissleModel();
GameObject<MissleModel> obj =
new GameObject<MissleModel>(model);