rngine 0.3.2 → 0.4.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 (72) hide show
  1. package/README.md +10 -7
  2. package/android/build.gradle +6 -4
  3. package/android/kls_database.db +0 -0
  4. package/android/src/main/cpp/GameLoopJNI.cpp +20 -9
  5. package/android/src/main/java/com/margelo/nitro/rngine/Asset.kt +9 -0
  6. package/android/src/main/java/com/margelo/nitro/rngine/GameAssets.kt +47 -0
  7. package/android/src/main/java/com/margelo/nitro/rngine/GameView.kt +46 -8
  8. package/android/src/main/java/com/margelo/nitro/rngine/Rect.kt +7 -5
  9. package/android/src/main/java/com/margelo/nitro/rngine/RectSerializer.kt +8 -6
  10. package/lib/module/GameAssets.nitro.js +5 -0
  11. package/lib/module/GameAssets.nitro.js.map +1 -0
  12. package/lib/module/GameMethods.js +1 -31
  13. package/lib/module/GameMethods.js.map +1 -1
  14. package/lib/module/GameMethods.nitro.js +2 -1
  15. package/lib/module/GameMethods.nitro.js.map +1 -1
  16. package/lib/module/Shared.js +72 -0
  17. package/lib/module/Shared.js.map +1 -0
  18. package/lib/module/index.js +1 -0
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/nativeTypes.js +2 -0
  21. package/lib/module/nativeTypes.js.map +1 -0
  22. package/lib/module/types.js +2 -0
  23. package/lib/typescript/src/GameAssets.nitro.d.ts +12 -0
  24. package/lib/typescript/src/GameAssets.nitro.d.ts.map +1 -0
  25. package/lib/typescript/src/GameMethods.d.ts +0 -7
  26. package/lib/typescript/src/GameMethods.d.ts.map +1 -1
  27. package/lib/typescript/src/GameMethods.nitro.d.ts +4 -2
  28. package/lib/typescript/src/GameMethods.nitro.d.ts.map +1 -1
  29. package/lib/typescript/src/Shared.d.ts +8 -0
  30. package/lib/typescript/src/Shared.d.ts.map +1 -0
  31. package/lib/typescript/src/index.d.ts +2 -0
  32. package/lib/typescript/src/index.d.ts.map +1 -1
  33. package/lib/typescript/src/nativeTypes.d.ts +79 -0
  34. package/lib/typescript/src/nativeTypes.d.ts.map +1 -0
  35. package/lib/typescript/src/types.d.ts +11 -47
  36. package/lib/typescript/src/types.d.ts.map +1 -1
  37. package/nitro.json +10 -0
  38. package/nitrogen/generated/android/c++/JHybridGameAssetsSpec.cpp +61 -0
  39. package/nitrogen/generated/android/c++/JHybridGameAssetsSpec.hpp +65 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngine/HybridGameAssetsSpec.kt +62 -0
  41. package/nitrogen/generated/android/rngine+autolinking.cmake +2 -0
  42. package/nitrogen/generated/android/rngineOnLoad.cpp +16 -0
  43. package/nitrogen/generated/ios/Rngine-Swift-Cxx-Bridge.cpp +17 -0
  44. package/nitrogen/generated/ios/Rngine-Swift-Cxx-Bridge.hpp +37 -0
  45. package/nitrogen/generated/ios/Rngine-Swift-Cxx-Umbrella.hpp +8 -0
  46. package/nitrogen/generated/ios/RngineAutolinking.mm +8 -0
  47. package/nitrogen/generated/ios/RngineAutolinking.swift +12 -0
  48. package/nitrogen/generated/ios/c++/HybridGameAssetsSpecSwift.cpp +11 -0
  49. package/nitrogen/generated/ios/c++/HybridGameAssetsSpecSwift.hpp +94 -0
  50. package/nitrogen/generated/ios/swift/HybridGameAssetsSpec.swift +57 -0
  51. package/nitrogen/generated/ios/swift/HybridGameAssetsSpec_cxx.swift +160 -0
  52. package/nitrogen/generated/shared/c++/Collision.hpp +91 -0
  53. package/nitrogen/generated/shared/c++/CollisionPair.hpp +87 -0
  54. package/nitrogen/generated/shared/c++/Entity.hpp +13 -5
  55. package/nitrogen/generated/shared/c++/EntityUpdate.hpp +9 -1
  56. package/nitrogen/generated/shared/c++/HybridGameAssetsSpec.cpp +23 -0
  57. package/nitrogen/generated/shared/c++/HybridGameAssetsSpec.hpp +64 -0
  58. package/nitrogen/generated/shared/c++/Screen.hpp +14 -5
  59. package/nitrogen/generated/shared/c++/System.hpp +20 -9
  60. package/package.json +1 -1
  61. package/shared/ColorUtils.hpp +6 -3
  62. package/shared/GameLoop.cpp +75 -12
  63. package/shared/GameLoop.hpp +2 -0
  64. package/shared/GameMethods.cpp +20 -7
  65. package/shared/Rect.hpp +8 -5
  66. package/src/GameAssets.nitro.ts +14 -0
  67. package/src/GameMethods.nitro.ts +6 -2
  68. package/src/GameMethods.ts +1 -34
  69. package/src/Shared.ts +83 -0
  70. package/src/index.tsx +2 -0
  71. package/src/nativeTypes.ts +83 -0
  72. package/src/types.ts +17 -49
