- Home /
How to carry List's and Vector3's to other classes? Interfaces don't work?
Using Unity 5.6 C#...
I'm having trouble understanding the "get; set;" function. I'm using interfaces, but they only seem to convey 'strings' 'ints' and 'bools'. What I really need is Vector3's and List conveyed. Not only a List of GameObjects, but a way to set said objects 'new Vector3's...
Now I've read some people who say Interfaces are useless, beginner only code. Or more clearly the 'get; set;' function. I will try to show only necessary code, with errors, and what I'm trying to do as clearly as possible:
public class MainCameraControl : MonoBehaviour, IAmSelected
{
IAmSelected selected; //Interface calling bool AmSelected, List<GameObject> selectedUnits
public List <GameObject> selectedUnits { get; set;}
//public List<GameObject> selectedUnits = new List<GameObject>();
public bool AmSelected { get; set;}
ObjectRayHit = hit.collider.gameObject;
if(ObjectRayHit.tag == "Units"){
selected = (IAmSelected)gameObject.GetComponent("IAmSelected");
selected.selectedUnits.Add (ObjectRayHit); //This doesn't work
//selectedUnits.Add(ObjectRayHit); This used to work with 'new List declaration'
}
if (selected.selectedUnits.Count > 0) { //This does not work
figureCentroid ();
foreach (GameObject unit in selectedUnits){
selected = (IAmSelected)unit.GetComponent("IAmSelected");
selected.AmSelected = true; //This did work in UnitClass with 'new List declaration'
}
}
public class MoveToFormation : MonoBehaviour, IAmSelected, IUnitInfo
{
public Vector3 locatorPosition { get; set; }
IAmSelected selected;
public bool AmSelected { get; set;}
public List<GameObject> selectedUnits { get; set; }
// I can't get anything to call or send any of this info below
// Foreach of selectedUnits doesn't work, and when it did
// I get the Error that "it.gameObject no definition for locatorPosition"
// Why can I not send that Vector3 with unit in UnitClass???
// Code may look messed up from hundreds of debug's, lol...
if (selected.selectedUnits.Count > 0)
{
total = selectedUnits.Count;
formation = iterate - (calcLine * rowCount);
foreach (GameObject it in selected.selectedUnits) {
if (selectedUnits.IndexOf(it) == iterate) {
if (North == true) {
it.locatorPosition.z = (gameObject.transform.position.z - (spacing * rowCount));
it.locatorPosition.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
it.North = true;
}
if (East == true) {
it.locatorPosition.z = (gameObject.transform.position.z + (spacing * formation)) - calcLine;
it.locatorPosition.x = (gameObject.transform.position.x - (spacing * rowCount));
it.East = true;
}
if (South == true) {
it.locatorPosition.z = (gameObject.transform.position.z + (spacing * rowCount));
it.locatorPosition.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
it.South = true;
}
if (West == true) {
it.locatorPosition.z = (gameObject.transform.position.z + (spacing * formation)) - calcLine;
it.locatorPosition.x = (gameObject.transform.position.x + (spacing * rowCount));
it.West = true;
}
iterate++;
}
}
}
}
public class UnitClass : MonoBehaviour, IAmSelected, IUnitInfo
{
IAmSelected selected;
public Vector3 locatorPosition { get; set; } // IUnitInfo interface should carry this over, but doesn't
public bool AmSelected { get; set; }
public List<GameObject> selectedUnits { get; set; }
foreach (GameObject it in selected.selectedUnits) {
Debug.Log ("it " + it.name); // Only get errors about selectedUnits List
}
// When I debug, even in a simple code move, locatorPosition is always 0.0x, 0.0z
// locatorPosition Ypos is set to Unit, for terrain differences
locatorPosition = new Vector3(locatorPosition.x, gameObject.transform.position.y, locatorPosition.z);
float dist = Vector3.Distance (gameObject.transform.position, locatorPosition);
gameObject.transform.LookAt (locatorPosition); // waypoint.transform.position
gameObject.transform.position += gameObject.transform.forward * speed * Time.deltaTime;
}
I just can't GET the list to work in any class, and can't SET any Vector3's where I want them. There's got to be a trick to this I'm not understanding clearly. If anyone can even slightly point me in the right direction, you are the coding master!
I did not move this to moderation! I don't even know what that means? can an ad$$anonymous$$istrator fix this?
I don't know why this ended here in the moderation queue. The pre-moderation has been disabled so things that end up in the moderation queue usually have been send there by a moderator.
However i don't see any reason why it might have been send here. I'll publish your question.
@SaraCecilia Is there still a problem with the moderation queue or has this been send to the moderation queue manually?
It must have been sent manually, but it's impossible to see by who. Another possibility is that it triggered the spam filter somehow.
Edit: tweaked the spam filter configurations a bit now, let's see if that does it.
On topic:
Relevant code would be your interface definition and how you use that interface. You also have some strange setup there. Your UnityClass implements the "IAmSelected" interface, but why do you have that "selected" field inside the class? You don't seem to initialize that field anywhere.
Are you sure you understand what an interface is?
This line:
public bool AmSelected { get; set;}
is actually just a shorthand for
private bool m_AmSelected;
public bool AmSelected
{
get { return m_AmSelected;}
set { m_AmSelected = value;}
}
It's not clear which object should reference which. Since every of those classes implement the "IAmSelected" interface every of those classes can be picked by GetComponent.
You should include the definition of your interfaces and tell us your exact error. What does "can't set" mean? Do you get a compiler error? Or a runtime error? If so what error exactly?
Too much code. Interfaces, gets and sets all work perfectly with Vector3 etc, but there's no way to know where your problem/lack of understanding is.
Just show a small piece of code that you say doesn't work, and say what you expected and what actually happened.
BTW interfaces are just to control visibility in APIs. They don't 'carry across' anything.
@dpoly If you check my answer, you'll see that the problem was right at the start. I wasn't sure what the problem was, because there was no post like this one, that accurately showed how they work. Interface questions, and explanations are always so bleak and short.
Answer by WideEyeNow · Jul 19, 2017 at 12:16 AM
Ok finally figured it all out! For one my understanding on interfaces was == null;... But I'll explain the answer for anyone else who will have a hard time understanding interfaces as I did :)
In my question code you see:
IAmSelected selected;
selected = (IAmSelected)gameObject.GetComponent("IAmSelected");
selected.selectedUnits.Add (ObjectRayHit); //This doesn't work
I had no idea that I was sending that unit back to my List in MainCameraControl! Whatever object you call after the (InterfaceName)objectName <-- is where your telling the interface to send it, obviously the gameObject you want is the one who's Component(script) you're trying to get it into!
Let's take a look at that code after I pulled all my hair out:
[Raycast logic here]
GameObject createWaypoint = Instantiate (waypointPrefab, MoveTo, defaultRotation);
newWaypoint = createWaypoint; //This is needed for certain situations
foreach (GameObject unit in selectedUnits) {
IAmSelected carryList = (IAmSelected)newWaypoint.GetComponent("IAmSelected");
carryList.selectedUnits.Add (unit);
}
You'll notice 'newWaypoint' the object that MoveToFormation.cs is the Component of, is where I'm telling that "unit" to go. Not back to myself again...
And the mayhem that happened when trying to send the Vector3, or nightmare rather, now looks like this:
[question related logic]
foreach (GameObject it in selectedUnits) {
if (selectedUnits.IndexOf (it) == iterate) {
IUnitInfo theUnit = (IUnitInfo)it.GetComponent ("IUnitInfo");
Vector3 pos = new Vector3 ();
formation = iterate - (calcLine * rowCount);
if (North == true) {
pos.z = (gameObject.transform.position.z - (spacing * rowCount));
pos.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
theUnit.North = true;
}
if (East == true) { Etc... }
if (South == true) {Etc... }
if (West == true) { Etc... }
theUnit.spot = pos;
iterate++;
}
}
A major thanks goes to @Bunny83 for showing me that I needed to call that Vector3 pos... That helped so much, you just don't understand.
And an even more humongous thanks goes to @UnityCoach for helping the less fortunate. He's an awesome guy, with a ton of tutorials on youtube, that probably show this issue. I just somehow didn't notice, lol...
Here's all the code in a nutshell for those who still don't get it, like where I was at several days ago:
public class MainCameraControl : MonoBehaviour {
//This is the intiating List, so it doesn't get/set
public List<GameObject> selectedUnits = new List<GameObject>();
Update(){
[Raycast logic here]
GameObject createWaypoint = Instantiate (waypointPrefab, MoveTo, defaultRotation);
newWaypoint = createWaypoint; //This is needed for certain situations
foreach (GameObject unit in selectedUnits) {
IAmSelected carryList = (IAmSelected)newWaypoint.GetComponent("IAmSelected");
carryList.selectedUnits.Add (unit);
}
[Raycast logic here]
ObjectRayHit = hit.collider.gameObject;
if(ObjectRayHit.tag == "Units")
if (!selectedUnits.Contains (ObjectRayHit))
selectedUnits.Add (ObjectRayHit);
}
private void ListSelected(){
foreach (GameObject unit in selectedUnits){
IAmSelected itSelected = (IAmSelected)unit.GetComponent("IAmSelected");
if(!itSelected.selectedUnits.Contains(unit)){
itSelected.AmSelected = true; //This tells unit to display Healthbar
}
}
}
}
Then the Interface class
public interface IAmSelected
{
bool AmSelected {get;set;}
List<GameObject> selectedUnits { get; set;}
}
The gameObject my units move to class
public class MoveToFormation : MonoBehaviour, IAmSelected, IUnitInfo {
public bool AmSelected {get;set;}
public bool North { get; set; }
public bool East { get; set; }
public bool South { get; set; }
public bool West { get; set; }
[SerializeField] private Vector3 _spot; // this is serializable and editable in the inspector
public Vector3 spot
{
get {return _spot;}
set
{
if (_spot != value)
{
_spot = value;
// you can catch when the value has changed, to do things like raise an event
}
}
}
/// You don't have to write it out like that, you could write it like this
/// If your code is simple:
public List<GameObject> selectedUnits {get;set;}
void Update(){
[question related logic]
foreach (GameObject it in selectedUnits) {
if (selectedUnits.IndexOf (it) == iterate) {
IUnitInfo theUnit = (IUnitInfo)it.GetComponent ("IUnitInfo");
Vector3 pos = new Vector3 ();
formation = iterate - (calcLine * rowCount);
if (North == true) {
pos.z = (gameObject.transform.position.z - (spacing * rowCount));
pos.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
theUnit.North = true;
}
if (East == true) { Etc... }
if (South == true) {Etc... }
if (West == true) { Etc... }
theUnit.spot = pos;
iterate++;
}
}
}
}
The interface class that carries the Vector3
public interface IUnitInfo
{
bool North { get; set; }
bool East { get; set; }
bool South { get; set; } // You should get the hint where these go
bool West { get; set; }
Vector3 spot { get; set; }
}
And finally the Unit class
public class UnitsParentScript : MonoBehaviour, IAmSelected, IUnitInfo {
[bools]
[Vector3 spot]
[List selectedUnits]
void Update(){
if (AmSelected == true)
DisplayHealthbar();
[plenty of question logic]
moveToFormation = new Vector3 (spot.x, gameObject.transform.position.y, spot.z);
mainControl.setWaypoint = false;
float dist = Vector3.Distance (gameObject.transform.position, moveToFormation);
gameObject.transform.LookAt (moveToFormation);
gameObject.transform.position += gameObject.transform.forward * speed * Time.deltaTime;
}
}
Answer by Cornelis-de-Jager · Jul 14, 2017 at 12:15 AM
Interfaces are not that useful except in big programs where you need to keep track of what items need to do. What you want is an Abstract Class. It works just like inheriting from a normal class but works like an interface but with added functionality.
@Cornelis-de-Jager when my answer comes out of moderation, you'll get why I needed interfaces. The game will be huge, lol. But I was told that interfaces use less performance, so a good quick "hey $$anonymous$$oveToFormation get all the selected units" and then "hey units grab all these vectors", is exactly what I'm going to need.
I guess when all your Units have the same script for all their actions then it would make sense to use an interface.
But say you have ranged units and melee units. They have different attack behaviour. Say the melee has a script called SwordsmanBehaviour and the archer has script called ArcherBehaviour.
Now you can't simply go All Units use this script to attack since they have different scripts. but if you inherit from a class then you can do that. You can say everyone with OverallCompbatBehaviour go attack.
An Abstract class just simply is like a normal class except you have to implement what is written in a abstract class (just like an interface).
But then again if you can make it work with an interface then do that ins$$anonymous$$d.
true, the interface will eventually call to the Unit Parent Class, then that will have children to the functions of melee vs. ranged. I just haven't got there yet, I'm trying to not get too far ahead without constant debugging and testing... An approach that obviously doesn't happen anymore, lol, you ever heard of a thing called updates? lmao
I don't get your reasoning why you think interfaces are not that useful. To use your analogy: Interfaces are just like abstract classes with the difference that you can't implement any code in them and you can't declare any fields. The big advantage of interfaces is that a single class can implement as many interfaces as you like while you can only have a single base class (no matter if it's abstract or not).
You actually want to avoid using abstract classes / base classes as Unity uses a component approach. Object composition uses flat class hierarchies and tries to avoid coupling between classes.
You can have several different scripts (SwordsmanBehaviour, ArcherBehaviour, ...). If all of them implement the same interface they can be treated the same way, as an instance of that interface.
An abstract class is just an ordinary base class but allows the definition of methods without an actual implementation. Interfaces provide much more "abstraction" than an abstract class. It may sound weird but is true,
Answer by Bunny83 · Jul 13, 2017 at 12:41 PM
We still don't know what your actual problem is, however i see one problem in your code:
A line like this:
it.locatorPosition.z = (gameObject.transform.position.z - (spacing * rowCount));
won't work since Vector3 is a value type and your "locatorPosition" is a property. A property is not a variable but a set of two methods. Imagine you have a method like this:
Vector3 get_locatorPosition()
{
return m_InternalLocatorPosition;
}
Here "m_InternalLocatorPosition" is an actual field which stores a Vector3 value. Your like essentially does this:
get_locatorPosition().x = (gameObject.transform.position.z - (spacing * rowCount));
The getter of a value-type property will return a copy of the vector3. Changing that vector does not affect the value where it came from. You have to invoke the "set" method. The set method is only executed when you assign a Vector3 to the property.
You can solve that problem by using a temporary local variable like this:
// execute the property "getter"
Vector3 pos = it.locatorPosition;
if (North)
{
pos.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
pos.z = (gameObject.transform.position.z - (spacing * rowCount));
it.North = true;
}
else if (East)
{
pos.x = (gameObject.transform.position.x - (spacing * rowCount));
pos.z = (gameObject.transform.position.z + (spacing * formation)) - calcLine;
it.East = true;
}
else if (South)
{
pos.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
pos.z = (gameObject.transform.position.z + (spacing * rowCount));
it.South = true;
}
else if (West)
{
pos.x = (gameObject.transform.position.x + (spacing * rowCount));
pos.z = (gameObject.transform.position.z + (spacing * formation)) - calcLine;
it.West = true;
}
// execute the property "setter".
it.locatorPosition = pos;
ps: Using 4 seperate bool variables in such a case is not a good idea. Since (according to your code) only one direction should be true at a time, using an enum would make more sense. But without more background information that's all just guessing.
@Bunny83 Thanks, I'll try this "Vector3 issue" soon, and let you know if I can get it working :)
oh the bool north issue, only one of those "if's" will be true in a split second of time, to just give the appropriate "Units" in the list his formation direction and specific location in the formation. I didn't think the else was needed, but if you think moving say 100 troops in this formation, without that else could be a performance issue? Then I'll definitely implement it, thanks.
Answer by WideEyeNow · Jul 14, 2017 at 02:39 PM
Ok, so apparently my detailed answer went to moderation and was deleted... So, I'll try to remember what I said...
If you notice in my problem code:
IAmSelected selected; //The call to the interface
selected = (IAmSelected)gameObject.GetComponent("IAmSelected");
selected.selectedUnits.Add (ObjectRayHit); //This doesn't work
I am actually telling the interface to send that object to get sent back to where I'm sending it from! Whatever gameObject you call after (interface)gameObject <-- is what object will receive that '.Add'...
After pulling out all of my hair, I finally figured it out:
public List<GameObject> selectedUnits = new List<GameObject>();
foreach (GameObject unit in selectedUnits) {
IAmSelected carryList = (IAmSelected)newWaypoint.GetComponent ("IAmSelected");
carryList.selectedUnits.Add (unit);
}
Notice I no longer called MainCameraControl.cs to get the List from IAmSelected. This is because the mainCamera actually makes the list, that I want referenced to else where. Then the interface just acts as a roadway, for said object to reach it's destination. In this case, the object I want to receive the List of selectedUnits is newWaypoint(MoveToFormation.cs)...
Incase you don't know what an Interface.cs Looks like:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IAmSelected {
bool AmSelected {get;set;}
List<GameObject> selectedUnits { get; set;}
}
So, that being said INTERFACES DO WORK! (for all those who told me they didn't)
And then you might ask, 'It sent a List, but what about the Vector3?' Ah! @Bunny83 was dead on the money helping with that one. Notice:
foreach (GameObject it in selectedUnits) {
if (selectedUnits.IndexOf (it) == iterate) {
IUnitInfo theUnit = (IUnitInfo)it.GetComponent ("IUnitInfo");
Vector3 pos = new Vector3 ();
formation = iterate - (calcLine * rowCount);
if (North == true) {
pos.z = (gameObject.transform.position.z - (spacing * rowCount));
pos.x = (gameObject.transform.position.x + (spacing * formation)) - calcLine;
theUnit.North = true;
}
if (East == true) {Etc...}
if (South == true) {Etc...}
if (West == true) {Etc...}
theUnit.spot = pos;
iterate++;
}
}
Once I got the List to work in MoveToFormation class, I then received the error of not being able to call "it.locatorPosition". So Bunny was right, I had to create the Vector3 pos, and through debugging changed the name of locatorPostion to "spot". Then MoveTo class had no need of sending the List to the UnitsClass, it just simply said each 'unit' in my list, accept this Vector3 when your Index is == iterate, or 0,1,2,3,4,etc... And it all works perfectly!
NOTE: IAmSelected only really called the List, then IUnitInfo wound up calling the bools of direction(north,south,etc...) and [Vector3 spot {get;set;}]... MoveToFormation.cs had Monobehavior, IAmSelected, IUnitInfo {... and UnitsClass.cs only had Monobehavoir, IUnitInfo {... MainCameraControl.cs called no interfaces after Monobehavior...
And a very special thanks to @UnityCoach for helping the idiot I am understand all this mess, he's a good guy, with training courses you should look into!
Why I am I not able to accept any answer? All my buttons 'bold', 'enter code snippet', etc.. are blank? $$anonymous$$y original answer went to moderation and was deleted? Can a moderator please accept this as the answer, and see if my account isn't banned or something???