GetComponts throws InvalidCastException

I have a GameObject with multiple AudioSources attached to it (think of a sound player) and a script on another gameobject trying at Start() to get the list of sounds from the “carrier” game object.

Now when I declare the object for the sounds like this:

var sounds : AudioSource[];

and try that in Start():

function Start() { 
	sounds = soundObject.GetComponents(AudioSource);
}

I’m getting a casting exception when starting the game:

InvalidCastException: Cannot cast from source type to destination type.

When I leave out the type definition in the variable declaration like this:

var sounds;

then it works, but I assume Unity casts then behind the scenes. What am I doing wrong?

I usually use C#, but this may help

sounds = soundObject.GetComponents(typeof(AudioSource)) as AudioSource[];

I think unity will always return a Component[ ] with GetComponents

Thanks, yeah, when I used “Components[ ]” as cast type it works too, but then it’s still a component and not the AudioSource type. Hence a “.Play()” won’t work on the component.

Perhaps JS has no array casting and hence fails?

If that would be the case, then the solution is simple. Return Components[ ] and loop through it and casting each entry on its own into the corresponding index of sounds

The problem is that GetComponents returns an array of Components, and while AudioSource is a kind of Component, the language doesn’t regard AudioSource[ ] as being a kind of Component[ ], so it won’t allow you to assign the latter to the former.

I tried using the ‘as’ operator, as suggested by rom, but in Javascript it merely allows the code to compile and appears to silently fail to do the conversion (by returning null) at runtime.

Here’s one way that does work:

var sounds : Component[];

function Start()
{
	sounds = GetComponentsInChildren(AudioSource);

	(sounds[0] as AudioSource).volume = 0.5;

	for(var sound : AudioSource in sounds)
	{
		sound.volume = 0.5;
	}
}

Typing the array as Component[ ] allows the assignment to work, but if you’re using static typing (#pragma strict or Unity iPhone) you’d want to make sure you specify the type when you access members of the array. I’m not sure if all this runtime casting has significant overhead.

A slightly messier way of getting the right type of array upfront would be to do this:

var sounds : AudioSource[];

function Start()
{
	sounds = Array(GetComponentsInChildren(AudioSource)).ToBuiltin(AudioSource);

	sounds[0].volume = 0.5;

	for(var sound in sounds)
	{
		sound.volume = 0.5;
	}
}

That way you have to pay a bit more to convert the array of Components to an array of AudioSources, but after that there will be no extra type verification overhead.

Again, I’m not sure if the overhead of the first example is actually significant. You might want to do some tests to see if it’s worth your while doing it the other way.

Incidentally, I was aware of this situation in C#, but I didn’t know that Javascript was also affected. If anyone else has a better solution, I’d be keen to hear it!

[EDIT: made the conversion into a one liner in the second example!]

Ah, quite interesting. I thought that AudioSource is simply derived from Component and hence thought a cast to Component[ ] should automatically work also on AudioSource[ ], just because it is derived. But maybe it is not derived in a object oriented manner like I thought.

Right, thanks for the example! I think I’m going the convert-to-audiosources way to avoid having a lot of casts during runtime.

They are drived right in an objectoriented manner. But the compiler cannot assume that Component[ ] is castable to AudioSouce[ ], there could potentially be several other types of components in that array. Therefore the cast does not work and you have to force the conversion.

Not really as I called GetComponents of the type AudioSource, hence the function should only return AudioSource components, at least to my understanding. It would be right if you call the GetComponentsInChildren function, maybe you referred to that.

Good point, fehaar. I hadn’t thought of that.

The problem is that both GetComponents and GetComponentsInChildren return Component[ ] irrespective of the actual type you asked for. The language doesn’t have any way of saying “this function takes this type, therefore it returns an array of the same type”, so you always get an array of the lowest common denominator. Once the array has been returned, the compiler has no way of remembering that the function was supposed to only put objects of that type into it, so it has to assume that the array might contain a mixture of kinds of Component.

Exactly. The issue is that you confuse what the compiler knows at compile time and what you know at runtime. You know that the call should only return AudioSources - but the compiler knowns that is should return Components.

This is exactly why generics were implemented!

Ok, but aside of that what I would expect as user (at least me) if I call GetComponents with AudioSource as parameter I already tell the engine what I’m looking for - nothing generic, just AudioSources. What I would have expected is that the blackbox behind would filter out anything that is NOT an AudioSource and return then an AudioSource[ ] array. At least that was my expectation… Oh well… :slight_smile:

hehe… Yeah. Programming is not always what you expect. Especially in C# where you have to be excruciatingly explicit in your type conversion.