You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+44-43Lines changed: 44 additions & 43 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -45,62 +45,55 @@ I am using Box2dNet for my own game so you can expect me to add more convenience
45
45
* the timer functions (b2CreateTimer, ..): use .NET timers :)
46
46
* b2DynamicTree_X: little value for much effort on my side. This is the spatial tree used internally by Box2D. Erin exposed these because people may want to use the tree elsewhere, but you don't need these functions for normal Box2D use.
47
47
48
-
# Dealing with pointers (IntPtr)
48
+
# Multi-threading support
49
49
50
-
The largest down-side of PInvoke wrappers is that many C pointers become `IntPtr` in .NET. Because of this, the type that was there in C of the `struct` or `delegate` is lost and replaced by `IntPtr` in C#.
50
+
Box2dNet comes with .NET integration for the new multi-threaded task system that Box2D uses.
51
51
52
-
To help with this, Box2dNet mentions the original C type in the C# generated comments wherever possible. Code completion should therefore show this information. Worst case, you'll have to look in the sources here on gitHub.
52
+
Simply use `B2Api.b2DefaultWorldDef_WithDotNetTpl()` instead of `B2Api.b2DefaultWorldDef`to create your Box2D world:
53
53
54
-
To help you with IntPtrs, the following sections show solutions for most use cases:
54
+
```C#
55
+
varworldDef=useMultiThreading
56
+
?B2Api.b2DefaultWorldDef_WithDotNetTpl() // <---- this is all it takes for default multi threading
57
+
:B2Api.b2DefaultWorldDef();
55
58
56
-
## Callbacks: setting struct.fields that are callback delegates.
59
+
varb2WorldId=B2Api.b2CreateWorld(worldDef);
60
+
```
57
61
58
-
> As of Box2dNet v3.1.5 all Box2D functions that have callback delegates parameters have a companion overload that takes in the strongly typed delegate. eg. use `b2World_SetPreSolveCallback(b2WorldId worldId, **b2PreSolveFcn fcn**, IntPtr context)`, not `b2World_SetPreSolveCallback(b2WorldId worldId, **IntPtr fcn**, IntPtr context)`
62
+
Note that multithreading incurs quite some overhead so you only gain net-profit when you simulate a lot of bodies. Measure your specific use cases!
59
63
60
-
Some Box2D struct fields are delegate pointers. These are `IntPtr` and must be set to a pointer to a method with the same definition as the delegate requires.
64
+
> the 'TPL' in `b2DefaultWorldDef_WithDotNetTpl` stands for Task Parallel Library, which is the name of .NET's Task framework.
61
65
62
-
To do this, you must:
66
+
# Dealing with pointers (IntPtr)
63
67
64
-
* check the generated comments to find the identifier of the original C delegate type
65
-
* write or generate a method that matches the delegate
66
-
* assign a function pointer retrieved with `Marshal.GetFunctionPointerForDelegate` to the IntPtr struct field.
68
+
The largest down-side of PInvoke wrappers is that many C pointers become `IntPtr` in .NET. Because of this, the type that was there in C of the `struct` or `delegate` is lost and replaced by `IntPtr` in C#.
67
69
68
-
Here is an example:
70
+
To help with this, Box2dNet has several helpers to deal with IntPtr, or to remove the need to deal with them at all.
The original C type of the pointer is in the generated comments. Code completion should show this information. Worst case, you'll have to look in the sources here on gitHub.
81
75
82
76
## Reading native arrays from IntPtr (without copying)
83
77
84
-
Some structs received from native Box2D contain arrays. To read those arrays Box2dNet provides convenience method `NativeArrayAsSpan` to loop over the native contents without making temporary copies or allocating an iterator.
78
+
Some structs received from native Box2D contain IntPtrs to arrays. Box2dNet provides companion properties called `~AsSpan` for these with which you can read the data directly from native memory, strongly typed (so no allocations for copies or iterators).
85
79
86
-
Example: field `IntPtr b2ContactEvents.beginEvents` shows in its comment that you should read it as an array of `b2ContactBeginTouchEvent`:
80
+
> If this companion property is not there, you can convert the IntPtr yourself to a Span using Box2dNet's convenience method `NativeArrayAsSpan(count)` which is simply what the `~AsSpan` does behind the scenes.
81
+
82
+
Example: struct `b2ContactEvents` contains an array in field `IntPtr beginEvents` which you can read easily using companion `beginEventsAsSpan`:
Console.WriteLine($"!!!!!!! HIT detected between {@event.shapeIdA} and {@event.shapeIdB}");
94
89
}
95
90
```
96
91
97
-
> Note that you must know the size of the array, which is always provided by Box2D in a sibling field.
98
-
99
92
## Pass a reference to a .NET object into native Box2D as IntPtr (eg. UserData)
100
93
101
-
If you want to pass a .NET object reference into native Box2D, like when tagging a Shape or Body with `userData`, you must pass in an `IntPtr` to the object. But in .NET objects can get relocated by the memory manager.
94
+
If you want to pass a .NET object reference into native Box2D, like when tagging a Shape or Body with `userData`, you must pass in an `IntPtr` to the object. But .NET objects are not guaranteed to stay at the same address in memory.
102
95
103
-
To solution is to allocate a `NativeHandle` for your .NET object and pass the handle's `IntPtr`to Box2D instead of a direct pointer to the instance. Example:
96
+
The solution is to allocate a `NativeHandle` for your .NET object and pass *that*to Box2D. Example:
104
97
105
98
```C#
106
99
_handle=NativeHandle.Alloc(ball); // allocate a IntPtr handle for the .NET object and return it as IntPtr.
@@ -112,7 +105,7 @@ var circle = new b2Circle() { radius = 1 };
After this, when Box2D passes the `IntPtr` back to .NET somewhere, for instance when you call `b2X_GetUserData()`, you can get hold of the corresponding .NET object like this:
108
+
After this, when Box2D passes the `IntPtr` back to .NET somewhere (eg. through `b2X_GetUserData()`) you can get hold of the corresponding .NET object like this:
@@ -126,25 +119,33 @@ When you're fully done with it, eg. when the game object is removed from your ga
126
119
NativeHandle.Free(_handle);
127
120
```
128
121
129
-
> A tip to avoid NativeHandle with UserData: abuse the UserData poointer by setting it to your gameobject's numerical ID (a normal int or long). IntPtr is just a numerical value, it doesn't have to be an actual pointer address: `new IntPtr(entity.Id)` works just fine.
122
+
> A tip to avoid needing to use NativeHandle with UserData: abuse the UserData pointer by setting it to your gameobject's numerical ID (a normal int or long). IntPtr is just a numerical value, it doesn't have to be an actual pointer address: `new IntPtr(entity.Id)` works just fine.
130
123
131
-
#Multi-threading support
124
+
## Callbacks: setting struct.fields that are callback delegates.
132
125
133
-
Box2dNet comes with .NET integration for the new multi-threaded task system that Box2D uses.
126
+
> As of Box2dNet v3.1.5 all Box2D functions that have callback delegates parameters have a companion overload that takes in the strongly typed delegate. eg. use `b2World_SetPreSolveCallback(b2WorldId worldId, **b2PreSolveFcn fcn**, IntPtr context)`, not `b2World_SetPreSolveCallback(b2WorldId worldId, **IntPtr fcn**, IntPtr context)`
134
127
135
-
Simply use `B2Api.b2DefaultWorldDef_WithDotNetTpl()` instead of `B2Api.b2DefaultWorldDef`to create your Box2D world:
128
+
Some Box2D struct fields are delegate pointers. These are `IntPtr` and must be set to a pointer to a method with the same definition as the delegate requires.
136
129
137
-
```C#
138
-
varworldDef=useMultiThreading
139
-
?B2Api.b2DefaultWorldDef_WithDotNetTpl() // <---- this is all it takes for default multi threading
140
-
:B2Api.b2DefaultWorldDef();
130
+
To do this, you must:
141
131
142
-
varb2WorldId=B2Api.b2CreateWorld(worldDef);
143
-
```
132
+
* check the generated comments to find the identifier of the original C delegate type
133
+
* write or generate a method that matches the delegate
134
+
* assign a function pointer retrieved with `Marshal.GetFunctionPointerForDelegate` to the IntPtr struct field.
144
135
145
-
Note that multithreading incurs quite some overhead so you only gain net-profit when you simulate a lot of bodies. Measure your specific use cases!
136
+
Here is an example:
146
137
147
-
> the 'TPL' in `b2DefaultWorldDef_WithDotNetTpl` stands for Task Parallel Library, which is the name of .NET's Task framework.
0 commit comments