I have a method that takes any object as an argument and it should go through the properties, whatevery they are. I'm trying to get properties for a custom shader (created with ShaderForge). This is the method:
public void save(object objectToSave) {
var newProperties = new List<PropertyInfo>(objectToSave.GetType().GetProperties());
foreach (PropertyInfo property in newProperties)
{
object value = property.GetValue(objectToSave, null);
}
}
When I pass an object of type Material (I'm passing the RenderSettings.skybox in my tests) with a custom shader as an argument, I get this error:
Material doesn't have a color property '_Color'
Maybe this is because objectToSave.GetType() returns the type Material instead of the custom parameters implemented by ShaderForge shader (which doesn't implement _Color). How can I tell the method this is a Material with custom shader so it doesn't try to find the regular fields and properties? How to make it get a list of the actual properties and fields?
When I open the material in Unity, it has these parameters. How do I access them via GetProperties?
UnityEditor class has ShaderUtil, which contains the following methods:
GetPropertyCount - Get the number of properties in Shader s.
GetPropertyDescription - Get the description of the shader propery at index propertyIdx of Shader s.
GetPropertyName - Get the name of the shader propery at index propertyIdx of Shader s.
GetPropertyType - Get the ShaderProperyType of the shader propery at index propertyIdx of Shader s.
GetRangeLimits - Get Limits for a range property at index propertyIdx of Shader s.
GetTexDim - Gets texture dimension of a shader property.
IsShaderPropertyHidden - Is the shader propery at index propertyIdx of Shader s hidden?
To use it, you include "using UnityEditor" in your script and then access it by typing ShaderUtil.(methodname).
With these methods you can iterate through the custom shader properties. It's weird that you can't do this with GetProperties like you do on types, but this worked for me.
The type RANGE was a mystery to me, but what it does is it returns a float value of the actual value and ALSO the min and max values of the parameter so you know what its range is (because it's not a known type).
Please note that this is only for editor, so you have to use conditional #if XXXXX conditions around it, because UnityEditor class and this method won't work runtime.
Unity documentation on ShaderUtil: https://docs.unity3d.com/ScriptReference/ShaderUtil.html
Related
I've had trouble searching around and may be critically misunderstanding how Unity wants you to structure your project, but here is my situation:
I have characters in my game and would like to configure them in the editor. I have made classes that represent different behavior types and actions that can be performed. Is there a way for me to make a field on a script typed for these classes so that I can drag and drop using the inspector to configure different prefabs?
Here is some code demonstrating the situation:
Unit.cs
// A unit on the board.
public class Unit : MonoBehaviour
{
public UnitAction action;
}
UnitAction.cs
// A unit on the board.
public class UnitAction : MonoBehaviour
{
// Does things
}
These fields show up in the inspector once this script is applied to a prefab, but there are no options to populate the default field value. If I make the field type UnityEngine.Object, I can put the scripts there, but that seems like something I do not want.
It sounds like you are trying to serialize references to scripts themselves instead of instances of those scripts. There are a couple of ways that you may want to do this:
You could attach your UnitAction scripts as components of a GameObject that is in a context accessible to your "Unit" object (in the same scene, a child of the "Unit" object, or - probably the most common case - the "Unit" object itself). Then you will be able to serialize those instantiated components into the fields in your Unit class. This is the most common use case.
You could create a prefab for each of your UnitAction components and then serialize those prefabs into your Unit class fields. You would then instantiate a UnitAction at runtime. This doesn't really seem appropriate for your case based on what you described because a UnitAction probably isn't something that needs to be dynamically instantiated, but it is important to be aware of. This article has an example of using this method for giving a unit a list of spells (and also provides some good context on how to think about using unity components that would probably be valuable to you):
Unity: Now You're Thinking With Components
I would guess and say you do not want these scripts exposed except in editor, so you can use an attribute called SerializeField. It will expose the field to the editor so you are able to drag in references, but other scripts will not be able to access the fields you drop in.
If you want a specific script to appear in the editor, simply write
[SerializeField] private YourScriptName scriptReference = null;
As lists are generic structures, if you want, you can also make a list of scripts in a similar way
[SerializeField] private List<YourScriptName> scriptListReference = new List<YourScriptName>();
As I do not think you will be assigning these references again, if you want to grab the reference at any point, you can make a getter method.
public YourScriptName GetYourScript(){ return scriptReference;}
There are very few fields that Unity can not serialize, so generally, if you want to drag in a reference to a component, script, object, etc. all you need to do is make it either SerializeField or public followed by the type of whatever you want to drag in.
From the docs, the serialization system
CANNOT serialize static fields.
CANNOT serialize properties.
CAN serialize public non-static fields (of serializable types)
CAN serialize nonpublic non-static fields marked with the SerializeField attribute.
The fields that it can serialize are as follows
All classes inheriting from UnityEngine.Object, for example
GameObject, Component, MonoBehaviour, Texture2D, AnimationClip.
All basic data types, such as int, string, float, bool.
Some built-in types, such as Vector2, Vector3, Vector4, Quaternion, Matrix4x4, Color, Rect, LayerMask.
Arrays of a serializable type Lists of a
serializable type
Enums
Structs
I have a master class called Door, and this door has 3 variables, aimationDuration, Start Delay and an Enum with 2 options - ClosingDoor and OpeningDoor. Now, I would like to know if its possible when I choose ClosingDoor the editor will display only the animationDuration variable and hide the StartDelay variable, and then choose the openingDoor and hide the animationDuration and show the StartDelay. Is this possible or is there another way to accomplish this?
There were improvements related to MetaTags since this question was asked and answered.
Starting with UE4.23:
The EditCondition meta tag is no longer limited to a single boolean property. It is now evaluated using a full-fledged expression parser, meaning you can include a full C++ expression.
This means we can do things like disabling (or even hiding) a property based on the value of an enum UPROPERTY. Hiding can be achieved by using the EditConditionHides MetaTag. Example:
UENUM(BlueprintType)
enum class EInterpolationMode : uint8 { Linear, Curve };
USTRUCT(BlueprintType)
struct FBlendableInterpolationData
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, EditAnywhere)
EInterpolationMode InterpolationMode = EInterpolationMode::Linear;
UPROPERTY(EditAnywhere, meta = (EditCondition = "InterpolationMode == EInterpolationMode::Linear", EditConditionHides))
float Duration;
UPROPERTY(EditAnywhere, meta = (EditCondition = "InterpolationMode == EInterpolationMode::Curve", EditConditionHides))
UCurveFloat* Curve;
};
WARNING: There is a special case where EditConditionHides can crash the editor for engine versions 4.26 and 4.27.
More examples for property disabling/hiding can be found here: https://thomassampson.co.uk/posts/ue4-uproperty-editcondition/
There is infact exactly something for this, but it might require a little bit of hackery as you need ENUM values and the method seems to be for boolean values.
Unreal's metadata specifiers have a 'editcondition' specifier that lets you point to a boolean variable and say, when that variable is true, let me edit this property, and it doesn't matter what the property is it works for everything.
Here's an example from the Unreal answerhub with some code:
https://answers.unrealengine.com/questions/189864/hide-and-show-variable-in-property-window.html
If the boolean method works for you that's great, otherwise you'll need to look into overriding AActor::PostEditChangeProperty() in order to do a hack where when you change the enum values, it sets a boolean value in that function (which gets called after any change in the property window for an actor), and then should work as you need it to.
If you actually want proper hiding/showing that's more complicated and requires you to use Slate which I have no idea of but here's the documentation:
https://docs.unrealengine.com/latest/INT/Programming/Slate/DetailsCustomization/index.html
Below you see the debug for an object of type FileReference in fluid. In fluid the debug looks like this: <f:debug>{fileReference}</f:debug>
The question is how do I access the properties highlighted in green, being width, height, and hovertext.
The original file is an image, so width & height are default T3 properties, hovertext has been added by my extension with it's own getter/setter.
I tried the following:
{fileReference.width}
{fileReference.mergedProperties.width}
{fileReference.originalResource.width}
No luck so far, what is the right way to access the values in mergedProperties?
Many Thanks
Florian
The f:debug shows something similar to the var_dump function, so the properties of an object. In fluid you can only access the getter functions or if it is an array the values of the array. So if you write something like {fileReference.mergedProperties} the method getMergedProperties() is called if it is present.
Knowing that you can look inside the sysext/core/Classes/Resource/FileReference.php File and see what getters it has. We can quickly find the public function getProperties() that returns the merged properties you marked, so the right solution should be this:
{fileReference.properties.width}
Is it possible to dynamically create objects or modify them on run-time ?for example,on button click,another button created or change number of lines of a road?
When I write this code for a button Action,in run-time
road123.setBackwardLanesCount(3);
I get error below:
root:
road123: Markup element is already initiated and cannot be modified.Please use constructor without arguments,perform setup and finally call initialize() .function
You'll get that error with any object you attempt to create at runtime using a parameterized constructor. If you create the object with a simple constructor (just "()") and then set all of the parameters individually, you won't run into that issue. Check the Anylogic API for specific information about the object you are using, because some require you to call .initiliaze() on that object after setting all of it's parameters if you created it using a simple constructor. Furthermore, if you want to add the object to the screen at runtime you'll need to add this code to the function that creates it:
#Override
public void onDraw( Panel panel, Graphics2D graphics) {
obj.drawModel(panel, graphics, true);
}
where obj is replaced with the name of the object you created dynamically.
For my game, I am trying to implement a custom shader that creates a zoom blur effect (I am quite new at using shaders so bear with me if I use improper terms). I have the .fsh shader file in my project, and I'm trying to apply it using SKShader.
I create the shader with this:
let testShader = SKShader(fileNamed: "ZoomBlur.fsh");
My shader takes two uniforms as input, blurCenter (vec2) and blurSize (float), so I try adding them to my shader:
testShader.addUniform(SKUniform(name: "blurSize", float: 50);
When trying to add blurCenter, there doesn't appear to be an initializer for a vec2, just float and texture. Looking at the class reference, it looks like I should be able to use this:
testShader.addUniform(SKUniform(name: "blurCenter", floatVector2: ...);
...But Xcode throws this error:
Incorrect argument label in call (have 'name:floatVector2:', expected 'name:float:')
How can I add a vec2 uniform? Am I completely missing something?
GLKVector2 is a union type, and Swift doesn't import those as of Beta 2. Any API that requires use of a non-imported type is itself not imported. Might not be a bad idea to file a bug.
For now, you'll need to work around this by writing your own C/ObjC functions/methods that forward to the non-imported API, and calling those from Swift via a bridging header.