I am trying to add value from another class to the list created in another class. I have set up a very simple experiment but it always throw me the error.
the first class is simply gameObject script which attached to the object
public class gameObject : Agent
{
public Area area;
public override void Initialize()
{
var testData = new test
{
name = "test value",
};
area.testList.Add(testData);
}
}
and I try to add that value to the list in another class as shown belows
public class test
{
public string name;
}
public class Area : MonoBehaviour
{
public List<test> testList = new List<test>()
}
why the value always return null ? it work properly if I add the data with in the area class
You initialize testList list in VoxelArea class but you are using Area.
replace Area with VoxelArea or initialize area.testList and area before adding.
public class gameObject : Agent
{
public Area area; //initialize Area
public override void Initialize()
{
var testData = new test
{
name = "test value",
};
//area = new Area(); //initialize Area
area.testList = new List<test>();//<--NOTE THIS
area.testList.Add(testData);
}
}
Related
I made a basic class
public class EditorSerializaion : Editor
{
public void DrawSerializedField(string name, string title)
{
SerializedProperty property;
property = serializedObject.FindProperty(name);
EditorGUILayout.PropertyField(property, new GUIContent(title));
serializedObject.ApplyModifiedProperties();
}
}
And I want to call it from the Custom editor class
[CustomEditor(typeof(BasicComponentScript))]
public class BaiscComponentEditor : Editor
{
EditorSerializaion editorSerializaion;
public override void OnInspectorGUI()
{
editorSerializaion.DrawSerializedField("pos", "Posotion");
}
}
But it won't work
(in the DrawSerializedField function the serializedObject.FindProperty(name) return null but I do it in the custom inspector with the same name it works)
You can pass serializedObject as a parameter to DrawSerializedField. And I don't think having EditorSerialization extend from Editor has any benefits in your case (you can't automagically access other Editor scripts' serializedObjects, as you've seen). I'd make it a static utility class if I were you:
public static void DrawSerializedField(SerializedObject serializedObject, string name, string title)
EditorSerializaion.DrawSerializedField(serializedObject, "pos", "Position");
The solution is to pass the serializedObject from the first object and it didn't work for me on the start because you need to create a new instance of object.
public class EditorSerializaion : Editor
{ // This has changed
public void DrawSerializedField(SerializedObject sb,string name, string title)
{
SerializedProperty property;
// This has changed
property = sb.FindProperty(name);
EditorGUILayout.PropertyField(property, new GUIContent(title));
// This has changed
sb.ApplyModifiedProperties();
}
}
[CustomEditor(typeof(BasicComponentScript))]
public class BaiscComponentEditor : Editor
{
EditorSerializaion editorSerializaion;
// This is new
private void OnEnable()
{
editorSerializaion = new EditorSerializaion();
}
public override void OnInspectorGUI()
{ // This has changed
editorSerializaion.DrawSerializedField(serializedObject,"pos", "Posotion");
}
}
You can also change the DrawSerializedField function to be static and then you won't need to create an instance of it
I have this Item class:
public class Item
{
public string Name;
public Entity ItemOwner;
public Action<Entity> effect;
public Item(string name, Action<Entity> effect)
{
Name = name;
this.effect = effect;
}
public void Use()
{
effect(ItemOwner);
}
}
and this Entity Class:
public class Entity
{
public string Name;
public float Health;
public List<Item> Items = new List<Item>();
public bool CanDie;
public bool Dead;
public Entity(string name, float health, bool canDie)
{
Name = name;
Health = health;
CanDie = canDie;
}
public void UseItem(string name)
{
foreach (var item in Items)
{
if (item.Name == name)
{
item.Use();
Items.Remove(item);
break;
}
}
}
}
I want to know is it possible to set 'EntityOwner' to a given entity when that item is placed within the entity's inventory.
what I currently can do is this 'Main Program':
Entity Player = new Entity("Dave", 50, true);
Item Potion = new Item("Healing Potion", ItemOwner => ItemOwner.Health += 25);
Potion.ItemOwner = Player;
Player.Items.Add(Potion);
Player.UseItem("Healing Potion");
I want this specific piece of code:
Potion.ItemOwner = Player;
To trigger when the item is placed inside the entity's inventory.
Is this possible somehow?
I don't think this could be achieved from within the item class super cleanly.
What you could do is add a function to the Entity class something like this :
public void AddItem(Item item) {
Items.Add(item);
item.EntityOwner = this;
}
Similarly you'd want a RemoveItem function that unsets the EntityOwner.
If you do this method you would likely want Items to be a protected variable so that no other class could add an item to the list without the AddItem function.
The other option is to add a OnAddedToList function to the Item class and in place of item.EntityOwner above you simply call the added to list function on that item. Might be what you want if each item has more unique things to do than just setting the owner.
I have a Skill base class and 2 separate Skill classes that inherit from the Class Skill. Each skill has its own SkillCoolDown value. I set the CoolDownManager Class that is visualising the cooldowns for the skills, but I don't understand how to set the SkillCoolDown value to the manager which is different for each skill. Would appreciate the help.
public class Skill
{
private string skillName;
private int skillDamage;
private string skillDescription;
private int skillCoolDown;
public string SkillName {get;set;}
public int SkillDamage {get;set;}
public string SkillDescription {get;set;}
public int SkillCoolDown {get;set;}
public Skill () {}
}
public class CoolDownManager : MonoBehaviour
{
public Skill skill;
private Stopwatch cooldown;
private Button button;
private Image fillImage;
private int skillCoolDown;
public void OnSkillUse(Button btn) {
skill = new Skill(); // HERE THE VALUE SkillCoolDown SHOULD BE IMPLEMENTED AND I WANT SOMEHOW TO SHOW WHICH SKILL HAS BEEN USED.
button = btn.GetComponent<Button>();
fillImage = btn.transform.GetChild(0).gameObject.GetComponent<Image>();
btn.interactable = false;
fillImage.fillAmount = 1;
cooldown = new Stopwatch();
cooldown.Start();
StartCoroutine(CoolDownAnimation());
}
private IEnumerator CoolDownAnimation() {
while(cooldown.IsRunning && cooldown.Elapsed.TotalSeconds < skill.SkillCoolDown) {
fillImage.fillAmount = ((float)cooldown.Elapsed.TotalSeconds / skill.SkillCoolDown);
yield return null;
}
fillImage.fillAmount = 0;
button.interactable = true;
cooldown.Stop();
cooldown.Reset();
}
}
And here is example of the Skill class from where the value SkillCoolDown should be passed.
public class BasicAttack : Skill
{
public BasicAttack()
{
SkillCoolDown = 2;
}
}
A Skill could be passed to the CooldownManager via constructor injection:
public class CoolDownManager : MonoBehaviour
{
public Skill skill;
// other properties
public CoolDownManager(Skill skillParam)
{
skill = skillParam;
}
// do whatever you want with skill
}
This is the perfect problem to solve using scriptable objects.
The following class inherits from ScriptableObject and defines the same data you have in your Skill class. Using the CreateAssetMenu attribute for this class, you can make a new asset of this type. Right-click in your project window and navigate to the menu item Combat/Skills/New Skill, and then you will have an asset of this type.
Note: Because this is an asset, it implicitly has the property name, so you can name the Skill asset accordingly.
[CreateAssetMenu(fileName = "NewSkill.asset", menuName = "Combat/Skills/New Skill")]
public class Skill : ScriptableObject
{
[SerializeField] private int _damage = default;
[SerializeField] private string _description = default;
[SerializeField] private int _cooldown = default;
public int damage => _damage;
public string description => _description;
public int cooldown => _cooldown;
}
Now that you can create Skill assets, go ahead and create your two skills and fill in the details in the inspector.
Next, you'll need a way to use the Skill. I've gotten rid of the CoolDownManager in favor of a button. Check out the following.
[RequireComponent(typeof(Button))]
public class SkillButton : MonoBehaviour
{
[SerializeField] private Skill _skill = default;
[SerializeField] private Button _button = default;
[SerializeField] private Image _fillImage = default;
private Stopwatch _stopwatch = new Stopwatch();
private void Awake()
{
// Hook up a method to listen for when the button is clicked.
_button.onClick.AddListener(OnClick);
}
// This method is invoked when the button is clicked.
private void OnClick()
{
StartCoroutine(CoolDownAnimation());
}
private IEnumerator CoolDownAnimation()
{
// Immediately keep the button from being clicked again.
_button.interactable = false;
_stopwatch.Start();
while (_stopwatch.IsRunning && _stopwatch.Elapsed.TotalSeconds < _skill.cooldown)
{
_fillImage.fillAmount = (float)_stopwatch.Elapsed.TotalSeconds / _skill.cooldown;
yield return null;
}
_fillImage.fillAmount = 0;
_button.interactable = true;
_stopwatch.Stop();
_stopwatch.Reset();
}
// Use this to hook up references automatically.
private void OnValidate()
{
if (_button == null)
{
_button = GetComponent<Button>();
}
if (_button != null && _fillImage == null)
{
_fillImage = _button.transform.GetChild(0).gameObject.GetComponent<Image>();
}
}
}
Attach the SkillButton component to the Button you wish to have controlling the skill, then hook up your various Skill objects to have them associated with that button.
With all of that out of the way, you should have functionality close to what you desire.
I'd like a NodeView to display some hierarchical data, something like this:
Father Mother
====== ======
Jon Ann
+Sons
+--Jon 20
+--Dave 10
+Daughters
+--Ann
Ron Mary
Paul Eve
+Sons
+--Bob 4
"Sons" and "Daughters" should not be shown if they are empty.
I have created three bussiness classes: Parents, Son and Daughter, and I'm creating TreeNode subclasses to display them.
I designed a Window with Monodevelop designer, and recreated the nodeView with code. The nodeview doesn't display anything at all, and I would like to know why. Here's the code, in a single file so anyone can test it:
using System;
using Gtk;
using System.Collections.Generic;
using System.Linq;
namespace Family.Model
{
public class Son {
public string Name {get;set;}
public string Age {get;set;}
public Son(string n,string a) {
Name=n;Age=a;
}
}
public class Parents
{
public string Father {get;set;}
public string Mother {get;set;}
public List<string> Daughters {get;set;}
public Dictionary<string,Son> Sons {get;set;}
public Parents() {
Daughters=new List<string>();
Sons=new Dictionary<string, Son>();
}
}
}
namespace Family.View
{
using Family.Model;
[TreeNode (ListOnly=false)]
public class ParentsNode:TreeNode
{
private Parents parents;
public ParentsNode (Parents p):base()
{
this.parents=p;
DaughtersRoot rootd=new DaughtersRoot();
SonsRoot roots=new SonsRoot();
if (p.Sons.Count>0) {
this.AddChild (roots);
p.Sons.Values.ToList ().ForEach (x=>roots.AddChild(new SonNode(x)));
}
if (p.Daughters.Count>0) {
this.AddChild (rootd);
p.Daughters.ForEach (x=>rootd.AddChild(new DaughterNode(x)));
}
OnChanged ();
}
[Gtk.TreeNodeValue(Column=0) ]
public string Father {
get { return parents.Father;}
set { parents.Father=value;OnChanged ();}
}
[Gtk.TreeNodeValue (Column=1)]
public string Mother {
get { return parents.Mother; }
set { parents.Mother=value;OnChanged ();}
}
}
[TreeNode(ListOnly=false)]
public class DaughtersRoot:TreeNode
{
[Gtk.TreeNodeValue(Column=0) ]
public string Label {
get {return "Daughters"; }
}
}
[TreeNode(ListOnly=false)]
public class SonsRoot:TreeNode
{
[Gtk.TreeNodeValue(Column=0) ]
public string Label {
get {return "Sons"; }
}
}
[TreeNode(ListOnly=false)]
public class DaughterNode:TreeNode {
private string mName;
public DaughterNode(string s):base() {
this.Name=s;
}
[Gtk.TreeNodeValue(Column=0) ]
public string Name {
get {return mName;}
set {mName=value;OnChanged ();}
}
}
[TreeNode(ListOnly=false)]
public class SonNode:TreeNode {
private Son son;
public SonNode(Son s):base() {
this.son=s;
OnChanged ();
}
[Gtk.TreeNodeValue(Column=0)]
public string Name {
get { return this.son.Name; }
set {son.Name=value;OnChanged ();}
}
[Gtk.TreeNodeValue(Column=1)]
public string Age {
get { return this.son.Age; }
set {son.Age=value;OnChanged ();}
}
}
public class MainWindow: Gtk.Window
{
private global::Gtk.ScrolledWindow GtkScrolledWindow;
private global::Gtk.NodeView treeFamily;
private NodeStore storeParents=new NodeStore(typeof(ParentsNode));
protected virtual void Build ()
{
global::Stetic.Gui.Initialize (this);
// Widget MainWindow
this.Name = "MainWindow";
this.Title = global::Mono.Unix.Catalog.GetString ("MainWindow");
this.WindowPosition = ((global::Gtk.WindowPosition)(4));
this.GtkScrolledWindow = new global::Gtk.ScrolledWindow ();
this.GtkScrolledWindow.Name = "GtkScrolledWindow";
this.GtkScrolledWindow.ShadowType = ((global::Gtk.ShadowType)(1));
this.treeFamily = new global::Gtk.NodeView ();
this.treeFamily.CanFocus = true;
this.treeFamily.Name = "treeFamily";
this.GtkScrolledWindow.Add (this.treeFamily);
this.Add (this.GtkScrolledWindow);
if ((this.Child != null)) {
this.Child.ShowAll ();
}
this.DefaultWidth = 400;
this.DefaultHeight = 300;
this.Show ();
this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent);
}
public MainWindow (): base (Gtk.WindowType.Toplevel) {
Build ();
Parents p=new Parents();
p.Father="Bob";
p.Mother="Mary";
storeParents.AddNode (new ParentsNode (p));
p=new Parents();
p.Father="Ron";
p.Mother="Ann";
p.Sons.Add ("David",new Son("David","20"));
p.Sons.Add ("Matt",new Son("Matt","10"));
p.Daughters.Add ("Elaine");
p.Daughters.Add ("Kate");
storeParents.AddNode (new ParentsNode(p));
this.treeFamily=new NodeView(storeParents);
Gtk.TreeViewColumn fatherColumn = new Gtk.TreeViewColumn
("Father",new CellRendererText(),"text",0);
Gtk.TreeViewColumn motherColumn = new Gtk.TreeViewColumn
("Mother",new CellRendererText(),"text",1);
treeFamily.AppendColumn (fatherColumn);
treeFamily.AppendColumn (motherColumn);
treeFamily.ShowAll ();
}
protected void OnDeleteEvent (object sender, DeleteEventArgs a) {
Application.Quit ();
a.RetVal = true;
}
}
class MainClass
{
public static void Main (string[] args) {
Application.Init ();
MainWindow win = new MainWindow ();
win.Show ();
Application.Run ();
}
}
}
Gtk model binding works quite different than other toolkits. That being said, here is what you need to know for starters:
There are several types of "controls" or widgets as they are called in GTK that can display matrix/list data:
NodeView (easiest)
TreeView
Now, AFAIC, NodeView only exists in GTK#, that is, the .Net bindings. TreeView is available in all other bindings and is part of the GTK+ Core. The difference is that somehow, NodeView is easier to use but more limited.
You'll want to use NodeView if you only need to display list data, that is, no hierarchical data (as you seem to need though)
If you need to display hierarchical data then you'll use a TreeView.
Whichever you use, you'll also have to set up what you need in that widget to display, your columns for example. The difference between a NodeView/TreeView cells in GTK and other basic toolkits is that a NodeView column for example can display other widgets inside its cell instead of just text, so you could have one column that has a cell that displays a progress bar, or a checkbox. The widgets that you use inside these cells are called CellRenderers and there are CellRendererText, CellRenderToggle, etc.
Now, these controls are "bound" to data by "stores", for example:
NodeStore
ListStore
TreeStore
Which one you'll use depends on your needs again and the type of widget you are going to use, so if you only need to display simple data use NodeStore, if you need to display hierarchical data use a TreeStore. One powerful thing about these data stores is that you can store in them not only the data that is shown on the NodeView/TreeView widget but any other data that does not necessarily needs to be displayed, you can even store objects, for example, you could have a store with 4 columns of which 3 are shown on the widget and the fourth keeps an instance of the full object.
In this link theres samples for each case I mentioned that you can try. I think that you should be using the "Tree" model to accomplish what you want and not the "Node".
GTK is a powerful toolkit but sometimes its hard to understand how it works. I hope this intro works for you.
public partial class TestConrol : UserControl
{
public TestConrol()
{
InitializeComponent();
}
public override string ToString()
{
return "asd";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestConrol tc1 = new TestConrol();
comboBox1.Items.Add(tc1);
TestConrol tc2 = new TestConrol();
comboBox1.Items.Add(tc2);
}
}
When form loaded, I see combobox has two items with empty names, instead of "asd" :/
But this work if I override ToString() in common class, not derived from anything:
public class TestClass
{
public override string ToString()
{
return "bla bla bla";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestClass tcl = new TestClass();
comboBox1.Items.Add(tcl);
}
}
After that I see in combobox "bla bla bla"
Create a property in you control and map the DisplayMember of the combobox to that property, it should work.
I tried to understand the source code(!). This is not a simple call to ToString().
There's an internal class System.Windows.Forms.Formatter doing some stuff. It eventually creates a converter. This is roughly equivalent to saying:
var conv = System.ComponentModel.TypeDescriptor.GetConverter(tc1.GetType());
where tc1 is the TestContol from your question. Now, had we used the TestClass tcl which doesn't implement any interfaces, this would have given us a converter which would eventually call ToString().
But in this example we use tc1, and it is a System.ComponentModel.IComponent. Our conv therefore becomes a System.ComponentModel.ComponentConverter. It uses the Site of the IComponent. When we say:
string result = conv.ConvertTo(tc1, typeof(string));
and the Site is null, we get the empty string "" you saw in your combo box. Had there been a Site it would have used its Name instead.
To demonstrate that, put the following into your TestControl instance constructor:
public TestConrol()
{
InitializeComponent();
Site = new DummySite(); // note: Site is public, so you can also
// write to it from outside the class.
// It is also virtual, so you can override
// its getter and setter.
}
where DummySite is something like:
class DummySite : ISite
{
public IComponent Component
{
get { throw new NotImplementedException(); }
}
public IContainer Container
{
get { throw new NotImplementedException(); }
}
public bool DesignMode
{
get { throw new NotImplementedException(); }
}
public string Name
{
get
{
return "asd"; // HERE'S YOUR TEXT
}
set
{
throw new NotImplementedException();
}
}
public object GetService(Type serviceType)
{
return null;
}
}
Use comboBox1.Items.Add(tc1.ToString()); instead of comboBox1.Items.Add(tcl);
This worked for me:
comboBox1.FormattingEnabled = false
In your UserControl, add a property, and call it FriendlyName for example, as such
namespace project
{
public partial class CustomUserControl : UserControl
{
public CustomUserControl()
{
InitializeComponent();
}
public String FriendlyName { get => "Custom name"; }
}
}
And then set the DisplayMember property of your ComboBox to "FriendlyName", as such
myComboBox.DisplayMember = "FriendlyName";
And to me, this was a very clean solution and gut tells me it is the intended way to do it.