Explore all Unity open source software, libraries, packages, source code, cloud functions and APIs.

Popular New Releases in Unity

dnSpy

v6.1.8

AirSim

v1.7.0 - Linux

ml-agents

ML-Agents Release 19

osu

2022.409.0

bullet3

PyBullet 3.22

Popular Libraries in Unity

tips

by git-tips doticonjavascriptdoticon

star image 19842 doticonMIT

Most commonly used git tips and tricks.

dnSpy

by dnSpy doticoncsharpdoticon

star image 18900 doticon

.NET debugger and assembly editor

AirSim

by microsoft doticonc++doticon

star image 12921 doticonNOASSERTION

Open source simulator for autonomous vehicles built on Unreal Engine / Unity, from Microsoft AI & Research

ml-agents

by Unity-Technologies doticoncsharpdoticon

star image 12332 doticonNOASSERTION

Unity Machine Learning Agents Toolkit

3d-game-shaders-for-beginners

by lettier doticonc++doticon

star image 11004 doticonBSD-3-Clause

🎮 A step-by-step guide to implementing SSAO, depth of field, lighting, normal mapping, and more for your 3D game.

osu

by ppy doticoncsharpdoticon

star image 9614 doticonMIT

rhythm is just a *click* away!

bullet3

by bulletphysics doticonc++doticon

star image 9090 doticonNOASSERTION

Bullet Physics SDK: real-time collision detection and multi-physics simulation for VR, games, visual effects, robotics, machine learning etc.

UnityCsReference

by Unity-Technologies doticoncsharpdoticon

star image 8960 doticonNOASSERTION

Unity C# reference source code.

DeepCopy

by myclabs doticonphpdoticon

star image 8159 doticonMIT

Create deep copies (clones) of your objects

Trending New libraries in Unity

jynew

by jynew doticoncsharpdoticon

star image 4611 doticonNOASSERTION

金庸群侠传3D重制版

open-project-1

by UnityTechnologies doticoncsharpdoticon

star image 3966 doticonApache-2.0

Unity Open Project #1: Chop Chop

puerts

by Tencent doticonc++doticon

star image 2646 doticonNOASSERTION

Write your game with TypeScript in UE4 or Unity. Puerts can be pronounced as pu-erh TS(普洱TS)

awesome-youtubers

by JoseDeFreitas doticonpythondoticon

star image 2346 doticonCC0-1.0

▶️ An awesome list containing awesome YouTubers that teach about technology.

Graphics

by Unity-Technologies doticoncsharpdoticon

star image 1628 doticonNOASSERTION

Unity Graphics - Including Scriptable Render Pipeline

GitHubGraduation-2021

by education doticonjavascriptdoticon

star image 1378 doticon

Join the GitHub Graduation Yearbook and "walk the stage" on June 5.

X-PostProcessing-Library

by QianMo doticoncsharpdoticon

star image 1293 doticonMIT

Unity Post Processing Stack Library | Unity引擎的高品质后处理库

JEngine

by JasonXuDeveloper doticoncsharpdoticon

star image 1206 doticonMIT

The solution that allows unity games update in runtime. 使Unity开发的游戏支持热更新的解决方案。

orbiter

by orbitersim doticonc++doticon

star image 1178 doticonNOASSERTION

Open-source repository of Orbiter Space Flight Simulator

Top Authors in Unity

1

keijiro

287 Libraries

star icon41824

2

svn2github

200 Libraries

star icon1928

3

baba-s

194 Libraries

star icon1473

4

Unity-Technologies

140 Libraries

star icon70479

5

microsoft

70 Libraries

star icon28622

6

PacktPublishing

50 Libraries

star icon1006

7

XJINE

47 Libraries

star icon415

8

Brackeys

40 Libraries

star icon3139

9

mattatz

37 Libraries

star icon4297

10

dilmerv

32 Libraries

star icon1200

1

287 Libraries

star icon41824

2

200 Libraries

star icon1928

3

194 Libraries

star icon1473

4

140 Libraries

star icon70479

5

70 Libraries

star icon28622

6

50 Libraries

star icon1006

7

47 Libraries

star icon415

8

40 Libraries

star icon3139

9

37 Libraries

star icon4297

10

32 Libraries

star icon1200

Trending Kits in Unity

No Trending Kits are available at this moment for Unity

Trending Discussions on Unity

How can I make a constant static immutable instance of a class?

Unity3D Display a UnityEvent Corrrectly in a ReorderableList

How do I avoid async void?

Converting Numbers from Base 10 to Base 60

React unity WebGL not working on React JS project

Unity missing reference at Library/PackageCache/com.unity.collab-proxy@1.2.16

Why is my character's horizontal velocity lost when jumping?

How do I replace a switch statement over an enum with runtime-dynamic type-based generic dispatch in C#?

Make the distance between neighbor notes the same (Theremin like app)

Extracting the measurement unit (degrees, metres, etc.) from spatial data in R

QUESTION

How can I make a constant static immutable instance of a class?

Asked 2022-Mar-07 at 22:32

I have a simple class that represents 3 dimensional coordinates called Coord3 that simply has x, y, and z int values.

I want to declare a static constant variable Coord3.zero where x, y, and z are set to 0.

I have attempted this with:

1public static readonly Coord3 zero = new Coord3(0, 0, 0);
2

However I found that this variable can be changed. For example if I do

1public static readonly Coord3 zero = new Coord3(0, 0, 0);
2Coord3 coord = Coord3.zero;
3coord.x = 5;
4

this actually changes the x value of Coord3.zero to be 5. Maybe I am misunderstanding readonly? I know that in Unity there is Vector3.zero which never changes. I am trying to achieve the same effect.

ANSWER

Answered 2022-Mar-07 at 21:41

readonly is not quite the same thing as immutable in the sense you mean. readonly means you cannot assign to a variable. So you couldn't do

1public static readonly Coord3 zero = new Coord3(0, 0, 0);
2Coord3 coord = Coord3.zero;
3coord.x = 5;
4public static readonly Cord3 zero = new Cord3(0, 0, 0);
5
6zero = new Cord3(0, 0, 1);
7

In order to achieve the effect you want, you could need to create a class, struct or record with readonly properties or fields. There's no way to achieve that effect with a type defined in an internal library. If the type allows mutability on a field or property, that field or property is mutable.

Source https://stackoverflow.com/questions/71387522

QUESTION

Unity3D Display a UnityEvent Corrrectly in a ReorderableList

Asked 2022-Mar-02 at 07:42

I am trying to make a custom inspector for my sequence class. The idea is to allow the user to configure various UnityEvents that get called at the start or the end of a sequence.

I want to have a collection of sequences in a ReorderableList so that it is highly configurable inside the inspector.

I am very close to a solution but i am fairly inexperienced with editor scripting. My scripts are almost working correctly. But I still need to solve the best way to dynamically adjust the vertical height of each item, in the DrawListItem method, and the total vertical height in the ElementHeight Method.

I am considering trying to deserialize the unity events so that i can use the GetPersistentEventCount method to get an idea of the required vertical height, but this seems like it is probably overkill. I suspect that there must be a simpler way to retrieve this data.

Currently when i add multiple items to the sequence I am getting what is pictured below, where the event fields overlap each other and the add/remove buttons are beneath the lower Unity Event.

Does anyone know the best way to resolve this?

enter image description here