@@ -31,6 +31,7 @@
31
31
 
32
32
 
33
33
  #include <string>
34
+ #include <optional>
34
35
 
35
36
  namespace margelo::nitro::rngine {
36
37
 
@@ -41,11 +42,13 @@ namespace margelo::nitro::rngine {
41
42
  public:
42
43
  double width SWIFT_PRIVATE;
43
44
  double height SWIFT_PRIVATE;
44
- std::string color SWIFT_PRIVATE;
45
+ std::optional<std::string> color SWIFT_PRIVATE;
46
+ std::optional<double> asset SWIFT_PRIVATE;
47
+ std::optional<double> progress SWIFT_PRIVATE;
45
48
 
46
49
  public:
47
50
  Screen() = default;
48
- explicit Screen(double width, double height, std::string color): width(width), height(height), color(color) {}
51
+ explicit Screen(double width, double height, std::optional<std::string> color, std::optional<double> asset, std::optional<double> progress): width(width), height(height), color(color), asset(asset), progress(progress) {}
49
52
 
50
53
  public:
51
54
  friend bool operator==(const Screen& lhs, const Screen& rhs) = default;
@@ -63,14 +66,18 @@ namespace margelo::nitro {
63
66
  return margelo::nitro::rngine::Screen(
64
67
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width"))),
65
68
  JSIConverter<double>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height"))),
66
- JSIConverter<std::string>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color")))
69
+ JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color"))),
70
+ JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "asset"))),
71
+ JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "progress")))
67
72
  );
68
73
  }
69
74
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rngine::Screen& arg) {
70
75
  jsi::Object obj(runtime);
71
76
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "width"), JSIConverter<double>::toJSI(runtime, arg.width));
72
77
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "height"), JSIConverter<double>::toJSI(runtime, arg.height));
73
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "color"), JSIConverter<std::string>::toJSI(runtime, arg.color));
78
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "color"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.color));
79
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "asset"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.asset));
80
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "progress"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.progress));
74
81
  return obj;
75
82
  }
76
83
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -83,7 +90,9 @@ namespace margelo::nitro {
83
90
  }
84
91
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "width")))) return false;
85
92
  if (!JSIConverter<double>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "height")))) return false;
86
- if (!JSIConverter<std::string>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color")))) return false;
93
+ if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "color")))) return false;
94
+ if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "asset")))) return false;
95
+ if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "progress")))) return false;
87
96
  return true;
88
97
  }
89
98
  };
@@ -28,12 +28,19 @@
28
28
  #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
29
29
  #endif
30
30
 
31
+ // Forward declaration of `CollisionPair` to properly resolve imports.
32
+ namespace margelo::nitro::rngine { struct CollisionPair; }
31
33
  // Forward declaration of `Entity` to properly resolve imports.
32
34
  namespace margelo::nitro::rngine { struct Entity; }
