rngine 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,28 +21,33 @@ configure({
21
21
  entities: [
22
22
  {
23
23
  id: 'player',
24
- px: 400,
25
- py: 400,
26
- width: 40,
27
- height: 40,
28
- color: '#00ff00',
24
+ px: 420,
25
+ py: 420,
26
+ color: '#0f0',
27
+ shape: {
28
+ radius: 20,
29
+ },
29
30
  },
30
31
  {
31
32
  id: 'enemy_1',
32
- px: 100,
33
- py: 100,
34
- width: 40,
35
- height: 40,
36
- color: '#ff0000',
33
+ px: 120,
34
+ py: 120,
35
+ color: '#f00',
36
+ shape: {
37
+ width: 40,
38
+ height: 40,
39
+ },
37
40
  vx: 1000,
38
41
  },
39
42
  {
40
43
  id: 'enemy_2',
41
- px: 200,
42
- py: 200,
43
- width: 40,
44
- height: 40,
45
- color: '#ff0000',
44
+ px: 220,
45
+ py: 220,
46
+ color: '#f00',
47
+ shape: {
48
+ width: 40,
49
+ height: 40,
50
+ },
46
51
  vx: -1000,
47
52
  },
48
53
  ],
@@ -53,7 +58,10 @@ configure({
53
58
  onTick: (enemies) => {
54
59
  enemies.forEach((enemy) => {
55
60
  // reverse direction when reaching screen edges
56
- if (enemy.px <= 0 || enemy.px + enemy.width >= 800) {
61
+ if (
62
+ enemy.px - enemy.width / 2 <= 0 ||
63
+ enemy.px + enemy.width / 2 >= 800
64
+ ) {
57
65
  update({ id: enemy.id, vx: -enemy.vx });
58
66
  }
59
67
  });
@@ -70,7 +78,7 @@ export default function App() {
70
78
 
71
79
  ## Concepts
72
80
 
73
- **Entities** are the objects in your game world. Each entity has a position, size, color, and velocity. See [`Entity`](./src/nativeTypes.ts).
81
+ **Entities** are the objects in your game world. Each entity has a position, shape, color, and velocity. See [`Entity`](./src/nativeTypes.ts).
74
82
 
75
83
  **Systems** define your game logic. Each system optionally declares which entities it cares about via `entities`, which collision pairs to watch via `collisions`, or both. Systems run every tick receiving the resolved entities and any active collisions. See [`System`](./src/types.ts).
76
84
 
@@ -13,6 +13,7 @@ add_library(
13
13
  src/main/cpp/GameLoopJNI.cpp
14
14
  ../shared/GameLoop.cpp
15
15
  ../shared/GameMethods.cpp
16
+ ../shared/CollisionUtils.cpp
16
17
  )
17
18
 
18
19
  # Add Nitrogen specs :)
@@ -3,6 +3,7 @@
3
3
  #include <cstdint>
4
4
  #include <jni.h>
5
5
  #include <mutex>
6
+ #include <variant>
6
7
 
7
8
  extern "C" {
8
9
  JNIEXPORT jbyteArray JNICALL
@@ -46,14 +47,37 @@ Java_com_margelo_nitro_rngine_GameView_getSnapshot(JNIEnv *env, jobject) {
46
47
  }
47
48
 
48
49
  for (const auto &[_, entitySnapshot] : entitiesSnapshot) {
49
- const auto entitySnapshotLeft =
50
- static_cast<float>(entitySnapshot.px - (entitySnapshot.width / 2));
51
- const auto entitySnapshotRight =
52
- static_cast<float>(entitySnapshot.px + (entitySnapshot.width / 2));
53
- const auto entitySnapshotTop =
54
- static_cast<float>(entitySnapshot.py - (entitySnapshot.height / 2));
55
- const auto entitySnapshotBottom =
56
- static_cast<float>(entitySnapshot.py + (entitySnapshot.height / 2));
50
+ uint8_t shapeType{0};
51
+ float entitySnapshotLeft{0}, entitySnapshotRight{0}, entitySnapshotTop{0},
52
+ entitySnapshotBottom{0}, circleRadius{0};
53
+
54
+ std::visit(
55
+ [&](const auto &shape) {
56
+ using T = std::decay_t<decltype(shape)>;
57
+ if constexpr (std::is_same_v<T, margelo::nitro::rngine::Rect>) {
58
+ shapeType = 0;
59
+ entitySnapshotLeft =
60
+ static_cast<float>(entitySnapshot.px - shape.width / 2.0);
61
+ entitySnapshotRight =
62
+ static_cast<float>(entitySnapshot.px + shape.width / 2.0);
63
+ entitySnapshotTop =
64
+ static_cast<float>(entitySnapshot.py - shape.height / 2.0);
65
+ entitySnapshotBottom =
66
+ static_cast<float>(entitySnapshot.py + shape.height / 2.0);
67
+ } else {
68
+ shapeType = 1;
69
+ circleRadius = static_cast<float>(shape.radius);
70
+ entitySnapshotLeft =
71
+ static_cast<float>(entitySnapshot.px - shape.radius);
72
+ entitySnapshotRight =
73
+ static_cast<float>(entitySnapshot.px + shape.radius);
74
+ entitySnapshotTop =
75
+ static_cast<float>(entitySnapshot.py - shape.radius);
76
+ entitySnapshotBottom =
77
+ static_cast<float>(entitySnapshot.py + shape.radius);
78
+ }
79
+ },
80
+ entitySnapshot.shape);
57
81
 
58
82
  if (entitySnapshotRight < 0 || entitySnapshotLeft > screenSnapshot.width ||
59
83
  entitySnapshotBottom < 0 || entitySnapshotTop > screenSnapshot.height) {
@@ -65,10 +89,19 @@ Java_com_margelo_nitro_rngine_GameView_getSnapshot(JNIEnv *env, jobject) {
65
89
  const auto entitySnapshotAsset =
66
90
  static_cast<int32_t>(entitySnapshot.asset.value_or(0));
67
91
 
68
- writeFloat(entitySnapshotLeft);
69
- writeFloat(entitySnapshotRight);
70
- writeFloat(entitySnapshotTop);
71
- writeFloat(entitySnapshotBottom);
92
+ buffer.push_back(shapeType);
93
+
94
+ if (shapeType == 0) {
95
+ writeFloat(entitySnapshotLeft);
96
+ writeFloat(entitySnapshotRight);
97
+ writeFloat(entitySnapshotTop);
98
+ writeFloat(entitySnapshotBottom);
99
+ } else {
100
+ writeFloat(static_cast<float>(entitySnapshot.px));
101
+ writeFloat(static_cast<float>(entitySnapshot.py));
102
+ writeFloat(circleRadius);
103
+ }
104
+
72
105
  writeU32(entitySnapshotColor);
73
106
  writeI32(entitySnapshotAsset);
74
107
 
@@ -1,7 +1,7 @@
1
1
  package com.margelo.nitro.rngine
2
2
 
3
3
  import android.content.Context
4
- import android.graphics.Color
4
+ import android.graphics.Canvas
5
5
  import android.graphics.Paint
6
6
  import android.util.AttributeSet
7
7
  import android.view.SurfaceView
@@ -23,52 +23,93 @@ class GameView(
23
23
  var onDetached: () -> Unit = {}
24
24
 
25
25
  private val paint = Paint().apply {
26
- color = Color.TRANSPARENT
27
26
  style = Paint.Style.FILL
28
27
  }
29
28
 
30
- private fun drawRenderable(
31
- canvas: android.graphics.Canvas,
29
+ private fun drawAsset(
30
+ canvas: Canvas,
32
31
  left: Float,
33
32
  top: Float,
34
33
  right: Float,
35
34
  bottom: Float,
36
- color: Int,
37
35
  asset: Int,
38
36
  progress: Float?,
39
- clampedLeft: Float = left,
40
- clampedTop: Float = top,
41
- clampedRight: Float = right,
42
- clampedBottom: Float = bottom,
43
37
  ) {
44
- paint.color = color
45
- canvas.drawRect(clampedLeft, clampedTop, clampedRight, clampedBottom, paint)
46
-
47
- canvas.withClip(clampedLeft, clampedTop, clampedRight, clampedBottom) {
48
- withTranslation(left, top) {
49
- when (val resolvedAsset = GameAssets.getAsset(asset)) {
50
- is Asset.Svg -> {
51
- scale(
52
- (right - left) / resolvedAsset.picture.width,
53
- (bottom - top) / resolvedAsset.picture.height
54
- )
55
- drawPicture(resolvedAsset.picture)
56
- }
38
+ canvas.withTranslation(left, top) {
39
+ when (val resolvedAsset = GameAssets.getAsset(asset)) {
40
+ is Asset.Svg -> {
41
+ scale(
42
+ (right - left) / resolvedAsset.picture.width,
43
+ (bottom - top) / resolvedAsset.picture.height
44
+ )
45
+ drawPicture(resolvedAsset.picture)
46
+ }
57
47
 
58
- is Asset.Lottie -> {
59
- progress?.let {
60
- resolvedAsset.drawable.progress = it
61
- }
62
- resolvedAsset.drawable.setBounds(0, 0, (right - left).toInt(), (bottom - top).toInt())
63
- resolvedAsset.drawable.draw(canvas)
48
+ is Asset.Lottie -> {
49
+ progress?.let {
50
+ resolvedAsset.drawable.progress = it
64
51
  }
65
-
66
- null -> {}
52
+ resolvedAsset.drawable.setBounds(0, 0, (right - left).toInt(), (bottom - top).toInt())
53
+ resolvedAsset.drawable.draw(canvas)
67
54
  }
55
+
56
+ null -> {}
68
57
  }
69
58
  }
70
59
  }
71
60
 
61
+ private fun drawRect(
62
+ canvas: Canvas,
63
+ left: Float,
64
+ top: Float,
65
+ right: Float,
66
+ bottom: Float,
67
+ color: Int,
68
+ asset: Int,
69
+ progress: Float?,
70
+ ) {
71
+ paint.color = color
72
+ canvas.drawRect(left, top, right, bottom, paint)
73
+
74
+ drawAsset(
75
+ canvas,
76
+ left,
77
+ top,
78
+ right,
79
+ bottom,
80
+ asset,
81
+ progress
82
+ )
83
+ }
84
+
85
+ private fun drawCircle(
86
+ canvas: Canvas,
87
+ cx: Float,
88
+ cy: Float,
89
+ radius: Float,
90
+ color: Int,
91
+ asset: Int,
92
+ progress: Float?,
93
+ ) {
94
+ val left = cx - radius
95
+ val top = cy - radius
96
+ val right = cx + radius
97
+ val bottom = cy + radius
98
+
99
+ paint.color = color
100
+ canvas.drawCircle(cx, cy, radius, paint)
101
+
102
+ drawAsset(
103
+ canvas,
104
+ left,
105
+ top,
106
+ right,
107
+ bottom,
108
+ asset,
109
+ progress,
110
+ )
111
+ }
112
+
72
113
  init {
73
114
  addOnAttachStateChangeListener(
74
115
  object : OnAttachStateChangeListener {
@@ -91,7 +132,7 @@ class GameView(
91
132
  val screenRight = screenLeft + snapshot.screen.width * scale
92
133
  val screenBottom = screenTop + snapshot.screen.height * scale
93
134
 
94
- drawRenderable(
135
+ drawRect(
95
136
  canvas,
96
137
  screenLeft,
97
138
  screenTop,
@@ -102,31 +143,44 @@ class GameView(
102
143
  snapshot.screen.progress,
103
144
  )
104
145
 
105
- snapshot.rects.forEach { rect ->
106
- val left = rect.left * scale + screenLeft
107
- val top = rect.top * scale + screenTop
108
- val right = rect.right * scale + screenLeft
109
- val bottom = rect.bottom * scale + screenTop
110
-
111
- val clampedLeft = left.coerceAtLeast(screenLeft)
112
- val clampedTop = top.coerceAtLeast(screenTop)
113
- val clampedRight = right.coerceAtMost(screenLeft + snapshot.screen.width * scale)
114
- val clampedBottom = bottom.coerceAtMost(screenTop + snapshot.screen.height * scale)
115
-
116
- drawRenderable(
117
- canvas,
118
- left,
119
- top,
120
- right,
121
- bottom,
122
- rect.color,
123
- rect.asset,
124
- rect.progress,
125
- clampedLeft,
126
- clampedTop,
127
- clampedRight,
128
- clampedBottom
129
- )
146
+ canvas.withClip(screenLeft, screenTop, screenRight, screenBottom) {
147
+ snapshot.shapes.forEach { shape ->
148
+ when (shape) {
149
+ is Shape.Rect -> {
150
+ val left = shape.left * scale + screenLeft
151
+ val top = shape.top * scale + screenTop
152
+ val right = shape.right * scale + screenLeft
153
+ val bottom = shape.bottom * scale + screenTop
154
+
155
+ drawRect(
156
+ canvas,
157
+ left,
158
+ top,
159
+ right,
160
+ bottom,
161
+ shape.color,
162
+ shape.asset,
163
+ shape.progress,
164
+ )
165
+ }
166
+
167
+ is Shape.Circle -> {
168
+ val cx = shape.px * scale + screenLeft
169
+ val cy = shape.py * scale + screenTop
170
+ val radius = shape.radius * scale
171
+
172
+ drawCircle(
173
+ canvas,
174
+ cx,
175
+ cy,
176
+ radius,
177
+ shape.color,
178
+ shape.asset,
179
+ shape.progress,
180
+ )
181
+ }
182
+ }
183
+ }
130
184
  }
131
185
  holder.unlockCanvasAndPost(canvas)
132
186
  }
@@ -0,0 +1,26 @@
1
+ package com.margelo.nitro.rngine
2
+
3
+ sealed class Shape(
4
+ val color: Int,
5
+ val asset: Int,
6
+ val progress: Float?,
7
+ ) {
8
+ class Rect(
9
+ val left: Float,
10
+ val right: Float,
11
+ val top: Float,
12
+ val bottom: Float,
13
+ color: Int,
14
+ asset: Int,
15
+ progress: Float?,
16
+ ) : Shape(color, asset, progress)
17
+
18
+ class Circle(
19
+ val px: Float,
20
+ val py: Float,
21
+ val radius: Float,
22
+ color: Int,
23
+ asset: Int,
24
+ progress: Float?,
25
+ ) : Shape(color, asset, progress)
26
+ }
@@ -1,3 +1,3 @@
1
1
  package com.margelo.nitro.rngine
2
2
 
3
- data class Snapshot(val screen: Screen, val rects: List<Rect>)
3
+ data class Snapshot(val screen: Screen, val shapes: List<Shape>)
@@ -1,5 +1,6 @@
1
1
  package com.margelo.nitro.rngine
2
2
 
3
+ import android.util.Log
3
4
  import java.nio.ByteBuffer
4
5
  import java.nio.ByteOrder
5
6
 
@@ -14,20 +15,39 @@ object SnapshotSerializer {
14
15
  val progress = if (asset < 0) buffer.float else null
15
16
 
16
17
  val screen = Screen(width, height, color, asset, progress)
17
- val rects = ArrayList<Rect>()
18
-
19
- while (buffer.remaining() >= 24) {
20
- val left = buffer.float
21
- val right = buffer.float
22
- val top = buffer.float
23
- val bottom = buffer.float
24
- val color = buffer.int
25
- val asset = buffer.int
26
- val progress = if (asset < 0) buffer.float else null
27
-
28
- rects.add(Rect(left, right, top, bottom, color, asset, progress))
18
+ val shapes = ArrayList<Shape>()
19
+
20
+ while (buffer.hasRemaining()) {
21
+ when (val shapeType = buffer.get().toInt()) {
22
+ 0 -> {
23
+ val left = buffer.float
24
+ val right = buffer.float
25
+ val top = buffer.float
26
+ val bottom = buffer.float
27
+
28
+ val color = buffer.int
29
+ val asset = buffer.int
30
+ val progress = if (asset < 0) buffer.float else null
31
+
32
+ shapes.add(Shape.Rect(left, right, top, bottom, color, asset, progress))
33
+ }
34
+
35
+ 1 -> {
36
+ val px = buffer.float
37
+ val py = buffer.float
38
+ val radius = buffer.float
39
+
40
+ val color = buffer.int
41
+ val asset = buffer.int
42
+ val progress = if (asset < 0) buffer.float else null
43
+
44
+ shapes.add(Shape.Circle(px, py, radius, color, asset, progress))
45
+ }
46
+
47
+ else -> Log.e("SnapshotSerializer", "Unknown shapeType: $shapeType")
48
+ }
29
49
  }
30
50
 
31
- return Snapshot(screen, rects)
51
+ return Snapshot(screen, shapes)
32
52
  }
33
53
  }
@@ -4,12 +4,20 @@ interface Positioned {
4
4
  /** Y position in game units. */
5
5
  py: number;
6
6
  }
7
- interface Shaped {
7
+ interface Rect {
8
8
  /** Width in game units. */
9
9
  width: number;
10
10
  /** Height in game units. */
11
11
  height: number;
12
12
  }
13
+ interface Circle {
14
+ /** Radius in game units. */
15
+ radius: number;
16
+ }
17
+ interface Shaped {
18
+ /** Shape of the entity. */
19
+ shape: Rect | Circle;
20
+ }
13
21
  interface Renderable {
14
22
  /** Fill color as a hex string e.g. `'#00ff00'`. */
15
23
  color?: string;
@@ -24,7 +32,7 @@ interface Kinematic {
24
32
  /** Velocity on the Y axis in game units per second. */
25
33
  vy?: number;
26
34
  }
27
- export interface Screen extends Shaped, Renderable {
35
+ export interface Screen extends Rect, Renderable {
28
36
  }
29
37
  export interface Entity extends Positioned, Shaped, Renderable, Kinematic {
30
38
  /** Unique identifier. Use `_` as a separator for group queries e.g. `'enemy_1'`. */
@@ -1 +1 @@
1
- {"version":3,"file":"nativeTypes.d.ts","sourceRoot":"","sources":["../../../src/nativeTypes.ts"],"names":[],"mappings":"AAAA,UAAU,UAAU;IAClB,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,UAAU,MAAM;IACd,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,UAAU;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,SAAS;IACjB,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,MAAO,SAAQ,MAAM,EAAE,UAAU;CAAG;AAErD,MAAM,WAAW,MAAO,SAAQ,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS;IACvE,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,YAAa,SAAQ,OAAO,CAC3C,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAC7C;IACC,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,CAAC,EAAE,MAAM,CAAC;IACV,yDAAyD;IACzD,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,CAAC,EAAE,MAAM,CAAC;IACV,yDAAyD;IACzD,CAAC,EAAE,MAAM,CAAC;IACV,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,MAAM;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,mEAAmE;IACnE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,MAAM,CAAC;CACjE"}
1
+ {"version":3,"file":"nativeTypes.d.ts","sourceRoot":"","sources":["../../../src/nativeTypes.ts"],"names":[],"mappings":"AAAA,UAAU,UAAU;IAClB,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,UAAU,IAAI;IACZ,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,MAAM;IACd,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,MAAM;IACd,2BAA2B;IAC3B,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC;CACtB;AAED,UAAU,UAAU;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,SAAS;IACjB,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,MAAO,SAAQ,IAAI,EAAE,UAAU;CAAG;AAEnD,MAAM,WAAW,MAAO,SAAQ,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS;IACvE,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,YAAa,SAAQ,OAAO,CAC3C,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAC7C;IACC,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,CAAC,EAAE,MAAM,CAAC;IACV,yDAAyD;IACzD,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,CAAC,EAAE,MAAM,CAAC;IACV,yDAAyD;IACzD,CAAC,EAAE,MAAM,CAAC;IACV,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,MAAM;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,mEAAmE;IACnE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,MAAM,CAAC;CACjE"}
@@ -0,0 +1,83 @@
1
+ ///
2
+ /// Circle.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
11
+ #include <NitroModules/JSIConverter.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
16
+ #include <NitroModules/NitroDefines.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/JSIHelpers.hpp>)
21
+ #include <NitroModules/JSIHelpers.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+ #if __has_include(<NitroModules/PropNameIDCache.hpp>)
26
+ #include <NitroModules/PropNameIDCache.hpp>
27
+ #else
28
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
+ #endif
30
+
31
+
32
+
33
+
34
+
35
+ namespace margelo::nitro::rngine {
36
+
37
+ /**
38
+ * A struct which can be represented as a JavaScript object (Circle).
39
+ */
40
+ struct Circle final {
41
+ public:
42
+ double radius SWIFT_PRIVATE;
43
+
44
+ public:
45
+ Circle() = default;
46
+ explicit Circle(double radius): radius(radius) {}
47
+
48
+ public:
49
+ friend bool operator==(const Circle& lhs, const Circle& rhs) = default;
50
+ };
51
+
52
+ } // namespace margelo::nitro::rngine
53
+
54
+ namespace margelo::nitro {
55
+
56
+ // C++ Circle <> JS Circle (object)
57
+ template <>
58
+ struct JSIConverter<margelo::nitro::rngine::Circle> final {
59
+ static inline margelo::nitro::rngine::Circle fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
60
+ jsi::Object obj = arg.asObject(runtime);
61
+ return margelo::nitro::rngine::Circle(
62
+ JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "radius")))
63
+ );
64
+ }
65
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rngine::Circle& arg) {
66
+ jsi::Object obj(runtime);
67
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "radius"), JSIConverter<double>::toJSI(runtime, arg.radius));
68
+ return obj;
69
+ }
70
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
71
+ if (!value.isObject()) {
72
+ return false;
73
+ }
74
+ jsi::Object obj = value.getObject(runtime);
75
+ if (!nitro::isPlainObject(runtime, obj)) {
76
+ return false;
77
+ }
78
+ if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "radius")))) return false;
79
+ return true;
80
+ }
81
+ };
82
+
83
+ } // namespace margelo::nitro
@@ -28,9 +28,15 @@
28
28
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
29
  #endif
30
30
 
31
-
31
+ // Forward declaration of `Rect` to properly resolve imports.
32
+ namespace margelo::nitro::rngine { struct Rect; }
33
+ // Forward declaration of `Circle` to properly resolve imports.
34
+ namespace margelo::nitro::rngine { struct Circle; }
32
35
 
33
36
  #include <string>
37
+ #include "Rect.hpp"
38
+ #include "Circle.hpp"
39
+ #include <variant>
34
40
  #include <optional>
35
41
 
36
42
  namespace margelo::nitro::rngine {
@@ -43,8 +49,7 @@ namespace margelo::nitro::rngine {
43
49
  std::string id SWIFT_PRIVATE;
44
50
  double px SWIFT_PRIVATE;
45
51
  double py SWIFT_PRIVATE;
46
- double width SWIFT_PRIVATE;
47
- double height SWIFT_PRIVATE;
52
+ std::variant<Rect, Circle> shape SWIFT_PRIVATE;
48
53
  std::optional<std::string> color SWIFT_PRIVATE;
49
54
  std::optional<double> asset SWIFT_PRIVATE;
50
55
  std::optional<double> progress SWIFT_PRIVATE;
@@ -53,7 +58,7 @@ namespace margelo::nitro::rngine {
53
58
 
54
59
  public:
55
60
  Entity() = default;
56
- explicit Entity(std::string id, double px, double py, double width, double height, std::optional<std::string> color, std::optional<double> asset, std::optional<double> progress, std::optional<double> vx, std::optional<double> vy): id(id), px(px), py(py), width(width), height(height), color(color), asset(asset), progress(progress), vx(vx), vy(vy) {}
61
+ explicit Entity(std::string id, double px, double py, std::variant<Rect, Circle> shape, std::optional<std::string> color, std::optional<double> asset, std::optional<double> progress, std::optional<double> vx, std::optional<double> vy): id(id), px(px), py(py), shape(shape), color(color), asset(asset), progress(progress), vx(vx), vy(vy) {}
57
62
 
58
63
  public:
59
64
  friend bool operator==(const Entity& lhs, const Entity& rhs) = default;
@@ -72,8 +77,7 @@ namespace margelo::nitro {
72
77
  JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))),
73
78
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "px"))),
74
79
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "py"))),
75
- JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))),
76
- JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))),
80
+ JSIConverter<std::variant<margelo::nitro::rngine::Rect, margelo::nitro::rngine::Circle>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "shape"))),
77
81
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color"))),
78
82
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "asset"))),
79
83
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "progress"))),
@@ -86,8 +90,7 @@ namespace margelo::nitro {
86
90
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter<std::string>::toJSI(runtime, arg.id));
87
91
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "px"), JSIConverter<double>::toJSI(runtime, arg.px));
88
92
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "py"), JSIConverter<double>::toJSI(runtime, arg.py));
89
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter<double>::toJSI(runtime, arg.width));
90
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter<double>::toJSI(runtime, arg.height));
93
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "shape"), JSIConverter<std::variant<margelo::nitro::rngine::Rect, margelo::nitro::rngine::Circle>>::toJSI(runtime, arg.shape));
91
94
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "color"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.color));
92
95
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "asset"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.asset));
93
96
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "progress"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.progress));
@@ -106,8 +109,7 @@ namespace margelo::nitro {
106
109
  if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false;
107
110
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "px")))) return false;
108
111
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "py")))) return false;
109
- if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false;
110
- if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false;
112
+ if (!JSIConverter<std::variant<margelo::nitro::rngine::Rect, margelo::nitro::rngine::Circle>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "shape")))) return false;
111
113
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color")))) return false;
112
114
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "asset")))) return false;
113
115
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "progress")))) return false;
@@ -28,10 +28,16 @@
28
28
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
29
  #endif
