rngine 0.8.3 → 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 (40) hide show
  1. package/README.md +28 -21
  2. package/android/CMakeLists.txt +5 -2
  3. package/lib/module/GameMethods.js +20 -7
  4. package/lib/module/GameMethods.js.map +1 -1
  5. package/lib/module/GameMethods.nitro.js.map +1 -1
  6. package/lib/module/index.js.map +1 -1
  7. package/lib/typescript/src/GameMethods.d.ts +11 -4
  8. package/lib/typescript/src/GameMethods.d.ts.map +1 -1
  9. package/lib/typescript/src/GameMethods.nitro.d.ts +8 -2
  10. package/lib/typescript/src/GameMethods.nitro.d.ts.map +1 -1
  11. package/lib/typescript/src/index.d.ts +1 -1
  12. package/lib/typescript/src/index.d.ts.map +1 -1
  13. package/lib/typescript/src/nativeTypes.d.ts +12 -2
  14. package/lib/typescript/src/nativeTypes.d.ts.map +1 -1
  15. package/lib/typescript/src/types.d.ts +2 -2
  16. package/lib/typescript/src/types.d.ts.map +1 -1
  17. package/nitrogen/generated/shared/c++/HybridGameMethodsSpec.cpp +2 -1
  18. package/nitrogen/generated/shared/c++/HybridGameMethodsSpec.hpp +5 -1
  19. package/nitrogen/generated/shared/c++/Screen.hpp +9 -1
  20. package/nitrogen/generated/shared/c++/ScreenUpdate.hpp +120 -0
  21. package/nitrogen/generated/shared/c++/System.hpp +8 -11
  22. package/nitrogen/generated/shared/c++/SystemContext.hpp +99 -0
  23. package/package.json +1 -1
  24. package/shared/GameLoop.cpp +16 -33
  25. package/shared/GameMethods.cpp +50 -52
  26. package/shared/GameMethods.hpp +3 -1
  27. package/shared/GameRenderer.cpp +26 -131
  28. package/shared/GameRenderer.hpp +2 -2
  29. package/shared/utils/AssetUtils.cpp +92 -0
  30. package/shared/utils/AssetUtils.hpp +11 -0
  31. package/shared/{CollisionUtils.cpp → utils/CollisionUtils.cpp} +9 -8
  32. package/shared/utils/CollisionUtils.hpp +10 -0
  33. package/src/GameMethods.nitro.ts +11 -1
  34. package/src/GameMethods.ts +20 -8
  35. package/src/index.tsx +1 -0
  36. package/src/nativeTypes.ts +13 -2
  37. package/src/types.ts +2 -2
  38. package/shared/CollisionUtils.hpp +0 -21
  39. /package/shared/{ColorUtils.cpp → utils/ColorUtils.cpp} +0 -0
  40. /package/shared/{ColorUtils.hpp → utils/ColorUtils.hpp} +0 -0
@@ -30,18 +30,15 @@
30
30
 
31
31
  // Forward declaration of `CollisionPair` to properly resolve imports.
32
32
  namespace margelo::nitro::rngine { struct CollisionPair; }
33
- // Forward declaration of `Entity` to properly resolve imports.
34
- namespace margelo::nitro::rngine { struct Entity; }
35
- // Forward declaration of `Collision` to properly resolve imports.
36
- namespace margelo::nitro::rngine { struct Collision; }
33
+ // Forward declaration of `SystemContext` to properly resolve imports.
34
+ namespace margelo::nitro::rngine { struct SystemContext; }
37
35
 
38
36
  #include <string>
39
37
  #include <vector>
40
38
  #include <optional>
41
39
  #include "CollisionPair.hpp"
42
40
  #include <NitroModules/Promise.hpp>
43
- #include "Entity.hpp"
44
- #include "Collision.hpp"
41
+ #include "SystemContext.hpp"
45
42
  #include <functional>
46
43
 