35
+ // Forward declaration of `Collision` to properly resolve imports.
36
+ namespace margelo::nitro::rngine { struct Collision; }
33
37
 
34
38
  #include <string>
35
39
  #include <vector>
40
+ #include <optional>
41
+ #include "CollisionPair.hpp"
36
42
  #include "Entity.hpp"
43
+ #include "Collision.hpp"
37
44
  #include <functional>
38
45
 
39
46
  namespace margelo::nitro::rngine {
@@ -43,12 +50,13 @@ namespace margelo::nitro::rngine {
43
50
  */
44
51
  struct System final {
45
52
  public:
46
- std::vector<std::string> ids SWIFT_PRIVATE;
47
- std::function<void(const std::vector<Entity>& /* entities */)> onTick SWIFT_PRIVATE;
53
+ std::optional<std::vector<std::string>> entities SWIFT_PRIVATE;
54
+ std::optional<std::vector<CollisionPair>> collisions SWIFT_PRIVATE;
55
+ std::function<void(const std::vector<Entity>& /* entities */, const std::vector<Collision>& /* collisions */)> onTick SWIFT_PRIVATE;
48
56
 
49
57
  public:
50
58
  System() = default;
51
- explicit System(std::vector<std::string> ids, std::function<void(const std::vector<Entity>& /* entities */)> onTick): ids(ids), onTick(onTick) {}
59
+ explicit System(std::optional<std::vector<std::string>> entities, std::optional<std::vector<CollisionPair>> collisions, std::function<void(const std::vector<Entity>& /* entities */, const std::vector<Collision>& /* collisions */)> onTick): entities(entities), collisions(collisions), onTick(onTick) {}
52
60
 
53
61
  public:
54
62
  // System is not equatable because these properties are not equatable: onTick
@@ -64,14 +72,16 @@ namespace margelo::nitro {
64
72
  static inline margelo::nitro::rngine::System fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
65
73
  jsi::Object obj = arg.asObject(runtime);
66
74
  return margelo::nitro::rngine::System(
67
- JSIConverter<std::vector<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "ids"))),
68
- JSIConverter<std::function<void(const std::vector<margelo::nitro::rngine::Entity>&)>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))
75
+ JSIConverter<std::optional<std::vector<std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "entities"))),
76
+ JSIConverter<std::optional<std::vector<margelo::nitro::rngine::CollisionPair>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "collisions"))),
77
+ JSIConverter<std::function<void(const std::vector<margelo::nitro::rngine::Entity>&, const std::vector<margelo::nitro::rngine::Collision>&)>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))
69
78
  );
70
79
  }
71
80
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rngine::System& arg) {
72
81
  jsi::Object obj(runtime);
73
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "ids"), JSIConverter<std::vector<std::string>>::toJSI(runtime, arg.ids));
74
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "onTick"), JSIConverter<std::function<void(const std::vector<margelo::nitro::rngine::Entity>&)>>::toJSI(runtime, arg.onTick));
82
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "entities"), JSIConverter<std::optional<std::vector<std::string>>>::toJSI(runtime, arg.entities));
83
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "collisions"), JSIConverter<std::optional<std::vector<margelo::nitro::rngine::CollisionPair>>>::toJSI(runtime, arg.collisions));
84
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "onTick"), JSIConverter<std::function<void(const std::vector<margelo::nitro::rngine::Entity>&, const std::vector<margelo::nitro::rngine::Collision>&)>>::toJSI(runtime, arg.onTick));
75
85
  return obj;
76
86
  }
77
87
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -82,8 +92,9 @@ namespace margelo::nitro {
82
92
  if (!nitro::isPlainObject(runtime, obj)) {
83
93
  return false;
84
94
  }
85
- if (!JSIConverter<std::vector<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "ids")))) return false;
86
- if (!JSIConverter<std::function<void(const std::vector<margelo::nitro::rngine::Entity>&)>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))) return false;
95
+ if (!JSIConverter<std::optional<std::vector<std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "entities")))) return false;
96
+ if (!JSIConverter<std::optional<std::vector<margelo::nitro::rngine::CollisionPair>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "collisions")))) return false;
97
+ if (!JSIConverter<std::function<void(const std::vector<margelo::nitro::rngine::Entity>&, const std::vector<margelo::nitro::rngine::Collision>&)>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))) return false;
87
98
  return true;