30
30
 
31
-
31
+ // Forward declaration of `Rect` to properly resolve imports.
32
+ namespace margelo::nitro::rngine { struct Rect; }
33
+ // Forward declaration of `Circle` to properly resolve imports.
34
+ namespace margelo::nitro::rngine { struct Circle; }
32
35
 
33
36
  #include <string>
34
37
  #include <optional>
38
+ #include "Rect.hpp"
39
+ #include "Circle.hpp"
40
+ #include <variant>
35
41
 
36
42
  namespace margelo::nitro::rngine {
37
43
 
@@ -43,8 +49,7 @@ namespace margelo::nitro::rngine {
43
49
  std::string id SWIFT_PRIVATE;
44
50
  std::optional<double> px SWIFT_PRIVATE;
45
51
  std::optional<double> py SWIFT_PRIVATE;
46
- std::optional<double> width SWIFT_PRIVATE;
47
- std::optional<double> height SWIFT_PRIVATE;
52
+ std::optional<std::variant<Rect, Circle>> shape SWIFT_PRIVATE;
48
53
  std::optional<std::string> color SWIFT_PRIVATE;
49
54
  std::optional<double> asset SWIFT_PRIVATE;
50
55
  std::optional<double> progress SWIFT_PRIVATE;
@@ -53,7 +58,7 @@ namespace margelo::nitro::rngine {
53
58
 
54
59
  public:
55
60
  EntityUpdate() = default;
56
- explicit EntityUpdate(std::string id, std::optional<double> px, std::optional<double> py, std::optional<double> width, std::optional<double> height, std::optional<std::string> color, std::optional<double> asset, std::optional<double> progress, std::optional<double> vx, std::optional<double> vy): id(id), px(px), py(py), width(width), height(height), color(color), asset(asset), progress(progress), vx(vx), vy(vy) {}
61
+ explicit EntityUpdate(std::string id, std::optional<double> px, std::optional<double> py, std::optional<std::variant<Rect, Circle>> shape, std::optional<std::string> color, std::optional<double> asset, std::optional<double> progress, std::optional<double> vx, std::optional<double> vy): id(id), px(px), py(py), shape(shape), color(color), asset(asset), progress(progress), vx(vx), vy(vy) {}
57
62
 
58
63
  public:
59
64
  friend bool operator==(const EntityUpdate& lhs, const EntityUpdate& rhs) = default;
@@ -72,8 +77,7 @@ namespace margelo::nitro {
72
77
  JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id"))),
73
78
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "px"))),
74
79
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "py"))),
75
- JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))),
76
- JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))),
80
+ JSIConverter<std::optional<std::variant<margelo::nitro::rngine::Rect, margelo::nitro::rngine::Circle>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "shape"))),
77
81
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color"))),
78
82
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "asset"))),
79
83
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "progress"))),
@@ -86,8 +90,7 @@ namespace margelo::nitro {
86
90
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "id"), JSIConverter<std::string>::toJSI(runtime, arg.id));
87
91
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "px"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.px));
88
92
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "py"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.py));
89
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.width));
90
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.height));
93
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "shape"), JSIConverter<std::optional<std::variant<margelo::nitro::rngine::Rect, margelo::nitro::rngine::Circle>>>::toJSI(runtime, arg.shape));
91
94
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "color"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.color));
92
95
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "asset"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.asset));
93
96
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "progress"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.progress));
@@ -106,8 +109,7 @@ namespace margelo::nitro {
106
109
  if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "id")))) return false;
