I have searched and searched for an answer to this but found nothing- its probably because I dont know how to ask my question.
What I want to do is create a script that creates a delay based on the value that you add into the ‘delay’ field.
Its just that I want this to be able to accept a typed in number of seconds OR you could drag an audio clip in there and I then want my script to work out the length of the audio clip and use that as the delay.
So first of all how do I make a field accept both of those types of input
And then secondly is there any way I can test what type of input was added, i.e how to find out if it was an audio clip for example
So having a single field accept any type is a bit difficult as Unity won’t serialize it.
I created a custom type that allows defining a field that will allow any of the common unity types (any serializable prim, or a UnityEngine.Obejct like Components, GameObjects or the sort). Here you can find it:
And it’s editor:
Note, it’s a pretty have type as it accepts all sorts of things, including expressions, and properties of referenced objects. Which means it has a lot of dependencies to my existing Spacepuppy Framework. To rip it out of the framework will require a bit of reworking… so if you plan to do that, expect to do a bit of work. Or you could include the framework (its open-source, you’re free to do so).
Anyways, with that you can reference a value of a ‘variant’ type that gets serialized correctly.
Now, it’s just a matter of pulling that value out of the reference and determining what to do with it. I’m going to use another tool of mine, ConvertUtil which facilitates seamless conversion between types:
Of course you could just test if it is a float rather than use my IsNumericType method. I just figured support all numeric types.
public class MyBehaviour : MonoBehaviour
{
[SerializeField()]
private VariantReference _delay;
public void DoSomething()
{
float delay = 0f;
var obj = _delay.Value;
if(ConvertUtil.ValueIsNumericType(obj))
delay = ConvertUtil.ToSingle(obj);
else if(obj is AudioClip)
delay = (obj as AudioClip).length;
else
delay = 0f; //not a number or an AudioClip, default the value
//do stuff with delay
//...
}
}
Anyways, that’s how I’d do it.
Of course, I wouldn’t restrict myself to just AudioClip and numbers. Because VariantReference allows referencing ‘properties’ of objects. When you reference an AudioClip you could put it in property mode, and just directly reference the ‘length’ property via the drop down that appears in the inspector (if you used my inspector with it as well). And your code would be more simply:
public class MyBehaviour : MonoBehaviour
{
[SerializeField()]
private VariantReference _delay;
public void DoSomething()
{
float delay = _delay.FloatValue;
//do stuff with delay
//...
}
}
What the editor looks like:
Note: the source code for VariantRefernence implements ISerializationCallbackReceiver, this is because I recently upgraded VariantReference to support ‘Property’ and ‘Eval’ modes. It used to not do that. Due to the changes, I needed a way to make it backwards compatible with existing projects until I update those projects. That’s why it’s there. If you ripped it out of the framework, you could purge this part of it.
I also plan to add nested property references in the future. So you could get to the property of a property of the object referenced. But I just haven’t gotten around to it yet… so much on the plate after all.
You could possibly create a custom editor that has an enum selection for the type of delay input you want to use, such as Float or an Audios length, then after you select the type, you will then display the correct field to retrieve the delay value.
Example (not tested but should work. Edit - I am from the future, and no, it wont really work ^^)
Click for code
Hey lordofduct, thanks very much for your suggestions. regards the SpacePuppy framework, I am a bit confused about what its kind of for? I really dont want to spend the time ripping the files you mention out of the framework, but I’m not really sure what its use would be for me… I am creating on Windows but targeting iOS and Android (so I’ll also have to build from Mac) so does the framework work in that scenario? Also I see that the framework itself is pretty big so I am wondering what the impact will be on my finished app’s file size, will it include itself in the builds?
Oh and I am also using Unity 5, is that going to be an issue?
Just in case either of you know a better way of achieving what I am trying to achieve and why I need this (a bit off topic I know)
What I am trying to do is make a SceneEvents asset that consists of an array of SceneEvent items which themselves contain an array of SceneEventElement items, reason being that you cannot have scripts in asset bundles, so the ‘Scene Manager’ script I created when I originally constructed my scene apparently would not be allowed in an asset bundle. Its a serious pain this as what I intend to do is create lots of scenes the app user can download and play on demand.
So the reason I need what I am asking about is that (among other things) in my original script all my animations were timed to music so what I am trying to do is recreate all that as an asset. that I can then add to a ‘ScenePlayer’ script that I’ll be adding to the base app executable. Obviously I need to make all this as flexible as possible, so I dont limit what I can do in my future downloadable scenes.
But there may be another way around this, it does sort of seem like something there should be an ‘out of the box’ solution for… Any ideas?
@HiddenMonk I’m not sure how to adjust your code so it targets the actual array object I am using…
This is my structure, its got an array of SceneEvent items inside the SceneEvents item itself and its those that I need to target with the CustomEditor script you wrote
[System.Serializable]
public class SceneEvents : ScriptableObject
{
[System.Serializable]
public class SceneEvent
{
public float delay = 0;
public enum happensOnOpts{Update,Start,Interraction}
public happensOnOpts happensOn;
public SceneEventElement[] eventElements;
}
public SceneEvent[] actualEvents;
So Im not sure how I should do the following correctly in this scenario…
AudioClip and float have pretty much nothing in common. So as mentioned, there is no easy way to do this.
@lordofduct is onto something with suggesting you do some editor magic to pull the float you want in. I did a similar thing in my trend.Me project, which might help out. And it might be easier to extract from its existing framework then getting something out of space puppy. (Can you still call it a puppy? It looks pretty grown up now :))
Anyway, here is a link to the RemoteField class. And the RemoteFieldDrawer. You should be able to extract just these two files on their own.
The idea then being that you can expose a RemoteFloatField in the inspector. The field can then be filled with any object. You then get a drop down list with the members of the object. Once you choose a float as the final member then you can access the RemoteFloatField as if it were a float.
A simpler implementation would be just to use an object field and then use OnValidate to check the type of object passed in. You could then use a series of if statements based on the type to find the value of interest. If the type is incorrect, then just set the field back to null. This will appear to the user like they can’t drop the object in the slot.
@lordofduct the next thing I want to do after I have solved this delay issue is have it so that when a game object is dropped into one of my SceneEventElement slots I want to generate a dropdown of the components on that object, and then when the component is selected, generate a list of the methods that can be used on that component, and then finally generate the fields that method requires… Would SpacePuppy help me there?
It’s just a collection of tools I use to build games faster. There’s all sorts of stuff in there like RadicalCoroutine, IRadicalYieldInstruction, Singleton base, Notification system, t&i system similar to UnityEvent but with a few extra features, object identities for things like game time, scenes, collision ignore relationships, etc. The VariantReference I show here. It’s big, so I could really go into depth about all the things in there.
Yes, it works across the platforms. And its understandable if you don’t want to rip it out of the framework.
It adds about 650k in size to your build. Less than a megabyte. This would really only be a concern in web builds.
I’m upgrading to Unity 5 next week, so no, sorry it wouldn’t help right now.
Really, I was showing you this as more of a see how you can abstract such ideas. My framework of stuff is things I use ALL the time. They’re these little scenarios that I come across all the time when writing code that I wish I could just wrap up into a simple reusable class (like you want to be able to allow the designer to specify the duration based on any number of possible inputs… this is specifically what I created VariantReference for a long while back).
You wouldn’t necessarily have to use my VariantReference, but see how someone might go about doing it. It’s the reason I have spacepuppy up on git… it’s not so much for people to use it, it’s about letting people see how some of us solve our problems. A lot of beginners on forums complain about the lack of real world code in tutorials and books.
Well… that’s an entire library of code I use to make games every day.
Ok interesting… I am not too fussed about the user experience since its only going to be me using it to begin with… As I say I am only doing this because I cant add the SceneManager script I made (that plays out all the animations and music in my scene in order) to an asset bundle and have it work on iOS/Android.
If there is a better way that avoids me having to do any of this- as I say I am all ears…
I also have a more simplified attribute that you can place on a ‘Component’ field called ‘SelectableComponentAttribute’ that uses this custom PropertyDrawer that allows dropping a gameobject into an object ref field, and then select a specific component from the list. Very useful for gameobject that have lots of components on it, and you want to select a specific one. Both the “T_s” and VariantReference use this same drawer to facilitate their use.
I missed the use case for this in the earlier posts.
For that I would build something much simpler. I would simply load all of this data into a file (JSON or XML or even just a plain csv). Then I would build a simple parser that iterates over the file at runtime and rebuilds all of the references.
I dont think my previous script would really work.
I think the delay would be set, but the audioclip will not serialize so it will return to null
You would have to either serialize it manually somehow, or have a public AudioClip and delayType in your SceneEvent object so it can be serialized there.
Thanks for the update @HiddenMonk If I didn’t choose to go down the XML route (which I my not because manually writing and testing XML files might be a bit of a pain where as this would be ‘drag and drop’ once its done) I could still use something like the Custom Editor trick to hide options depending on what is selected from the enum I guess?
I still cant get the editor file to work though…my SceneEvents script is for creating a SceneEvents asset and is set to be ScriptableObject (see my code above) does that make any difference?
I’ll have a look at the SpacePuppy files and see if I can find any clues there…
From this page it looks like your script id doing things in what they cite as ‘the old way’ (the second script example), maybe I need to do it the ‘new way’ (first example)? Am trying but still cant make anything happen…