88
99
  }
89
100
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rngine",
3
- "version": "0.3.2",
3
+ "version": "0.4.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",
@@ -3,9 +3,12 @@
3
3
 
4
4
  namespace margelo::nitro::rngine {
5
5
 
6
- inline uint32_t parseHexColor(const std::string &color) {
7
- if (!color.empty() && color[0] == '#') {
8
- std::string hex = color.substr(1);
6
+ inline uint32_t parseHexColor(const std::optional<std::string> &color) {
7
+ if (!color.has_value())
8
+ return 0x00000000;
9
+ const std::string &c = color.value();
10
+ if (!c.empty() && c[0] == '#') {
11
+ std::string hex = c.substr(1);
9
12
 
10
13
  if (hex.size() == 3 || hex.size() == 4) {
11
14
  std::string expanded;
@@ -1,9 +1,11 @@
1
1
  #include "GameLoop.hpp"
2
+ #include "Collision.hpp"
2
3
  #include "ColorUtils.hpp"
3
4
  #include "Entity.hpp"
4
5
  #include <android/log.h>
5
6
  #include <chrono>
6
7
  #include <cinttypes>
8
+ #include <set>
7
9
 
8
10
  namespace margelo::nitro::rngine {
9
11
 
@@ -50,13 +52,24 @@ GameLoop::resolveEntitiesInternal(const std::string &prefix) {
50
52
  return results;
51
53
  }
52
54
 
55
+ void GameLoop::registerLottieDuration(double id, double duration) {
56
+ std::lock_guard<std::mutex> lock(_mutex);
57
+ _lottieDurations[id] = duration;
58
+ __android_log_print(ANDROID_LOG_DEBUG, "GameLoop",
59
+ "Registered lottie duration: id: %.0f, duration: %.4f",
60
+ id, duration);
61
+ }
62
+
53
63
  std::vector<Rect> GameLoop::getRectsSnapshot() {
54
64
  std::lock_guard<std::mutex> lock(_mutex);
55
65
  std::vector<Rect> rects;
56
66
  rects.reserve(_entities.size() + 1);
57
67
 
58
- rects.push_back(
59
- {0, _screen.width, 0, _screen.height, parseHexColor(_screen.color)});
68
+ rects.push_back({0, static_cast<float>(_screen.width), 0,
69
+ static_cast<float>(_screen.height),
70
+ static_cast<float>(_screen.progress.value_or(0)),
71
+ parseHexColor(_screen.color),
72
+ static_cast<int32_t>(_screen.asset.value_or(0))});
60
73
 
61
74
  for (const auto &[id, entity] : _entities) {
62
75
  if (entity.px + entity.width < 0 || entity.px > _screen.width ||
@@ -64,11 +77,13 @@ std::vector<Rect> GameLoop::getRectsSnapshot() {
64
77
  continue;
65
78
  }
66
79
 
67
- rects.push_back({std::clamp(entity.px, 0.0, _screen.width),
68
- std::clamp(entity.px + entity.width, 0.0, _screen.width),
69
- std::clamp(entity.py, 0.0, _screen.height),
70
- std::clamp(entity.py + entity.height, 0.0, _screen.height),
71
- parseHexColor(entity.color)});
80
+ rects.push_back({static_cast<float>(entity.px),
81
+ static_cast<float>(entity.px + entity.width),
82
+ static_cast<float>(entity.py),
83
+ static_cast<float>(entity.py + entity.height),
84
+ static_cast<float>(entity.progress.value_or(0)),
85
+ parseHexColor(entity.color),
86
+ static_cast<int32_t>(entity.asset.value_or(0))});
72
87
  }
73
88
  return rects;
74
89
  }
@@ -109,14 +124,53 @@ void GameLoop::runGameLoop() {
109
124
  void GameLoop::runSystems() {
110
125
  for (auto &system : _systems) {
111
126
  std::vector<Entity> entities;
127
+ std::vector<Collision> collisions;
112
128
 
113
- for (const auto &id : system.ids) {
114
- auto resolvedEntitiesInternal = resolveEntitiesInternal(id);
115
- for (auto *entity : resolvedEntitiesInternal)
116
- entities.push_back(*entity);
129
+ if (system.entities.has_value()) {
130
+ for (const auto &id : system.entities.value()) {
131
+ auto resolvedEntitiesInternal = resolveEntitiesInternal(id);
132
+ for (auto *entity : resolvedEntitiesInternal)
133
+ entities.push_back(*entity);
134
+ }
117
135
  }
118
136
 
119
- system.onTick(entities);
137
+ if (system.collisions.has_value()) {
138
+ std::set<std::pair<std::string, std::string>> uniqueEntityIdPairs;
139
+
140
+ for (const auto &[a, b] : system.collisions.value()) {
141
+ auto resolvedEntitiesInternalA = resolveEntitiesInternal(a);
142
+ auto resolvedEntitiesInternalB = resolveEntitiesInternal(b);
143
+
144
+ for (auto *entityA : resolvedEntitiesInternalA) {
145
+ for (auto *entityB : resolvedEntitiesInternalB) {
146
+ if (entityA == entityB)
147
+ continue;
148
+
149
+ auto uniqueEntityIdPairKey = std::minmax(entityA->id, entityB->id);
150
+
151
+ if (uniqueEntityIdPairs.count(uniqueEntityIdPairKey))
152
+ continue;
153
+
154
+ uniqueEntityIdPairs.insert(uniqueEntityIdPairKey);
155
+
156
+ float aLeft = entityA->px, aRight = entityA->px + entityA->width;
157
+ float aTop = entityA->py, aBottom = entityA->py + entityA->height;
158
+ float bLeft = entityB->px, bRight = entityB->px + entityB->width;
159
+ float bTop = entityB->py, bBottom = entityB->py + entityB->height;
160
+
161
+ float overlapX = std::min(aRight, bRight) - std::max(aLeft, bLeft);
162
+ float overlapY = std::min(aBottom, bBottom) - std::max(aTop, bTop);
163
+
164
+ if (overlapX > 0 && overlapY > 0) {
165
+ collisions.push_back(Collision(entityA->id, entityB->id,
166
+ std::min(overlapX, overlapY)));
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ system.onTick(entities, collisions);
120
174
  }
121
175
  }
122
176
 
@@ -156,9 +210,18 @@ void GameLoop::updateEntities(double deltaTime) {
156
210
  if (entity.vx) {
157
211
  entity.px += *entity.vx * deltaTime;
158
212
  }
213
+
159
214
  if (entity.vy) {
160
215
  entity.py += *entity.vy * deltaTime;
161
216
  }
217
+
218
+ if (entity.asset && entity.asset < 0) {
219
+ auto it = _lottieDurations.find(*entity.asset);
220
+ if (it != _lottieDurations.end() && it->second > 0.0) {
221
+ entity.progress =
222
+ fmod(entity.progress.value_or(0.0) + deltaTime / it->second, 1.0);
223
+ }
224
+ }
162
225
  }
163
226
  }
164
227
  } // namespace margelo::nitro::rngine
@@ -28,6 +28,7 @@ public:
28
28
  Screen &getScreenInternal() { return _screen; };
29
29
 
30
30
  std::vector<Entity *> resolveEntitiesInternal(const std::string &prefix);
31
+ void registerLottieDuration(double, double);
31
32
  std::vector<Rect> getRectsSnapshot();
32
33
 
33
34
  void setTickRate(double tickRate) { _tickRate = tickRate; };
@@ -36,6 +37,7 @@ private:
36
37
  explicit GameLoop();
37
38
  std::mutex _mutex;
38
39
  std::map<std::string, Entity> _entities;
40
+ std::map<double, double> _lottieDurations;
39
41
  std::vector<System> _systems{};
40
42
  std::atomic<bool> _isRunning{true};
41
43
  std::atomic<bool> _isPaused{true};
@@ -114,20 +114,33 @@ void GameMethods::update(const std::vector<EntityUpdate> &updates) {
114
114
  }
115
115
 
116
116
  for (auto *entity : resolvedEntitiesInternals) {
117
- if (update.px.has_value())
117
+ if (update.px.has_value()) {
118
118
  entity->px = update.px.value();
119
- if (update.py.has_value())
119
+ }
120
+ if (update.py.has_value()) {
120
121
  entity->py = update.py.value();
121
- if (update.width.has_value())
122
+ }
123
+ if (update.width.has_value()) {
122
124
  entity->width = update.width.value();
123
- if (update.height.has_value())
125
+ }
126
+ if (update.height.has_value()) {
124
127
  entity->height = update.height.value();
125
- if (update.color.has_value())
128
+ }
129
+ if (update.asset.has_value()) {
130
+ entity->asset = update.asset.value();
131
+ }
132
+ if (update.progress.has_value()) {
133
+ entity->progress = update.progress.value();
134
+ }
135
+ if (update.color.has_value()) {
126
136
  entity->color = update.color.value();
127
- if (update.vx.has_value())
137
+ }
138
+ if (update.vx.has_value()) {
128
139
  entity->vx = update.vx.value();
129
- if (update.vy.has_value())
140
+ }
141
+ if (update.vy.has_value()) {
130
142
  entity->vy = update.vy.value();
143
+ }
131
144
  }
132
145
 
133
146
  __android_log_print(ANDROID_LOG_INFO, "GameMethods",
package/shared/Rect.hpp CHANGED
@@ -1,13 +1,16 @@
1
1
  #pragma once
2
2
 
3
3
  namespace margelo::nitro::rngine {
4
- static constexpr int RECT_SIZE = sizeof(double) * 4 + sizeof(uint32_t);
4
+ static constexpr int RECT_SIZE =
5
+ sizeof(float) * 5 + sizeof(uint32_t) + sizeof(int32_t);
5
6
 
6
7
  struct Rect {
7
- double left;
8
- double right;
9
- double top;
10
- double bottom;
8
+ float left;
9
+ float right;
10
+ float top;
11
+ float bottom;
12
+ float progress;
11
13
  uint32_t color;
14
+ int32_t asset;
12
15
  };
13
16
  } // namespace margelo::nitro::rngine
@@ -0,0 +1,14 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import { NitroModules } from 'react-native-nitro-modules';
3
+
4
+ interface GameAssets extends HybridObject<{
5
+ ios: 'swift';
6
+ android: 'kotlin';
7
+ }> {
8
+ isAssetRegistered(id: number): boolean;
9
+ registerSvg(id: number, uri: string): void;
10
+ registerLottie(id: number, json: string): void;
11
+ }
12
+
13
+ export const gameAssets =
14
+ NitroModules.createHybridObject<GameAssets>('GameAssets');
@@ -1,7 +1,8 @@
1
1
  import type { HybridObject } from 'react-native-nitro-modules';
2
- import type { Entity, EntityUpdate, System, Screen } from './types';
2
+ import type { Entity, EntityUpdate, System, Screen } from './nativeTypes';
3
+ import { NitroModules } from 'react-native-nitro-modules';
3
4
 
4
- export interface GameMethods extends HybridObject<{
5
+ interface GameMethods extends HybridObject<{
5
6
  ios: 'c++';
6
7
  android: 'c++';
7
8
  }> {
@@ -15,3 +16,6 @@ export interface GameMethods extends HybridObject<{
15
16
  despawn(id: string): void;
16
17
  update(updates: EntityUpdate[]): void;
17
18
  }
19
+
20
+ export const gameMethods =
21
+ NitroModules.createHybridObject<GameMethods>('GameMethods');
@@ -1,27 +1,4 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- import type { GameMethods } from './GameMethods.nitro';
3
- import type { Config, Entity, EntityUpdate } from './types';
4
-
5
- const gameMethods = NitroModules.createHybridObject<GameMethods>('GameMethods');
6
-
7
- /** Sets up the game engine with the given configuration. Call this before anything else. */
8
- export function configure({
9
- tickRate,
10
- screen,
11
- entities = [],
12
- systems = [],
13
- paused = true,
14
- }: Config) {
15
- gameMethods.setTickRate(tickRate);
16
- gameMethods.setScreen(screen);
17
- gameMethods.setEntities(entities);
18
- gameMethods.setSystems(systems);
19
- if (paused) {
20
- gameMethods.pause();
21
- } else {
22
- gameMethods.resume();
23
- }
24
- }
1
+ import { gameMethods } from './GameMethods.nitro';
25
2
 
26
3
  /** Pauses the game loop. Systems will stop running. */
27
4
  export const pause = () => gameMethods.pause();
@@ -29,15 +6,5 @@ export const pause = () => gameMethods.pause();
29
6
  /** Resumes the game loop. */
30
7
  export const resume = () => gameMethods.resume();
31
8
 
32
- /** Spawns one or more entities into the world. Skips duplicates by id. */
33
- export const spawn = (entities: Entity | Entity[]) => {
34
- gameMethods.spawn(Array.isArray(entities) ? entities : [entities]);
35
- };
36
-
37
9
  /** Removes an entity or all entities matching the given id prefix from the world. */
38
10
  export const despawn = (id: string) => gameMethods.despawn(id);
39
-
40
- /** Updates one or more entities. Only the provided fields are changed. */
41
- export const update = (updates: EntityUpdate | EntityUpdate[]) => {
42
- gameMethods.update(Array.isArray(updates) ? updates : [updates]);
43
- };
package/src/Shared.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { Image } from 'react-native';
2
+ import { gameAssets } from './GameAssets.nitro';
3
+ import { gameMethods } from './GameMethods.nitro';
4
+ import type {
5
+ Screen as NativeScreen,
6
+ Entity as NativeEntity,
7
+ EntityUpdate as NativeEntityUpdate,
8
+ } from './nativeTypes';
9
+ import type { Asset, Config, Entity, EntityUpdate } from './types';
10
+
11
+ const assetCache = new WeakMap<object, number>();
12
+ let nextId = -1;
13
+
14
+ const registerAssetIfNotExist = (asset?: Asset) => {
15
+ switch (typeof asset) {
16
+ case 'object':
17
+ if (assetCache.has(asset)) {
18
+ return assetCache.get(asset);
19
+ }
20
+ const assetId = nextId--;
21
+ assetCache.set(asset, assetId);
22
+ gameAssets.registerLottie(assetId, JSON.stringify(asset));
23
+ return assetId;
24
+
25
+ case 'number':
26
+ if (gameAssets.isAssetRegistered(asset)) {
27
+ return asset;
28
+ }
29
+ const assetUri = Image.resolveAssetSource(asset)?.uri;
30
+ if (!assetUri) {
31
+ break;
32
+ }
33
+ gameAssets.registerSvg(asset, assetUri);
34
+ return asset;
35
+ }
36
+ return;
37
+ };
38
+
39
+ /** Sets up the game engine with the given configuration. Call this before anything else. */
40
+ export const configure = ({
41
+ tickRate,
42
+ screen,
43
+ entities = [],
44
+ systems = [],
45
+ paused = true,
46
+ }: Config) => {
47
+ screen.asset = registerAssetIfNotExist(screen.asset);
48
+ for (const entity of entities) {
49
+ entity.asset = registerAssetIfNotExist(entity.asset);
50
+ }
51
+
52
+ gameMethods.setTickRate(tickRate);
53
+ gameMethods.setScreen(screen as NativeScreen);
54
+ gameMethods.setEntities(entities as NativeEntity[]);
55
+ gameMethods.setSystems(systems);
56
+ if (paused) {
57
+ gameMethods.pause();
58
+ } else {
59
+ gameMethods.resume();
60
+ }
61
+ };
62
+
63
+ /** Spawns one or more entities into the world. Skips duplicates by id. */
64
+ export const spawn = (entities: Entity | Entity[]) => {
65
+ const entityArray = Array.isArray(entities) ? entities : [entities];
66
+
67
+ for (const entity of entityArray) {
68
+ entity.asset = registerAssetIfNotExist(entity.asset);
69
+ }
70
+
71
+ gameMethods.spawn(entityArray as NativeEntity[]);
72
+ };
73
+
74
+ /** Updates one or more entities. Only the provided fields are changed. */
75
+ export const update = (updates: EntityUpdate | EntityUpdate[]) => {
76
+ const entityUpdateArray = Array.isArray(updates) ? updates : [updates];
77
+
78
+ for (const entityUpdate of entityUpdateArray) {
79
+ entityUpdate.asset = registerAssetIfNotExist(entityUpdate.asset);
80
+ }
81
+
82
+ gameMethods.update(entityUpdateArray as NativeEntityUpdate[]);
83
+ };
package/src/index.tsx CHANGED
@@ -1,5 +1,7 @@
1
1
  import GameEngine from './GameEngine';
2
2
 
3
3
  export * from './GameMethods';
4
+ export * from './Shared';
5
+ export type { System } from './nativeTypes';
4
6
  export * from './types';
5
7
  export { GameEngine };
@@ -0,0 +1,83 @@
1
+ export type Screen = {
2
+ /** Width of the screen in game units. */
3
+ width: number;
4
+ /** Height of the screen in game units. */
5
+ height: number;
6
+ /** Background color of the screen as a hex string e.g. `'#1a1a1a'`. */
7
+ color?: string;
8
+ /** Asset to render, use `require` with the file path to get the id. */
9
+ asset?: number;
10
+ /** Animation progress between 0 and 1, automatically advanced each tick for Lottie assets. */
11
+ progress?: number;
12
+ };
13
+
14
+ export type Entity = {
15
+ /** Unique identifier. Use `_` as a separator for group queries e.g. `'enemy_1'`. */
16
+ id: string;
17
+ /** X position in game units. */
18
+ px: number;
19
+ /** Y position in game units. */
20
+ py: number;
21
+ /** Width in game units. */
22
+ width: number;
23
+ /** Height in game units. */
24
+ height: number;
25
+ /** Fill color as a hex string e.g. `'#00ff00'`. */
26
+ color?: string;
27
+ /** Asset to render, use `require` with the file path to get the id. */
28
+ asset?: number;
29
+ /** Animation progress between 0 and 1, automatically advanced each tick for Lottie assets. */
30
+ progress?: number;
31
+ /** Velocity on the X axis in game units per second. */
32
+ vx?: number;
33
+ /** Velocity on the Y axis in game units per second. */
34
+ vy?: number;
35
+ };
36
+
37
+ export type CollisionPair = {
38
+ /** Entity id or prefix of the first entity to watch. */
39
+ a: string;
40
+ /** Entity id or prefix of the second entity to watch. */
41
+ b: string;
42
+ };
43
+
44
+ export type Collision = {
45
+ /** Id of the first entity involved in the collision. */
46
+ a: string;
47
+ /** Id of the second entity involved in the collision. */
48
+ b: string;
49
+ /** Penetration depth of the collision. */
50
+ depth: number;
51
+ };
52
+
53
+ export type System = {
54
+ /** Entity ids or prefixes this system subscribes to. */
55
+ entities?: string[];
56
+ /** Collision pairs to watch for every tick. */
57
+ collisions?: CollisionPair[];
58
+ /** Called every tick with the resolved entities and collisions. */
59
+ onTick: (entities: Entity[], collisions: Collision[]) => void;
60
+ };
61
+
62
+ export type EntityUpdate = {
63
+ /** Unique identifier or prefix of the entities to update. */
64
+ id: string;
65
+ /** X position in game units. */
66
+ px?: number;
67
+ /** Y position in game units. */
68
+ py?: number;
69
+ /** Width in game units. */
70
+ width?: number;
71
+ /** Height in game units. */
72
+ height?: number;
73
+ /** Fill color as a hex string e.g. `'#00ff00'`. */
74
+ color?: string;
75
+ /** Asset to render, use `require` with the file path to get the id. */
76
+ asset?: number;
77
+ /** Animation progress between 0 and 1, automatically advanced each tick for Lottie assets. */
78
+ progress?: number;
79
+ /** Velocity on the X axis in game units per second. */
80
+ vx?: number;
81
+ /** Velocity on the Y axis in game units per second. */
82
+ vy?: number;
83
+ };