107
110
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "px")))) return false;
108
111
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "py")))) return false;
109
- if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false;
110
- if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false;
112
+ if (!JSIConverter<std::optional<std::variant<margelo::nitro::rngine::Rect, margelo::nitro::rngine::Circle>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "shape")))) return false;
111
113
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color")))) return false;
112
114
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "asset")))) return false;
113
115
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "progress")))) return false;
@@ -0,0 +1,87 @@
1
+ ///
2
+ /// Rect.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/JSIConverter.hpp>)
11
+ #include <NitroModules/JSIConverter.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+ #if __has_include(<NitroModules/NitroDefines.hpp>)
16
+ #include <NitroModules/NitroDefines.hpp>
17
+ #else
18
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
19
+ #endif
20
+ #if __has_include(<NitroModules/JSIHelpers.hpp>)
21
+ #include <NitroModules/JSIHelpers.hpp>
22
+ #else
23
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
24
+ #endif
25
+ #if __has_include(<NitroModules/PropNameIDCache.hpp>)
26
+ #include <NitroModules/PropNameIDCache.hpp>
27
+ #else
28
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
+ #endif
30
+
31
+
32
+
33
+
34
+
35
+ namespace margelo::nitro::rngine {
36
+
37
+ /**
38
+ * A struct which can be represented as a JavaScript object (Rect).
39
+ */
40
+ struct Rect final {
41
+ public:
42
+ double width SWIFT_PRIVATE;
43
+ double height SWIFT_PRIVATE;
44
+
45
+ public:
46
+ Rect() = default;
47
+ explicit Rect(double width, double height): width(width), height(height) {}
48
+
49
+ public:
50
+ friend bool operator==(const Rect& lhs, const Rect& rhs) = default;
51
+ };
52
+
53
+ } // namespace margelo::nitro::rngine
54
+
55
+ namespace margelo::nitro {
56
+
57
+ // C++ Rect <> JS Rect (object)
58
+ template <>
59
+ struct JSIConverter<margelo::nitro::rngine::Rect> final {
60
+ static inline margelo::nitro::rngine::Rect fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
61
+ jsi::Object obj = arg.asObject(runtime);
62
+ return margelo::nitro::rngine::Rect(
63
+ JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))),
64
+ JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))
65
+ );
66
+ }
67
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rngine::Rect& arg) {
68
+ jsi::Object obj(runtime);
69
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter<double>::toJSI(runtime, arg.width));
70
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter<double>::toJSI(runtime, arg.height));
71
+ return obj;
72
+ }
73
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
74
+ if (!value.isObject()) {
75
+ return false;
76
+ }
77
+ jsi::Object obj = value.getObject(runtime);
78
+ if (!nitro::isPlainObject(runtime, obj)) {
79
+ return false;
80
+ }
81
+ if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false;
82
+ if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false;
83
+ return true;
84
+ }
85
+ };
86
+
87
+ } // namespace margelo::nitro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rngine",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "React Native 2D game engine powered by Nitro Modules",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -0,0 +1,67 @@
1
+ #include "CollisionUtils.hpp"
2
+
3
+ namespace margelo::nitro::rngine::CollisionUtils {
4
+ std::optional<double> overlap(const Rect &a, double ax, double ay,
5
+ const Rect &b, double bx, double by) {
6
+
7
+ double aLeft = ax - a.width / 2.0, aRight = ax + a.width / 2.0;
8
+ double aTop = ay - a.height / 2.0, aBottom = ay + a.height / 2.0;
9
+ double bLeft = bx - b.width / 2.0, bRight = bx + b.width / 2.0;
10
+ double bTop = by - b.height / 2.0, bBottom = by + b.height / 2.0;
11
+
12
+ double overlapX = std::min(aRight, bRight) - std::max(aLeft, bLeft);
13
+ double overlapY = std::min(aBottom, bBottom) - std::max(aTop, bTop);
14
+
15
+ if (overlapX > 0 && overlapY > 0)
16
+ return std::min(overlapX, overlapY);
17
+ return std::nullopt;
18
+ }
19
+
20
+ std::optional<double> overlap(const Circle &a, double ax, double ay,
21
+ const Circle &b, double bx, double by) {
22
+ double dx = bx - ax, dy = by - ay;
23
+ double dist = std::sqrt(dx * dx + dy * dy);
24
+ double depth = (a.radius + b.radius) - dist;
25
+
26
+ if (depth > 0)
27
+ return depth;
28
+ return std::nullopt;
29
+ }
30
+
31
+ std::optional<double> overlap(const Rect &a, double ax, double ay,
32
+ const Circle &b, double bx, double by) {
33
+ double left = ax - a.width / 2.0, right = ax + a.width / 2.0;
34
+ double top = ay - a.height / 2.0, bottom = ay + a.height / 2.0;
35
+
36
+ double closestX = std::clamp(bx, left, right);
37
+ double closestY = std::clamp(by, top, bottom);
38
+
39
+ double dx = bx - closestX, dy = by - closestY;
40
+ double distSq = dx * dx + dy * dy;
41
+
42
+ if (distSq >= b.radius * b.radius)
43
+ return std::nullopt;
44
+
45
+ double dist = std::sqrt(distSq);
46
+ return b.radius - dist;
47
+ }
48
+
49
+ std::optional<double> overlap(const Circle &a, double ax, double ay,
50
+ const Rect &b, double bx, double by) {
51
+ return overlap(b, bx, by, a, ax, ay);
52
+ }
53
+
54
+ std::optional<double> shapeOverlap(const Entity &entityA,
55
+ const Entity &entityB) {
56
+ return std::visit(
57
+ [&](const auto &shapeA) -> std::optional<double> {
58
+ return std::visit(
59
+ [&](const auto &shapeB) -> std::optional<double> {
60
+ return overlap(shapeA, entityA.px, entityA.py, shapeB, entityB.px,
61
+ entityB.py);
62
+ },
63
+ entityB.shape);
64
+ },
65
+ entityA.shape);
66
+ }
67
+ } // namespace margelo::nitro::rngine::CollisionUtils
@@ -0,0 +1,19 @@
1
+ #pragma once
2
+
3
+ #include "Circle.hpp"
4
+ #include "Entity.hpp"
5
+ #include "Rect.hpp"
6
+ #include <optional>
7
+
8
+ namespace margelo::nitro::rngine::CollisionUtils {
9
+ std::optional<double> overlap(const Rect &a, double ax, double ay,
10
+ const Rect &b, double bx, double by);
11
+ std::optional<double> overlap(const Circle &a, double ax, double ay,
12
+ const Circle &b, double bx, double by);
13
+ std::optional<double> overlap(const Rect &a, double ax, double ay,
14
+ const Circle &b, double bx, double by);
15
+ std::optional<double> overlap(const Circle &a, double ax, double ay,
16
+ const Rect &b, double bx, double by);
17
+ std::optional<double> shapeOverlap(const Entity &entityA,
18
+ const Entity &entityB);
19
+ } // namespace margelo::nitro::rngine::CollisionUtils
@@ -1,5 +1,6 @@
1
1
  #include "GameLoop.hpp"