47
44
  namespace margelo::nitro::rngine {
@@ -53,11 +50,11 @@ namespace margelo::nitro::rngine {
53
50
  public:
54
51
  std::optional<std::vector<std::string>> entities SWIFT_PRIVATE;
55
52
  std::optional<std::vector<CollisionPair>> collisions SWIFT_PRIVATE;
56
- std::function<std::shared_ptr<Promise<double>>(const std::vector<Entity>& /* entities */, const std::vector<Collision>& /* collisions */)> onTick SWIFT_PRIVATE;
53
+ std::function<std::shared_ptr<Promise<double>>(const SystemContext& /* systemContext */)> onTick SWIFT_PRIVATE;
57
54
 
58
55
  public:
59
56
  System() = default;
60
- explicit System(std::optional<std::vector<std::string>> entities, std::optional<std::vector<CollisionPair>> collisions, std::function<std::shared_ptr<Promise<double>>(const std::vector<Entity>& /* entities */, const std::vector<Collision>& /* collisions */)> onTick): entities(entities), collisions(collisions), onTick(onTick) {}
57
+ explicit System(std::optional<std::vector<std::string>> entities, std::optional<std::vector<CollisionPair>> collisions, std::function<std::shared_ptr<Promise<double>>(const SystemContext& /* systemContext */)> onTick): entities(entities), collisions(collisions), onTick(onTick) {}
61
58
 
62
59
  public:
63
60
  // System is not equatable because these properties are not equatable: onTick
@@ -75,14 +72,14 @@ namespace margelo::nitro {
75
72
  return margelo::nitro::rngine::System(
76
73
  JSIConverter<std::optional<std::vector<std::string>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "entities"))),
77
74
  JSIConverter<std::optional<std::vector<margelo::nitro::rngine::CollisionPair>>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "collisions"))),
78
- JSIConverter<std::function<std::shared_ptr<Promise<double>>(const std::vector<margelo::nitro::rngine::Entity>&, const std::vector<margelo::nitro::rngine::Collision>&)>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))
75
+ JSIConverter<std::function<std::shared_ptr<Promise<double>>(const margelo::nitro::rngine::SystemContext&)>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))
79
76
  );
80
77
  }
81
78
  static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rngine::System& arg) {
82
79
  jsi::Object obj(runtime);
83
80
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "entities"), JSIConverter<std::optional<std::vector<std::string>>>::toJSI(runtime, arg.entities));
84
81
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "collisions"), JSIConverter<std::optional<std::vector<margelo::nitro::rngine::CollisionPair>>>::toJSI(runtime, arg.collisions));
85
- obj.setProperty(runtime, PropNameIDCache::get(runtime, "onTick"), JSIConverter<std::function<std::shared_ptr<Promise<double>>(const std::vector<margelo::nitro::rngine::Entity>&, const std::vector<margelo::nitro::rngine::Collision>&)>>::toJSI(runtime, arg.onTick));
82
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "onTick"), JSIConverter<std::function<std::shared_ptr<Promise<double>>(const margelo::nitro::rngine::SystemContext&)>>::toJSI(runtime, arg.onTick));
86
83
  return obj;
87
84
  }
88
85
  static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
@@ -95,7 +92,7 @@ namespace margelo::nitro {
95
92
  }
96
93
  if (!JSIConverter<std::optional<std::vector<std::string>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "entities")))) return false;
97
94
  if (!JSIConverter<std::optional<std::vector<margelo::nitro::rngine::CollisionPair>>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "collisions")))) return false;
98
- if (!JSIConverter<std::function<std::shared_ptr<Promise<double>>(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;
95
+ if (!JSIConverter<std::function<std::shared_ptr<Promise<double>>(const margelo::nitro::rngine::SystemContext&)>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "onTick")))) return false;
99
96
  return true;
100
97
  }
101
98
  };
