rngine 0.8.2 → 0.9.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.
Files changed (43) hide show
  1. package/README.md +28 -21
  2. package/android/CMakeLists.txt +5 -2
  3. package/android/kls_database.db +0 -0
  4. package/android/src/main/java/com/margelo/nitro/rngine/ImageLoaderHelper.kt +56 -0
  5. package/lib/module/GameMethods.js +23 -10
  6. package/lib/module/GameMethods.js.map +1 -1
  7. package/lib/module/GameMethods.nitro.js.map +1 -1
  8. package/lib/module/index.js.map +1 -1
  9. package/lib/typescript/src/GameMethods.d.ts +11 -4
  10. package/lib/typescript/src/GameMethods.d.ts.map +1 -1
  11. package/lib/typescript/src/GameMethods.nitro.d.ts +9 -3
  12. package/lib/typescript/src/GameMethods.nitro.d.ts.map +1 -1
  13. package/lib/typescript/src/index.d.ts +1 -1
  14. package/lib/typescript/src/index.d.ts.map +1 -1
  15. package/lib/typescript/src/nativeTypes.d.ts +12 -2
  16. package/lib/typescript/src/nativeTypes.d.ts.map +1 -1
  17. package/lib/typescript/src/types.d.ts +2 -2
  18. package/lib/typescript/src/types.d.ts.map +1 -1
  19. package/nitrogen/generated/shared/c++/HybridGameMethodsSpec.cpp +3 -2
  20. package/nitrogen/generated/shared/c++/HybridGameMethodsSpec.hpp +6 -2
  21. package/nitrogen/generated/shared/c++/Screen.hpp +9 -1
  22. package/nitrogen/generated/shared/c++/ScreenUpdate.hpp +120 -0
  23. package/nitrogen/generated/shared/c++/System.hpp +8 -11
  24. package/nitrogen/generated/shared/c++/SystemContext.hpp +99 -0
  25. package/package.json +1 -1
  26. package/shared/GameLoop.cpp +16 -33
  27. package/shared/GameMethods.cpp +124 -92
  28. package/shared/GameMethods.hpp +5 -3
  29. package/shared/GameRenderer.cpp +27 -81
  30. package/shared/GameRenderer.hpp +8 -5
  31. package/shared/utils/AssetUtils.cpp +92 -0
  32. package/shared/utils/AssetUtils.hpp +11 -0
  33. package/shared/{CollisionUtils.cpp → utils/CollisionUtils.cpp} +9 -8
  34. package/shared/utils/CollisionUtils.hpp +10 -0
  35. package/src/GameMethods.nitro.ts +12 -2
  36. package/src/GameMethods.ts +23 -11
  37. package/src/index.tsx +1 -0
  38. package/src/nativeTypes.ts +13 -2
  39. package/src/types.ts +2 -2
  40. package/android/src/main/java/com/margelo/nitro/rngine/SvgLoaderHelper.kt +0 -24
  41. package/shared/CollisionUtils.hpp +0 -21
  42. /package/shared/{ColorUtils.cpp → utils/ColorUtils.cpp} +0 -0
  43. /package/shared/{ColorUtils.hpp → utils/ColorUtils.hpp} +0 -0
@@ -24,14 +24,16 @@ public:
24
24
 
25
25
  void despawn(const std::string &id) override;
26
26
 
27
- void update(const std::vector<EntityUpdate> &updates) override;
27
+ void updateEntities(const std::vector<EntityUpdate> &entityUpdates) override;
28
+
29
+ void updateScreen(const ScreenUpdate &screenUpdate) override;
28
30
 
29
31
  bool isAssetLoaded(double id) override;
30
32
 
31
33
  std::shared_ptr<Promise<bool>>
32
34
  loadLottie(double id, const std::string &jsonStr) override;
33
35
 
34
- std::shared_ptr<Promise<bool>> loadSvg(double id,
35
- const std::string &svgUri) override;
36
+ std::shared_ptr<Promise<bool>> loadImage(double id,
37
+ const std::string &svgUri) override;
36
38
  };