1using UnityEngine;
2using UnityEditor;
3using UnityEditorInternal;
4using System;
5using UnityEngine.Events;
6
7[CustomEditor(typeof(SequenceManager))]
8public class SequenceManagerEditor : Editor
9{
10    SerializedProperty Sequences;
11
12    ReorderableList list;
13    private void OnEnable()
14    {
15        Sequences = serializedObject.FindProperty("Sequences");
16        list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
17        list.drawElementCallback = DrawListItems;
18        list.drawHeaderCallback = DrawHeader;
19        list.elementHeightCallback = ElementHeight;
20    }
21
22    //Draws the elements in the list
23    void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
24    {
25        SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
26        
27
28        ////NAME
29        EditorGUI.LabelField(new Rect(
30            rect.x, 
31            rect.y + EditorGUIUtility.standardVerticalSpacing, 
32            50, 
33            EditorGUIUtility.singleLineHeight), "Name");
34        EditorGUI.PropertyField(
35            new Rect(
36                rect.x + 50, 
37                rect.y + EditorGUIUtility.standardVerticalSpacing, 
38                rect.width - 50, 
39                EditorGUIUtility.singleLineHeight),
40            element.FindPropertyRelative("Name"),
41            GUIContent.none
42            );
43
44        //ON INIT
45            EditorGUI.LabelField(new Rect(
46            rect.x, 
47            rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
48            50, 
49            EditorGUIUtility.singleLineHeight), "OnInit");
50        EditorGUI.PropertyField(new Rect(
51                rect.x + 50, 
52                rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
53                rect.width - 50, 
54                3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
55            element.FindPropertyRelative("OnInit"),
56            GUIContent.none);
57
58        //ON DONE
59        EditorGUI.LabelField(new Rect(
60            rect.x, 
61            rect.y + 7 * EditorGUIUtility.singleLineHeight, 
62            50, 
63            EditorGUIUtility.singleLineHeight), "OnDone");
64        EditorGUI.PropertyField(
65            new Rect(
66                rect.x + 50, 
67                rect.y + 7 * EditorGUIUtility.singleLineHeight, 
68                rect.width - 50, 
69                3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
70               element.FindPropertyRelative("OnDone"),
71            GUIContent.none);
72
73        SerializedProperty indexProperty = element.FindPropertyRelative("index");
74        indexProperty.intValue = index;
75    }
76
77    private float ElementHeight(int index)
78    {
79        return (13 * EditorGUIUtility.singleLineHeight);
80    }
81
82    //Draws the header
83    void DrawHeader(Rect rect)
84    {
85        string name = "Sequences";
86        EditorGUI.LabelField(rect, name);
87    }
88
89
90    public override void OnInspectorGUI()
91    {
92        //base.OnInspectorGUI();
93        serializedObject.Update();
94        this.list.DoLayoutList();
95        serializedObject.ApplyModifiedProperties();
96     }    
97 }
98

for the sake of completeness, I've also added the Sequence class and SequenceManager class below.

1using UnityEngine;
2using UnityEditor;
3using UnityEditorInternal;
4using System;
5using UnityEngine.Events;
6
7[CustomEditor(typeof(SequenceManager))]
8public class SequenceManagerEditor : Editor
9{
10    SerializedProperty Sequences;
11
12    ReorderableList list;
13    private void OnEnable()
14    {
15        Sequences = serializedObject.FindProperty("Sequences");
16        list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
17        list.drawElementCallback = DrawListItems;
18        list.drawHeaderCallback = DrawHeader;
19        list.elementHeightCallback = ElementHeight;
20    }
21
22    //Draws the elements in the list
23    void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
24    {
25        SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
26        
27
28        ////NAME
29        EditorGUI.LabelField(new Rect(
30            rect.x, 
31            rect.y + EditorGUIUtility.standardVerticalSpacing, 
32            50, 
33            EditorGUIUtility.singleLineHeight), "Name");
34        EditorGUI.PropertyField(
35            new Rect(
36                rect.x + 50, 
37                rect.y + EditorGUIUtility.standardVerticalSpacing, 
38                rect.width - 50, 
39                EditorGUIUtility.singleLineHeight),
40            element.FindPropertyRelative("Name"),
41            GUIContent.none
42            );
43
44        //ON INIT
45            EditorGUI.LabelField(new Rect(
46            rect.x, 
47            rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
48            50, 
49            EditorGUIUtility.singleLineHeight), "OnInit");
50        EditorGUI.PropertyField(new Rect(
51                rect.x + 50, 
52                rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
53                rect.width - 50, 
54                3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
55            element.FindPropertyRelative("OnInit"),
56            GUIContent.none);
57
58        //ON DONE
59        EditorGUI.LabelField(new Rect(
60            rect.x, 
61            rect.y + 7 * EditorGUIUtility.singleLineHeight, 
62            50, 
63            EditorGUIUtility.singleLineHeight), "OnDone");
64        EditorGUI.PropertyField(
65            new Rect(
66                rect.x + 50, 
67                rect.y + 7 * EditorGUIUtility.singleLineHeight, 
68                rect.width - 50, 
69                3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
70               element.FindPropertyRelative("OnDone"),
71            GUIContent.none);
72
73        SerializedProperty indexProperty = element.FindPropertyRelative("index");
74        indexProperty.intValue = index;
75    }
76
77    private float ElementHeight(int index)
78    {
79        return (13 * EditorGUIUtility.singleLineHeight);
80    }
81
82    //Draws the header
83    void DrawHeader(Rect rect)
84    {
85        string name = "Sequences";
86        EditorGUI.LabelField(rect, name);
87    }
88
89
90    public override void OnInspectorGUI()
91    {
92        //base.OnInspectorGUI();
93        serializedObject.Update();
94        this.list.DoLayoutList();
95        serializedObject.ApplyModifiedProperties();
96     }    
97 }
98using UnityEngine;
99using UnityEditor;
100using System;
101using UnityEngine.Events;
102
103[Serializable]
104public class Sequence
105{
106    public string Name;
107    public UnityEvent OnInit;
108    public UnityEvent OnDone;
109    private Module _module;
110    public int index;
111    private bool active;
112
113    //Called By The Sequence Manager At the start of a sequence 
114    internal void Init(Module p_module)
115    {
116        Debug.Log($"sequence: {Name} with index: {index} has started");
117        active = true;
118        _module = p_module;
119         if(OnInit.HasNoListners())
120        {
121            Done();
122        } 
123        else
124        {
125            OnInit.Invoke();
126        }
127    }
128    
129    
130    //Called Manually to Trigger the End of the Sequence
131    internal void Done()
132    {
133        if (!OnDone.HasNoListners())
134        {
135            OnDone.Invoke();
136        }
137        active = false;
138        Debug.Log($"sequence: {Name} with index: {index} is done");
139        _module.FinishedSequence(index);
140    }
141
142    //Check if active
143    internal bool GetActive()
144    {
145        return active;
146    }
147}
148
149
1using UnityEngine;
2using UnityEditor;
3using UnityEditorInternal;
4using System;
5using UnityEngine.Events;
6
7[CustomEditor(typeof(SequenceManager))]
8public class SequenceManagerEditor : Editor
9{
10    SerializedProperty Sequences;
11
12    ReorderableList list;
13    private void OnEnable()
14    {
15        Sequences = serializedObject.FindProperty("Sequences");
16        list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
17        list.drawElementCallback = DrawListItems;
18        list.drawHeaderCallback = DrawHeader;
19        list.elementHeightCallback = ElementHeight;
20    }
21
22    //Draws the elements in the list
23    void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
24    {
25        SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
26        
27
28        ////NAME
29        EditorGUI.LabelField(new Rect(
30            rect.x, 
31            rect.y + EditorGUIUtility.standardVerticalSpacing, 
32            50, 
33            EditorGUIUtility.singleLineHeight), "Name");
34        EditorGUI.PropertyField(
35            new Rect(
36                rect.x + 50, 
37                rect.y + EditorGUIUtility.standardVerticalSpacing, 
38                rect.width - 50, 
39                EditorGUIUtility.singleLineHeight),
40            element.FindPropertyRelative("Name"),
41            GUIContent.none
42            );
43
44        //ON INIT
45            EditorGUI.LabelField(new Rect(
46            rect.x, 
47            rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
48            50, 
49            EditorGUIUtility.singleLineHeight), "OnInit");
50        EditorGUI.PropertyField(new Rect(
51                rect.x + 50, 
52                rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
53                rect.width - 50, 
54                3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
55            element.FindPropertyRelative("OnInit"),
56            GUIContent.none);
57
58        //ON DONE
59        EditorGUI.LabelField(new Rect(
60            rect.x, 
61            rect.y + 7 * EditorGUIUtility.singleLineHeight, 
62            50, 
63            EditorGUIUtility.singleLineHeight), "OnDone");
64        EditorGUI.PropertyField(
65            new Rect(
66                rect.x + 50, 
67                rect.y + 7 * EditorGUIUtility.singleLineHeight, 
68                rect.width - 50, 
69                3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
70               element.FindPropertyRelative("OnDone"),
71            GUIContent.none);
72
73        SerializedProperty indexProperty = element.FindPropertyRelative("index");
74        indexProperty.intValue = index;
75    }
76
77    private float ElementHeight(int index)
78    {
79        return (13 * EditorGUIUtility.singleLineHeight);
80    }
81
82    //Draws the header
83    void DrawHeader(Rect rect)
84    {
85        string name = "Sequences";
86        EditorGUI.LabelField(rect, name);
87    }
88
89
90    public override void OnInspectorGUI()
91    {
92        //base.OnInspectorGUI();
93        serializedObject.Update();
94        this.list.DoLayoutList();
95        serializedObject.ApplyModifiedProperties();
96     }    
97 }
98using UnityEngine;
99using UnityEditor;
100using System;
101using UnityEngine.Events;
102
103[Serializable]
104public class Sequence
105{
106    public string Name;
107    public UnityEvent OnInit;
108    public UnityEvent OnDone;
109    private Module _module;
110    public int index;
111    private bool active;
112
113    //Called By The Sequence Manager At the start of a sequence 
114    internal void Init(Module p_module)
115    {
116        Debug.Log($"sequence: {Name} with index: {index} has started");
117        active = true;
118        _module = p_module;
119         if(OnInit.HasNoListners())
120        {
121            Done();
122        } 
123        else
124        {
125            OnInit.Invoke();
126        }
127    }
128    
129    
130    //Called Manually to Trigger the End of the Sequence
131    internal void Done()
132    {
133        if (!OnDone.HasNoListners())
134        {
135            OnDone.Invoke();
136        }
137        active = false;
138        Debug.Log($"sequence: {Name} with index: {index} is done");
139        _module.FinishedSequence(index);
140    }
141
142    //Check if active
143    internal bool GetActive()
144    {
145        return active;
146    }
147}
148
149using System;
150namespace UnityEngine
151{
152    [Serializable]
153    public class SequenceManager: MonoBehaviour
154    {
155       
156        #region Properties
157        public Sequence[] Sequences;
158        #endregion
159    }
160}
161

ANSWER

Answered 2022-Mar-02 at 03:49

It's possible to access public fields of Sequence by casting the elements of serializedObject.targetObjects in your editor script. You can also use serializedObject.targetObject or Editor.target if you aren't using [CanEditMultipleObjects].

1using UnityEngine;
2using UnityEditor;
3using UnityEditorInternal;
4using System;
5using UnityEngine.Events;
6
7[CustomEditor(typeof(SequenceManager))]
8public class SequenceManagerEditor : Editor
9{
10    SerializedProperty Sequences;
11
12    ReorderableList list;
13    private void OnEnable()
14    {
15        Sequences = serializedObject.FindProperty("Sequences");
16        list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
17        list.drawElementCallback = DrawListItems;
18        list.drawHeaderCallback = DrawHeader;
19        list.elementHeightCallback = ElementHeight;
20    }
21
22    //Draws the elements in the list
23    void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
24    {
25        SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
26        
27
28        ////NAME
29        EditorGUI.LabelField(new Rect(
30            rect.x, 
31            rect.y + EditorGUIUtility.standardVerticalSpacing, 
32            50, 
33            EditorGUIUtility.singleLineHeight), "Name");
34        EditorGUI.PropertyField(
35            new Rect(
36                rect.x + 50, 
37                rect.y + EditorGUIUtility.standardVerticalSpacing, 
38                rect.width - 50, 
39                EditorGUIUtility.singleLineHeight),
40            element.FindPropertyRelative("Name"),
41            GUIContent.none
42            );
43
44        //ON INIT
45            EditorGUI.LabelField(new Rect(
46            rect.x, 
47            rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
48            50, 
49            EditorGUIUtility.singleLineHeight), "OnInit");
50        EditorGUI.PropertyField(new Rect(
51                rect.x + 50, 
52                rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
53                rect.width - 50, 
54                3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
55            element.FindPropertyRelative("OnInit"),
56            GUIContent.none);
57
58        //ON DONE
59        EditorGUI.LabelField(new Rect(
60            rect.x, 
61            rect.y + 7 * EditorGUIUtility.singleLineHeight, 
62            50, 
63            EditorGUIUtility.singleLineHeight), "OnDone");
64        EditorGUI.PropertyField(
65            new Rect(
66                rect.x + 50, 
67                rect.y + 7 * EditorGUIUtility.singleLineHeight, 
68                rect.width - 50, 
69                3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
70               element.FindPropertyRelative("OnDone"),
71            GUIContent.none);
72
73        SerializedProperty indexProperty = element.FindPropertyRelative("index");
74        indexProperty.intValue = index;
75    }
76
77    private float ElementHeight(int index)
78    {
79        return (13 * EditorGUIUtility.singleLineHeight);
80    }
81
82    //Draws the header
83    void DrawHeader(Rect rect)
84    {
85        string name = "Sequences";
86        EditorGUI.LabelField(rect, name);
87    }
88
89
90    public override void OnInspectorGUI()
91    {
92        //base.OnInspectorGUI();
93        serializedObject.Update();
94        this.list.DoLayoutList();
95        serializedObject.ApplyModifiedProperties();
96     }    
97 }
98using UnityEngine;
99using UnityEditor;
100using System;
101using UnityEngine.Events;
102
103[Serializable]
104public class Sequence
105{
106    public string Name;
107    public UnityEvent OnInit;
108    public UnityEvent OnDone;
109    private Module _module;
110    public int index;
111    private bool active;
112
113    //Called By The Sequence Manager At the start of a sequence 
114    internal void Init(Module p_module)
115    {
116        Debug.Log($"sequence: {Name} with index: {index} has started");
117        active = true;
118        _module = p_module;
119         if(OnInit.HasNoListners())
120        {
121            Done();
122        } 
123        else
124        {
125            OnInit.Invoke();
126        }
127    }
128    
129    
130    //Called Manually to Trigger the End of the Sequence
131    internal void Done()
132    {
133        if (!OnDone.HasNoListners())
134        {
135            OnDone.Invoke();
136        }
137        active = false;
138        Debug.Log($"sequence: {Name} with index: {index} is done");
139        _module.FinishedSequence(index);
140    }
141
142    //Check if active
143    internal bool GetActive()
144    {
145        return active;
146    }
147}
148
149using System;
150namespace UnityEngine
151{
152    [Serializable]
153    public class SequenceManager: MonoBehaviour
154    {
155       
156        #region Properties
157        public Sequence[] Sequences;
158        #endregion
159    }
160}
161if(target is not SequenceManager manager)
162{
163    //target was not a SequenceManager, the manager variable will be null
164    return;
165}
166

and within ElementHeight():

1using UnityEngine;
2using UnityEditor;
3using UnityEditorInternal;
4using System;
5using UnityEngine.Events;
6
7[CustomEditor(typeof(SequenceManager))]
8public class SequenceManagerEditor : Editor
9{
10    SerializedProperty Sequences;
11
12    ReorderableList list;
13    private void OnEnable()
14    {
15        Sequences = serializedObject.FindProperty("Sequences");
16        list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
17        list.drawElementCallback = DrawListItems;
18        list.drawHeaderCallback = DrawHeader;
19        list.elementHeightCallback = ElementHeight;
20    }
21
22    //Draws the elements in the list
23    void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
24    {
25        SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
26        
27
28        ////NAME
29        EditorGUI.LabelField(new Rect(
30            rect.x, 
31            rect.y + EditorGUIUtility.standardVerticalSpacing, 
32            50, 
33            EditorGUIUtility.singleLineHeight), "Name");
34        EditorGUI.PropertyField(
35            new Rect(
36                rect.x + 50, 
37                rect.y + EditorGUIUtility.standardVerticalSpacing, 
38                rect.width - 50, 
39                EditorGUIUtility.singleLineHeight),
40            element.FindPropertyRelative("Name"),
41            GUIContent.none
42            );
43
44        //ON INIT
45            EditorGUI.LabelField(new Rect(
46            rect.x, 
47            rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
48            50, 
49            EditorGUIUtility.singleLineHeight), "OnInit");
50        EditorGUI.PropertyField(new Rect(
51                rect.x + 50, 
52                rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
53                rect.width - 50, 
54                3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
55            element.FindPropertyRelative("OnInit"),
56            GUIContent.none);
57
58        //ON DONE
59        EditorGUI.LabelField(new Rect(
60            rect.x, 
61            rect.y + 7 * EditorGUIUtility.singleLineHeight, 
62            50, 
63            EditorGUIUtility.singleLineHeight), "OnDone");
64        EditorGUI.PropertyField(
65            new Rect(
66                rect.x + 50, 
67                rect.y + 7 * EditorGUIUtility.singleLineHeight, 
68                rect.width - 50, 
69                3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
70               element.FindPropertyRelative("OnDone"),
71            GUIContent.none);
72
73        SerializedProperty indexProperty = element.FindPropertyRelative("index");
74        indexProperty.intValue = index;
75    }
76
77    private float ElementHeight(int index)
78    {
79        return (13 * EditorGUIUtility.singleLineHeight);
80    }
81
82    //Draws the header
83    void DrawHeader(Rect rect)
84    {
85        string name = "Sequences";
86        EditorGUI.LabelField(rect, name);
87    }
88
89
90    public override void OnInspectorGUI()
91    {
92        //base.OnInspectorGUI();
93        serializedObject.Update();
94        this.list.DoLayoutList();
95        serializedObject.ApplyModifiedProperties();
96     }    
97 }
98using UnityEngine;
99using UnityEditor;
100using System;
101using UnityEngine.Events;
102
103[Serializable]
104public class Sequence
105{
106    public string Name;
107    public UnityEvent OnInit;
108    public UnityEvent OnDone;
109    private Module _module;
110    public int index;
111    private bool active;
112
113    //Called By The Sequence Manager At the start of a sequence 
114    internal void Init(Module p_module)
115    {
116        Debug.Log($"sequence: {Name} with index: {index} has started");
117        active = true;
118        _module = p_module;
119         if(OnInit.HasNoListners())
120        {
121            Done();
122        } 
123        else
124        {
125            OnInit.Invoke();
126        }
127    }
128    
129    
130    //Called Manually to Trigger the End of the Sequence
131    internal void Done()
132    {
133        if (!OnDone.HasNoListners())
134        {
135            OnDone.Invoke();
136        }
137        active = false;
138        Debug.Log($"sequence: {Name} with index: {index} is done");
139        _module.FinishedSequence(index);
140    }
141
142    //Check if active
143    internal bool GetActive()
144    {
145        return active;
146    }
147}
148
149using System;
150namespace UnityEngine
151{
152    [Serializable]
153    public class SequenceManager: MonoBehaviour
154    {
155       
156        #region Properties
157        public Sequence[] Sequences;
158        #endregion
159    }
160}
161if(target is not SequenceManager manager)
162{
163    //target was not a SequenceManager, the manager variable will be null
164    return;
165}
166var sequence = manager.Sequences[index];
167var size = sequence.OnInit.GetPersistentEventCount()
168         + sequence.OnDone.GetPersistentEventCount();
169

Edit - After trying out some things, I learned that you can get the array size through the serialized property. You can just use this in ElementHeight():

1using UnityEngine;
2using UnityEditor;
3using UnityEditorInternal;
4using System;
5using UnityEngine.Events;
6
7[CustomEditor(typeof(SequenceManager))]
8public class SequenceManagerEditor : Editor
9{
10    SerializedProperty Sequences;
11
12    ReorderableList list;
13    private void OnEnable()
14    {
15        Sequences = serializedObject.FindProperty("Sequences");
16        list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
17        list.drawElementCallback = DrawListItems;
18        list.drawHeaderCallback = DrawHeader;
19        list.elementHeightCallback = ElementHeight;
20    }
21
22    //Draws the elements in the list
23    void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
24    {
25        SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
26        
27
28        ////NAME
29        EditorGUI.LabelField(new Rect(
30            rect.x, 
31            rect.y + EditorGUIUtility.standardVerticalSpacing, 
32            50, 
33            EditorGUIUtility.singleLineHeight), "Name");
34        EditorGUI.PropertyField(
35            new Rect(
36                rect.x + 50, 
37                rect.y + EditorGUIUtility.standardVerticalSpacing, 
38                rect.width - 50, 
39                EditorGUIUtility.singleLineHeight),
40            element.FindPropertyRelative("Name"),
41            GUIContent.none
42            );
43
44        //ON INIT
45            EditorGUI.LabelField(new Rect(
46            rect.x, 
47            rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
48            50, 
49            EditorGUIUtility.singleLineHeight), "OnInit");
50        EditorGUI.PropertyField(new Rect(
51                rect.x + 50, 
52                rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3, 
53                rect.width - 50, 
54                3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
55            element.FindPropertyRelative("OnInit"),
56            GUIContent.none);
57
58        //ON DONE
59        EditorGUI.LabelField(new Rect(
60            rect.x, 
61            rect.y + 7 * EditorGUIUtility.singleLineHeight, 
62            50, 
63            EditorGUIUtility.singleLineHeight), "OnDone");
64        EditorGUI.PropertyField(
65            new Rect(
66                rect.x + 50, 
67                rect.y + 7 * EditorGUIUtility.singleLineHeight, 
68                rect.width - 50, 
69                3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
70               element.FindPropertyRelative("OnDone"),
71            GUIContent.none);
72
73        SerializedProperty indexProperty = element.FindPropertyRelative("index");
74        indexProperty.intValue = index;
75    }
76
77    private float ElementHeight(int index)
78    {
79        return (13 * EditorGUIUtility.singleLineHeight);
80    }
81
82    //Draws the header
83    void DrawHeader(Rect rect)
84    {
85        string name = "Sequences";
86        EditorGUI.LabelField(rect, name);
87    }
88
89
90    public override void OnInspectorGUI()
91    {
92        //base.OnInspectorGUI();
93        serializedObject.Update();
94        this.list.DoLayoutList();
95        serializedObject.ApplyModifiedProperties();
96     }    
97 }
98using UnityEngine;
99using UnityEditor;
100using System;
101using UnityEngine.Events;
102
103[Serializable]
104public class Sequence
105{
106    public string Name;
107    public UnityEvent OnInit;
108    public UnityEvent OnDone;
109    private Module _module;
110    public int index;
111    private bool active;
112
113    //Called By The Sequence Manager At the start of a sequence 
114    internal void Init(Module p_module)
115    {
116        Debug.Log($"sequence: {Name} with index: {index} has started");
117        active = true;
118        _module = p_module;
119         if(OnInit.HasNoListners())
120        {
121            Done();
122        } 
123        else
124        {
125            OnInit.Invoke();
126        }
127    }
128    
129    
130    //Called Manually to Trigger the End of the Sequence
131    internal void Done()
132    {
133        if (!OnDone.HasNoListners())
134        {
135            OnDone.Invoke();
136        }
137        active = false;
138        Debug.Log($"sequence: {Name} with index: {index} is done");
139        _module.FinishedSequence(index);
140    }
141
142    //Check if active
143    internal bool GetActive()
144    {
145        return active;
146    }
147}
148
149using System;
150namespace UnityEngine
151{
152    [Serializable]
153    public class SequenceManager: MonoBehaviour
154    {
155       
156        #region Properties
157        public Sequence[] Sequences;
158        #endregion
159    }
160}
161if(target is not SequenceManager manager)
162{
163    //target was not a SequenceManager, the manager variable will be null
164    return;
165}
166var sequence = manager.Sequences[index];
167var size = sequence.OnInit.GetPersistentEventCount()
168         + sequence.OnDone.GetPersistentEventCount();
169var element = list.serializedProperty.GetArrayElementAtIndex(index);
170var size = element.FindPropertyRelative("OnInit.m_PersistentCalls.m_Calls").arraySize
171         + element.FindPropertyRelative("OnDone.m_PersistentCalls.m_Calls").arraySize;
172

Source https://stackoverflow.com/questions/71316272

QUESTION

How do I avoid async void?

Asked 2022-Feb-03 at 13:24

Note: Turns out this issue is specific to Unity.

I read that async void was to be avoided. I am trying to do so using Result, but my application keeps locking up. How can I avoid using async void?

1public async void PrintNumberWithAwait()
2{
3    int number = await GetNumber();
4    Debug.Log(number); //Successfully prints "5"
5}
6
7public void PrintNumberWithResult()
8{
9    int number = GetNumber().Result;
10    Debug.Log(number); //Application Freezes
11}
12
13private async Task<int> GetNumber()
14{
15    await Task.Delay(1000);
16    return 5;
17}
18

I thought this was correct, but I must be missing something. How do I use async/await without having async void?

I ran my tests separately with the following code (commented one out at a time):

1public async void PrintNumberWithAwait()
2{
3    int number = await GetNumber();
4    Debug.Log(number); //Successfully prints "5"
5}
6
7public void PrintNumberWithResult()
8{
9    int number = GetNumber().Result;
10    Debug.Log(number); //Application Freezes
11}
12
13private async Task<int> GetNumber()
14{
15    await Task.Delay(1000);
16    return 5;
17}
18PrintNumberWithAwait();
19PrintNumberWithResult();
20

ANSWER

Answered 2022-Jan-11 at 08:34

You misunderstood what is meant by the async void that is to be avoided.

It doesn't mean you should never use a task with no result attached. It just says the asynchronous methods that invoke them should return a Task, not void.

Simply take the signature of your async method from

1public async void PrintNumberWithAwait()
2{
3    int number = await GetNumber();
4    Debug.Log(number); //Successfully prints "5"
5}
6
7public void PrintNumberWithResult()
8{
9    int number = GetNumber().Result;
10    Debug.Log(number); //Application Freezes
11}
12
13private async Task<int> GetNumber()
14{
15    await Task.Delay(1000);
16    return 5;
17}
18PrintNumberWithAwait();
19PrintNumberWithResult();
20public async void PrintNumberWithAwait()
21

and replace void with Task

1public async void PrintNumberWithAwait()
2{
3    int number = await GetNumber();
4    Debug.Log(number); //Successfully prints "5"
5}
6
7public void PrintNumberWithResult()
8{
9    int number = GetNumber().Result;
10    Debug.Log(number); //Application Freezes
11}
12
13private async Task<int> GetNumber()
14{
15    await Task.Delay(1000);
16    return 5;
17}
18PrintNumberWithAwait();
19PrintNumberWithResult();
20public async void PrintNumberWithAwait()
21public async Task PrintNumberWithAwait()
22{
23    int number = await GetNumber();
24    Debug.Log(number); //Successfully prints "5"
25}
26
27

Now calling methods have the option of awaiting the result, when and if they choose to. Either:

1public async void PrintNumberWithAwait()
2{
3    int number = await GetNumber();
4    Debug.Log(number); //Successfully prints "5"
5}
6
7public void PrintNumberWithResult()
8{
9    int number = GetNumber().Result;
10    Debug.Log(number); //Application Freezes
11}
12
13private async Task<int> GetNumber()
14{
15    await Task.Delay(1000);
16    return 5;
17}
18PrintNumberWithAwait();
19PrintNumberWithResult();
20public async void PrintNumberWithAwait()
21public async Task PrintNumberWithAwait()
22{
23    int number = await GetNumber();
24    Debug.Log(number); //Successfully prints "5"
25}
26
27await PrintNumberWithAwait();
28

Or

1public async void PrintNumberWithAwait()
2{
3    int number = await GetNumber();
4    Debug.Log(number); //Successfully prints "5"
5}
6
7public void PrintNumberWithResult()
8{
9    int number = GetNumber().Result;
10    Debug.Log(number); //Application Freezes
11}
12
13private async Task<int> GetNumber()
14{
15    await Task.Delay(1000);
16    return 5;
17}
18PrintNumberWithAwait();
19PrintNumberWithResult();
20public async void PrintNumberWithAwait()
21public async Task PrintNumberWithAwait()
22{
23    int number = await GetNumber();
24    Debug.Log(number); //Successfully prints "5"
25}
26
27await PrintNumberWithAwait();
28Task t = PrintNumberWithAwait();
29// Do other stuff
30// ...
31await t;
32

Source https://stackoverflow.com/questions/70660049

QUESTION

Converting Numbers from Base 10 to Base 60

Asked 2022-Jan-31 at 05:15

Recently, I was reading about the Ancient Babylonian Civilization that used a number system with base 60 instead of base 10. Even with this number system at base 60, they were still able to approximate the square root of 2 — and that too, thousands of years ago!

enter image description here

I was curious about this, and wanted to see how numbers from our decimal system (base 10) can be converted into the sexagesimal system (base 60). Using the R programming language, I found this link in which an answer is provided on converting numbers from some base to a different base.

However, it seems here that the base can only be between 2 and 36 (I want base 60):

1base <- function(b, base = 10)
2{
3  base <- as.integer(base)
4  if(base > 36 | base < 2) stop("'base' must be between 2 and 36.")
5  
6  structure(lapply(b, function(x) 
7    {
8      n   <- ceiling(log(x, base))
9      vec <- numeric()
10      val <- x
11      
12      while(n >= 0)
13      {
14        rem <- val %/% base^n
15        val <- val - rem * base^n
16        vec <- c(vec, rem)
17        n <- n - 1
18      }
19      
20      while(vec[1] == 0 & length(vec) > 1) vec <- vec[-1]
21      structure(x, base = base, representation = vec) 
22    }), class = "base")
23}
24

The article that I linked to reads in the headline "One eighth equals seven and thirty in this strange base 60 world" - I would like to see this and convert "1/8" from the decimal system into "7 and 30" in the sexagesimal system.

Can someone please help me with this?

ANSWER

Answered 2022-Jan-30 at 20:41

The code as given almost works. The limitation to bases < 36 is only there because the original author wanted to express the values with the symbols [0-9A-Z]. Removing that limitation and extending the algorithm to allow extra digits 'after the decimal point' (or 'after the sexagesimal point' in the case of base 60 :-) ) we get something that almost works (function definition below):

1base &lt;- function(b, base = 10)
2{
3  base &lt;- as.integer(base)
4  if(base &gt; 36 | base &lt; 2) stop(&quot;'base' must be between 2 and 36.&quot;)
5  
6  structure(lapply(b, function(x) 
7    {
8      n   &lt;- ceiling(log(x, base))
9      vec &lt;- numeric()
10      val &lt;- x
11      
12      while(n &gt;= 0)
13      {
14        rem &lt;- val %/% base^n
15        val &lt;- val - rem * base^n
16        vec &lt;- c(vec, rem)
17        n &lt;- n - 1
18      }
19      
20      while(vec[1] == 0 &amp; length(vec) &gt; 1) vec &lt;- vec[-1]
21      structure(x, base = base, representation = vec) 
22    }), class = &quot;base&quot;)
23}
24base(1/8, base = 60, digits = 6)
25[[1]]
26[1] 0.125
27attr(,&quot;base&quot;)
28[1] 60
29attr(,&quot;representation&quot;)
30[1]  7 29 59 59 59 59
31
32attr(,&quot;class&quot;)
33[1] &quot;base&quot;
34

Instead of "7 30" we get "7 29 (59 repeating)", which is analogous to doing a decimal calculation that should be 0.2 and instead getting 0.1999....

This would presumably be fixable with an appropriate 'numeric fuzz' threshold.

The other thing that's missing from the code, now that it does fractional parts, is that the result should return information that tells you where the 'decimal' point is located (at the simplest, including the value of digits in the output).

There are other aspects of the code that could be improved (e.g. pre-allocating vec rather than building it up iteratively).


1base &lt;- function(b, base = 10)
2{
3  base &lt;- as.integer(base)
4  if(base &gt; 36 | base &lt; 2) stop(&quot;'base' must be between 2 and 36.&quot;)
5  
6  structure(lapply(b, function(x) 
7    {
8      n   &lt;- ceiling(log(x, base))
9      vec &lt;- numeric()
10      val &lt;- x
11      
12      while(n &gt;= 0)
13      {
14        rem &lt;- val %/% base^n
15        val &lt;- val - rem * base^n
16        vec &lt;- c(vec, rem)
17        n &lt;- n - 1
18      }
19      
20      while(vec[1] == 0 &amp; length(vec) &gt; 1) vec &lt;- vec[-1]
21      structure(x, base = base, representation = vec) 
22    }), class = &quot;base&quot;)
23}
24base(1/8, base = 60, digits = 6)
25[[1]]
26[1] 0.125
27attr(,&quot;base&quot;)
28[1] 60
29attr(,&quot;representation&quot;)
30[1]  7 29 59 59 59 59
31
32attr(,&quot;class&quot;)
33[1] &quot;base&quot;
34base &lt;- function(b, base = 10, digits = 0) {
35  base &lt;- as.integer(base)
36  structure(lapply(b, function(x)
37    {
38      n   &lt;- ceiling(log(x, base))
39      vec &lt;- numeric()
40      val &lt;- x
41
42      while(n &gt;= -1*digits )  {
43        rem &lt;- val %/% base^n
44        val &lt;- val - rem * base^n
45        vec &lt;- c(vec, rem)
46        n &lt;- n - 1
47      }
48
49      while(vec[1] == 0 &amp; length(vec) &gt; 1) vec &lt;- vec[-1]
50      structure(x, base = base, representation = vec)
51    }), class = &quot;base&quot;)
52}
53

Source https://stackoverflow.com/questions/70918281

QUESTION

React unity WebGL not working on React JS project

Asked 2022-Jan-24 at 23:38

Hey there trying to make my Unity WebGL export work on my React JS project but for some reason is not working I'm using the following React Unity WebGL version (react-unity-webgl@7.x # For Unity 2018 and 2019 (Active LTS)) and I have followed the instructions accordingly to the version I'm using, someone ran into the same issue than me and asked a question in the GitHub and I followed what they said which is basically to put the unity folder in the public folder but still is not working. Anyone has any idea of how to fix this ?

This is how I have my folder structure:

enter image description here

And this is the 2 codes I have try (which they are basically the same):

Code A:

1import React from 'react'
2import Unity, { UnityContent } from &quot;react-unity-webgl&quot;;
3
4let unityContent = new UnityContent(
5  &quot;Megaman/Build/Megaman.json&quot;,
6  &quot;Megaman/Build/UnityLoader.js&quot;
7);
8
9function MegamanGame() {
10    return (
11        &lt;div&gt;
12          &lt;h1&gt;Is not working&lt;/h1&gt;
13          &lt;Unity unityContent={unityContent} /&gt;
14        &lt;/div&gt;
15      
16    )
17}
18
19export default MegamanGame
20

Code B:

1import React from 'react'
2import Unity, { UnityContent } from &quot;react-unity-webgl&quot;;
3
4let unityContent = new UnityContent(
5  &quot;Megaman/Build/Megaman.json&quot;,
6  &quot;Megaman/Build/UnityLoader.js&quot;
7);
8
9function MegamanGame() {
10    return (
11        &lt;div&gt;
12          &lt;h1&gt;Is not working&lt;/h1&gt;
13          &lt;Unity unityContent={unityContent} /&gt;
14        &lt;/div&gt;
15      
16    )
17}
18
19export default MegamanGame
20import Unity, { UnityContent } from &quot;react-unity-webgl&quot;;
21export default function Megaman() {
22    let unityContent = new UnityContent(
23        &quot;Megaman/Build/Megaman.json&quot;,
24        &quot;Megaman/Build/UnityLoader.js&quot;
25    );
26    return (
27        &lt;div&gt;&lt;Unity unityContent={unityContent} /&gt;&lt;/div&gt;
28    );
29}
30

And this is how it renders

enter image description here

Take in mind I have try the path in 2 ways either "Megaman/Build/Megaman.json" or just "/Build/Megaman.json"

ANSWER

Answered 2022-Jan-08 at 23:13

you have to move your unitybuid folder as child of Public file, as this /public/Build/yourfiles.json

i am havinbg problems after that, it runs well on local host, but when building on a hosting sites, it doesnt loading for me.

Source https://stackoverflow.com/questions/70598132

QUESTION

Unity missing reference at Library/PackageCache/com.unity.collab-proxy@1.2.16

Asked 2022-Jan-22 at 20:35

I just bought template products in Unity expecting to learn some logic

i just encountered errors of CS0246 when importing. and it seems all coming from one package in Library below is one of the error message.

Library/PackageCache/com.unity.collab-proxy@1.2.16/Editor/Collab/Presenters/CollabHistoryPresenter.cs(21,9): error CS0246: The type or namespace name 'BuildAccess' could not be found (are you missing a using directive or an assembly reference?)

i am a beginner and i don't know how to handle on this kind of error. can anyone kindly help me point out where and why this error happen?

ANSWER

Answered 2021-Jul-27 at 04:56

Here are a few suggestions for tracking down the issue. Hard to tell what is missing.

  • close unity and delete Library and obj folders. Then open Unity again. It will re-sync those folders.
  • disable collaborate from services window in 2019- or window/collaborate in 2020+ and see if the issue gets resolved.

Source https://stackoverflow.com/questions/68537938

QUESTION

Why is my character's horizontal velocity lost when jumping?

Asked 2022-Jan-10 at 15:17

GOAL
I'm relatively new to Unity and I want my character to be able to run and jump at the same time causing the character to go up diagonally.

PROBLEM
However after making some adjustments to give the character some acceleration, the jump seems to clear all existing velocity, meaning the character goes up and then to the side instead of both at the same time:

Video of issue

(I apologise if its a bit hard to see)

CODE
This is my character movement script:

1    Rigidbody2D rb;
2    BoxCollider2D bc;   
3
4    [Header(&quot;Run&quot;)]
5    float xInput = 0f;
6    public float maxRunSpeed;
7    public float acceleration;
8    [Space]
9
10    [Header(&quot;Jump&quot;)]
11    public float jumpHeight;
12    public float lowJumpHeight;
13    public float fallSpeed;
14    public float airControl;
15
16    [Space]
17    public LayerMask groundLayer;
18    public bool onGround;
19
20    [Space]
21    public Vector2 bottomOffset;
22    public Vector2 boxSize;
23    public float coyoteTime;
24
25    void Start() {
26        // Gets a reference to the components attatched to the player
27        rb = GetComponent&lt;Rigidbody2D&gt;();
28        bc = GetComponent&lt;BoxCollider2D&gt;();
29    }
30
31    void Update() {
32        Jump();
33
34        // Takes input for running and returns a value from 1 (right) to -1 (left)
35        xInput =  Math.Sign(Input.GetAxisRaw(&quot;Horizontal&quot;));
36    }
37
38    // Applies a velocity scaled by runSpeed to the player depending on the direction of the input
39    // Increaces the velocity by accerleration until the max velocity is reached
40    void FixedUpdate() {
41        rb.velocity = Math.Abs(rb.velocity.x) &lt; Math.Abs(xInput) * maxRunSpeed ? rb.velocity + new Vector2(acceleration * xInput, rb.velocity.y) * Time.deltaTime : new Vector2(xInput * maxRunSpeed, rb.velocity.y);   
42    }
43
44    void Jump() {
45        // Checks whether the player is on the ground and if it is, replenishes coyote time, but if not, it starts to tick it down
46        coyoteTime = onGround ?  0.1f :  coyoteTime - Time.deltaTime;
47        // Draws a box to check whether the player is touching objects on the ground layer
48        onGround = Physics2D.OverlapBox((Vector2)transform.position + bottomOffset, boxSize, 0f, groundLayer);
49
50        // Adds an upwards velocity to player when there is still valid coyote time and the jump button is pressed
51        if (Input.GetButtonDown(&quot;Jump&quot;) &amp;&amp; coyoteTime &gt; 0) {
52            rb.velocity = Vector2.up * jumpHeight;
53        }
54        
55        // Increases gravity of player when falling down or when the jump button is let go mid-jump
56        if (rb.velocity.y &lt; 0 ) {
57            rb.velocity += Vector2.up * Physics2D.gravity.y * (fallSpeed - 1) * Time.deltaTime;
58        } else if (rb.velocity.y &gt; 0 &amp;&amp; !Input.GetButton(&quot;Jump&quot;)) {
59            rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpHeight - 1) * Time.deltaTime;
60        }
61    }
62

Sorry for there being a lot of unecessary code, it's just im not sure what's causing the issue so i don't want to remove anything. Hopefully my comments make some sense?

ANSWER

Answered 2022-Jan-09 at 23:05

This is happening because you're setting the velocity of your rigidbody directly with rb.velocity = Vector2.up * jumpHeight. So that will wipe all existing velocity.

If you want to just add a force to the velocity rather than replacing it entirely, you can do that with methods like Rigidbody2D.AddForce.

Source https://stackoverflow.com/questions/70646256

QUESTION

How do I replace a switch statement over an enum with runtime-dynamic type-based generic dispatch in C#?

Asked 2021-Dec-30 at 16:43

Background:

I am building an editor extension for Unity (although this question is not strictly unity related). The user can select a binary operation from a dropdown and the operation is performed on the inputs, as seen in the diagram:

User selected binary operation

The code is taken from a tutorial, and uses an enum here in combination with a switch statement here to achieve the desired behavior.

This next image demonstrates the relationship between the code and the behavior in the graph UI:

enter image description here

Problem

Based on my prior experience programming in other languages, and my desire to allow for user-extensible operations that don't require users to edit a switch statement in the core code, I would LIKE the resulting code to look something like this (invalid) C# code:

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25

Reference Behavior To be crystal clear about the kind of behavior I'm hoping to achieve, here is a reference implementation in Python.

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44

Specific Questions So ultimately this boils down to three interrelated questions:

  1. What sort of type do I assign to MathOperations so that it can hold a collection of the subtypes of GenericOperation?

  2. How do I get the subtypes of GenericOperation?

  3. What type do I assign operation, which can be one of several types?

Work So Far

I have been looking into generics and reflection from some of the following sources, but so far none seem to provide exactly the information I'm looking for.

  1. https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/generics
  2. https://igoro.com/archive/fun-with-c-generics-down-casting-to-a-generic-type/
  3. Using enum as generic type parameter in C#
  4. https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generics-and-reflection

Edit: I edited the comments in the C# psuedocode to reflect that the primary confusion boils down to what the types should be for MathOperations and operation, and to note that the editor itself selects the operation from the MathOperations when the user clicks on the dropdown. I also changed the question so that they can be answered factually.

ANSWER

Answered 2021-Dec-30 at 16:43

Usually I'd say your question is quite broad and the use case very tricky and requires a lot of not so trivial steps to approach. But I see you also have put quite an effort in research and your question so I'll try to do the same (little Christmas Present) ;)

In general I think generics is not what you want to use here. Generics always require compile time constant parameters.

As I am only on the phone and don't know I can't give you a full solution right now but I hope I can bring you into the right track.


1. Common Interface or base class

I think the simplest thing would rather be a common interface such as e.g.

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48

A common abstract base class would of course do as well. (You could even go for a certain attribute on methods)

And then have some implementations such as e.g.

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64

2. Find all implementations using Reflection

You can then use Reflection (you already were on the right track there) in order to automatically find all available implementations of that interface like e.g. this

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73

3. Store/Serialize a selected type in Unity

Now you have all the types ...

However, in order to really use these within Unity you will need an additional special class that is [Serializable] and can store a type e.g. like

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92

4. Interface type selection and drawing the drop-down

Then since you don't want to type the names manually you would need a special drawer for the drop down menu with the given types that implement your interface (you see we are connecting the dots).

I would probably use an attribute like e.g.

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102

You could then expose the field as e.g.

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102[Implements(typeof (ITwoFloatOperation))]
103public SerializableType operationType;
104

and then have a custom drawer. This depends of course on your needs. Honestly my editor scripting knowledge is more based on MonoBehaviour etc so I just hope you can somehow translate this into your graph thingy.

Something like e.g.

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102[Implements(typeof (ITwoFloatOperation))]
103public SerializableType operationType;
104 [CustomPropertyDrawer(typeof(ImplementsAttribute))]
105public class ImplementsDrawer : PropertyDrawer
106{
107    // Return the underlying type of s serialized property
108    private static Type GetType(SerializedProperty property)
109    {
110        // A little bit hacky we first get the type of the object that has this field
111        var parentType = property.serializedObject.targetObject.GetType();
112        // And then once again we use reflection to get the field via it's name again
113        var fi = parentType.GetField(property.propertyPath);
114        return fi.FieldType;
115    }
116
117    private static Type[] FindTypes (Type baseType)
118    {
119        var type = typeof(ITwoFloatOperation);
120        var types = AppDomain.CurrentDomain.GetAssemblies()
121            .SelectMany(s =&gt; s.GetTypes())
122            .Where(p =&gt; type.IsAssignableFrom(p));
123
124        return types.OrderBy(t =&gt; t.AssemblyQualifiedName).ToArray();
125    }
126
127    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
128    {   
129        label = EditorGUI.BeginProperty(position, label, property);
130
131        var implements = attribute as ImplementsAttribute;
132
133        if (GetType(property) != typeof (SerializableType))
134        {
135            EditorGUI.HelpBox(position, MessageType.Error, &quot;Implements only works for SerializableType!&quot;);
136            return;
137        }
138
139        var typeNameProperty = property.FindPropertyRelative(&quot;typeName&quot;);
140
141        var options = FindTypes (implements.baseType);
142
143        var guiOptions = options.Select(o =&gt; o.AssemblyQualifiedName).ToArray();
144
145        var currentType = string.IsNullOrWhiteSpace(typeNameProperty.stringValue) ? null : Type.GetType(typeNameProperty.stringValue);
146
147        var currentIndex = options.FindIndex(o =&gt; o == curtentType);
148
149        var newIndex = EditorGUI.Popup(position, label.text, currentIndex, guiOptions);
150
151        var newTypeName = newIndex &gt;= 0 ? options[newIndex] : &quot;&quot;;
152       
153        property.stringValue = newTypeName;   
154        EditorGUI.EndProperty();  
155    }
156}
157

5. Using the type to create an instance

Once you somehow can store and get the desired type as a last step we want to use it ^^

Again the solution would be reflection and the Activator which allows us to create an instance of any given dynamic type using Activator.CreateInstance

so once you have the field you would e.g. do

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102[Implements(typeof (ITwoFloatOperation))]
103public SerializableType operationType;
104 [CustomPropertyDrawer(typeof(ImplementsAttribute))]
105public class ImplementsDrawer : PropertyDrawer
106{
107    // Return the underlying type of s serialized property
108    private static Type GetType(SerializedProperty property)
109    {
110        // A little bit hacky we first get the type of the object that has this field
111        var parentType = property.serializedObject.targetObject.GetType();
112        // And then once again we use reflection to get the field via it's name again
113        var fi = parentType.GetField(property.propertyPath);
114        return fi.FieldType;
115    }
116
117    private static Type[] FindTypes (Type baseType)
118    {
119        var type = typeof(ITwoFloatOperation);
120        var types = AppDomain.CurrentDomain.GetAssemblies()
121            .SelectMany(s =&gt; s.GetTypes())
122            .Where(p =&gt; type.IsAssignableFrom(p));
123
124        return types.OrderBy(t =&gt; t.AssemblyQualifiedName).ToArray();
125    }
126
127    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
128    {   
129        label = EditorGUI.BeginProperty(position, label, property);
130
131        var implements = attribute as ImplementsAttribute;
132
133        if (GetType(property) != typeof (SerializableType))
134        {
135            EditorGUI.HelpBox(position, MessageType.Error, &quot;Implements only works for SerializableType!&quot;);
136            return;
137        }
138
139        var typeNameProperty = property.FindPropertyRelative(&quot;typeName&quot;);
140
141        var options = FindTypes (implements.baseType);
142
143        var guiOptions = options.Select(o =&gt; o.AssemblyQualifiedName).ToArray();
144
145        var currentType = string.IsNullOrWhiteSpace(typeNameProperty.stringValue) ? null : Type.GetType(typeNameProperty.stringValue);
146
147        var currentIndex = options.FindIndex(o =&gt; o == curtentType);
148
149        var newIndex = EditorGUI.Popup(position, label.text, currentIndex, guiOptions);
150
151        var newTypeName = newIndex &gt;= 0 ? options[newIndex] : &quot;&quot;;
152       
153        property.stringValue = newTypeName;   
154        EditorGUI.EndProperty();  
155    }
156}
157var instance = (ITwoFloatOperation) Activator.CreateInstance(operationType.Type));
158var result = instance.GetResult(floatA, floatB);
159

Once all this is setup an working correctly ( ^^ ) your "users"/developers can add new operations as simple as implementing your interface.




Alternative Approach - "Scriptable Behaviors"

Thinking about it further I think I have another - maybe a bit more simple approach.

This option is maybe not what you were targeting originally and is not a drop-down but we will rather simply use the already existing object selection popup for assets!


You could use something I like to call "Scriptable Behaviours" and have a base ScriptableObject like

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102[Implements(typeof (ITwoFloatOperation))]
103public SerializableType operationType;
104 [CustomPropertyDrawer(typeof(ImplementsAttribute))]
105public class ImplementsDrawer : PropertyDrawer
106{
107    // Return the underlying type of s serialized property
108    private static Type GetType(SerializedProperty property)
109    {
110        // A little bit hacky we first get the type of the object that has this field
111        var parentType = property.serializedObject.targetObject.GetType();
112        // And then once again we use reflection to get the field via it's name again
113        var fi = parentType.GetField(property.propertyPath);
114        return fi.FieldType;
115    }
116
117    private static Type[] FindTypes (Type baseType)
118    {
119        var type = typeof(ITwoFloatOperation);
120        var types = AppDomain.CurrentDomain.GetAssemblies()
121            .SelectMany(s =&gt; s.GetTypes())
122            .Where(p =&gt; type.IsAssignableFrom(p));
123
124        return types.OrderBy(t =&gt; t.AssemblyQualifiedName).ToArray();
125    }
126
127    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
128    {   
129        label = EditorGUI.BeginProperty(position, label, property);
130
131        var implements = attribute as ImplementsAttribute;
132
133        if (GetType(property) != typeof (SerializableType))
134        {
135            EditorGUI.HelpBox(position, MessageType.Error, &quot;Implements only works for SerializableType!&quot;);
136            return;
137        }
138
139        var typeNameProperty = property.FindPropertyRelative(&quot;typeName&quot;);
140
141        var options = FindTypes (implements.baseType);
142
143        var guiOptions = options.Select(o =&gt; o.AssemblyQualifiedName).ToArray();
144
145        var currentType = string.IsNullOrWhiteSpace(typeNameProperty.stringValue) ? null : Type.GetType(typeNameProperty.stringValue);
146
147        var currentIndex = options.FindIndex(o =&gt; o == curtentType);
148
149        var newIndex = EditorGUI.Popup(position, label.text, currentIndex, guiOptions);
150
151        var newTypeName = newIndex &gt;= 0 ? options[newIndex] : &quot;&quot;;
152       
153        property.stringValue = newTypeName;   
154        EditorGUI.EndProperty();  
155    }
156}
157var instance = (ITwoFloatOperation) Activator.CreateInstance(operationType.Type));
158var result = instance.GetResult(floatA, floatB);
159public abstract class TwoFloatOperation : ScriptableObject
160{
161    public abstract float GetResult(float a, float b);
162}
163

And then multiple implementations (note: all these have to be in different files!)

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102[Implements(typeof (ITwoFloatOperation))]
103public SerializableType operationType;
104 [CustomPropertyDrawer(typeof(ImplementsAttribute))]
105public class ImplementsDrawer : PropertyDrawer
106{
107    // Return the underlying type of s serialized property
108    private static Type GetType(SerializedProperty property)
109    {
110        // A little bit hacky we first get the type of the object that has this field
111        var parentType = property.serializedObject.targetObject.GetType();
112        // And then once again we use reflection to get the field via it's name again
113        var fi = parentType.GetField(property.propertyPath);
114        return fi.FieldType;
115    }
116
117    private static Type[] FindTypes (Type baseType)
118    {
119        var type = typeof(ITwoFloatOperation);
120        var types = AppDomain.CurrentDomain.GetAssemblies()
121            .SelectMany(s =&gt; s.GetTypes())
122            .Where(p =&gt; type.IsAssignableFrom(p));
123
124        return types.OrderBy(t =&gt; t.AssemblyQualifiedName).ToArray();
125    }
126
127    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
128    {   
129        label = EditorGUI.BeginProperty(position, label, property);
130
131        var implements = attribute as ImplementsAttribute;
132
133        if (GetType(property) != typeof (SerializableType))
134        {
135            EditorGUI.HelpBox(position, MessageType.Error, &quot;Implements only works for SerializableType!&quot;);
136            return;
137        }
138
139        var typeNameProperty = property.FindPropertyRelative(&quot;typeName&quot;);
140
141        var options = FindTypes (implements.baseType);
142
143        var guiOptions = options.Select(o =&gt; o.AssemblyQualifiedName).ToArray();
144
145        var currentType = string.IsNullOrWhiteSpace(typeNameProperty.stringValue) ? null : Type.GetType(typeNameProperty.stringValue);
146
147        var currentIndex = options.FindIndex(o =&gt; o == curtentType);
148
149        var newIndex = EditorGUI.Popup(position, label.text, currentIndex, guiOptions);
150
151        var newTypeName = newIndex &gt;= 0 ? options[newIndex] : &quot;&quot;;
152       
153        property.stringValue = newTypeName;   
154        EditorGUI.EndProperty();  
155    }
156}
157var instance = (ITwoFloatOperation) Activator.CreateInstance(operationType.Type));
158var result = instance.GetResult(floatA, floatB);
159public abstract class TwoFloatOperation : ScriptableObject
160{
161    public abstract float GetResult(float a, float b);
162}
163[CreateAssetMenu (fileName = &quot;Add&quot;, menuName = &quot;TwoFloatOperations/Add&quot;)]
164public class Add : TwoFloatOperation
165{
166    public float GetResult(float a, float b) =&gt; a + b;
167}
168
169[CreateAssetMenu (fileName = &quot;Multiply&quot;, menuName = &quot;TwoFloatOperations/Multiply&quot;)]
170public class Multiply : TwoFloatOperation
171{
172    public float GetResult(float a, float b) =&gt; a * b;
173}
174
175[CreateAssetMenu (fileName = &quot;Power&quot;, menuName = &quot;TwoFloatOperations/Power&quot;]
176public class Power : TwoFloatOperation
177{
178    public float GetResult(float a, float b) Mathf.Pow(a, b);
179}
180

Then you create one instance of each vis the ProjectView -> Right Click -> Create -> TwoFloatOperations

Once you did this for each type you can simply expose a field of type

1... snip ...
2
3        // OperatorSelection.GetSelections() is automagically populated by inheritors of the GenericOperation class
4        // So it would represent a collection of types?
5        // so the confusion is primarily around what type this should be
6        public GenericOperations /* ?? */ MathOperations = GenericOperation.GetOperations();
7
8        // this gets assigned by the editor when the user clicks 
9        // the dropdown, but I'm unclear on what the type should
10        // be since it can be one of several types
11        // from the MathOperations collection
12        public Operation /* ?? */ operation;
13        
14        public override object GetValue(NodePort port)
15        {
16            float a = GetInputValue&lt;float&gt;(&quot;a&quot;, this.a);
17            float b = GetInputValue&lt;float&gt;(&quot;b&quot;, this.b);
18            result = 0f;
19            result = operation(a, b);
20            return result;
21        }
22
23... snip ...
24
25class GenericOperation:
26
27    @classmethod
28    def get_operations(cls):
29        return cls.__subclasses__()
30
31
32class AddOperation(GenericOperation):
33
34    def __call__(self, a, b):
35        return a + b
36
37
38if __name__ == '__main__':
39    op = AddOperation()
40    res = op(1, 2)
41    print(res)  # 3
42    print(GenericOperation.get_operations())  # {&lt;class '__main__.AddOperation'&gt;}
43
44public interface ITwoFloatOperation
45{
46    public float GetResult(float a, float b);
47}
48public class Add : ITwoFloatOperation
49{
50    public float GetResult(float a, float b) =&gt; a + b;
51}
52
53public class Multiply : ITwoFloatOperation
54{
55    public float GetResult(float a, float b) =&gt; a * b;
56}
57
58public class Power : ITwoFloatOperation
59{
60    public float GetResult(float a, float b) Mathf.Pow(a, b);
61}
62
63... etc
64using System.Reflection;
65using System.Linq;
66
67...
68
69var type = typeof(ITwoFloatOperation);
70var types = AppDomain.CurrentDomain.GetAssemblies()
71    .SelectMany(s =&gt; s.GetTypes())
72    .Where(p =&gt; type.IsAssignableFrom(p));
73[Serializable]
74// See https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html
75public class SerializableType : ISerializationCallbackReceiver
76{
77    private Type type;
78    [SerializeField] private string typeName;
79
80    public Type Type =&gt; type;
81
82    public void OnBeforeSerialize()
83    {
84        typeName = type != null ? type.AssemblyQualifiedName : &quot;&quot;;
85    }
86
87    public void OnAfterDeserialize()
88    {
89        if(!string.NullOrWhiteSpace(typeName)) type = Type.GetType(typeName);
90    }
91}
92[AttributeUsage(AttributeTarget.Field)]
93public ImplementsAttribute : PropertyAttribute
94{
95    public Type baseType;
96
97    public ImplementsAttribute (Type type)
98    {
99        baseType = type;
100    }
101}
102[Implements(typeof (ITwoFloatOperation))]
103public SerializableType operationType;
104 [CustomPropertyDrawer(typeof(ImplementsAttribute))]
105public class ImplementsDrawer : PropertyDrawer
106{
107    // Return the underlying type of s serialized property
108    private static Type GetType(SerializedProperty property)
109    {
110        // A little bit hacky we first get the type of the object that has this field
111        var parentType = property.serializedObject.targetObject.GetType();
112        // And then once again we use reflection to get the field via it's name again
113        var fi = parentType.GetField(property.propertyPath);
114        return fi.FieldType;
115    }
116
117    private static Type[] FindTypes (Type baseType)
118    {
119        var type = typeof(ITwoFloatOperation);
120        var types = AppDomain.CurrentDomain.GetAssemblies()
121            .SelectMany(s =&gt; s.GetTypes())
122            .Where(p =&gt; type.IsAssignableFrom(p));
123
124        return types.OrderBy(t =&gt; t.AssemblyQualifiedName).ToArray();
125    }
126
127    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
128    {   
129        label = EditorGUI.BeginProperty(position, label, property);
130
131        var implements = attribute as ImplementsAttribute;
132
133        if (GetType(property) != typeof (SerializableType))
134        {
135            EditorGUI.HelpBox(position, MessageType.Error, &quot;Implements only works for SerializableType!&quot;);
136            return;
137        }
138
139        var typeNameProperty = property.FindPropertyRelative(&quot;typeName&quot;);
140
141        var options = FindTypes (implements.baseType);
142
143        var guiOptions = options.Select(o =&gt; o.AssemblyQualifiedName).ToArray();
144
145        var currentType = string.IsNullOrWhiteSpace(typeNameProperty.stringValue) ? null : Type.GetType(typeNameProperty.stringValue);
146
147        var currentIndex = options.FindIndex(o =&gt; o == curtentType);
148
149        var newIndex = EditorGUI.Popup(position, label.text, currentIndex, guiOptions);
150
151        var newTypeName = newIndex &gt;= 0 ? options[newIndex] : &quot;&quot;;
152       
153        property.stringValue = newTypeName;   
154        EditorGUI.EndProperty();  
155    }
156}
157var instance = (ITwoFloatOperation) Activator.CreateInstance(operationType.Type));
158var result = instance.GetResult(floatA, floatB);
159public abstract class TwoFloatOperation : ScriptableObject
160{
161    public abstract float GetResult(float a, float b);
162}
163[CreateAssetMenu (fileName = &quot;Add&quot;, menuName = &quot;TwoFloatOperations/Add&quot;)]
164public class Add : TwoFloatOperation
165{
166    public float GetResult(float a, float b) =&gt; a + b;
167}
168
169[CreateAssetMenu (fileName = &quot;Multiply&quot;, menuName = &quot;TwoFloatOperations/Multiply&quot;)]
170public class Multiply : TwoFloatOperation
171{
172    public float GetResult(float a, float b) =&gt; a * b;
173}
174
175[CreateAssetMenu (fileName = &quot;Power&quot;, menuName = &quot;TwoFloatOperations/Power&quot;]
176public class Power : TwoFloatOperation
177{
178    public float GetResult(float a, float b) Mathf.Pow(a, b);
179}
180public TwoFloatOperation operation;
181

and let Unity do all the reflection work to find instances which implement this in the assets.

You can simply click on the little dot next to the object field and Unity will list you all available options and you can even use the search bar to find one by name.

Advantage:

  • No dirty, expensive and error prone reflection required
  • Basically all based on already built-in functionality of the editor -> less worries with serialization etc

Disadvantage:

  • This breaks a little with the actual concept behind ScriptableObject since usually there would be multiple instances with different settings, not only a single one
  • As you see your developers have to not only inherit a certain type but additionally add the CreateAssetMenu attribute and actually create an instance in order to be able to use it.

As said typing this on the phone but I hope this helps with your use case and gives you an idea of how I would approach this

Source https://stackoverflow.com/questions/70523456

QUESTION

Make the distance between neighbor notes the same (Theremin like app)

Asked 2021-Dec-24 at 13:44

I'm making a Theremin-like app in Unity (C#).

I have horizontal Axis X, on which I can click (with a mouse or with a finger on a smartphone). This X-axis determines the frequency, which will be played. The user will specify the frequency range of the board (X-Axis), let's say from frequency 261.63 (note C4) to 523.25 (note C5).

I'll calculate x_position_ratio which is a number between 0 and 1 determining, where did the user click on the X-axis (0 being on the most left (note C4 in this example), 1 on the most right (note C5))

From this, I will calculate the frequency to play by equation

1float freqRange = maxFreq - minFreq;
2float frequency = (x_position_ratio * freqRange) + minFreq;
3

And then play the frequency. It works just fine.

If I draw the notes on the board (X-axis), we can see, that the higher is the frequency, the higher is the jump between the 2 notes.

1float freqRange = maxFreq - minFreq;
2float frequency = (x_position_ratio * freqRange) + minFreq;
3// Drawing just note A4 to demonstrate the code
4float a4 = 440.0f //frequency of note A4
5float x_position = (a4 - minFreq) / freqRange;
6

loc_x_position indicating the position of the note on the X-axis between 0 to 1

output

Question:

I would like to make the jump, same, between 2 notes (Make it linear instead of exponential, if you understand what I mean). Found the equation on Wikipedia Piano_key_frequencies but it's for the keys. I want it to every frequency and I cannot figure out how to implement it in my 2 code examples I posted

ANSWER

Answered 2021-Dec-24 at 13:44

I figured it out. Tried to plot it logarithmic to at least approximate the result.

I was inspired by this answer Plotting logarithmic graph Turns out this solution worked

To draw notes on the x-axis I used this:

1float freqRange = maxFreq - minFreq;
2float frequency = (x_position_ratio * freqRange) + minFreq;
3// Drawing just note A4 to demonstrate the code
4float a4 = 440.0f //frequency of note A4
5float x_position = (a4 - minFreq) / freqRange;
6minFreq = Mathf.Log10(minFreq);
7maxFreq = Mathf.Log10(maxFreq);
8float freqRange = maxFreq - minFreq;
9x_position = (Mathf.Log10(frequencyOfNoteToPlot) - minFreq) / freqRange;
10

and to calculate the frequency I just derived frequency from the equation for the x_position and ended up with this piece of code:

1float freqRange = maxFreq - minFreq;
2float frequency = (x_position_ratio * freqRange) + minFreq;
3// Drawing just note A4 to demonstrate the code
4float a4 = 440.0f //frequency of note A4
5float x_position = (a4 - minFreq) / freqRange;
6minFreq = Mathf.Log10(minFreq);
7maxFreq = Mathf.Log10(maxFreq);
8float freqRange = maxFreq - minFreq;
9x_position = (Mathf.Log10(frequencyOfNoteToPlot) - minFreq) / freqRange;
10frequency = Mathf.Pow(10, x_position * freqRange + minFreq);
11

One thing I still don't get is, why it doesn't matter, which base of the logarithm I use. Is it because I'm always getting ratio (value between 0 to 1)?

Source https://stackoverflow.com/questions/70457054

QUESTION

Extracting the measurement unit (degrees, metres, etc.) from spatial data in R

Asked 2021-Dec-21 at 15:05

I would like to extract the unit of measurement (decimal degrees, metres, feet, etc.) from a spatial object in R. For example, if I have an SF data frame that uses the WGS84 co-ordinate reference system (EPSG:4326), I would like to be able to determine that the co-ordinates are specified in decimal degrees. Similarly, I'd like to be able to determine that UTM co-ordinates (e.g. EPSG:32615) are specified in metres.

I have tried using the st_crs() function from the sf package, which returns the co-ordinate reference system in well-known text format. However, I'm struggling to be certain that a regex that extracts the unit of measurement from that well-known text will operate reliably for a wide range of co-ordinate systems.

Is there an existing function that returns the measurement unit for a spatial object?

For example, the following code produces an SF data frame that uses the WGS84 co-ordinate system:

1library(sf)
2#&gt; Linking to GEOS 3.8.1, GDAL 3.2.1, PROJ 7.2.1
3
4cities &lt;- st_sf(city = &quot;London&quot;, geometry = st_sfc(st_point(c(-0.1276, 51.5072))), crs = 4326)
5
6cities
7#&gt; Simple feature collection with 1 feature and 1 field
8#&gt; Geometry type: POINT
9#&gt; Dimension:     XY
10#&gt; Bounding box:  xmin: -0.1276 ymin: 51.5072 xmax: -0.1276 ymax: 51.5072
11#&gt; Geodetic CRS:  WGS 84
12#&gt;     city                geometry
13#&gt; 1 London POINT (-0.1276 51.5072)
14

Created on 2021-12-21 by the reprex package (v2.0.1)

I am ideally looking for a function that allows me to determine that the spatial unit for this dataset is decimal degrees, e.g. if the function was called st_crs_unit() I would like to call st_crs_unit(cities) and that function return the unit "degrees" or similar.

st_crs() produces information about the CRS in well-known text format, including that the co-ordinate system (CS[]) uses the ANGLEUNIT "degree" for both axes, but the structure of this text varies considerably across co-ordinate systems, so I cannot be sure a regex trained on some systems will work for all.

1library(sf)
2#&gt; Linking to GEOS 3.8.1, GDAL 3.2.1, PROJ 7.2.1
3
4cities &lt;- st_sf(city = &quot;London&quot;, geometry = st_sfc(st_point(c(-0.1276, 51.5072))), crs = 4326)
5
6cities
7#&gt; Simple feature collection with 1 feature and 1 field
8#&gt; Geometry type: POINT
9#&gt; Dimension:     XY
10#&gt; Bounding box:  xmin: -0.1276 ymin: 51.5072 xmax: -0.1276 ymax: 51.5072
11#&gt; Geodetic CRS:  WGS 84
12#&gt;     city                geometry
13#&gt; 1 London POINT (-0.1276 51.5072)
14st_crs(cities)
15#&gt; Coordinate Reference System:
16#&gt;   User input: EPSG:4326 
17#&gt;   wkt:
18#&gt; GEOGCRS[&quot;WGS 84&quot;,
19#&gt;     DATUM[&quot;World Geodetic System 1984&quot;,
20#&gt;         ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
21#&gt;             LENGTHUNIT[&quot;metre&quot;,1]]],
22#&gt;     PRIMEM[&quot;Greenwich&quot;,0,
23#&gt;         ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
24#&gt;     CS[ellipsoidal,2],
25#&gt;         AXIS[&quot;geodetic latitude (Lat)&quot;,north,
26#&gt;             ORDER[1],
27#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
28#&gt;         AXIS[&quot;geodetic longitude (Lon)&quot;,east,
29#&gt;             ORDER[2],
30#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
31#&gt;     USAGE[
32#&gt;         SCOPE[&quot;Horizontal component of 3D system.&quot;],
33#&gt;         AREA[&quot;World.&quot;],
34#&gt;         BBOX[-90,-180,90,180]],
35#&gt;     ID[&quot;EPSG&quot;,4326]]
36

Created on 2021-12-21 by the reprex package (v2.0.1)

For example, if we transform the same data to use the UTM zone 30N co-ordinate system, the output from st_crs() changes substantially.

1library(sf)
2#&gt; Linking to GEOS 3.8.1, GDAL 3.2.1, PROJ 7.2.1
3
4cities &lt;- st_sf(city = &quot;London&quot;, geometry = st_sfc(st_point(c(-0.1276, 51.5072))), crs = 4326)
5
6cities
7#&gt; Simple feature collection with 1 feature and 1 field
8#&gt; Geometry type: POINT
9#&gt; Dimension:     XY
10#&gt; Bounding box:  xmin: -0.1276 ymin: 51.5072 xmax: -0.1276 ymax: 51.5072
11#&gt; Geodetic CRS:  WGS 84
12#&gt;     city                geometry
13#&gt; 1 London POINT (-0.1276 51.5072)
14st_crs(cities)
15#&gt; Coordinate Reference System:
16#&gt;   User input: EPSG:4326 
17#&gt;   wkt:
18#&gt; GEOGCRS[&quot;WGS 84&quot;,
19#&gt;     DATUM[&quot;World Geodetic System 1984&quot;,
20#&gt;         ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
21#&gt;             LENGTHUNIT[&quot;metre&quot;,1]]],
22#&gt;     PRIMEM[&quot;Greenwich&quot;,0,
23#&gt;         ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
24#&gt;     CS[ellipsoidal,2],
25#&gt;         AXIS[&quot;geodetic latitude (Lat)&quot;,north,
26#&gt;             ORDER[1],
27#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
28#&gt;         AXIS[&quot;geodetic longitude (Lon)&quot;,east,
29#&gt;             ORDER[2],
30#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
31#&gt;     USAGE[
32#&gt;         SCOPE[&quot;Horizontal component of 3D system.&quot;],
33#&gt;         AREA[&quot;World.&quot;],
34#&gt;         BBOX[-90,-180,90,180]],
35#&gt;     ID[&quot;EPSG&quot;,4326]]
36st_crs(st_transform(cities, crs = 32630))
37#&gt; Coordinate Reference System:
38#&gt;   User input: EPSG:32630 
39#&gt;   wkt:
40#&gt; PROJCRS[&quot;WGS 84 / UTM zone 30N&quot;,
41#&gt;     BASEGEOGCRS[&quot;WGS 84&quot;,
42#&gt;         DATUM[&quot;World Geodetic System 1984&quot;,
43#&gt;             ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
44#&gt;                 LENGTHUNIT[&quot;metre&quot;,1]]],
45#&gt;         PRIMEM[&quot;Greenwich&quot;,0,
46#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
47#&gt;         ID[&quot;EPSG&quot;,4326]],
48#&gt;     CONVERSION[&quot;UTM zone 30N&quot;,
49#&gt;         METHOD[&quot;Transverse Mercator&quot;,
50#&gt;             ID[&quot;EPSG&quot;,9807]],
51#&gt;         PARAMETER[&quot;Latitude of natural origin&quot;,0,
52#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433],
53#&gt;             ID[&quot;EPSG&quot;,8801]],
54#&gt;         PARAMETER[&quot;Longitude of natural origin&quot;,-3,
55#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433],
56#&gt;             ID[&quot;EPSG&quot;,8802]],
57#&gt;         PARAMETER[&quot;Scale factor at natural origin&quot;,0.9996,
58#&gt;             SCALEUNIT[&quot;unity&quot;,1],
59#&gt;             ID[&quot;EPSG&quot;,8805]],
60#&gt;         PARAMETER[&quot;False easting&quot;,500000,
61#&gt;             LENGTHUNIT[&quot;metre&quot;,1],
62#&gt;             ID[&quot;EPSG&quot;,8806]],
63#&gt;         PARAMETER[&quot;False northing&quot;,0,
64#&gt;             LENGTHUNIT[&quot;metre&quot;,1],
65#&gt;             ID[&quot;EPSG&quot;,8807]]],
66#&gt;     CS[Cartesian,2],
67#&gt;         AXIS[&quot;(E)&quot;,east,
68#&gt;             ORDER[1],
69#&gt;             LENGTHUNIT[&quot;metre&quot;,1]],
70#&gt;         AXIS[&quot;(N)&quot;,north,
71#&gt;             ORDER[2],
72#&gt;             LENGTHUNIT[&quot;metre&quot;,1]],
73#&gt;     USAGE[
74#&gt;         SCOPE[&quot;Engineering survey, topographic mapping.&quot;],
75#&gt;         AREA[&quot;Between 6°W and 0°W, northern hemisphere between equator and 84°N, onshore and offshore. Algeria. Burkina Faso. Côte' Ivoire (Ivory Coast). Faroe Islands - offshore. France. Ghana. Gibraltar. Ireland - offshore Irish Sea. Mali. Mauritania. Morocco. Spain. United Kingdom (UK).&quot;],
76#&gt;         BBOX[0,-6,84,0]],
77#&gt;     ID[&quot;EPSG&quot;,32630]]
78

Created on 2021-12-21 by the reprex package (v2.0.1)

Is there an existing R function that returns the measurement unit for a spatial object?

ANSWER

Answered 2021-Dec-21 at 15:05

st_crs() has a parameters argument that returns a list of useful CRS parameters when TRUE, including the units of the CRS. Here's an example with the built-in nc data:

1library(sf)
2#&gt; Linking to GEOS 3.8.1, GDAL 3.2.1, PROJ 7.2.1
3
4cities &lt;- st_sf(city = &quot;London&quot;, geometry = st_sfc(st_point(c(-0.1276, 51.5072))), crs = 4326)
5
6cities
7#&gt; Simple feature collection with 1 feature and 1 field
8#&gt; Geometry type: POINT
9#&gt; Dimension:     XY
10#&gt; Bounding box:  xmin: -0.1276 ymin: 51.5072 xmax: -0.1276 ymax: 51.5072
11#&gt; Geodetic CRS:  WGS 84
12#&gt;     city                geometry
13#&gt; 1 London POINT (-0.1276 51.5072)
14st_crs(cities)
15#&gt; Coordinate Reference System:
16#&gt;   User input: EPSG:4326 
17#&gt;   wkt:
18#&gt; GEOGCRS[&quot;WGS 84&quot;,
19#&gt;     DATUM[&quot;World Geodetic System 1984&quot;,
20#&gt;         ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
21#&gt;             LENGTHUNIT[&quot;metre&quot;,1]]],
22#&gt;     PRIMEM[&quot;Greenwich&quot;,0,
23#&gt;         ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
24#&gt;     CS[ellipsoidal,2],
25#&gt;         AXIS[&quot;geodetic latitude (Lat)&quot;,north,
26#&gt;             ORDER[1],
27#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
28#&gt;         AXIS[&quot;geodetic longitude (Lon)&quot;,east,
29#&gt;             ORDER[2],
30#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
31#&gt;     USAGE[
32#&gt;         SCOPE[&quot;Horizontal component of 3D system.&quot;],
33#&gt;         AREA[&quot;World.&quot;],
34#&gt;         BBOX[-90,-180,90,180]],
35#&gt;     ID[&quot;EPSG&quot;,4326]]
36st_crs(st_transform(cities, crs = 32630))
37#&gt; Coordinate Reference System:
38#&gt;   User input: EPSG:32630 
39#&gt;   wkt:
40#&gt; PROJCRS[&quot;WGS 84 / UTM zone 30N&quot;,
41#&gt;     BASEGEOGCRS[&quot;WGS 84&quot;,
42#&gt;         DATUM[&quot;World Geodetic System 1984&quot;,
43#&gt;             ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
44#&gt;                 LENGTHUNIT[&quot;metre&quot;,1]]],
45#&gt;         PRIMEM[&quot;Greenwich&quot;,0,
46#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
47#&gt;         ID[&quot;EPSG&quot;,4326]],
48#&gt;     CONVERSION[&quot;UTM zone 30N&quot;,
49#&gt;         METHOD[&quot;Transverse Mercator&quot;,
50#&gt;             ID[&quot;EPSG&quot;,9807]],
51#&gt;         PARAMETER[&quot;Latitude of natural origin&quot;,0,
52#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433],
53#&gt;             ID[&quot;EPSG&quot;,8801]],
54#&gt;         PARAMETER[&quot;Longitude of natural origin&quot;,-3,
55#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433],
56#&gt;             ID[&quot;EPSG&quot;,8802]],
57#&gt;         PARAMETER[&quot;Scale factor at natural origin&quot;,0.9996,
58#&gt;             SCALEUNIT[&quot;unity&quot;,1],
59#&gt;             ID[&quot;EPSG&quot;,8805]],
60#&gt;         PARAMETER[&quot;False easting&quot;,500000,
61#&gt;             LENGTHUNIT[&quot;metre&quot;,1],
62#&gt;             ID[&quot;EPSG&quot;,8806]],
63#&gt;         PARAMETER[&quot;False northing&quot;,0,
64#&gt;             LENGTHUNIT[&quot;metre&quot;,1],
65#&gt;             ID[&quot;EPSG&quot;,8807]]],
66#&gt;     CS[Cartesian,2],
67#&gt;         AXIS[&quot;(E)&quot;,east,
68#&gt;             ORDER[1],
69#&gt;             LENGTHUNIT[&quot;metre&quot;,1]],
70#&gt;         AXIS[&quot;(N)&quot;,north,
71#&gt;             ORDER[2],
72#&gt;             LENGTHUNIT[&quot;metre&quot;,1]],
73#&gt;     USAGE[
74#&gt;         SCOPE[&quot;Engineering survey, topographic mapping.&quot;],
75#&gt;         AREA[&quot;Between 6°W and 0°W, northern hemisphere between equator and 84°N, onshore and offshore. Algeria. Burkina Faso. Côte' Ivoire (Ivory Coast). Faroe Islands - offshore. France. Ghana. Gibraltar. Ireland - offshore Irish Sea. Mali. Mauritania. Morocco. Spain. United Kingdom (UK).&quot;],
76#&gt;         BBOX[0,-6,84,0]],
77#&gt;     ID[&quot;EPSG&quot;,32630]]
78library(sf)
79
80nc_4267 &lt;- read_sf(system.file(&quot;shape/nc.shp&quot;, package=&quot;sf&quot;))
81nc_3857 &lt;- st_transform(nc_4267, 3857)
82
83st_crs(nc_4267, parameters = TRUE)$units_gdal
84#&gt; [1] &quot;degree&quot;
85st_crs(nc_3857, parameters = TRUE)$units_gdal
86#&gt; [1] &quot;metre&quot;
87

Note that for some purposes st_is_longlat() might be sufficient:

1library(sf)
2#&gt; Linking to GEOS 3.8.1, GDAL 3.2.1, PROJ 7.2.1
3
4cities &lt;- st_sf(city = &quot;London&quot;, geometry = st_sfc(st_point(c(-0.1276, 51.5072))), crs = 4326)
5
6cities
7#&gt; Simple feature collection with 1 feature and 1 field
8#&gt; Geometry type: POINT
9#&gt; Dimension:     XY
10#&gt; Bounding box:  xmin: -0.1276 ymin: 51.5072 xmax: -0.1276 ymax: 51.5072
11#&gt; Geodetic CRS:  WGS 84
12#&gt;     city                geometry
13#&gt; 1 London POINT (-0.1276 51.5072)
14st_crs(cities)
15#&gt; Coordinate Reference System:
16#&gt;   User input: EPSG:4326 
17#&gt;   wkt:
18#&gt; GEOGCRS[&quot;WGS 84&quot;,
19#&gt;     DATUM[&quot;World Geodetic System 1984&quot;,
20#&gt;         ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
21#&gt;             LENGTHUNIT[&quot;metre&quot;,1]]],
22#&gt;     PRIMEM[&quot;Greenwich&quot;,0,
23#&gt;         ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
24#&gt;     CS[ellipsoidal,2],
25#&gt;         AXIS[&quot;geodetic latitude (Lat)&quot;,north,
26#&gt;             ORDER[1],
27#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
28#&gt;         AXIS[&quot;geodetic longitude (Lon)&quot;,east,
29#&gt;             ORDER[2],
30#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
31#&gt;     USAGE[
32#&gt;         SCOPE[&quot;Horizontal component of 3D system.&quot;],
33#&gt;         AREA[&quot;World.&quot;],
34#&gt;         BBOX[-90,-180,90,180]],
35#&gt;     ID[&quot;EPSG&quot;,4326]]
36st_crs(st_transform(cities, crs = 32630))
37#&gt; Coordinate Reference System:
38#&gt;   User input: EPSG:32630 
39#&gt;   wkt:
40#&gt; PROJCRS[&quot;WGS 84 / UTM zone 30N&quot;,
41#&gt;     BASEGEOGCRS[&quot;WGS 84&quot;,
42#&gt;         DATUM[&quot;World Geodetic System 1984&quot;,
43#&gt;             ELLIPSOID[&quot;WGS 84&quot;,6378137,298.257223563,
44#&gt;                 LENGTHUNIT[&quot;metre&quot;,1]]],
45#&gt;         PRIMEM[&quot;Greenwich&quot;,0,
46#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433]],
47#&gt;         ID[&quot;EPSG&quot;,4326]],
48#&gt;     CONVERSION[&quot;UTM zone 30N&quot;,
49#&gt;         METHOD[&quot;Transverse Mercator&quot;,
50#&gt;             ID[&quot;EPSG&quot;,9807]],
51#&gt;         PARAMETER[&quot;Latitude of natural origin&quot;,0,
52#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433],
53#&gt;             ID[&quot;EPSG&quot;,8801]],
54#&gt;         PARAMETER[&quot;Longitude of natural origin&quot;,-3,
55#&gt;             ANGLEUNIT[&quot;degree&quot;,0.0174532925199433],
56#&gt;             ID[&quot;EPSG&quot;,8802]],
57#&gt;         PARAMETER[&quot;Scale factor at natural origin&quot;,0.9996,
58#&gt;             SCALEUNIT[&quot;unity&quot;,1],
59#&gt;             ID[&quot;EPSG&quot;,8805]],
60#&gt;         PARAMETER[&quot;False easting&quot;,500000,
61#&gt;             LENGTHUNIT[&quot;metre&quot;,1],
62#&gt;             ID[&quot;EPSG&quot;,8806]],
63#&gt;         PARAMETER[&quot;False northing&quot;,0,
64#&gt;             LENGTHUNIT[&quot;metre&quot;,1],
65#&gt;             ID[&quot;EPSG&quot;,8807]]],
66#&gt;     CS[Cartesian,2],
67#&gt;         AXIS[&quot;(E)&quot;,east,
68#&gt;             ORDER[1],
69#&gt;             LENGTHUNIT[&quot;metre&quot;,1]],
70#&gt;         AXIS[&quot;(N)&quot;,north,
71#&gt;             ORDER[2],
72#&gt;             LENGTHUNIT[&quot;metre&quot;,1]],
73#&gt;     USAGE[
74#&gt;         SCOPE[&quot;Engineering survey, topographic mapping.&quot;],
75#&gt;         AREA[&quot;Between 6°W and 0°W, northern hemisphere between equator and 84°N, onshore and offshore. Algeria. Burkina Faso. Côte' Ivoire (Ivory Coast). Faroe Islands - offshore. France. Ghana. Gibraltar. Ireland - offshore Irish Sea. Mali. Mauritania. Morocco. Spain. United Kingdom (UK).&quot;],
76#&gt;         BBOX[0,-6,84,0]],
77#&gt;     ID[&quot;EPSG&quot;,32630]]
78library(sf)
79
80nc_4267 &lt;- read_sf(system.file(&quot;shape/nc.shp&quot;, package=&quot;sf&quot;))
81nc_3857 &lt;- st_transform(nc_4267, 3857)
82
83st_crs(nc_4267, parameters = TRUE)$units_gdal
84#&gt; [1] &quot;degree&quot;
85st_crs(nc_3857, parameters = TRUE)$units_gdal
86#&gt; [1] &quot;metre&quot;
87st_is_longlat(nc_4267)
88#&gt; [1] TRUE
89st_is_longlat(nc_3857)
90#&gt; [1] FALSE
91

Source https://stackoverflow.com/questions/70436019

Community Discussions contain sources that include Stack Exchange Network

Tutorials and Learning Resources in Unity

Share this Page

share link

Get latest updates on Unity