@@ -0,0 +1,99 @@
1
+ ///
2
+ /// SystemContext.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
+ // Forward declaration of `Screen` to properly resolve imports.
32
+ namespace margelo::nitro::rngine { struct Screen; }
33
+ // Forward declaration of `Entity` to properly resolve imports.
34
+ namespace margelo::nitro::rngine { struct Entity; }
35
+ // Forward declaration of `Collision` to properly resolve imports.
36
+ namespace margelo::nitro::rngine { struct Collision; }
37
+
38
+ #include "Screen.hpp"
39
+ #include "Entity.hpp"
40
+ #include <vector>
41
+ #include "Collision.hpp"
42
+
43
+ namespace margelo::nitro::rngine {
44
+
45
+ /**
46
+ * A struct which can be represented as a JavaScript object (SystemContext).
47
+ */
48
+ struct SystemContext final {
49
+ public:
50
+ Screen screen SWIFT_PRIVATE;
51
+ std::vector<Entity> entities SWIFT_PRIVATE;
52
+ std::vector<Collision> collisions SWIFT_PRIVATE;
53
+
54
+ public:
55
+ SystemContext() = default;
56
+ explicit SystemContext(Screen screen, std::vector<Entity> entities, std::vector<Collision> collisions): screen(screen), entities(entities), collisions(collisions) {}
57
+
58
+ public:
59
+ friend bool operator==(const SystemContext& lhs, const SystemContext& rhs) = default;
60
+ };
61
+
62
+ } // namespace margelo::nitro::rngine
63
+
64
+ namespace margelo::nitro {
65
+
66
+ // C++ SystemContext <> JS SystemContext (object)
67
+ template <>
68
+ struct JSIConverter<margelo::nitro::rngine::SystemContext> final {
69
+ static inline margelo::nitro::rngine::SystemContext fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
70
+ jsi::Object obj = arg.asObject(runtime);
71
+ return margelo::nitro::rngine::SystemContext(
72
+ JSIConverter<margelo::nitro::rngine::Screen>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "screen"))),
73
+ JSIConverter<std::vector<margelo::nitro::rngine::Entity>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "entities"))),
74
+ JSIConverter<std::vector<margelo::nitro::rngine::Collision>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "collisions")))
75
+ );
76
+ }
77
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::rngine::SystemContext& arg) {
78
+ jsi::Object obj(runtime);
79
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "screen"), JSIConverter<margelo::nitro::rngine::Screen>::toJSI(runtime, arg.screen));
80
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "entities"), JSIConverter<std::vector<margelo::nitro::rngine::Entity>>::toJSI(runtime, arg.entities));
81
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "collisions"), JSIConverter<std::vector<margelo::nitro::rngine::Collision>>::toJSI(runtime, arg.collisions));
82
+ return obj;
83
+ }
84
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
85
+ if (!value.isObject()) {
86
+ return false;
87
+ }
88
+ jsi::Object obj = value.getObject(runtime);
89
+ if (!nitro::isPlainObject(runtime, obj)) {
90
+ return false;
91
+ }
92
+ if (!JSIConverter<margelo::nitro::rngine::Screen>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "screen")))) return false;
93
+ if (!JSIConverter<std::vector<margelo::nitro::rngine::Entity>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "entities")))) return false;
94
+ if (!JSIConverter<std::vector<margelo::nitro::rngine::Collision>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "collisions")))) return false;
95
+ return true;
96
+ }
97
+ };
98
+
99
+ } // namespace margelo::nitro
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rngine",
3
- "version": "0.8.3",
3
+ "version": "0.9.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",
@@ -1,8 +1,10 @@
1
1
  #include "GameLoop.hpp"
2
+ #include "AssetUtils.hpp"
2
3
  #include "Collision.hpp"
3
4
  #include "CollisionUtils.hpp"
4
5
  #include "Entity.hpp"
5
6
  #include "GameRenderer.hpp"
7
+ #include "SystemContext.hpp"
6
8
  #include <android/log.h>
7
9
  #include <chrono>
8
10
  #include <cinttypes>