37
39
  } // namespace margelo::nitro::rngine
@@ -1,4 +1,5 @@
1
1
  #include "GameRenderer.hpp"
2
+ #include "AssetUtils.hpp"
2
3
  #include "ColorUtils.hpp"
3
4
  #include "Rect.hpp"
4
5
 
@@ -10,6 +11,7 @@
10
11
  #include "include/core/SkColorSpace.h"
11
12
  #include "include/core/SkPaint.h"
12
13
  #include "include/core/SkRect.h"
14
+ #include "include/core/SkRefCnt.h"
13
15
  #include "include/core/SkSurface.h"
14
16
 
15
17
  #include "include/gpu/ganesh/GrBackendSurface.h"
@@ -133,15 +135,20 @@ void GameRenderer::render(const Screen &screen,
133
135
 
134
136
  float scale = std::min(scaleX, scaleY);
135
137
 
136
- float offsetX = (static_cast<float>(_width) - (virtualWidth * scale)) * 0.5f;
137
- float offsetY =
138
- (static_cast<float>(_height) - (virtualHeight * scale)) * 0.5f;
138
+ float offsetX =
139
+ static_cast<float>(_width) * 0.5f - static_cast<float>(screen.px) * scale;
140
+ float offsetY = static_cast<float>(_height) * 0.5f -
141
+ static_cast<float>(screen.py) * scale;
139
142
 
140
143
  canvas->save();
141
144
  canvas->translate(offsetX, offsetY);
142
145
  canvas->scale(scale, scale);
143
146
 
144
- SkRect gameBounds = SkRect::MakeWH(virtualWidth, virtualHeight);
147
+ SkRect gameBounds =
148
+ SkRect::MakeXYWH(static_cast<float>(screen.px) - virtualWidth * 0.5f,
149
+ static_cast<float>(screen.py) - virtualHeight * 0.5f,
150
+ virtualWidth, virtualHeight);
151
+
145
152
  canvas->clipRect(gameBounds);
146
153
 
147
154
  if (screen.color.has_value()) {
@@ -149,44 +156,16 @@ void GameRenderer::render(const Screen &screen,
149
156
  }
150
157
 
151
158
  if (screen.asset.has_value() && screen.asset.value() != 0) {
152
- double assetId = screen.asset.value();
153
-
154
- auto svgIt = _svgCache.find(assetId);
155
- if (svgIt != _svgCache.end() && svgIt->second) {
156
- auto &svgDom = svgIt->second;
157
-
158
- SkSize intrinsicSize = svgDom->containerSize();
159
- if (intrinsicSize.isEmpty()) {
160
- svgDom->setContainerSize(SkSize::Make(virtualWidth, virtualHeight));
161
- intrinsicSize = SkSize::Make(virtualWidth, virtualHeight);
162
- }
163
-
164
- float scaleX = virtualWidth / intrinsicSize.width();
165
- float scaleY = virtualHeight / intrinsicSize.height();
166
-
167
- canvas->save();
168
- if (screen.flipH.has_value() || screen.flipV.has_value()) {
169
- canvas->translate(virtualWidth / 2.f, virtualHeight / 2.f);
170
- canvas->scale(screen.flipH.value_or(false) ? -1.f : 1.f,
171
- screen.flipV.value_or(false) ? -1.f : 1.f);
172
- canvas->translate(-virtualWidth / 2.f, -virtualHeight / 2.f);
173
- }
174
- canvas->scale(scaleX, scaleY);
175
- svgDom->render(canvas);
176
- canvas->restore();
177
- } else {
178
- auto lottieIt = _lottieCache.find(assetId);
179
- if (lottieIt != _lottieCache.end() && lottieIt->second) {
180
- auto &lottie = lottieIt->second;
181
- lottie->seek(screen.progress.value_or(0.0));
182
- SkRect dstBounds = SkRect::MakeWH(virtualWidth, virtualHeight);
183
- lottie->render(canvas, &dstBounds);
184
- }
185
- }
159
+ SkAutoCanvasRestore restore(canvas, true);
160
+ canvas->translate(gameBounds.left(), gameBounds.top());
161
+ AssetUtils::drawAsset(canvas, screen.asset.value(), virtualWidth,
162
+ virtualHeight, screen.flipH.value_or(false),
163
+ screen.flipV.value_or(false),
164
+ screen.progress.value_or(0.0));
186
165
  }
187
166
 
188
167
  for (const auto &[id, entity] : entities) {
189
- if (!isEntityVisible(entity, virtualWidth, virtualHeight)) {
168
+ if (!isEntityVisible(entity, gameBounds)) {
190
169
  continue;
191
170
  }
192
171
 
@@ -219,8 +198,6 @@ void GameRenderer::render(const Screen &screen,
219
198
  }
220
199
 
221
200
  if ((entity.asset.has_value() && entity.asset.value() != 0)) {
222
- double assetId = entity.asset.value();
223
-
224
201
  float width = 0.f, height = 0.f;
225
202
  std::visit(
226
203
  [&](const auto &shape) {
@@ -239,39 +216,10 @@ void GameRenderer::render(const Screen &screen,
239
216
  SkAutoCanvasRestore autoRestore(canvas, true);
240
217
  canvas->translate(px - width / 2, py - height / 2);
241
218
 
242
- if (entity.flipH.has_value() || entity.flipV.has_value()) {
243
- canvas->translate(width / 2.f, height / 2.f);
244
- canvas->scale(entity.flipH.value_or(false) ? -1.f : 1.f,
245
- entity.flipV.value_or(false) ? -1.f : 1.f);
246
- canvas->translate(-width / 2.f, -height / 2.f);
247
- }
248
-
249
- auto svgIt = _svgCache.find(assetId);
250
- if (svgIt != _svgCache.end() && svgIt->second) {
251
- auto &svgDom = svgIt->second;
252
-
253
- SkSize intrinsicSize = svgDom->containerSize();
254
- if (intrinsicSize.isEmpty()) {
255
- svgDom->setContainerSize(SkSize::Make(width, height));
256
- intrinsicSize = SkSize::Make(width, height);
257
- }
258
-
259
- float scaleX = width / intrinsicSize.width();
260
- float scaleY = height / intrinsicSize.height();
261
-
262
- canvas->save();
263
- canvas->scale(scaleX, scaleY);
264
- svgDom->render(canvas);
265
- canvas->restore();
266
- } else {
267
- auto lottieIt = _lottieCache.find(assetId);
268
- if (lottieIt != _lottieCache.end() && lottieIt->second) {
269
- auto &lottie = lottieIt->second;
270
- lottie->seek(entity.progress.value_or(0.0));
271
- SkRect dstBounds = SkRect::MakeWH(width, height);
272
- lottie->render(canvas, &dstBounds);
273
- }
274
- }
219
+ AssetUtils::drawAsset(canvas, entity.asset.value(), width, height,
220
+ entity.flipH.value_or(false),
221
+ entity.flipV.value_or(false),
222
+ entity.progress.value_or(0.0));
275
223
  }
276
224
  }
277
225
  }
@@ -284,15 +232,13 @@ void GameRenderer::render(const Screen &screen,
284
232
  __android_log_print(ANDROID_LOG_DEBUG, "GameRenderer", "render: complete");
285
233
  }
286
234
 
287
- bool GameRenderer::isEntityVisible(const Entity &entity, float screenWidth,
288
- float screenHeight) {
289
- float width = 0.f;
290
- float height = 0.f;
235
+ bool GameRenderer::isEntityVisible(const Entity &entity,
236
+ const SkRect &viewBounds) {
237
+ float width = 0.f, height = 0.f;
291
238
 
292
239
  std::visit(
293
240
  [&](const auto &shape) {
294
241
  using T = std::decay_t<decltype(shape)>;
295
-
296
242
  if constexpr (std::is_same_v<T, Rect>) {
297
243
  width = static_cast<float>(shape.width);
298
244
  height = static_cast<float>(shape.height);
@@ -307,8 +253,8 @@ bool GameRenderer::isEntityVisible(const Entity &entity, float screenWidth,
307
253
  float top = static_cast<float>(entity.py) - height / 2.f;
308
254
  float bottom = static_cast<float>(entity.py) + height / 2.f;
309
255
 
310
- return right > 0.f && left < screenWidth && bottom > 0.f &&
311
- top < screenHeight;
256
+ return right > viewBounds.left() && left < viewBounds.right() &&
257
+ bottom > viewBounds.top() && top < viewBounds.bottom();
312
258
  }
313
259
 
314
260
  bool GameRenderer::initializeEGL() {
@@ -1,6 +1,8 @@
1
1
  #pragma once
2
2
  #include "Entity.hpp"
3
3
  #include "Screen.hpp"
4
+ #include "include/core/SkImage.h"
5
+ #include "include/core/SkRect.h"
4
6
  #include "include/core/SkSurface.h"
5
7
  #include "include/gpu/ganesh/GrDirectContext.h"
6
8
  #include "modules/skottie/include/Skottie.h"
@@ -21,9 +23,11 @@ public:
21
23
 
22
24
  ~GameRenderer();
23
25
 
24
- std::map<double, sk_sp<SkSVGDOM>> &getSvgCacheInternal() {
25
- return _svgCache;
26
+ std::map<double, std::variant<sk_sp<SkSVGDOM>, sk_sp<SkImage>>> &
27
+ getImageCacheInternal() {
28
+ return _imageCache;
26
29
  };
30
+
27
31
  std::map<double, sk_sp<skottie::Animation>> &getLottieCacheInternal() {
28
32
  return _lottieCache;
29
33
  };
@@ -52,11 +56,10 @@ private:
52
56
  int _height{0};
53
57
  bool _initialized{false};
54
58
 
55
- std::map<double, sk_sp<SkSVGDOM>> _svgCache;
56
59
  std::map<double, sk_sp<skottie::Animation>> _lottieCache;
60
+ std::map<double, std::variant<sk_sp<SkSVGDOM>, sk_sp<SkImage>>> _imageCache;
57
61
 
58
- bool isEntityVisible(const Entity &entity, float screenWidth,
59
- float screenHeight);
62
+ bool isEntityVisible(const Entity &entity, const SkRect &viewBounds);
60
63
 
61
64
  bool initializeEGL();
62
65
  bool createEGLSurface();
@@ -0,0 +1,92 @@
1
+ #include "AssetUtils.hpp"
2
+ #include "GameRenderer.hpp"
3
+ #include <variant>
4
+
5
+ namespace margelo::nitro::rngine::AssetUtils {
6
+ static void drawSVG(SkCanvas *canvas, const sk_sp<SkSVGDOM> &svg, float width,
7
+ float height) {
8
+ SkSize intrinsicSize = svg->containerSize();
9
+ if (intrinsicSize.isEmpty()) {
10
+ svg->setContainerSize(SkSize::Make(width, height));
11
+ intrinsicSize = SkSize::Make(width, height);
12
+ }
13
+
14
+ SkAutoCanvasRestore restore(canvas, true);
15
+ canvas->scale(width / intrinsicSize.width(), height / intrinsicSize.height());
16
+ svg->render(canvas);
17
+ }
18
+
19
+ static void drawRaster(SkCanvas *canvas, const sk_sp<SkImage> &raster,
20
+ float width, float height) {
21
+ float intrinsicW = static_cast<float>(raster->width());
22
+ float intrinsicH = static_cast<float>(raster->height());
23
+ if (intrinsicW <= 0.0f || intrinsicH <= 0.0f)
24
+ return;
25
+
26
+ SkAutoCanvasRestore restore(canvas, true);
27
+ canvas->scale(width / intrinsicW, height / intrinsicH);
28
+ SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kLinear);
29
+ canvas->drawImage(raster, 0.0f, 0.0f, sampling);
30
+ }
31
+
32
+ void drawAsset(SkCanvas *canvas, double asset, float targetWidth,
33
+ float targetHeight, bool flipH, bool flipV, double progress) {
34
+ SkAutoCanvasRestore restoreGuard(canvas, true);
35
+
36
+ if (flipH || flipV) {
37
+ canvas->translate(targetWidth * 0.5f, targetHeight * 0.5f);
38
+ canvas->scale(flipH ? -1.0f : 1.0f, flipV ? -1.0f : 1.0f);
39
+ canvas->translate(-targetWidth * 0.5f, -targetHeight * 0.5f);
40
+ }
41
+
42
+ if (asset == 0) {
43
+ return;
44
+ }
45
+
46
+ auto &gameRenderer = GameRenderer::getInstance();
47
+
48
+ if (asset > 0) {
49
+ auto &imageCache = gameRenderer.getImageCacheInternal();
50
+ auto imageIt = imageCache.find(asset);
51
+ if (imageIt != imageCache.end()) {
52
+ std::visit(
53
+ [&](const auto &image) {
54
+ using T = std::decay_t<decltype(image)>;
55
+ if constexpr (std::is_same_v<T, sk_sp<SkSVGDOM>>) {
56
+ drawSVG(canvas, image, targetWidth, targetHeight);
57
+ } else {
58
+ drawRaster(canvas, image, targetWidth, targetHeight);
59
+ }
60
+ },
61
+ imageIt->second);
62
+ return;
63
+ }
64
+ }
65
+
66
+ auto &lottieCache = gameRenderer.getLottieCacheInternal();
67
+ auto lottieIt = lottieCache.find(asset);
68
+ if (lottieIt != lottieCache.end() && lottieIt->second) {
69
+ lottieIt->second->seek(progress);
70
+ SkRect dstBounds = SkRect::MakeWH(targetWidth, targetHeight);
71
+ lottieIt->second->render(canvas, &dstBounds);
72
+ }
73
+ }
74
+
75
+ void updateProgress(std::optional<double> &progress,
76
+ std::optional<double> &speed, double asset,
77
+ double deltaTime) {
78
+ if (asset < 0) {
79
+ auto &lottieCache = GameRenderer::getInstance().getLottieCacheInternal();
80
+ auto it = lottieCache.find(asset);
81
+ if (it != lottieCache.end() && it->second) {
82
+ if (speed.has_value() && speed.value() > 0) {
83
+ progress = fmod(progress.value_or(0.0) + (deltaTime * speed.value()) /
84
+ it->second->duration(),
85
+ 1.0);
86
+ }
87
+ }
88
+ } else if (progress.has_value()) {
89
+ progress.reset();
90
+ }
91
+ }
92
+ } // namespace margelo::nitro::rngine::AssetUtils
@@ -0,0 +1,11 @@
1
+ #pragma once
2
+ #include "include/core/SkCanvas.h"
3
+ #include "modules/svg/include/SkSVGDOM.h"
4
+
5
+ namespace margelo::nitro::rngine::AssetUtils {
6
+ void drawAsset(SkCanvas *canvas, double asset, float targetWidth,
7
+ float targetHeight, bool flipH, bool flipV, double progress);
8
+ void updateProgress(std::optional<double> &progress,
9
+ std::optional<double> &speed, double asset,
10
+ double deltaTime);
11
+ } // namespace margelo::nitro::rngine::AssetUtils
@@ -1,10 +1,11 @@
1
1
  #include "CollisionUtils.hpp"
2
2
  #include "Collision.hpp"
3
+ #include "Overlap.hpp"
3
4
  #include <optional>
4
5
 
5
6
  namespace margelo::nitro::rngine::CollisionUtils {
6
- std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
7
- const Rect &b, double bx, double by) {
7
+ static std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
8
+ const Rect &b, double bx, double by) {
8
9
 
9
10
  double aLeft = ax - a.width / 2.0, aRight = ax + a.width / 2.0;
10
11
  double aTop = ay - a.height / 2.0, aBottom = ay + a.height / 2.0;
@@ -26,8 +27,8 @@ std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
26
27
  }
27
28
  }
28
29
 
29
- std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
30
- const Circle &b, double bx, double by) {
30
+ static std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
31
+ const Circle &b, double bx, double by) {
31
32
  double dx = bx - ax, dy = by - ay;
32
33
  double dist = std::sqrt(dx * dx + dy * dy);
33
34
  double depth = (a.radius + b.radius) - dist;
@@ -41,8 +42,8 @@ std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
41
42
  return Overlap{depth, dx / dist, dy / dist};
42
43
  }
43
44
 
44
- std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
45
- const Circle &b, double bx, double by) {
45
+ static std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
46
+ const Circle &b, double bx, double by) {
46
47
  double left = ax - a.width / 2.0, right = ax + a.width / 2.0;
47
48
  double top = ay - a.height / 2.0, bottom = ay + a.height / 2.0;
48
49
 
@@ -67,8 +68,8 @@ std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
67
68
  return Overlap{b.radius - dist, dx / dist, dy / dist};
68
69
  }
69
70
 
70
- std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
71
- const Rect &b, double bx, double by) {
71
+ static std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
72
+ const Rect &b, double bx, double by) {
72
73
  auto result = overlap(b, bx, by, a, ax, ay);
73
74
 
74
75
  if (!result.has_value()) {
@@ -0,0 +1,10 @@
1
+ #pragma once
2
+
3
+ #include "Collision.hpp"
4
+ #include "Entity.hpp"
5
+ #include <optional>
6
+
7
+ namespace margelo::nitro::rngine::CollisionUtils {
8
+ std::optional<Collision> shapeOverlap(const Entity &entityA,
9
+ const Entity &entityB);
10
+ } // namespace margelo::nitro::rngine::CollisionUtils
@@ -5,6 +5,7 @@ import type {
5
5
  System,
6
6
  Screen,
7
7
  World,
8
+ ScreenUpdate,
8
9
  } from './nativeTypes';
9
10
  import { NitroModules } from 'react-native-nitro-modules';
10
11
 
@@ -12,19 +13,28 @@ interface GameMethods extends HybridObject<{
12
13
  ios: 'c++';
13
14
  android: 'c++';
14
15
  }> {
16
+ /** Configuration Methods */
15
17
  setWorld(world: World): void;
16
18
  setScreen(screen: Screen): void;
17
19
  setEntities(entities: Entity[]): void;
18
20
  setSystems(systems: System[]): void;
21
+
22
+ /** Pause/Resume Methods */
19
23
  pause(): void;
20
24
  resume(): void;
25
+
26
+ /** Entity Update Methods */
21
27
  spawn(entities: Entity[]): void;
22
28
  despawn(id: string): void;
23
- update(updates: EntityUpdate[]): void;
29
+ updateEntities(entityUpdates: EntityUpdate[]): void;
30
+
31
+ /** Screen Update Methods */
32
+ updateScreen(screenUpdate: ScreenUpdate): void;
24
33
 
34
+ /** Asset Methods */
25
35
  isAssetLoaded(id: number): boolean;
26
36
  loadLottie(id: number, jsonStr: string): Promise<boolean>;
27
- loadSvg(id: number, svgUri: string): Promise<boolean>;
37
+ loadImage(id: number, imageUri: string): Promise<boolean>;
28
38
  }
29
39
 
30
40
  export const gameMethods =
@@ -1,5 +1,5 @@
1
1
  import { gameMethods } from './GameMethods.nitro';
2
- import type { Entity, EntityUpdate, System } from './nativeTypes';
2
+ import type { Entity, EntityUpdate, ScreenUpdate, System } from './nativeTypes';
3
3
  import type { Config } from './types';
4
4
  import { Image } from 'react-native';
5
5
 
@@ -17,9 +17,9 @@ export const configure = ({
17
17
  nativeSystems.push({
18
18
  entities: system.entities,
19
19
  collisions: system.collisions,
20
- onTick: (e, c) => {
20
+ onTick: (systemContext) => {
21
21
  const start = Date.now();
22
- system.onTick(e, c);
22
+ system.onTick(systemContext);
23
23
  const finish = Date.now();
24
24
  return finish - start;
25
25
  },
@@ -49,13 +49,22 @@ export const spawn = (entities: Entity | Entity[]) => {
49
49
  gameMethods.spawn(entityArray);
50
50
  };
51
51
 
52
- /** Removes an entity or all entities matching the given id prefix from the world. */
52
+ /** Removes an entity or all entities matching the given id prefix from the world. Only the provided fields are changed. */
53
53
  export const despawn = (id: string) => gameMethods.despawn(id);
54
54
 
55
- /** Updates one or more entities. Only the provided fields are changed. */
56
- export const update = (updates: EntityUpdate | EntityUpdate[]) => {
57
- const entityUpdateArray = Array.isArray(updates) ? updates : [updates];
58
- gameMethods.update(entityUpdateArray);
55
+ /** Fires one update, applied to entities matching the given id prefix from the world. Only the provided fields are changed. */
56
+ export const updateEntity = (entityUpdate: EntityUpdate) => {
57
+ gameMethods.updateEntities([entityUpdate]);
58
+ };
59
+
60
+ /** Fires multiple updates, each applied to entities matching the given id prefix from the world. */
61
+ export const updateEntities = (entityUpdates: EntityUpdate[]) => {
62
+ gameMethods.updateEntities(entityUpdates);
63
+ };
64
+
65
+ /** Updates the screen. Only the provided fields are changed. */
66
+ export const updateScreen = (screenUpdate: ScreenUpdate) => {
67
+ gameMethods.updateScreen(screenUpdate);
59
68
  };
60
69
 
61
70
  const assetCache = new WeakMap<object, number>();
@@ -89,9 +98,9 @@ const loadAsset = async (asset: unknown) => {
89
98
  if (!assetUri) {
90
99
  throw new Error(`loadAssets: could not resolve asset URI for ${asset}`);
91
100
  }
92
- const isSvgLoaded = await gameMethods.loadSvg(asset, assetUri);
93
- if (!isSvgLoaded) {
94
- throw new Error('loadAssets: Failed to load svg asset');
101
+ const isLoaded = await gameMethods.loadImage(asset, assetUri);
102
+ if (!isLoaded) {
103
+ throw new Error('loadAssets: Failed to load image asset');
95
104
  }
96
105
  return asset;
97
106
  }
@@ -99,6 +108,9 @@ const loadAsset = async (asset: unknown) => {
99
108
  throw new Error(`loadAssets: unsupported asset type "${typeof asset}"`);
100
109
  };
101
110
 
111
+ /** Preloads one or more SVG, PNG or Lottie assets ahead of time.
112
+ * Takes an object mapping names to `require(...)` sources (SVG/PNG) or imported JSON (Lottie),
113
+ * and resolves to an object of the same shape with numeric asset ids. */
102
114
  export const loadAssets = async <T extends Record<string, unknown>>(
103
115
  assets: T
104
116
  ): Promise<{ [K in keyof T]: number }> => {
package/src/index.tsx CHANGED
@@ -9,5 +9,6 @@ export type {
9
9
  CollisionPair,
10
10
  Collision,
11
11
  EntityUpdate,
12
+ SystemContext,
12
13
  } from './nativeTypes';
13
14
  export { GameEngine };
@@ -61,7 +61,7 @@ export interface World {
61
61
  gy?: number;
62
62
  }
63
63
 
64
- export interface Screen extends Rect, Renderable {}
64
+ export interface Screen extends Positioned, Rect, Renderable {}
65
65
 
66
66
  export interface Entity extends Positioned, Shaped, Renderable, Kinematic {
67
67
  /** Unique identifier. Use `_` as a separator for group queries e.g. `'enemy_1'`. */
@@ -75,6 +75,8 @@ export interface EntityUpdate extends Partial<
75
75
  id: string;
76
76
  }
77
77
 
78
+ export interface ScreenUpdate extends Partial<Screen> {}
79
+
78
80
  export interface CollisionPair {
79
81
  /** Entity id or prefix of the first entity to watch. */
80
82
  a: string;
@@ -95,11 +97,20 @@ export interface Collision {
95
97
  ny: number;
96
98
  }
97
99
 
100
+ export interface SystemContext {
101
+ /** Current game screen state. */
102
+ screen: Screen;
103
+ /** Entities resolved from the system's entity subscriptions. */
104
+ entities: Entity[];
105
+ /** Collisions matching the system's collision subscriptions. */
106
+ collisions: Collision[];
107
+ }
108
+
98
109
  export interface System {
99
110
  /** Entity ids or prefixes this system subscribes to. */
100
111
  entities?: string[];
101
112
  /** Collision pairs to watch for every tick. */
102
113
  collisions?: CollisionPair[];
103
114
  /** Called every tick with the resolved entities and collisions. */
104
- onTick: (entities: Entity[], collisions: Collision[]) => number;
115
+ onTick: (systemContext: SystemContext) => number;
105
116
  }
package/src/types.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import type {
2
2
  System as NativeSystem,
3
- Collision,
4
3
  Screen,
5
4
  Entity,
6
5
  World,
6
+ SystemContext,
7
7
  } from './nativeTypes';
8
8
 
9
9
  export type System = Omit<NativeSystem, 'onTick'> & {
10
10
  /** Called every tick with the resolved entities and collisions. */
11
- onTick: (entities: Entity[], collisions: Collision[]) => void;
11
+ onTick: (systemContext: SystemContext) => void;
12
12
  };
13
13
 
14
14
  export type Config = {
@@ -1,24 +0,0 @@
1
- package com.margelo.nitro.rngine
2
-
3
- import androidx.annotation.Keep
4
- import com.margelo.nitro.NitroModules
5
- import java.net.URL
6
-
7
- @Keep
8
- object SvgLoaderHelper {
9
- @JvmStatic
10
- fun loadFromUrl(uri: String): ByteArray {
11
- return URL(uri).openStream().use { it.readBytes() }
12
- }
13
-
14
- @JvmStatic
15
- fun loadFromResource(uri: String): ByteArray {
16
- val context = NitroModules.applicationContext
17
- ?: throw IllegalStateException("No Context available!")
18
- val resId = context.resources.getIdentifier(uri, "raw", context.packageName)
19
- if (resId == 0) {
20
- throw IllegalStateException("Could not resolve raw resource for asset: $uri")
21
- }
22
- return context.resources.openRawResource(resId).use { it.readBytes() }
23
- }
24
- }
@@ -1,21 +0,0 @@
1
- #pragma once
2
-
3
- #include "Circle.hpp"
4
- #include "Collision.hpp"
5
- #include "Entity.hpp"
6
- #include "Overlap.hpp"
7
- #include "Rect.hpp"
8
- #include <optional>
9
-
10
- namespace margelo::nitro::rngine::CollisionUtils {
11
- std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
12
- const Rect &b, double bx, double by);
13
- std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
14
- const Circle &b, double bx, double by);
15
- std::optional<Overlap> overlap(const Rect &a, double ax, double ay,
16
- const Circle &b, double bx, double by);
17
- std::optional<Overlap> overlap(const Circle &a, double ax, double ay,
18
- const Rect &b, double bx, double by);
19
- std::optional<Collision> shapeOverlap(const Entity &entityA,
20
- const Entity &entityB);
21
- } // namespace margelo::nitro::rngine::CollisionUtils
File without changes
File without changes