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!

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.

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.

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 :slight_smile:

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;
	}
}

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!