unity-editor-custom

danmaku

尝试给我的弹幕游戏设计技能.

技能的信息写在了ScriptableObject内…

具体思路是这样的.通过timelineList中存储的时间片信息,来获取每个时刻需要释放的弹幕的信息.从而实现弹幕变换的效果.

根据timelineList中的时间片信息.获取对应的额外信息.这些额外信息会在游戏中被运用.

叭多说,show you the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Skill : ScriptableObject{
public GameObject danmaku;
public float basicDamage;
public float basicSpeed;
protected SortedSet<TimelineUnit> timelineUnits;
public List<TimelineUnit> timelineList;
[Serializable]
public class TimelineUnit:IComparable{
public float showingTime;
public GameObject danmakuReference;
public UnitType unitType;
public UnitAdditionalInfo unitAdditionalInfo;
public enum UnitType{
Flowering,
Single,
Sector,
Beam,
BeamingDanmaku
}
public int CompareTo(object other){
TimelineUnit timeLineOther =other as TimelineUnit;
if (timeLineOther != null) return (int) ((showingTime - timeLineOther.showingTime) * 100);
return 0;
}
}
[Serializable]
public class UnitAdditionalInfo{
public float speedModifier;
}
public void Trigger(){

}
}
[Serializable]
public class FloweringAdditionalInfo:Skill.UnitAdditionalInfo{
public int numbers;
public float damageModifier;
}

//。。。。

同时editor方面,根据选定的UnitType来生成自定义信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class SkillInfoDrawer : PropertyDrawer{
public int fieldNum;

public Skill.TimelineUnit.UnitType unitType;

// Start is called before the first frame update
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label){
var LabelWidth = EditorGUIUtility.labelWidth;
var targetSkill = property.serializedObject.targetObject as Skill;
var propertyPath = property.propertyPath;
var lastIndexOf = propertyPath.LastIndexOf('.');
if (lastIndexOf == -1){
Debug.LogError("Wrong path of object " + property);
return;
}
var timeUnitPath = propertyPath.Substring(0, lastIndexOf);
var timeUnit = property.serializedObject.FindProperty(timeUnitPath);
var unitTypeFromTimeUnit = property.serializedObject.FindProperty(timeUnitPath + ".unitType");
var indexOfUnitType = unitTypeFromTimeUnit.enumValueIndex;
var nameOfUnitType = unitTypeFromTimeUnit.enumNames[indexOfUnitType];
var baseName = "AdditionalInfo";
var reflectionName = nameOfUnitType + baseName;
var type = Type.GetType(reflectionName);
if (type == null){
Debug.LogError("No such reflection unit info " + reflectionName);
return;
}
var timelineUnitFromSerializedProperty = Tools.SerializedPropertyToObject<Skill.TimelineUnit>(timeUnit);
Skill.UnitAdditionalInfo unitAdditionalInfo = null;
if (timelineUnitFromSerializedProperty == null) return;
if (timelineUnitFromSerializedProperty.unitAdditionalInfo.GetType()!=type){
var constructors = type.GetConstructors();
unitAdditionalInfo = constructors[0].Invoke(null) as Skill.UnitAdditionalInfo;
timelineUnitFromSerializedProperty.unitAdditionalInfo = unitAdditionalInfo;
}
else{
unitAdditionalInfo = timelineUnitFromSerializedProperty.unitAdditionalInfo;
Debug.Log("hi");
}
var fields = type.GetFields();
var startPosition = new Rect(position);
if (unitAdditionalInfo == null) return;
foreach (var field in fields){
var labelRect = new Rect(startPosition.x, startPosition.y, LabelWidth, 16);
var valueRect = new Rect(startPosition.x + 200, startPosition.y, LabelWidth, 16);
EditorGUI.LabelField(labelRect, field.Name);
if (field.FieldType == typeof(float)){
var res = field.GetValue(unitAdditionalInfo) is float ? (float) field.GetValue(unitAdditionalInfo) : -1;
res = EditorGUI.FloatField(valueRect, res);
field.SetValue(unitAdditionalInfo, res);
property.serializedObject.ApplyModifiedProperties();
}

startPosition.y += 16;
}

fieldNum = fields.Length;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label){
return fieldNum * base.GetPropertyHeight(property, label);
}
}

在网络上看到了一段serializedProperty to custom class的代码.魔改了一部分让它能够兼容list..

我觉得这串代码效率和思路有问题.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
public static T SerializedPropertyToObject<T>(SerializedProperty property){
return GetNestedObject<T>(property.propertyPath, GetSerializedPropertyRootComponent(property),
true); //The "true" means we will also check all base classes
}

public static Object GetSerializedPropertyRootComponent(SerializedProperty property){
return property.serializedObject.targetObject;
}

public static T GetNestedObject<T>(string path, object obj, bool includeAllBases = false){
//taoria: here the code has some issues.
foreach (var part in path.Split('.')){
obj = GetFieldOrPropertyValue<object>(part, obj, includeAllBases);
Debug.Log(obj);
if (obj == null) continue;

if (obj.GetType().GetGenericTypeDefinition() == typeof(List<>)){
var first = path.LastIndexOf('[');
var last = path.LastIndexOf(']');
var index = int.Parse(path.Substring(first + 1, last - first - 1));
var list = (List<T>) obj;
return list[index];
break;
}
}

return (T) obj;
}

public static T GetFieldOrPropertyValue<T>(string fieldName, object obj, bool includeAllBases = false,
BindingFlags bindings =
BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic){
var field = obj.GetType().GetField(fieldName, bindings);
if (field != null) return (T) field.GetValue(obj);
var property = obj.GetType().GetProperty(fieldName, bindings);
if (property != null) return (T) property.GetValue(obj, null);

if (includeAllBases)
foreach (var type in GetBaseClassesAndInterfaces(obj.GetType())){
field = type.GetField(fieldName, bindings);
if (field != null) return (T) field.GetValue(obj);

property = type.GetProperty(fieldName, bindings);
if (property != null) return (T) property.GetValue(obj, null);
}

return default;
}

public static void SetFieldOrPropertyValue<T>(string fieldName, object obj, object value,
bool includeAllBases = false,
BindingFlags bindings =
BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic){
var field = obj.GetType().GetField(fieldName, bindings);
if (field != null){
field.SetValue(obj, value);
return;
}

var property = obj.GetType().GetProperty(fieldName, bindings);
if (property != null){
property.SetValue(obj, value, null);
return;
}

if (includeAllBases)
foreach (var type in GetBaseClassesAndInterfaces(obj.GetType())){
field = type.GetField(fieldName, bindings);
if (field != null){
field.SetValue(obj, value);
return;
}

property = type.GetProperty(fieldName, bindings);
if (property != null){
property.SetValue(obj, value, null);
return;
}
}
}

public static IEnumerable<Type> GetBaseClassesAndInterfaces(this Type type, bool includeSelf = false){
var allTypes = new List<Type>();

if (includeSelf) allTypes.Add(type);

if (type.BaseType == typeof(object))
allTypes.AddRange(type.GetInterfaces());
else
allTypes.AddRange(
Enumerable
.Repeat(type.BaseType, 1)
.Concat(type.GetInterfaces())
.Concat(type.BaseType.GetBaseClassesAndInterfaces())
.Distinct());
//I found this on stackoverflow

return allTypes;
}