- Home /
How to get an object in a list to have a number assigned to it based on list position.
Hello, I am trying to make a system where my character changes collision boxes based on what animation is playing. I am doing this by giving the player character "collision" child objects. Each child object has a single, uniquely sized collision box, and a "ChildColliderController" script which will hold a number, and set a collision box to "isCurrentCollisionBox". The parent object, "Player", has a list called "Child Collider List", which all the child object's BoxColliders are stored.
Now, the problem: I want to be able to pass a number to each instance of ChildCollider based on their respective position in the list. I'm not sure how to do this.
The next problem (not relevant till the first one is figured out, but might as well post it): I want the child objects to be able to pass their number into the parent object based on the bool "isCurrentCollisionBox". I think this is the same problem (essentially) as the first one, but in case it's not, here it is.
This is the code in the parent
public List<BoxCollider2D> childColliderList;
int activeCollider=0;
public void ChangeCollisionBox()
{
collider = childColliderList[activeCollider];
}
This is the code in the child
bool isActive=false;
public bool listNumber;
There isn't anything in there that actually does anything yet. The point of all this is so I can work with many different children and not have to manually enter an ID of a sort for each one. Any help would be appreciated, Thanks in advance!
Here is what I finally did (note this is a "proof-of-concept" code, and it is not as fleshed out as it will be when fully implemented).
This is the code inside the Parent
public BoxCollider2D collider;
public void ChangeCollisionBox(Vector2 newSize, Vector2 newOffset)
{
collider.size = newSize;
collider.offset = newOffset;
CalculateRaySpacing();
}
This is the code inside the Child
[SerializeField]
private string iD;
private Vector2 colliderSize;
private Vector2 colliderOffset;
private void Awake()
{
colliderSize = GetComponent<BoxCollider2D>().size;
colliderOffset = GetComponent<BoxCollider2D>().offset;
}
private void Update()
{
if(Input.GetAxisRaw("Horizontal")==0 && Input.GetAxisRaw("Vertical")==0)
{
IsIdle();
}
else { IsAccelerating(); }
}
void IsIdle()
{
if(iD=="Idle")
{
GetComponentInParent<RaycastController>().ChangeCollisionBox(colliderSize, colliderOffset);
}
}
void IsAccelerating()
{
if (iD == "Accelerate")
{
GetComponentInParent<RaycastController>().ChangeCollisionBox(colliderSize, colliderOffset);
}
}
Answer by UnityCoach · Jan 23, 2018 at 05:45 AM
To do just what you asked for, you could use list.IndexOf().
Yeah that's exactly what I was looking for. I'm still learning C#, so I don't know where all of my options are hiding (Looking through the drop-down auto-finish in Visual Studio helps, but I still miss a lot). Thank you!
I know what you mean :)
When it comes to C#, $$anonymous$$SDN is quite useful.
Answer by Harinezumi · Jan 22, 2018 at 11:35 PM
I probably don't understand the question, but I think you will be better off if in the parent you store List<ChildColliderController> childColliderList
and set from the parent ChildColliderController.listNumber
by looping through the list:
// do this whenever you modify the list
for (int i = 0; i < childColliderList.Count; ++i) { childColliderList[i].listNumber = i; }
Was this your question, and did this answer it?
Answer by MadboyJames · Jan 23, 2018 at 12:05 AM
Kinda. I actually found a different solution. I came back here to update this and say "hey I found something simpler". Thank you for your response though, I didn't think of a loop, although that was what I was looking for. I'm not done coding my new solution, but it'll work better than passing a value from a list to a child then back to the list. What I am doing now is having a method that takes two Vector2 vars and puts one in the Collider.size, and the other in Collider.transform.position. The child would call the method whenever the conditions are met for a new BoxCollider I'll post the code here when I get it working. Or I'll post another question if I get stumped. :)
EDIT: I put this in the comments before realizing I should put it here. This is a Copy-Past of the first comment.
"Here is what I finally did (note this is a "proof-of-concept" code, and it is not as fleshed out as it will be when fully implemented).
This is the code inside the Parent
public BoxCollider2D collider;
public void ChangeCollisionBox(Vector2 newSize, Vector2 newOffset)
{
collider.size = newSize;
collider.offset = newOffset;
CalculateRaySpacing();
}
This is the code inside the Child
[SerializeField] private string iD;
private Vector2 colliderSize;
private Vector2 colliderOffset;
private void Awake()
{
colliderSize = GetComponent<BoxCollider2D>().size;
colliderOffset = GetComponent<BoxCollider2D>().offset;
}
private void Update()
{
if(Input.GetAxisRaw("Horizontal")==0 && Input.GetAxisRaw("Vertical")==0)
{
IsIdle();
}
else { IsAccelerating(); }
}
void IsIdle()
{
if(iD=="Idle")
{
GetComponentInParent<RaycastController>().ChangeCollisionBox(colliderSize, colliderOffset);
}
}
void IsAccelerating()
{
if (iD == "Accelerate")
{
GetComponentInParent<RaycastController>().ChangeCollisionBox(colliderSize, colliderOffset);
}
}
"
Looks like a good solution, although I'm wondering how the different Child scripts don't fight for setting the size of the collider in RaycastController
...? Is it the iD
field that is only set if this is the active Child?
Apart from this the only thing I would recommend is to save the RaycastController
of the parent to a member variable in Awake()
so that you don't call GetComponentInParent()
in every Update()
.
Huh, I didn't think of setting a RaycastController var. Yeah that would be a better practice. And the children don't fight over what size to set the parents BoxCollider because of "[SerializeField] private string iD;" which is at the top, which didn't get included in the code snippet (I'm just seeing this now. I'll fix that). That allows me to set each child as a different "state" for the script to handle. Only the Child with the correct ID for whatever state is happening (Idle, Acceleration, $$anonymous$$axSpeed, Skidding, Jumping, Etc.) will be able to give its BoxCollider information to the parent.
Yes, that's what I thought, you control who gets to call ChangeCollisionBox()
by setting iD
. In this case, I would move the checking the Input.GetAxisRaw()
part to where you set iD = "Idle"
, because that is the only case in which setting "Idle" makes sense, and you also avoid checking input in Child objects that don't have iD
set to Idle
.
Also, I'm pretty sure you are doing it, but make sure that when you set a valid iD
to any Child, you set the iD
of everyone else to something invalid, like null
or ""
.
I set the ID in the inspector manually. I think I'd need to set the ID by code if I wanted to put the "input.getAxisRaw" up with it. And no, every child has thier own ID at every point.