@@ -105,14 +107,15 @@ void GameLoop::runGameLoop() {
105
107
  void GameLoop::runSystems() {
106
108
  for (size_t systemIndex = 0; systemIndex < _systems.size(); ++systemIndex) {
107
109
  auto &system = _systems[systemIndex];
108
- std::vector<Entity> entities;
109
- std::vector<Collision> collisions;
110
+ SystemContext systemContext;
111
+
112
+ systemContext.screen = _screen;
110
113
 
111
114
  if (system.entities.has_value()) {
112
115
  for (const auto &id : system.entities.value()) {
113
116
  auto resolvedEntitiesInternal = resolveEntitiesInternal(id);
114
117
  for (auto *entity : resolvedEntitiesInternal)
115
- entities.push_back(*entity);
118
+ systemContext.entities.push_back(*entity);
116
119
  }
117
120
  }
118
121
 
@@ -124,7 +127,7 @@ void GameLoop::runSystems() {
124
127
  bool matchesReverse =
125
128
  entityIdMatches(c.a, pair.b) && entityIdMatches(c.b, pair.a);
126
129
  if (matchesForward || matchesReverse) {
127
- collisions.push_back(c);
130
+ systemContext.collisions.push_back(c);
128
131
  }
129
132
  }
130
133
  }
@@ -133,10 +136,10 @@ void GameLoop::runSystems() {
133
136
  __android_log_print(
134
137
  ANDROID_LOG_DEBUG, "GameLoop",
135
138
  "System Index: %zu, Entities: %zu, Collisions: %zu, Calling onTick",
136
- systemIndex, entities.size(), collisions.size());
139
+ systemIndex, systemContext.entities.size(),
140
+ systemContext.collisions.size());
137
141
 
138
- std::shared_ptr<Promise<double>> promise =
139
- system.onTick(entities, collisions);
142
+ std::shared_ptr<Promise<double>> promise = system.onTick(systemContext);
140
143
  std::future<double> future = promise->await();
141
144
  double duration = future.get();
142
145
 
@@ -179,19 +182,9 @@ void GameLoop::updateStats(double deltaTime) {
179
182
 
180
183
  void GameLoop::updateScreen(double deltaTime) {
181
184
  std::lock_guard<std::mutex> lock(_mutex);
182
- if (_screen.asset.has_value() && _screen.asset.value() < 0) {
183
- auto &lottieCache = GameRenderer::getInstance().getLottieCacheInternal();
184
- auto it = lottieCache.find(_screen.asset.value());
185
- if (it != lottieCache.end() && it->second) {
186
- if (_screen.speed.has_value() && _screen.speed.value() > 0) {
187
- _screen.progress = fmod(_screen.progress.value_or(0.0) +
188
- (deltaTime * _screen.speed.value()) /
189
- it->second->duration(),
190
- 1.0);
191
- }
192
- }
193
- } else if (_screen.progress.has_value()) {
194
- _screen.progress.reset();
185
+ if (_screen.asset.has_value()) {
186
+ AssetUtils::updateProgress(_screen.progress, _screen.speed,
187
+ _screen.asset.value(), deltaTime);
195
188
  }
196
189
  }
197
190
 
@@ -224,19 +217,9 @@ void GameLoop::updateEntities(double deltaTime) {
224
217
  entity.py += entity.vy.value() * deltaTime;
225
218
  }
226
219
 
227
- if (entity.asset.has_value() && entity.asset.value() < 0) {
228
- auto &lottieCache = GameRenderer::getInstance().getLottieCacheInternal();
229
- auto it = lottieCache.find(entity.asset.value());
230
- if (it != lottieCache.end() && it->second) {
231
- if (entity.speed.has_value() && entity.speed.value() > 0) {
232
- entity.progress = fmod(entity.progress.value_or(0.0) +
233
- (deltaTime * entity.speed.value()) /
234
- it->second->duration(),
235
- 1.0);
236
- }
237
- }
238
- } else if (entity.progress.has_value()) {
239
- entity.progress.reset();
220
+ if (entity.asset.has_value()) {
221
+ AssetUtils::updateProgress(entity.progress, entity.speed,
222
+ entity.asset.value(), deltaTime);
240
223
  }
241
224
  }
242
225
  }
@@ -106,73 +106,71 @@ void GameMethods::despawn(const std::string &id) {
106
106
  }
107
107
  }
108
108
 
109
- void GameMethods::update(const std::vector<EntityUpdate> &updates) {
109
+ static void patch(const auto &field, auto &target) {
110
+ if (field.has_value()) {
111
+ target = field.value();
112
+ }
113
+ }
114
+
115
+ void GameMethods::updateEntities(
116
+ const std::vector<EntityUpdate> &entityUpdates) {
110
117
  auto &instance = GameLoop::getInstance();
111
118
  std::lock_guard<std::mutex> lock(instance.getMutexInternal());
112
- for (const auto &update : updates) {
119
+ for (const auto &entityUpdate : entityUpdates) {
113
120
  auto resolvedEntitiesInternals =
114
- instance.resolveEntitiesInternal(update.id);
121
+ instance.resolveEntitiesInternal(entityUpdate.id);
115
122
  if (resolvedEntitiesInternals.empty()) {
116
123
  __android_log_print(ANDROID_LOG_WARN, "GameMethods",
117
- "update: no entity found for id=%s",
118
- update.id.c_str());
124
+ "entityUpdate: no entity found for id=%s",
125
+ entityUpdate.id.c_str());
119
126
  continue;
120
127
  }
121
128
 
122
129
  for (auto *entity : resolvedEntitiesInternals) {
123
- if (update.px.has_value()) {
124
- entity->px = update.px.value();
125
- }
126
- if (update.py.has_value()) {
127
- entity->py = update.py.value();
128
- }
129
- if (update.shape.has_value()) {
130
- entity->shape = update.shape.value();
131
- }
132
- if (update.isSensor.has_value()) {
133
- entity->isSensor = update.isSensor.value();
134
- }
135
- if (update.asset.has_value()) {
136
- entity->asset = update.asset.value();
137
- }
138
- if (update.progress.has_value()) {
139
- entity->progress = update.progress.value();
140
- }
141
- if (update.speed.has_value()) {
142
- entity->speed = update.speed.value();
143
- }
144
- if (update.color.has_value()) {
145
- entity->color = update.color.value();
146
- }
147
- if (update.flipH.has_value()) {
148
- entity->flipH = update.flipH.value();
149
- }
150
- if (update.flipV.has_value()) {
151
- entity->flipV = update.flipV.value();
152
- }
153
- if (update.vx.has_value()) {
154
- entity->vx = update.vx.value();
155
- }
156
- if (update.vy.has_value()) {
157
- entity->vy = update.vy.value();
158
- }
159
- if (update.ax.has_value()) {
160
- entity->ax = update.ax.value();
161
- }
162
- if (update.ay.has_value()) {
163
- entity->ay = update.ay.value();
164
- }
165
- if (update.mass.has_value()) {
166
- entity->mass = update.mass.value();
167
- }
130
+ patch(entityUpdate.px, entity->px);
131
+ patch(entityUpdate.py, entity->py);
132
+ patch(entityUpdate.shape, entity->shape);
133
+ patch(entityUpdate.isSensor, entity->isSensor);
134
+ patch(entityUpdate.asset, entity->asset);
135
+ patch(entityUpdate.progress, entity->progress);
136
+ patch(entityUpdate.speed, entity->speed);
137
+ patch(entityUpdate.color, entity->color);
138
+ patch(entityUpdate.flipH, entity->flipH);
139
+ patch(entityUpdate.flipV, entity->flipV);
140
+ patch(entityUpdate.vx, entity->vx);
141
+ patch(entityUpdate.vy, entity->vy);
142
+ patch(entityUpdate.ax, entity->ax);
143
+ patch(entityUpdate.ay, entity->ay);
144
+ patch(entityUpdate.mass, entity->mass);
168
145
  }
169
146
 
170
147
  __android_log_print(ANDROID_LOG_INFO, "GameMethods",
171
- "update: patched %zu entities for id=%s",
172
- resolvedEntitiesInternals.size(), update.id.c_str());
148
+ "entityUpdateEntities: patched %zu entities for id=%s",
149
+ resolvedEntitiesInternals.size(),
150
+ entityUpdate.id.c_str());
173
151
  }
174
152
  }
175
153
 
154
+ void GameMethods::updateScreen(const ScreenUpdate &screenUpdate) {
155
+ auto &instance = GameLoop::getInstance();
156
+ std::lock_guard<std::mutex> lock(instance.getMutexInternal());
157
+ auto &screen = instance.getScreenInternal();
158
+
159
+ patch(screenUpdate.px, screen.px);
160
+ patch(screenUpdate.py, screen.py);
161
+ patch(screenUpdate.width, screen.width);
162
+ patch(screenUpdate.height, screen.height);
163
+ patch(screenUpdate.asset, screen.asset);
164
+ patch(screenUpdate.progress, screen.progress);
165
+ patch(screenUpdate.speed, screen.speed);
166
+ patch(screenUpdate.color, screen.color);
167
+ patch(screenUpdate.flipH, screen.flipH);
168
+ patch(screenUpdate.flipV, screen.flipV);
169
+
170
+ __android_log_print(ANDROID_LOG_INFO, "GameMethods",
171
+ "updateScreen: patched screen");
172
+ }
173
+
176
174
  bool GameMethods::isAssetLoaded(double id) {
177
175
  auto &gameRenderer = GameRenderer::getInstance();
178
176
 
@@ -24,7 +24,9 @@ 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
 
@@ -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
 
@@ -20,7 +21,6 @@
20
21
 
21
22
  #include "include/gpu/ganesh/gl/GrGLDirectContext.h"
22
23
  #include "include/gpu/ganesh/gl/GrGLInterface.h"
23
- #include "modules/svg/include/SkSVGDOM.h"
24
24
 
25
25
  #include <EGL/egl.h>
26
26
  #include <GLES3/gl3.h>
@@ -135,15 +135,20 @@ void GameRenderer::render(const Screen &screen,
135
135
 
136
136
  float scale = std::min(scaleX, scaleY);
137
137
 
138
- float offsetX = (static_cast<float>(_width) - (virtualWidth * scale)) * 0.5f;
139
- float offsetY =
140
- (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;
141
142
 
142
143
  canvas->save();
143
144
  canvas->translate(offsetX, offsetY);
144
145
  canvas->scale(scale, scale);
145
146
 
146
- 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
+
147
152
  canvas->clipRect(gameBounds);
148
153
 
149
154
  if (screen.color.has_value()) {
@@ -151,73 +156,16 @@ void GameRenderer::render(const Screen &screen,
151
156
  }
152
157
 
153
158
  if (screen.asset.has_value() && screen.asset.value() != 0) {
154
- double assetId = screen.asset.value();
155
-
156
- auto imageIt = _imageCache.find(assetId);
157
- if (imageIt != _imageCache.end()) {
158
- std::visit(
159
- [&](const auto image) {
160
- using T = std::decay_t<decltype(image)>;
161
- if constexpr (std::is_same_v<T, sk_sp<SkSVGDOM>>) {
162
- SkSize intrinsicSize = image->containerSize();
163
-
164
- if (intrinsicSize.isEmpty()) {
165
- image->setContainerSize(
166
- SkSize::Make(virtualWidth, virtualHeight));
167
- intrinsicSize = SkSize::Make(virtualWidth, virtualHeight);
168
- }
169
-
170
- float scaleX = virtualWidth / intrinsicSize.width();
171
- float scaleY = virtualHeight / intrinsicSize.height();
172
-
173
- canvas->save();
174
- if (screen.flipH.has_value() || screen.flipV.has_value()) {
175
- canvas->translate(virtualWidth / 2.f, virtualHeight / 2.f);
176
- canvas->scale(screen.flipH.value_or(false) ? -1.f : 1.f,
177
- screen.flipV.value_or(false) ? -1.f : 1.f);
178
- canvas->translate(-virtualWidth / 2.f, -virtualHeight / 2.f);
179
- }
180
- canvas->scale(scaleX, scaleY);
181
- image->render(canvas);
182
- canvas->restore();
183
- } else {
184
- float intrinsicW = static_cast<float>(image->width());
185
- float intrinsicH = static_cast<float>(image->height());
186
- if (intrinsicW <= 0.f || intrinsicH <= 0.f)
187
- return;
188
-
189
- float scaleX = virtualWidth / intrinsicW;
190
- float scaleY = virtualHeight / intrinsicH;
191
-
192
- canvas->save();
193
- if (screen.flipH.has_value() || screen.flipV.has_value()) {
194
- canvas->translate(virtualWidth / 2.f, virtualHeight / 2.f);
195
- canvas->scale(screen.flipH.value_or(false) ? -1.f : 1.f,
196
- screen.flipV.value_or(false) ? -1.f : 1.f);
197
- canvas->translate(-virtualWidth / 2.f, -virtualHeight / 2.f);
198
- }
199
- canvas->scale(scaleX, scaleY);
200
-
201
- SkSamplingOptions sampling(SkFilterMode::kLinear,
202
- SkMipmapMode::kLinear);
203
- canvas->drawImage(image, 0.f, 0.f, sampling);
204
- canvas->restore();
205
- }
206
- },
207
- imageIt->second);
208
- } else {
209
- auto lottieIt = _lottieCache.find(assetId);
210
- if (lottieIt != _lottieCache.end() && lottieIt->second) {
211
- auto &lottie = lottieIt->second;
212
- lottie->seek(screen.progress.value_or(0.0));
213
- SkRect dstBounds = SkRect::MakeWH(virtualWidth, virtualHeight);
214
- lottie->render(canvas, &dstBounds);
215
- }
216
- }
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));
217
165
  }
218
166
 
219
167
  for (const auto &[id, entity] : entities) {
220
- if (!isEntityVisible(entity, virtualWidth, virtualHeight)) {
168
+ if (!isEntityVisible(entity, gameBounds)) {
221
169
  continue;
222
170
  }
223
171
 
@@ -250,8 +198,6 @@ void GameRenderer::render(const Screen &screen,
250
198
  }
251
199
 
252
200
  if ((entity.asset.has_value() && entity.asset.value() != 0)) {
253
- double assetId = entity.asset.value();
254
-
255
201
  float width = 0.f, height = 0.f;
256
202
  std::visit(
257
203
  [&](const auto &shape) {
@@ -270,59 +216,10 @@ void GameRenderer::render(const Screen &screen,
270
216
  SkAutoCanvasRestore autoRestore(canvas, true);
271
217
  canvas->translate(px - width / 2, py - height / 2);
272
218
 
273
- if (entity.flipH.has_value() || entity.flipV.has_value()) {
274
- canvas->translate(width / 2.f, height / 2.f);
275
- canvas->scale(entity.flipH.value_or(false) ? -1.f : 1.f,
276
- entity.flipV.value_or(false) ? -1.f : 1.f);
277
- canvas->translate(-width / 2.f, -height / 2.f);
278
- }
279
-
280
- auto imageIt = _imageCache.find(assetId);
281
- if (imageIt != _imageCache.end()) {
282
- std::visit(
283
- [&](const auto image) {
284
- using T = std::decay_t<decltype(image)>;
285
- if constexpr (std::is_same_v<T, sk_sp<SkSVGDOM>>) {
286
- SkSize intrinsicSize = image->containerSize();
287
- if (intrinsicSize.isEmpty()) {
288
- image->setContainerSize(SkSize::Make(width, height));
289
- intrinsicSize = SkSize::Make(width, height);
290
- }
291
-
292
- float scaleX = width / intrinsicSize.width();
293
- float scaleY = height / intrinsicSize.height();
294
-
295
- canvas->save();
296
- canvas->scale(scaleX, scaleY);
297
- image->render(canvas);
298
- canvas->restore();
299
- } else {
300
- float intrinsicW = static_cast<float>(image->width());
301
- float intrinsicH = static_cast<float>(image->height());
302
- if (intrinsicW <= 0.f || intrinsicH <= 0.f)
303
- return;
304
-
305
- float scaleX = width / intrinsicW;
306
- float scaleY = height / intrinsicH;
307
-
308
- canvas->save();
309
- canvas->scale(scaleX, scaleY);
310
- SkSamplingOptions sampling(SkFilterMode::kLinear,
311
- SkMipmapMode::kLinear);
312
- canvas->drawImage(image, 0.f, 0.f, sampling);
313
- canvas->restore();
314
- }
315
- },
316
- imageIt->second);
317
- } else {
318
- auto lottieIt = _lottieCache.find(assetId);
319
- if (lottieIt != _lottieCache.end() && lottieIt->second) {
320
- auto &lottie = lottieIt->second;
321
- lottie->seek(entity.progress.value_or(0.0));
322
- SkRect dstBounds = SkRect::MakeWH(width, height);
323
- lottie->render(canvas, &dstBounds);
324
- }
325
- }
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));
326
223
  }
327
224
  }
328
225
  }
@@ -335,15 +232,13 @@ void GameRenderer::render(const Screen &screen,
335
232
  __android_log_print(ANDROID_LOG_DEBUG, "GameRenderer", "render: complete");
336
233
  }
337
234
 
338
- bool GameRenderer::isEntityVisible(const Entity &entity, float screenWidth,
339
- float screenHeight) {
340
- float width = 0.f;
341
- float height = 0.f;
235
+ bool GameRenderer::isEntityVisible(const Entity &entity,
236
+ const SkRect &viewBounds) {
237
+ float width = 0.f, height = 0.f;
342
238
 
343
239
  std::visit(
344
240
  [&](const auto &shape) {
345
241
  using T = std::decay_t<decltype(shape)>;
346
-
347
242
  if constexpr (std::is_same_v<T, Rect>) {
348
243
  width = static_cast<float>(shape.width);
349
244
  height = static_cast<float>(shape.height);
@@ -358,8 +253,8 @@ bool GameRenderer::isEntityVisible(const Entity &entity, float screenWidth,
358
253
  float top = static_cast<float>(entity.py) - height / 2.f;
359
254
  float bottom = static_cast<float>(entity.py) + height / 2.f;
360
255
 
361
- return right > 0.f && left < screenWidth && bottom > 0.f &&
362
- top < screenHeight;
256
+ return right > viewBounds.left() && left < viewBounds.right() &&
257
+ bottom > viewBounds.top() && top < viewBounds.bottom();
363
258
  }
364
259
 
365
260
  bool GameRenderer::initializeEGL() {
@@ -2,6 +2,7 @@
2
2
  #include "Entity.hpp"
3
3
  #include "Screen.hpp"
4
4
  #include "include/core/SkImage.h"
5
+ #include "include/core/SkRect.h"
5
6
  #include "include/core/SkSurface.h"
6
7
  #include "include/gpu/ganesh/GrDirectContext.h"
7
8
  #include "modules/skottie/include/Skottie.h"
@@ -58,8 +59,7 @@ private:
58
59
  std::map<double, sk_sp<skottie::Animation>> _lottieCache;
59
60
  std::map<double, std::variant<sk_sp<SkSVGDOM>, sk_sp<SkImage>>> _imageCache;
60
61
 
61
- bool isEntityVisible(const Entity &entity, float screenWidth,
62
- float screenHeight);
62
+ bool isEntityVisible(const Entity &entity, const SkRect &viewBounds);
63
63
 
64
64
  bool initializeEGL();
65
65
  bool createEGLSurface();