正确设置Unity SerializedProperty值

问题描述:

我正在使用IronPython在Unity3d项目中执行(仅编辑器)清理工作。除了一个恼人的陷阱之外,这个工作很好。正确设置Unity SerializedProperty值

我有一个Python包装类,隐藏使用SerializedProperties的C#冗长:

class PropertyProxy(object): 
    PROPTYPES = { 
     SerializedPropertyType.Integer:"intValue", 
     SerializedPropertyType.Boolean:"boolValue", 
     SerializedPropertyType.Float:"floatValue", 
     SerializedPropertyType.String:"stringValue", 
     SerializedPropertyType.Color:"colorValue", 
     SerializedPropertyType.ObjectReference:"objectReferenceValue", 
     SerializedPropertyType.LayerMask:"objectReferenceValue", 
     SerializedPropertyType.Enum:"enumValueIndex", 
     SerializedPropertyType.Vector2:"vector2Value", 
     SerializedPropertyType.Vector3:"vector3Value", 
     SerializedPropertyType.Vector4:"vector4DValue", 
     SerializedPropertyType.Rect:"rectValue", 
     SerializedPropertyType.ArraySize:"arraySize", 
     SerializedPropertyType.Character:"objectReferenceValue", 
     SerializedPropertyType.AnimationCurve:"animationCurveValue", 
     SerializedPropertyType.Bounds:"boundsValue", 
     SerializedPropertyType.Gradient:"objectReferenceValue", 
     SerializedPropertyType.Generic:"objectReferenceValue" 
    } 


    def __init__(self, owner): 
     self.owner = owner 
     self.prop_path = self.PROPTYPES[owner.propertyType] 

    def __repr__(self): 
     return self.owner.serializedObject.targetObject.name +"." + self.owner.propertyPath 


    @property 
    def value(self): 
     if self.owner.isArray: 
      return [] 
     else: 
      return getattr(self.owner, self.prop_path) 

    @value.setter 
    def set_value(self, val): 
     setattr(self.owner, self.prop_path, val) 
     self.owner.serializedObject.ApplyModifiedProperties() 

get功能工作正常,并返回正确的价值观。然而,二传手抱怨道。如果您尝试设置PropertyProxy的值,我会收到一条错误消息,声称我正试图设置只读属性。没有问题

myPropertyProxy.owner.floatValue = 2.0 

,但是当设置器试图做

setattr (myPropertyProxy.owner, 'floatValue', 2.0) 

我得到的错误: - 这不是完全正确的,我可以做到这一点。

我认为这是IronPython无法获得Unity侧属性的正确setter的某种问题,但我只是猜测。有人有更多的信息在这里发生了什么?

更新

直接发行

setattr(myProp.owner, 'floatValue', 2.0) 
myProp.owner.serializedObject.ApplyModifiedProperties() 

- 这是类 - 工程。哎呀。

更新2 如果你叫他们没有财产装饰:(

原来的问题原来是准红鲱鱼;上面贴的代码工作,如果你转换

@property 
def value(self): 
    if self.owner.isArray: 
     return [] 
    else: 
     return getattr(self.owner, self.prop_path) 

@value.setter 
def set_value(self, val): 
    setattr(self.owner, self.prop_path, val) 
    self.owner.serializedObject.ApplyModifiedProperties() 

到比较老套:

def _value(self): 
    if self.owner.isArray: 
     return [] 
    else: 
     return getattr(self.owner, self.prop_path) 

def _set_value(self, val): 
    setattr(self.owner, self.prop_path, val) 
    self.owner.serializedObject.ApplyModifiedProperties() 

value = property(fget = _value, fset = _set_value) 

我认为这代表其执行@property装饰的CPython的和IronPython之间的分歧,因为在普通的CPython两者是可以互换的。

我不能肯定完全明白你正在尝试做的,反正谈到SerializedPropertyvalueset_value职能的工作。

当你想设置的SerializedProperty(通常编写自定义代码检查员)的值,你有几种方法:

  • 使用PropertyField自动默认风格通过SerializedProperty显示它
  • 访问现场和反序列化回SerializedObject(比如你的方法)使用EditorGUI.[Begin|End]Property
  • 直接接入序列化领域
  • 裹SerializedProperty访问。

现在,每种方法都取决于特定的用例,并最终带来其缺点。要记住的主要原因是[de]序列化过程由不同的线程处理,需要知道在反序列化之前哪个SerializedProperty已被修改。

所以你的情况(如果我理解正确的话),你需要至少2个函数调用,以使它在任何情况下工作:

  1. 调用(视情况而定或UpdateIfDirtyOrScriptUpdate在修改任何财产之前。这确保您指的是一致的SerializedObject状态。
  2. 修改SerializedProperty(就像你正在做)(在你的更新等)
  3. 呼叫serializedObject.ApplyModifiedProperties

我认为这是某种形式的问题与IronPython的是不能 得到正确的二传手在Unity方面属性,但我只是 猜测。有人有更多的信息在这里发生了什么?

我不知道很多关于Python的,所以我没有一个明确的答案,但也许这是关系到一个事实,即二传手的floatValue和referes外部函数(可能在发动机的C++侧):

public extern float floatValue 
{ 
    [WrapperlessIcall] 
    [MethodImpl(MethodImplOptions.InternalCall)] 
    get; 
    [WrapperlessIcall] 
    [MethodImpl(MethodImplOptions.InternalCall)] 
    set; 
} 
+0

感谢您的回复!这里的总体目标是允许我自己对大量游戏数据(主要是ScriptableObjects)进行批量编辑 - 诸如“找到所有精灵的生命值为5并将其设置为6'。 我一直在解决即时问题,将我的属性装饰器转换为常规方法调用;我认为这必须是某种IronPython cPython delta,我以前没有见过。然而'UpdateIfDirtyOrScript'事是我之前从未见过的,这可能解释了我在其他情况下遇到的一些问题。 – theodox 2014-10-09 16:37:00

+0

一个相关的问题:对于磁盘上的资产,是否有可能只使用传统的C#反射并根本不更改SerializedProperty系统的值? – theodox 2014-10-09 16:38:03

+0

对于磁盘上的资产,您可以简单地编辑文件(如果以文本形式存储),元文件(例如用于预制件)以及最终用于预制覆盖属性的场景文件。我认为这将是单调乏味的,因为您需要解析yaml通过你自己,并尽量保持资产交叉引用consistents。 – Heisenbug 2014-10-09 17:02:28