2
2
  #include "Collision.hpp"
3
+ #include "CollisionUtils.hpp"
3
4
  #include "Entity.hpp"
4
5
  #include <android/log.h>
5
6
  #include <chrono>
@@ -113,8 +114,8 @@ void GameLoop::runSystems() {
113
114
  auto resolvedEntitiesInternalA = resolveEntitiesInternal(a);
114
115
  auto resolvedEntitiesInternalB = resolveEntitiesInternal(b);
115
116
 
116
- for (auto *entityA : resolvedEntitiesInternalA) {
117
- for (auto *entityB : resolvedEntitiesInternalB) {
117
+ for (const auto &entityA : resolvedEntitiesInternalA) {
118
+ for (const auto &entityB : resolvedEntitiesInternalB) {
118
119
  if (entityA == entityB)
119
120
  continue;
120
121
 
@@ -125,22 +126,9 @@ void GameLoop::runSystems() {
125
126
 
126
127
  uniqueEntityIdPairs.insert(uniqueEntityIdPairKey);
127
128
 
128
- double aLeft = entityA->px - entityA->width / 2.0;
129
- double aRight = entityA->px + entityA->width / 2.0;
130
- double aTop = entityA->py - entityA->height / 2.0;
131
- double aBottom = entityA->py + entityA->height / 2.0;
132
-
133
- double bLeft = entityB->px - entityB->width / 2.0;
134
- double bRight = entityB->px + entityB->width / 2.0;
135
- double bTop = entityB->py - entityB->height / 2.0;
136
- double bBottom = entityB->py + entityB->height / 2.0;
137
-
138
- double overlapX = std::min(aRight, bRight) - std::max(aLeft, bLeft);
139
- double overlapY = std::min(aBottom, bBottom) - std::max(aTop, bTop);
140
-
141
- if (overlapX > 0 && overlapY > 0) {
142
- collisions.push_back(Collision(entityA->id, entityB->id,
143
- std::min(overlapX, overlapY)));
129
+ if (auto ov = CollisionUtils::shapeOverlap(*entityA, *entityB)) {
130
+ collisions.push_back(
131
+ Collision(entityA->id, entityB->id, ov.value()));
144
132
  }
145
133
  }
146
134
  }
@@ -127,11 +127,8 @@ void GameMethods::update(const std::vector<EntityUpdate> &updates) {
127
127
  if (update.py.has_value()) {
128
128
  entity->py = update.py.value();
129
129
  }
130
- if (update.width.has_value()) {
131
- entity->width = update.width.value();
132
- }
133
- if (update.height.has_value()) {
134
- entity->height = update.height.value();
130
+ if (update.shape.has_value()) {
131
+ entity->shape = update.shape.value();
135
132
  }
136
133
  if (update.asset.has_value()) {
137
134
  entity->asset = update.asset.value();
@@ -5,13 +5,23 @@ interface Positioned {
5
5
  py: number;
6
6
  }
7
7
 
8
- interface Shaped {
8
+ interface Rect {
9
9
  /** Width in game units. */
10
10
  width: number;
11
11
  /** Height in game units. */
12
12
  height: number;
13
13
  }
14
14
 
15
+ interface Circle {
16
+ /** Radius in game units. */
17
+ radius: number;
18
+ }
19
+
20
+ interface Shaped {
21
+ /** Shape of the entity. */
22
+ shape: Rect | Circle;
23
+ }
24
+
15
25
  interface Renderable {
16
26
  /** Fill color as a hex string e.g. `'#00ff00'`. */
17
27
  color?: string;
@@ -28,7 +38,7 @@ interface Kinematic {
28
38
  vy?: number;
29
39
  }
30
40
 
31
- export interface Screen extends Shaped, Renderable {}
41
+ export interface Screen extends Rect, Renderable {}
32
42
 
33
43
  export interface Entity extends Positioned, Shaped, Renderable, Kinematic {
34
44
  /** Unique identifier. Use `_` as a separator for group queries e.g. `'enemy_1'`. */
@@ -1,11 +0,0 @@
1
- package com.margelo.nitro.rngine
2
-
3
- data class Rect(
4
- val left: Float,
5
- val right: Float,
6
- val top: Float,
7
- val bottom: Float,
8
- val color: Int,
9
- val asset: Int,
10
- val progress: Float?,
11
- )