mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
24 Commits
experiment
...
culling-op
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8a55078119 | ||
![]() |
4417d7e958 | ||
![]() |
79cd327c68 | ||
![]() |
c3580fd207 | ||
![]() |
d34f1a654f | ||
![]() |
5abc74b66b | ||
![]() |
a848764318 | ||
![]() |
c0c6fddcbb | ||
![]() |
95ac26f05d | ||
![]() |
1f852648ef | ||
![]() |
b7da4361c3 | ||
![]() |
71b369d40e | ||
![]() |
4d8cf56922 | ||
![]() |
1a6e0ba5a1 | ||
![]() |
ec1ebcbf5b | ||
![]() |
4ee9ccd7a9 | ||
![]() |
1ac248bfa4 | ||
![]() |
65b78d4db7 | ||
![]() |
639d90d743 | ||
![]() |
45d0a78656 | ||
![]() |
0a0811e355 | ||
![]() |
a8f9df3dac | ||
![]() |
35db56d778 | ||
![]() |
27efe8e3dd |
@@ -30,7 +30,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
@@ -107,7 +107,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
- run:
|
||||
name: Install system dependencies for Electron builder
|
||||
@@ -127,7 +127,8 @@ jobs:
|
||||
# Build GDevelop.js (and run tests to ensure it works)
|
||||
- run:
|
||||
name: Build GDevelop.js
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
|
||||
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
|
||||
|
||||
# GDevelop IDE dependencies (after building GDevelop.js to avoid downloading a pre-built version)
|
||||
- run:
|
||||
@@ -184,7 +185,7 @@ jobs:
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
command: git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
|
||||
# GDevelop.js dependencies
|
||||
- restore_cache:
|
||||
@@ -200,7 +201,8 @@ jobs:
|
||||
# Build GDevelop.js (and run tests to ensure it works)
|
||||
- run:
|
||||
name: Build GDevelop.js
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test && cd ..
|
||||
# Use "--runInBand" as it's faster and avoid deadlocks on CircleCI Linux machines (probably because limited in processes number).
|
||||
command: cd GDevelop.js && source ../emsdk/emsdk_env.sh && npm run build && npm test -- --runInBand && cd ..
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
|
@@ -14,7 +14,7 @@ tasks:
|
||||
init: |
|
||||
sudo apt-get update
|
||||
sudo apt install cmake python-is-python3 python3-distutils -y
|
||||
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
git clone https://github.com/juj/emsdk.git && cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
cd GDevelop.js
|
||||
npm install
|
||||
source ../emsdk/emsdk_env.sh && npm run build -- --dev
|
||||
|
@@ -39,7 +39,7 @@ install:
|
||||
- cd ..
|
||||
# Install Emscripten (for GDevelop.js)
|
||||
- git clone https://github.com/juj/emsdk.git
|
||||
- cd emsdk && ./emsdk install 1.39.6 && ./emsdk activate 1.39.6 && cd ..
|
||||
- cd emsdk && ./emsdk install 3.1.21 && ./emsdk activate 3.1.21 && cd ..
|
||||
# Install GDevelop.js dependencies
|
||||
- cd GDevelop.js && npm install && cd ..
|
||||
# Build GDevelop.js
|
||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -113,7 +113,8 @@
|
||||
"memory_resource": "cpp",
|
||||
"__bits": "cpp",
|
||||
"__verbose_abort": "cpp",
|
||||
"variant": "cpp"
|
||||
"variant": "cpp",
|
||||
"charconv": "cpp"
|
||||
},
|
||||
"files.exclude": {
|
||||
"Binaries/*build*": true,
|
||||
|
@@ -164,7 +164,7 @@ void LinkEvent::UnserializeFrom(gd::Project& project,
|
||||
}
|
||||
|
||||
bool LinkEvent::AcceptVisitor(gd::EventVisitor &eventVisitor) {
|
||||
return BaseEvent::AcceptVisitor(eventVisitor) |
|
||||
return BaseEvent::AcceptVisitor(eventVisitor) ||
|
||||
eventVisitor.VisitLinkEvent(*this);
|
||||
}
|
||||
|
||||
|
@@ -491,11 +491,6 @@ class GD_CORE_API ExpressionParser2 {
|
||||
std::vector<std::unique_ptr<ExpressionNode>> parameters;
|
||||
gd::String lastObjectName = "";
|
||||
|
||||
// By convention, object is always the first parameter, and behavior the
|
||||
// second one.
|
||||
size_t parameterIndex =
|
||||
WrittenParametersFirstIndex(objectName, behaviorName);
|
||||
|
||||
bool previousCharacterIsParameterSeparator = false;
|
||||
while (!IsEndReached()) {
|
||||
SkipAllWhitespaces();
|
||||
@@ -514,7 +509,6 @@ class GD_CORE_API ExpressionParser2 {
|
||||
SkipAllWhitespaces();
|
||||
previousCharacterIsParameterSeparator = CheckIfChar(IsParameterSeparator);
|
||||
SkipIfChar(IsParameterSeparator);
|
||||
parameterIndex++;
|
||||
}
|
||||
|
||||
ExpressionParserLocation invalidClosingParenthesisLocation;
|
||||
|
@@ -548,7 +548,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"ChangeLayerTimeScale",
|
||||
_("Change layer time scale"),
|
||||
_("Layer time scale"),
|
||||
_("Change the time scale applied to the objects of the layer."),
|
||||
_("Set the time scale of layer _PARAM1_ to _PARAM2_"),
|
||||
"",
|
||||
@@ -594,7 +594,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsCameraExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"SetLayerAmbientLightColor",
|
||||
_("Set the ambient light color"),
|
||||
_("Ambient light color"),
|
||||
_("Set the ambient light color of the lighting layer in format "
|
||||
"\"R;G;B\" string."),
|
||||
_("Set the ambient color of the lighting layer _PARAM1_ to _PARAM2_"),
|
||||
|
@@ -44,7 +44,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAnimatableExtension(
|
||||
"number",
|
||||
"Index",
|
||||
_("Animation (by number)"),
|
||||
_("the number of the animation played by the object (the number from "
|
||||
_("the animation played by the object using the animation number (from "
|
||||
"the animations list)"),
|
||||
_("the number of the animation"),
|
||||
_("Animations and images"),
|
||||
|
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#include "AllBuiltinExtensions.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
#include "GDCore/Extensions/Metadata/MultipleInstructionMetadata.h"
|
||||
|
||||
using namespace std;
|
||||
namespace gd {
|
||||
@@ -57,7 +58,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
extension
|
||||
.AddCondition("DoesSceneExist",
|
||||
_("Does scene exist"),
|
||||
_("Check if scene exists."),
|
||||
_("Check if a scene exists."),
|
||||
_("Scene _PARAM1_ exists"),
|
||||
"",
|
||||
"res/actions/texte.png",
|
||||
@@ -163,6 +164,45 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsSceneExtension(
|
||||
"res/actions/window.png")
|
||||
.SetHelpPath("/interface/scene-editor/events")
|
||||
.AddCodeOnlyParameter("currentScene", "");
|
||||
|
||||
extension
|
||||
.AddAction("PrioritizeLoadingOfScene",
|
||||
_("Preload scene"),
|
||||
_("Preload a scene resources as soon as possible in background."),
|
||||
_("Preload scene _PARAM1_ in background"),
|
||||
"",
|
||||
"res/actions/replaceScene24.png",
|
||||
"res/actions/replaceScene.png")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Name of the new scene"))
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension.AddExpressionAndCondition("number",
|
||||
"SceneLoadingProgress",
|
||||
_("Scene loading progress"),
|
||||
_("The progress of resources loading in background for a scene (between 0 and 1)."),
|
||||
_("_PARAM0_ loading progress"),
|
||||
_(""),
|
||||
"res/actions/replaceScene24.png")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
.UseStandardParameters("number", ParameterOptions::MakeNewOptions())
|
||||
.MarkAsAdvanced();
|
||||
|
||||
extension
|
||||
.AddCondition("AreSceneAssetsLoaded",
|
||||
_("Scene preloaded"),
|
||||
_("Check if scene resources have finished to load in background."),
|
||||
_("Scene _PARAM1_ was preloaded in background"),
|
||||
"",
|
||||
"res/actions/replaceScene24.png",
|
||||
"res/actions/replaceScene.png")
|
||||
.SetHelpPath("/all-features/resources-loading")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("sceneName", _("Scene name"))
|
||||
.MarkAsAdvanced();
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -87,8 +87,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsWindowExtension(
|
||||
extension
|
||||
.AddAction(
|
||||
"SetWindowSize",
|
||||
_("Change the size of the game window"),
|
||||
_("This action changes the size of the game window. Note that this "
|
||||
_("Game window size"),
|
||||
_("Changes the size of the game window. Note that this "
|
||||
"will only work on platform supporting this operation: games "
|
||||
"running in browsers or on mobile phones can not update their "
|
||||
"window size. Game resolution can still be updated."),
|
||||
|
@@ -191,6 +191,16 @@ class GD_CORE_API MultipleInstructionMetadata : public AbstractFunctionMetadata
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::SetHelpPath
|
||||
*/
|
||||
MultipleInstructionMetadata &SetHelpPath(const gd::String &path) {
|
||||
if (expression) expression->SetHelpPath(path);
|
||||
if (condition) condition->SetHelpPath(path);
|
||||
if (action) action->SetHelpPath(path);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \see gd::InstructionMetadata::MarkAsSimple
|
||||
*/
|
||||
|
@@ -4,7 +4,6 @@
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
#include "DependenciesAnalyzer.h"
|
||||
#include <algorithm>
|
||||
#include "GDCore/Events/Builtin/LinkEvent.h"
|
||||
@@ -29,9 +28,9 @@ DependenciesAnalyzer::DependenciesAnalyzer(const gd::Project& project_,
|
||||
|
||||
bool DependenciesAnalyzer::Analyze() {
|
||||
if (layout)
|
||||
return Analyze(layout->GetEvents(), true);
|
||||
return Analyze(layout->GetEvents());
|
||||
else if (externalEvents)
|
||||
return Analyze(externalEvents->GetEvents(), true);
|
||||
return Analyze(externalEvents->GetEvents());
|
||||
|
||||
std::cout << "ERROR: DependenciesAnalyzer called without any layout or "
|
||||
"external events.";
|
||||
@@ -40,63 +39,38 @@ bool DependenciesAnalyzer::Analyze() {
|
||||
|
||||
DependenciesAnalyzer::~DependenciesAnalyzer() {}
|
||||
|
||||
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLevel) {
|
||||
bool DependenciesAnalyzer::Analyze(const gd::EventsList& events) {
|
||||
for (unsigned int i = 0; i < events.size(); ++i) {
|
||||
const gd::LinkEvent* linkEvent = dynamic_cast<const gd::LinkEvent*>(&events[i]);
|
||||
if (linkEvent) {
|
||||
DependenciesAnalyzer analyzer(*this);
|
||||
|
||||
gd::String linked = linkEvent->GetTarget();
|
||||
if (project.HasExternalEventsNamed(linked)) {
|
||||
if (std::find(parentExternalEvents.begin(),
|
||||
parentExternalEvents.end(),
|
||||
linked) != parentExternalEvents.end())
|
||||
return false; // Circular dependency!
|
||||
|
||||
externalEventsDependencies.insert(
|
||||
linked); // There is a direct dependency
|
||||
if (!isOnTopLevel) notTopLevelExternalEventsDependencies.insert(linked);
|
||||
analyzer.AddParentExternalEvents(linked);
|
||||
if (!analyzer.Analyze(project.GetExternalEvents(linked).GetEvents(),
|
||||
isOnTopLevel))
|
||||
linked) != parentExternalEvents.end()) {
|
||||
// Circular dependency!
|
||||
return false;
|
||||
|
||||
}
|
||||
bool wasDependencyJustAdded = externalEventsDependencies.insert(linked).second;
|
||||
if (wasDependencyJustAdded) {
|
||||
parentExternalEvents.push_back(linked);
|
||||
if (!Analyze(project.GetExternalEvents(linked).GetEvents()))
|
||||
return false;
|
||||
parentExternalEvents.pop_back();
|
||||
}
|
||||
} else if (project.HasLayoutNamed(linked)) {
|
||||
if (std::find(parentScenes.begin(), parentScenes.end(), linked) !=
|
||||
parentScenes.end())
|
||||
return false; // Circular dependency!
|
||||
|
||||
scenesDependencies.insert(linked); // There is a direct dependency
|
||||
if (!isOnTopLevel) notTopLevelScenesDependencies.insert(linked);
|
||||
analyzer.AddParentScene(linked);
|
||||
if (!analyzer.Analyze(project.GetLayout(linked).GetEvents(),
|
||||
isOnTopLevel))
|
||||
parentScenes.end()) {
|
||||
// Circular dependency!
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update with indirect dependencies.
|
||||
scenesDependencies.insert(analyzer.GetScenesDependencies().begin(),
|
||||
analyzer.GetScenesDependencies().end());
|
||||
externalEventsDependencies.insert(
|
||||
analyzer.GetExternalEventsDependencies().begin(),
|
||||
analyzer.GetExternalEventsDependencies().end());
|
||||
sourceFilesDependencies.insert(
|
||||
analyzer.GetSourceFilesDependencies().begin(),
|
||||
analyzer.GetSourceFilesDependencies().end());
|
||||
notTopLevelScenesDependencies.insert(
|
||||
analyzer.GetNotTopLevelScenesDependencies().begin(),
|
||||
analyzer.GetNotTopLevelScenesDependencies().end());
|
||||
notTopLevelExternalEventsDependencies.insert(
|
||||
analyzer.GetNotTopLevelExternalEventsDependencies().begin(),
|
||||
analyzer.GetNotTopLevelExternalEventsDependencies().end());
|
||||
|
||||
if (!isOnTopLevel) {
|
||||
notTopLevelScenesDependencies.insert(
|
||||
analyzer.GetScenesDependencies().begin(),
|
||||
analyzer.GetScenesDependencies().end());
|
||||
notTopLevelExternalEventsDependencies.insert(
|
||||
analyzer.GetExternalEventsDependencies().begin(),
|
||||
analyzer.GetExternalEventsDependencies().end());
|
||||
}
|
||||
bool wasDependencyJustAdded = scenesDependencies.insert(linked).second;
|
||||
if (wasDependencyJustAdded) {
|
||||
parentScenes.push_back(linked);
|
||||
if (!Analyze(project.GetLayout(linked).GetEvents()))
|
||||
return false;
|
||||
parentScenes.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,45 +86,9 @@ bool DependenciesAnalyzer::Analyze(const gd::EventsList& events, bool isOnTopLev
|
||||
|
||||
// Analyze sub events dependencies
|
||||
if (events[i].CanHaveSubEvents()) {
|
||||
if (!Analyze(events[i].GetSubEvents(), false)) return false;
|
||||
if (!Analyze(events[i].GetSubEvents())) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene() {
|
||||
if (!externalEvents) {
|
||||
std::cout << "ERROR: ExternalEventsCanBeCompiledForAScene called without "
|
||||
"external events set!"
|
||||
<< std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
gd::String sceneName;
|
||||
for (unsigned int i = 0; i < project.GetLayoutsCount(); ++i) {
|
||||
// For each layout, compute the dependencies and the dependencies which are
|
||||
// not coming from a top level event.
|
||||
DependenciesAnalyzer analyzer(project, project.GetLayout(i));
|
||||
if (!analyzer.Analyze()) continue; // Analyze failed -> Cyclic dependencies
|
||||
const std::set<gd::String>& dependencies =
|
||||
analyzer.GetExternalEventsDependencies();
|
||||
const std::set<gd::String>& notTopLevelDependencies =
|
||||
analyzer.GetNotTopLevelExternalEventsDependencies();
|
||||
|
||||
// Check if the external events is a dependency, and that is is only present
|
||||
// as a link on the top level.
|
||||
if (dependencies.find(externalEvents->GetName()) != dependencies.end() &&
|
||||
notTopLevelDependencies.find(externalEvents->GetName()) ==
|
||||
notTopLevelDependencies.end()) {
|
||||
if (!sceneName.empty())
|
||||
return ""; // External events can be compiled only if one scene is
|
||||
// including them.
|
||||
else
|
||||
sceneName = project.GetLayout(i).GetName();
|
||||
}
|
||||
}
|
||||
|
||||
return sceneName; // External events can be compiled and used for the scene.
|
||||
}
|
||||
#endif
|
||||
|
@@ -39,11 +39,6 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
|
||||
/**
|
||||
* \brief Constructor for analyzing the dependencies of external events.
|
||||
*
|
||||
* You can also call then
|
||||
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene to check if the
|
||||
* external events can be compiled separately and called by a scene. \see
|
||||
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene
|
||||
*/
|
||||
DependenciesAnalyzer(const gd::Project& project_,
|
||||
const gd::ExternalEvents& externalEvents);
|
||||
@@ -60,18 +55,6 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
*/
|
||||
bool Analyze();
|
||||
|
||||
/**
|
||||
* Check if the external events (passed in the constructor) can be compiled
|
||||
* and called by a single scene:<br> This is possible when the link calling
|
||||
* the external events does not have any parent event and when this situation
|
||||
* occurs only in a single scene and not in another.
|
||||
*
|
||||
* \return The name of the scene which is able to call the compiled external
|
||||
* events. If empty, no scene is able to call them. (So external events have
|
||||
* to be included directly by links).
|
||||
*/
|
||||
gd::String ExternalEventsCanBeCompiledForAScene();
|
||||
|
||||
/**
|
||||
* \brief Return the scenes being dependencies of the scene or external events
|
||||
* passed in the constructor.
|
||||
@@ -96,25 +79,6 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
return sourceFilesDependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return the scenes being dependencies of the scene or external events
|
||||
* passed in the constructor, but being not top level dependencies: The links
|
||||
* including them are not a top level events (i.e: They have a parent event).
|
||||
*/
|
||||
const std::set<gd::String>& GetNotTopLevelScenesDependencies() const {
|
||||
return notTopLevelScenesDependencies;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return the external events being dependencies of the scene or
|
||||
* external events passed in the constructor, but being not top level
|
||||
* dependencies: The links including them are not a top level events (i.e:
|
||||
* They have a parent event).
|
||||
*/
|
||||
const std::set<gd::String>& GetNotTopLevelExternalEventsDependencies() const {
|
||||
return notTopLevelExternalEventsDependencies;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Analyze the dependencies of the events.
|
||||
@@ -124,32 +88,11 @@ class GD_CORE_API DependenciesAnalyzer {
|
||||
* (they have no parents). \return false if a circular dependency exists, true
|
||||
* otherwise.
|
||||
*/
|
||||
bool Analyze(const gd::EventsList& events, bool isOnTopLevel);
|
||||
|
||||
void AddParentScene(gd::String parentScene) {
|
||||
parentScenes.push_back(parentScene);
|
||||
};
|
||||
void AddParentExternalEvents(gd::String parentExternalEvents_) {
|
||||
parentExternalEvents.push_back(parentExternalEvents_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if all links pointing to external events called \a
|
||||
* externalEventsName are only at the top level of \a events. The function
|
||||
* return false as soon as it discover a link to external events which is not
|
||||
* at the top level ( i.e: It has a parent event ).
|
||||
*
|
||||
* \warning The function assumes that there are not cyclic dependencies.
|
||||
*/
|
||||
bool CheckIfExternalEventsIsLinkedOnlyAtTopLevel(
|
||||
const gd::String& externalEventsName,
|
||||
std::vector<std::shared_ptr<gd::BaseEvent> >& events);
|
||||
bool Analyze(const gd::EventsList& events);
|
||||
|
||||
std::set<gd::String> scenesDependencies;
|
||||
std::set<gd::String> externalEventsDependencies;
|
||||
std::set<gd::String> sourceFilesDependencies;
|
||||
std::set<gd::String> notTopLevelScenesDependencies;
|
||||
std::set<gd::String> notTopLevelExternalEventsDependencies;
|
||||
std::vector<gd::String>
|
||||
parentScenes; ///< Used to check for circular dependencies.
|
||||
std::vector<gd::String>
|
||||
|
@@ -226,7 +226,7 @@ void EventsIdentifiersFinder::FindArgumentsInEventsAndDependencies(
|
||||
eventWorker.Launch(layout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
|
||||
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
|
||||
dependenciesAnalyzer.Analyze();
|
||||
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
|
||||
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
|
||||
|
@@ -258,7 +258,7 @@ void EventsVariablesFinder::FindArgumentsInEventsAndDependencies(
|
||||
eventWorker.Launch(layout.GetEvents(),
|
||||
gd::ProjectScopedContainers::MakeNewProjectScopedContainersForProjectAndLayout(project, layout));
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer = DependenciesAnalyzer(project, layout);
|
||||
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
|
||||
dependenciesAnalyzer.Analyze();
|
||||
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
|
||||
const gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
|
||||
|
@@ -43,7 +43,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
|
||||
struct dirent *lecture;
|
||||
DIR *rep;
|
||||
rep = opendir(directory.c_str());
|
||||
int l = 0;
|
||||
|
||||
if (rep == NULL) {
|
||||
cout << "Unable to open Extensions (" << directory << ") directory."
|
||||
@@ -63,8 +62,6 @@ void ExtensionsLoader::LoadAllExtensions(const gd::String &directory,
|
||||
|
||||
LoadExtension(directory + "/" + lec, platform, forgiving);
|
||||
librariesLoaded.push_back(directory + "/" + lec);
|
||||
|
||||
l++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +100,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
|
||||
struct dirent *lecture;
|
||||
DIR *rep;
|
||||
rep = opendir(directory.c_str());
|
||||
int l = 0;
|
||||
|
||||
if (rep == NULL) {
|
||||
cout << "Unable to open Extensions (" << directory << ") directory."
|
||||
@@ -118,7 +114,6 @@ void ExtensionsLoader::ExtensionsLoadingDone(const gd::String &directory) {
|
||||
lec.find(".xgd" + suffix, lec.length() - 4 - suffix.length()) !=
|
||||
string::npos) {
|
||||
librariesLoaded.push_back(directory + "/" + lec);
|
||||
l++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -262,13 +262,6 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
|
||||
return false;
|
||||
};
|
||||
|
||||
void LaunchResourceWorkerOnEvents(const gd::Project& project,
|
||||
gd::EventsList& events,
|
||||
gd::ArbitraryResourceWorker& worker) {
|
||||
gd::ResourceWorkerInEventsWorker eventsWorker(project, worker);
|
||||
eventsWorker.Launch(events);
|
||||
}
|
||||
|
||||
gd::ResourceWorkerInEventsWorker
|
||||
GetResourceWorkerOnEvents(const gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
|
@@ -37,7 +37,7 @@ namespace gd {
|
||||
* \see ResourcesMergingHelper
|
||||
* \see gd::ResourcesInUseHelper
|
||||
*
|
||||
* \see gd::LaunchResourceWorkerOnEvents
|
||||
* \see gd::GetResourceWorkerOnEvents
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
|
@@ -51,7 +51,6 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
// Copy resources
|
||||
map<gd::String, gd::String>& resourcesNewFilename =
|
||||
resourcesMergingHelper.GetAllResourcesOldAndNewFilename();
|
||||
unsigned int i = 0;
|
||||
for (map<gd::String, gd::String>::const_iterator it =
|
||||
resourcesNewFilename.begin();
|
||||
it != resourcesNewFilename.end();
|
||||
@@ -71,8 +70,6 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
destinationFile + _("\"."));
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
35
Core/GDCore/IDE/Project/SceneResourcesFinder.cpp
Normal file
35
Core/GDCore/IDE/Project/SceneResourcesFinder.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#include "SceneResourcesFinder.h"
|
||||
|
||||
#include "GDCore/IDE/ResourceExposer.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
std::set<gd::String> SceneResourcesFinder::FindProjectResources(gd::Project &project) {
|
||||
gd::SceneResourcesFinder resourceWorker;
|
||||
gd::ResourceExposer::ExposeProjectResources(project, resourceWorker);
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
std::set<gd::String> SceneResourcesFinder::FindSceneResources(gd::Project &project,
|
||||
gd::Layout &layout) {
|
||||
gd::SceneResourcesFinder resourceWorker;
|
||||
gd::ResourceExposer::ExposeLayoutResources(project, layout, resourceWorker);
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
void SceneResourcesFinder::AddUsedResource(gd::String &resourceName) {
|
||||
if (resourceName.empty()) {
|
||||
return;
|
||||
}
|
||||
resourceNames.insert(resourceName);
|
||||
}
|
||||
|
||||
} // namespace gd
|
86
Core/GDCore/IDE/Project/SceneResourcesFinder.h
Normal file
86
Core/GDCore/IDE/Project/SceneResourcesFinder.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2008-2023 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Find resource usages in several parts of the project.
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class SceneResourcesFinder : private gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
/**
|
||||
* @brief Find resource usages in a given scenes.
|
||||
*
|
||||
* It doesn't include resources used globally.
|
||||
*/
|
||||
static std::set<gd::String> FindSceneResources(gd::Project &project,
|
||||
gd::Layout &layout);
|
||||
|
||||
/**
|
||||
* @brief Find resource that are used globally in the project.
|
||||
*
|
||||
* It doesn't include resources used in scenes.
|
||||
*/
|
||||
static std::set<gd::String> FindProjectResources(gd::Project &project);
|
||||
|
||||
virtual ~SceneResourcesFinder(){};
|
||||
|
||||
private:
|
||||
SceneResourcesFinder() : gd::ArbitraryResourceWorker(){};
|
||||
|
||||
void AddUsedResource(gd::String &resourceName);
|
||||
|
||||
void ExposeFile(gd::String &resourceFileName) override{
|
||||
// Don't do anything: we're renaming resources, not the files they are
|
||||
// pointing to.
|
||||
};
|
||||
void ExposeImage(gd::String &imageResourceName) override {
|
||||
AddUsedResource(imageResourceName);
|
||||
};
|
||||
void ExposeAudio(gd::String &audioResourceName) override {
|
||||
AddUsedResource(audioResourceName);
|
||||
};
|
||||
void ExposeFont(gd::String &fontResourceName) override {
|
||||
AddUsedResource(fontResourceName);
|
||||
};
|
||||
void ExposeJson(gd::String &jsonResourceName) override {
|
||||
AddUsedResource(jsonResourceName);
|
||||
};
|
||||
void ExposeTilemap(gd::String &tilemapResourceName) override {
|
||||
AddUsedResource(tilemapResourceName);
|
||||
};
|
||||
void ExposeTileset(gd::String &tilesetResourceName) override {
|
||||
AddUsedResource(tilesetResourceName);
|
||||
};
|
||||
void ExposeVideo(gd::String &videoResourceName) override {
|
||||
AddUsedResource(videoResourceName);
|
||||
};
|
||||
void ExposeBitmapFont(gd::String &bitmapFontName) override {
|
||||
AddUsedResource(bitmapFontName);
|
||||
};
|
||||
void ExposeModel3D(gd::String &resourceName) override {
|
||||
AddUsedResource(resourceName);
|
||||
};
|
||||
|
||||
std::set<gd::String> resourceNames;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -20,6 +20,7 @@
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Project/ProjectScopedContainers.h"
|
||||
#include "GDCore/String.h"
|
||||
#include "GDCore/IDE/DependenciesAnalyzer.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -33,27 +34,8 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
// Add events based extensions
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
// Add (free) events functions
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
|
||||
// Add (behavior) events functions
|
||||
for (auto &&eventsBasedBehavior :
|
||||
eventsFunctionsExtension.GetEventsBasedBehaviors()
|
||||
.GetInternalVector()) {
|
||||
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
|
||||
}
|
||||
|
||||
// Add (object) events functions
|
||||
for (auto &&eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
auto &objectEventsFunctions = eventsBasedObject->GetEventsFunctions();
|
||||
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
}
|
||||
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +51,7 @@ void ProjectBrowserHelper::ExposeProjectEventsWithoutExtensions(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
|
||||
@@ -85,7 +67,7 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
void ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
auto projectScopedContainers =
|
||||
@@ -103,6 +85,32 @@ void ProjectBrowserHelper::ExposeLayoutEvents(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
// Add layouts events
|
||||
worker.Launch(layout.GetEvents());
|
||||
|
||||
DependenciesAnalyzer dependenciesAnalyzer(project, layout);
|
||||
bool hasCircularDependencies = !dependenciesAnalyzer.Analyze();
|
||||
if (hasCircularDependencies) {
|
||||
// The analyzer stops when it finds circular dependencies so the dependencies are not complete.
|
||||
// TODO Should the analyzer still continue to avoid side effect on thing that would not be code generation related?
|
||||
// Maybe a boolean parameter should be added?
|
||||
return;
|
||||
}
|
||||
for (const gd::String& externalEventName : dependenciesAnalyzer.GetExternalEventsDependencies()) {
|
||||
gd::ExternalEvents& externalEvents = project.GetExternalEvents(externalEventName);
|
||||
|
||||
worker.Launch(externalEvents.GetEvents());
|
||||
}
|
||||
for (const gd::String& sceneName : dependenciesAnalyzer.GetScenesDependencies()) {
|
||||
gd::Layout& dependencyLayout = project.GetLayout(sceneName);
|
||||
|
||||
worker.Launch(dependencyLayout.GetEvents());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
gd::Project &project, gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
// See also gd::Project::ExposeResources for a method that traverse the whole
|
||||
@@ -130,8 +138,43 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
// Add events based extensions
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
// Add (free) events functions
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, worker);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
// Add (free) events functions
|
||||
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
|
||||
gd::ObjectsContainer globalObjectsAndGroups;
|
||||
gd::ObjectsContainer objectsAndGroups;
|
||||
gd::EventsFunctionTools::FreeEventsFunctionToObjectsContainer(
|
||||
project, eventsFunctionsExtension, *eventsFunction,
|
||||
globalObjectsAndGroups, objectsAndGroups);
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
|
||||
// Add (behavior) events functions
|
||||
for (auto &&eventsBasedBehavior :
|
||||
eventsFunctionsExtension.GetEventsBasedBehaviors()
|
||||
.GetInternalVector()) {
|
||||
ExposeEventsBasedBehaviorEvents(project, *eventsBasedBehavior, worker);
|
||||
}
|
||||
|
||||
// Add (object) events functions
|
||||
for (auto &&eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project, const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
// Add (free) events functions
|
||||
for (auto &&eventsFunction : eventsFunctionsExtension.GetInternalVector()) {
|
||||
gd::ObjectsContainer globalObjectsAndGroups;
|
||||
gd::ObjectsContainer objectsAndGroups;
|
||||
@@ -157,7 +200,6 @@ void ProjectBrowserHelper::ExposeProjectEvents(
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
ExposeEventsBasedObjectEvents(project, *eventsBasedObject, worker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
|
||||
@@ -189,6 +231,21 @@ void ProjectBrowserHelper::ExposeEventsBasedBehaviorEvents(
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject,
|
||||
gd::ArbitraryEventsWorker &worker) {
|
||||
auto &objectEventsFunctions = eventsBasedObject.GetEventsFunctions();
|
||||
for (auto &&eventsFunction : objectEventsFunctions.GetInternalVector()) {
|
||||
gd::ObjectsContainer globalObjectsAndGroups;
|
||||
gd::ObjectsContainer objectsAndGroups;
|
||||
gd::EventsFunctionTools::ObjectEventsFunctionToObjectsContainer(
|
||||
project, eventsBasedObject, *eventsFunction, globalObjectsAndGroups,
|
||||
objectsAndGroups);
|
||||
|
||||
worker.Launch(eventsFunction->GetEvents());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedObjectEvents(
|
||||
gd::Project &project, const gd::EventsBasedObject &eventsBasedObject,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker) {
|
||||
@@ -216,7 +273,7 @@ void ProjectBrowserHelper::ExposeProjectObjects(
|
||||
|
||||
// Layout objects
|
||||
for (size_t i = 0; i < project.GetLayoutsCount(); i++) {
|
||||
worker.Launch(project.GetLayout(i));
|
||||
gd::ProjectBrowserHelper::ExposeLayoutObjects(project.GetLayout(i), worker);
|
||||
}
|
||||
|
||||
// Event based objects children
|
||||
@@ -232,6 +289,14 @@ void ProjectBrowserHelper::ExposeProjectObjects(
|
||||
}
|
||||
};
|
||||
|
||||
void ProjectBrowserHelper::ExposeLayoutObjects(gd::Layout &layout,
|
||||
gd::ArbitraryObjectsWorker &worker) {
|
||||
// In the future, layouts may have children object containers.
|
||||
|
||||
// Layout objects
|
||||
worker.Launch(layout);
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeProjectFunctions(
|
||||
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) {
|
||||
|
||||
|
@@ -60,18 +60,52 @@ public:
|
||||
* \brief Call the specified worker on all events of a layout and
|
||||
* its external events.
|
||||
*/
|
||||
static void ExposeLayoutEvents(gd::Project &project, gd::Layout &layout,
|
||||
static void ExposeLayoutEventsAndExternalEvents(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of a layout and
|
||||
* its external events.
|
||||
*/
|
||||
static void ExposeLayoutEvents(gd::Project &project, gd::Layout &layout,
|
||||
static void ExposeLayoutEventsAndExternalEvents(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of a layout and
|
||||
* its dependencies according to EventLink (external events or other layout
|
||||
* events).
|
||||
*/
|
||||
static void
|
||||
ExposeLayoutEventsAndDependencies(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* behavior
|
||||
* extension.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an events
|
||||
* based extension.
|
||||
*/
|
||||
static void ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* extension.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an events
|
||||
* based extension.
|
||||
*/
|
||||
static void ExposeEventsFunctionsExtensionEvents(
|
||||
gd::Project &project,
|
||||
const gd::EventsFunctionsExtension &eventsFunctionsExtension,
|
||||
gd::ArbitraryEventsWorkerWithContext &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* behavior.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an events
|
||||
* based behavior.
|
||||
@@ -93,10 +127,22 @@ public:
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* behavior.
|
||||
* object.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an
|
||||
* event-based behavior.
|
||||
* event-based object.
|
||||
*/
|
||||
static void
|
||||
ExposeEventsBasedObjectEvents(gd::Project &project,
|
||||
const gd::EventsBasedObject &eventsBasedObject,
|
||||
gd::ArbitraryEventsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all events of the event-based
|
||||
* object.
|
||||
*
|
||||
* This should be the preferred way to traverse all the events of an
|
||||
* event-based object.
|
||||
*/
|
||||
static void
|
||||
ExposeEventsBasedObjectEvents(gd::Project &project,
|
||||
@@ -112,6 +158,14 @@ public:
|
||||
static void ExposeProjectObjects(gd::Project &project,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all ObjectContainers of the layout.
|
||||
*
|
||||
* This should be the preferred way to traverse all the objects of a layout.
|
||||
*/
|
||||
static void ExposeLayoutObjects(gd::Layout &layout,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all FunctionsContainers of the project
|
||||
* (global, layouts...)
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "GDCore/Extensions/Platform.h"
|
||||
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
|
||||
#include "GDCore/Extensions/Metadata/EffectMetadata.h"
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -36,6 +37,7 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
|
||||
|
||||
// Expose any project resources as files.
|
||||
worker.ExposeResources(resourcesManager);
|
||||
|
||||
project.GetPlatformSpecificAssets().ExposeResources(worker);
|
||||
|
||||
// Expose event resources
|
||||
@@ -73,6 +75,49 @@ void ResourceExposer::ExposeWholeProjectResources(gd::Project& project, gd::Arbi
|
||||
worker.ExposeImage(loadingScreen.GetBackgroundImageResourceName());
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeProjectResources(gd::Project& project, gd::ArbitraryResourceWorker& worker) {
|
||||
// Expose global objects configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
objectWorker.Launch(project);
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeLayoutResources(
|
||||
gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
|
||||
// Expose object configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutObjects(layout, objectWorker);
|
||||
|
||||
// Expose layer effect resources
|
||||
for (std::size_t layerIndex = 0; layerIndex < layout.GetLayersCount();
|
||||
layerIndex++) {
|
||||
auto &layer = layout.GetLayer(layerIndex);
|
||||
|
||||
auto &effects = layer.GetEffects();
|
||||
for (size_t effectIndex = 0; effectIndex < effects.GetEffectsCount();
|
||||
effectIndex++) {
|
||||
auto &effect = effects.GetEffect(effectIndex);
|
||||
gd::ResourceExposer::ExposeEffectResources(project.GetCurrentPlatform(),
|
||||
effect, worker);
|
||||
}
|
||||
}
|
||||
|
||||
// Expose event resources
|
||||
auto eventWorker = gd::GetResourceWorkerOnEvents(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndDependencies(project, layout,
|
||||
eventWorker);
|
||||
|
||||
// Exposed extension event resources
|
||||
// Note that using resources in extensions is very unlikely and probably not
|
||||
// worth the effort of something smart.
|
||||
for (std::size_t e = 0; e < project.GetEventsFunctionsExtensionsCount();
|
||||
e++) {
|
||||
auto &eventsFunctionsExtension = project.GetEventsFunctionsExtension(e);
|
||||
gd::ProjectBrowserHelper::ExposeEventsFunctionsExtensionEvents(project, eventsFunctionsExtension, eventWorker);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeEffectResources(
|
||||
gd::Platform &platform, gd::Effect &effect,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
@@ -88,11 +133,13 @@ void ResourceExposer::ExposeEffectResources(
|
||||
auto &resourceType = propertyDescriptor.GetExtraInfo()[0];
|
||||
|
||||
const gd::String &resourceName = effect.GetStringParameter(propertyName);
|
||||
gd::String potentiallyUpdatedResourceName = resourceName;
|
||||
worker.ExposeResourceWithType(resourceType,
|
||||
potentiallyUpdatedResourceName);
|
||||
if (potentiallyUpdatedResourceName != resourceName) {
|
||||
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
|
||||
if (!resourceName.empty()) {
|
||||
gd::String potentiallyUpdatedResourceName = resourceName;
|
||||
worker.ExposeResourceWithType(resourceType,
|
||||
potentiallyUpdatedResourceName);
|
||||
if (potentiallyUpdatedResourceName != resourceName) {
|
||||
effect.SetStringParameter(propertyName, potentiallyUpdatedResourceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ class Platform;
|
||||
class Project;
|
||||
class ArbitraryResourceWorker;
|
||||
class Effect;
|
||||
class Layout;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -31,6 +32,25 @@ public:
|
||||
static void ExposeWholeProjectResources(gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose only the resources used globally on a project.
|
||||
*
|
||||
* It doesn't include resources used in layouts.
|
||||
*/
|
||||
static void ExposeProjectResources(gd::Project &project,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given layout.
|
||||
*
|
||||
* It doesn't include resources used globally.
|
||||
*/
|
||||
static void ExposeLayoutResources(gd::Project &project, gd::Layout &layout,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given effect.
|
||||
*/
|
||||
static void ExposeEffectResources(gd::Platform &platform, gd::Effect &effect,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
};
|
||||
|
@@ -1538,7 +1538,7 @@ void WholeProjectRefactorer::RenameLayer(gd::Project &project,
|
||||
return;
|
||||
gd::ProjectElementRenamer projectElementRenamer(project.GetCurrentPlatform(),
|
||||
"layer", oldName, newName);
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
}
|
||||
|
||||
@@ -1552,7 +1552,7 @@ void WholeProjectRefactorer::RenameLayerEffect(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "layerEffectName", oldName, newName);
|
||||
projectElementRenamer.SetLayerConstraint(layer.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
}
|
||||
|
||||
@@ -1566,7 +1566,7 @@ void WholeProjectRefactorer::RenameObjectAnimation(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "objectAnimationName", oldName, newName);
|
||||
projectElementRenamer.SetObjectConstraint(object.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
}
|
||||
|
||||
@@ -1580,7 +1580,7 @@ void WholeProjectRefactorer::RenameObjectPoint(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "objectPointName", oldName, newName);
|
||||
projectElementRenamer.SetObjectConstraint(object.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
}
|
||||
|
||||
@@ -1594,7 +1594,7 @@ void WholeProjectRefactorer::RenameObjectEffect(gd::Project &project,
|
||||
gd::ProjectElementRenamer projectElementRenamer(
|
||||
project.GetCurrentPlatform(), "objectEffectName", oldName, newName);
|
||||
projectElementRenamer.SetObjectConstraint(object.GetName());
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEvents(project, layout,
|
||||
gd::ProjectBrowserHelper::ExposeLayoutEventsAndExternalEvents(project, layout,
|
||||
projectElementRenamer);
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
#include "CustomConfigurationHelper.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/Project/Behavior.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
@@ -13,8 +15,6 @@
|
||||
#include "GDCore/Serialization/Serializer.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
using namespace gd;
|
||||
|
||||
void CustomConfigurationHelper::InitializeContent(
|
||||
@@ -25,7 +25,8 @@ void CustomConfigurationHelper::InitializeContent(
|
||||
auto propertyType = property->GetType();
|
||||
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior") {
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
element.SetStringValue(property->GetValue());
|
||||
} else if (propertyType == "Number") {
|
||||
element.SetDoubleValue(property->GetValue().To<double>());
|
||||
@@ -51,7 +52,8 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
|
||||
|
||||
if (configurationContent.HasChild(propertyName)) {
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior") {
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
newProperty.SetValue(
|
||||
configurationContent.GetChild(propertyName).GetStringValue());
|
||||
} else if (propertyType == "Number") {
|
||||
@@ -59,8 +61,9 @@ std::map<gd::String, gd::PropertyDescriptor> CustomConfigurationHelper::GetPrope
|
||||
configurationContent.GetChild(propertyName).GetDoubleValue()));
|
||||
} else if (propertyType == "Boolean") {
|
||||
newProperty.SetValue(
|
||||
configurationContent.GetChild(propertyName).GetBoolValue() ? "true"
|
||||
: "false");
|
||||
configurationContent.GetChild(propertyName).GetBoolValue()
|
||||
? "true"
|
||||
: "false");
|
||||
}
|
||||
} else {
|
||||
// No value was serialized for this property. `newProperty`
|
||||
@@ -85,7 +88,8 @@ bool CustomConfigurationHelper::UpdateProperty(
|
||||
const gd::String &propertyType = property.GetType();
|
||||
|
||||
if (propertyType == "String" || propertyType == "Choice" ||
|
||||
propertyType == "Color" || propertyType == "Behavior") {
|
||||
propertyType == "Color" || propertyType == "Behavior" ||
|
||||
propertyType == "resource") {
|
||||
element.SetStringValue(newValue);
|
||||
} else if (propertyType == "Number") {
|
||||
element.SetDoubleValue(newValue.To<double>());
|
||||
|
@@ -534,19 +534,7 @@ void ResourcesManager::SerializeTo(SerializerElement& element) const {
|
||||
if (resources[i] == std::shared_ptr<Resource>()) break;
|
||||
|
||||
SerializerElement& resourceElement = resourcesElement.AddChild("resource");
|
||||
resourceElement.SetAttribute("kind", resources[i]->GetKind());
|
||||
resourceElement.SetAttribute("name", resources[i]->GetName());
|
||||
resourceElement.SetAttribute("metadata", resources[i]->GetMetadata());
|
||||
|
||||
const gd::String& originName = resources[i]->GetOriginName();
|
||||
const gd::String& originIdentifier = resources[i]->GetOriginIdentifier();
|
||||
if (!originName.empty() || !originIdentifier.empty()) {
|
||||
resourceElement.AddChild("origin")
|
||||
.SetAttribute("name", originName)
|
||||
.SetAttribute("identifier", originIdentifier);
|
||||
}
|
||||
|
||||
resources[i]->SerializeTo(resourceElement);
|
||||
gd::ResourcesManager::SerializeResourceTo(*resources[i], resourceElement);
|
||||
}
|
||||
|
||||
SerializerElement& resourcesFoldersElement =
|
||||
@@ -556,6 +544,22 @@ void ResourcesManager::SerializeTo(SerializerElement& element) const {
|
||||
folders[i].SerializeTo(resourcesFoldersElement.AddChild("folder"));
|
||||
}
|
||||
|
||||
void ResourcesManager::SerializeResourceTo(gd::Resource &resource,
|
||||
SerializerElement &resourceElement) {
|
||||
resourceElement.SetAttribute("kind", resource.GetKind());
|
||||
resourceElement.SetAttribute("name", resource.GetName());
|
||||
resourceElement.SetAttribute("metadata", resource.GetMetadata());
|
||||
|
||||
const gd::String &originName = resource.GetOriginName();
|
||||
const gd::String &originIdentifier = resource.GetOriginIdentifier();
|
||||
if (!originName.empty() || !originIdentifier.empty()) {
|
||||
resourceElement.AddChild("origin")
|
||||
.SetAttribute("name", originName)
|
||||
.SetAttribute("identifier", originIdentifier);
|
||||
}
|
||||
resource.SerializeTo(resourceElement);
|
||||
}
|
||||
|
||||
void ImageResource::SetFile(const gd::String& newFile) {
|
||||
file = NormalizePathSeparator(newFile);
|
||||
}
|
||||
|
@@ -662,6 +662,11 @@ class GD_CORE_API ResourcesManager {
|
||||
*/
|
||||
void SerializeTo(SerializerElement& element) const;
|
||||
|
||||
/**
|
||||
* \brief Serialize one resource.
|
||||
*/
|
||||
static void SerializeResourceTo(gd::Resource& resource, SerializerElement& resourceElement);
|
||||
|
||||
/**
|
||||
* \brief Unserialize the object.
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -5398,12 +5398,10 @@ class Runner {
|
||||
getRegistryHub().getTestCaseRegistry().getFilteredTests(
|
||||
testSpec, *m_config, testCases);
|
||||
|
||||
int testsRunForGroup = 0;
|
||||
for (std::vector<TestCase>::const_iterator it = testCases.begin(),
|
||||
itEnd = testCases.end();
|
||||
it != itEnd;
|
||||
++it) {
|
||||
testsRunForGroup++;
|
||||
if (m_testsAlreadyRun.find(*it) == m_testsAlreadyRun.end()) {
|
||||
if (context.aborting()) break;
|
||||
|
||||
|
@@ -86,11 +86,13 @@ namespace gdjs {
|
||||
|
||||
export const turnCameraTowardObject = (
|
||||
runtimeScene: RuntimeScene,
|
||||
object: gdjs.RuntimeObject,
|
||||
object: gdjs.RuntimeObject | null,
|
||||
layerName: string,
|
||||
cameraIndex: integer,
|
||||
isStandingOnY: boolean
|
||||
) => {
|
||||
if (!object) return;
|
||||
|
||||
const layer = runtimeScene.getLayer(layerName);
|
||||
const layerRenderer = layer.getRenderer();
|
||||
|
||||
|
@@ -36,7 +36,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'Focus',
|
||||
_('Change focus of the window'),
|
||||
_('Window focus'),
|
||||
_('Make the window gain or lose focus.'),
|
||||
_('Focus the window: _PARAM0_'),
|
||||
_('Windows, Linux, macOS'),
|
||||
@@ -72,7 +72,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'Show',
|
||||
_('Change visibility of the window'),
|
||||
_('Window visibility'),
|
||||
_('Make the window visible or invisible.'),
|
||||
_('Window visible: _PARAM0_'),
|
||||
_('Windows, Linux, macOS'),
|
||||
@@ -624,7 +624,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'SetOpacity',
|
||||
_('Set window opacity'),
|
||||
_('Window opacity'),
|
||||
_('Changes the window opacity.'),
|
||||
_('Set the window opacity to _PARAM0_'),
|
||||
_('Windows, Linux, macOS'),
|
||||
@@ -645,7 +645,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'SetWindowPosition',
|
||||
_('Set window position'),
|
||||
_('Window position'),
|
||||
_('Changes the window position.'),
|
||||
_('Set the window position to _PARAM0_;_PARAM1_'),
|
||||
_('Windows, Linux, macOS'),
|
||||
|
@@ -30,6 +30,7 @@ describe('gdjs.AnchorRuntimeBehavior', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
|
||||
function createObject(behaviorProperties) {
|
||||
|
@@ -41,7 +41,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the additional border that the object must cross "
|
||||
"before being deleted."),
|
||||
_("the additional border"),
|
||||
"",
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png",
|
||||
"CppPlatform/Extensions/destroyoutsideicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -56,7 +56,7 @@ void DeclareDestroyOutsideBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the additional border that the object must cross "
|
||||
"before being deleted."),
|
||||
_("the additional border"),
|
||||
"",
|
||||
_("Destroy outside configuration"),
|
||||
"CppPlatform/Extensions/destroyoutsideicon24.png",
|
||||
"CppPlatform/Extensions/destroyoutsideicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
|
@@ -39,7 +39,7 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Being dragged"),
|
||||
_("Check if the object is being dragged."),
|
||||
_("_PARAM0_ is being dragged"),
|
||||
"",
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
|
||||
@@ -51,7 +51,7 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Was just dropped"),
|
||||
_("Check if the object was just dropped after being dragged."),
|
||||
_("_PARAM0_ was just dropped"),
|
||||
"",
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
|
||||
|
@@ -27,6 +27,7 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
|
||||
var object = new gdjs.TestRuntimeObject(runtimeScene, {
|
||||
|
@@ -86,7 +86,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'AnalyticsSetUID',
|
||||
_('Change user UID'),
|
||||
_('User UID'),
|
||||
_(
|
||||
"Changes the current user's analytics identifier. " +
|
||||
'This is what let Analytics differentiate user, ' +
|
||||
@@ -400,7 +400,7 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetAuthToken',
|
||||
_('Get the user authentication token'),
|
||||
_('User authentication token'),
|
||||
_(
|
||||
'Get the user authentication token. The token is the proof of authentication.'
|
||||
),
|
||||
@@ -438,8 +438,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetUserEmail',
|
||||
_('Get the user email address'),
|
||||
_('Gets the user email address.'),
|
||||
_('User email address'),
|
||||
_('Return the user email address.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -455,8 +455,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetAccountCreationTime',
|
||||
_('Get the accounts creation time'),
|
||||
_('Gets the accounts creation time.'),
|
||||
_('Accounts creation time'),
|
||||
_('Return the accounts creation time.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -472,8 +472,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetLastLoginTime',
|
||||
_('Get the user last login time'),
|
||||
_('Gets the user last login time.'),
|
||||
_('User last login time'),
|
||||
_('Return the user last login time.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -489,8 +489,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetUserDisplayName',
|
||||
_('Get the user display name'),
|
||||
_('Gets the user display name.'),
|
||||
_('User display name'),
|
||||
_('Return the user display name.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -506,8 +506,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetPhoneNumber',
|
||||
_('Get the user phone number'),
|
||||
_('Gets the user phone number.'),
|
||||
_('User phone number'),
|
||||
_('Return the user phone number.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -523,9 +523,9 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetUserUID',
|
||||
_('Get the user UID'),
|
||||
_('User UID'),
|
||||
_(
|
||||
'Gets the user Unique IDentifier. Use that to link data to an ' +
|
||||
'Return the user Unique IDentifier. Use that to link data to an ' +
|
||||
'user instead of the name or email.'
|
||||
),
|
||||
_('Authentication/User Management'),
|
||||
@@ -543,8 +543,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetTenantID',
|
||||
_('Get the user tenant ID'),
|
||||
_('Gets the user tenant ID. For advanced usage only.'),
|
||||
_('User tenant ID'),
|
||||
_('Return the user tenant ID. For advanced usage only.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -560,8 +560,8 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetRefreshToken',
|
||||
_('Get the user refresh token'),
|
||||
_('Gets the user refresh token. For advanced usage only.'),
|
||||
_('User refresh token'),
|
||||
_('Return the user refresh token. For advanced usage only.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
)
|
||||
@@ -577,7 +577,7 @@ module.exports = {
|
||||
extension
|
||||
.addStrExpression(
|
||||
'GetPhotoURL',
|
||||
_('Get the user profile picture URL'),
|
||||
_('Profile picture URL'),
|
||||
_('Gets an URL to the user profile picture.'),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
@@ -635,7 +635,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'SetDisplayName',
|
||||
_('Set display name'),
|
||||
_('Display name'),
|
||||
_('Sets the user display name.'),
|
||||
_("Set the user's display name to _PARAM0_"),
|
||||
_('Authentication/User Management'),
|
||||
@@ -655,9 +655,9 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'SetPhotoURL',
|
||||
_('Set the user profile picture'),
|
||||
_('Sets the user profile picture URL to a new one.'),
|
||||
_("Set the user's profile picture URL to _PARAM0_"),
|
||||
_('Profile picture'),
|
||||
_('Change the user profile picture URL to a new one.'),
|
||||
_("Change the user's profile picture URL to _PARAM0_"),
|
||||
_('Authentication/User Management'),
|
||||
'JsPlatform/Extensions/firebase.png',
|
||||
'JsPlatform/Extensions/firebase.png'
|
||||
@@ -676,7 +676,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'ChangeEmail',
|
||||
_('Change the user email'),
|
||||
_('User email'),
|
||||
_(
|
||||
'This action is dangerous so it requires reauthentication.\n' +
|
||||
"Changes the user's email address."
|
||||
@@ -716,7 +716,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'ChangeEmailProvider',
|
||||
_('Change the user email (Provider)'),
|
||||
_('User email (Provider)'),
|
||||
_(
|
||||
'This action is dangerous so it requires reauthentication.\n' +
|
||||
"Changes the user's email address.\n" +
|
||||
@@ -755,7 +755,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'ChangePassword',
|
||||
_('Change the user password'),
|
||||
_('User password'),
|
||||
_(
|
||||
'This action is dangerous so it requires reauthentication.\n' +
|
||||
'Changes the user password.'
|
||||
@@ -796,7 +796,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'ChangePasswordProvider',
|
||||
_('Change the user password (Provider)'),
|
||||
_('User password (Provider)'),
|
||||
_(
|
||||
'This action is dangerous so it requires reauthentication.\n' +
|
||||
'Changes the user password.\n' +
|
||||
@@ -1782,7 +1782,7 @@ module.exports = {
|
||||
.addAction(
|
||||
'FirestoreGetField',
|
||||
_('Get a field of a document'),
|
||||
_('Gets the value of a field in a firestore document.'),
|
||||
_('Return the value of a field in a firestore document.'),
|
||||
_(
|
||||
'Load field _PARAM2_ of firestore document _PARAM1_ in collection _PARAM0_ into _PARAM3_ (store result state in _PARAM4_)'
|
||||
),
|
||||
@@ -2213,7 +2213,7 @@ module.exports = {
|
||||
'DatabaseGetField',
|
||||
_('Get a field of a variable'),
|
||||
_(
|
||||
'Gets the value of a field in a variable from the database and store it in a scene variable.'
|
||||
'Return the value of a field in a variable from the database and store it in a scene variable.'
|
||||
),
|
||||
_(
|
||||
'Load field _PARAM1_ of database variable _PARAM0_ into _PARAM2_ (store result state in _PARAM3_)'
|
||||
|
@@ -203,7 +203,7 @@ module.exports = {
|
||||
object
|
||||
.addAction(
|
||||
'SetRadius',
|
||||
_('Set the radius of light object'),
|
||||
_('Light radius'),
|
||||
_('Set the radius of light object'),
|
||||
_('Set the radius of _PARAM0_ to: _PARAM1_'),
|
||||
'',
|
||||
@@ -218,7 +218,7 @@ module.exports = {
|
||||
object
|
||||
.addAction(
|
||||
'SetColor',
|
||||
_('Set the color of light object'),
|
||||
_('Light color'),
|
||||
_('Set the color of light object in format "R;G;B" string.'),
|
||||
_('Set the color of _PARAM0_ to: _PARAM1_'),
|
||||
'',
|
||||
|
@@ -195,8 +195,8 @@ namespace gdjs {
|
||||
|
||||
export const linkObjects = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objA: gdjs.RuntimeObject,
|
||||
objB: gdjs.RuntimeObject
|
||||
objA: gdjs.RuntimeObject | null,
|
||||
objB: gdjs.RuntimeObject | null
|
||||
) {
|
||||
if (objA === null || objB === null) {
|
||||
return;
|
||||
@@ -206,8 +206,8 @@ namespace gdjs {
|
||||
|
||||
export const removeLinkBetween = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objA: gdjs.RuntimeObject,
|
||||
objB: gdjs.RuntimeObject
|
||||
objA: gdjs.RuntimeObject | null,
|
||||
objB: gdjs.RuntimeObject | null
|
||||
) {
|
||||
if (objA === null || objB === null) {
|
||||
return;
|
||||
@@ -231,7 +231,7 @@ namespace gdjs {
|
||||
export const pickObjectsLinkedTo = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objectsLists: Hashtable<gdjs.RuntimeObject[]>,
|
||||
obj: gdjs.RuntimeObject,
|
||||
obj: gdjs.RuntimeObject | null,
|
||||
eventsFunctionContext: EventsFunctionContext | undefined
|
||||
) {
|
||||
if (obj === null) {
|
||||
|
@@ -33,6 +33,7 @@ describe('gdjs.LinksManager', function () {
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
usedResources: [],
|
||||
});
|
||||
|
||||
const manager = gdjs.LinksManager.getManager(runtimeScene);
|
||||
|
@@ -637,8 +637,8 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
.SetFunctionName("GetCost");
|
||||
|
||||
aut.AddAction("SetImpassable",
|
||||
_("Should object be impassable?"),
|
||||
_("Decide if the object is an impassable obstacle"),
|
||||
_("Should object be impassable"),
|
||||
_("Decide if the object is an impassable obstacle."),
|
||||
_("Set _PARAM0_ as an impassable obstacle: _PARAM2_"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon24.png",
|
||||
@@ -646,12 +646,12 @@ void DeclarePathfindingBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PathfindingObstacleBehavior")
|
||||
.AddParameter("yesorno", _("Impassable?"))
|
||||
.AddParameter("yesorno", _("Impassable"))
|
||||
.SetFunctionName("SetImpassable");
|
||||
|
||||
aut.AddCondition("IsImpassable",
|
||||
_("Is object impassable?"),
|
||||
_("Check if the obstacle is impassable"),
|
||||
_("Impassable obstacle"),
|
||||
_("Check if the obstacle is impassable."),
|
||||
_("_PARAM0_ is impassable"),
|
||||
_("Obstacles"),
|
||||
"CppPlatform/Extensions/pathfindingobstacleicon24.png",
|
||||
|
@@ -34,6 +34,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
setFramePerSecond(runtimeScene, framePerSecond);
|
||||
return runtimeScene;
|
||||
|
@@ -37,6 +37,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
setFramePerSecond(runtimeScene, framePerSecond);
|
||||
return runtimeScene;
|
||||
|
@@ -39,6 +39,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / 60) * 1000;
|
||||
|
@@ -1837,7 +1837,7 @@ namespace gdjs {
|
||||
addDistanceJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
length: float,
|
||||
@@ -2083,7 +2083,7 @@ namespace gdjs {
|
||||
addRevoluteJointBetweenTwoBodies(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
enableLimit: boolean,
|
||||
@@ -2383,7 +2383,7 @@ namespace gdjs {
|
||||
addPrismaticJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
axisAngle: float,
|
||||
@@ -2754,7 +2754,7 @@ namespace gdjs {
|
||||
addPulleyJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
groundX1: float,
|
||||
@@ -3237,7 +3237,7 @@ namespace gdjs {
|
||||
addWheelJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
axisAngle: float,
|
||||
@@ -3522,7 +3522,7 @@ namespace gdjs {
|
||||
addWeldJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
referenceAngle: float,
|
||||
@@ -3673,7 +3673,7 @@ namespace gdjs {
|
||||
addRopeJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
maxLength: float,
|
||||
@@ -3782,7 +3782,7 @@ namespace gdjs {
|
||||
addFrictionJoint(
|
||||
x1: float,
|
||||
y1: float,
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
x2: float,
|
||||
y2: float,
|
||||
maxForce: float,
|
||||
@@ -3914,7 +3914,7 @@ namespace gdjs {
|
||||
|
||||
// Motor joint
|
||||
addMotorJoint(
|
||||
other: gdjs.RuntimeObject,
|
||||
other: gdjs.RuntimeObject | null,
|
||||
offsetX: float,
|
||||
offsetY: float,
|
||||
offsetAngle: float,
|
||||
|
@@ -17,326 +17,326 @@ This project is released under the MIT License.
|
||||
void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
extension
|
||||
.SetExtensionInformation("PhysicsBehavior",
|
||||
_("Physics Engine (deprecated)"),
|
||||
("Physics Engine (deprecated)"),
|
||||
"This is the old, deprecated physics engine. Prefer to use the Physics Engine 2.0.",
|
||||
"Florian Rival",
|
||||
"Open source (MIT License)")
|
||||
.SetCategory("Movement")
|
||||
.SetExtensionHelpPath("/behaviors/physics");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Physics Engine (deprecated)"))
|
||||
.SetIcon("res/physics16.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(("Physics Engine (deprecated)"))
|
||||
.SetIcon("res/physics-deprecated16.png");
|
||||
|
||||
{
|
||||
gd::BehaviorMetadata& aut = extension.AddBehavior(
|
||||
"PhysicsBehavior",
|
||||
_("Physics Engine"),
|
||||
_("Physics"),
|
||||
_("Make objects move as if they are subject to the laws of physics. If "
|
||||
("Physics Engine"),
|
||||
("Physics"),
|
||||
("Make objects move as if they are subject to the laws of physics. If "
|
||||
"you're creating a new game, prefer Physics Engine 2.0"),
|
||||
"",
|
||||
"res/physics32.png",
|
||||
"res/physics-deprecated32.png",
|
||||
"PhysicsBehavior",
|
||||
std::make_shared<PhysicsBehavior>(),
|
||||
std::make_shared<ScenePhysicsDatas>());
|
||||
|
||||
aut.AddAction("SetStatic",
|
||||
_("Make the object static"),
|
||||
_("Make the object immovable."),
|
||||
_("Make _PARAM0_ static"),
|
||||
_("Movement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Make the object static"),
|
||||
("Make the object immovable."),
|
||||
("Make _PARAM0_ static"),
|
||||
("Movement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetStatic");
|
||||
|
||||
aut.AddAction("SetDynamic",
|
||||
_("Make the object dynamic"),
|
||||
_("Make the object dynamic ( affected by forces and other "
|
||||
("Make the object dynamic"),
|
||||
("Make the object dynamic ( affected by forces and other "
|
||||
"objects )."),
|
||||
_("Make _PARAM0_ dynamic"),
|
||||
_("Movement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Make _PARAM0_ dynamic"),
|
||||
("Movement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetDynamic");
|
||||
|
||||
aut.AddCondition("IsDynamic",
|
||||
_("The object is dynamic"),
|
||||
_("Test if an object is dynamic ( affected by forces and "
|
||||
("The object is dynamic"),
|
||||
("Test if an object is dynamic ( affected by forces and "
|
||||
"other objects )."),
|
||||
_("_PARAM0_ is dynamic"),
|
||||
_("Movement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
("_PARAM0_ is dynamic"),
|
||||
("Movement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.SetFunctionName("IsDynamic");
|
||||
|
||||
aut.AddAction("SetFixedRotation",
|
||||
_("Fix rotation"),
|
||||
_("Prevent the object from rotating"),
|
||||
_("Fix rotation of _PARAM0_"),
|
||||
_("Rotation"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Fix rotation"),
|
||||
("Prevent the object from rotating"),
|
||||
("Fix rotation of _PARAM0_"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetFixedRotation");
|
||||
|
||||
aut.AddAction(
|
||||
"AddRevoluteJoint",
|
||||
_("Add a hinge"),
|
||||
_("Add a hinge that the object will rotate around.\nThe distance "
|
||||
("Add a hinge"),
|
||||
("Add a hinge that the object will rotate around.\nThe distance "
|
||||
"between the hinge and the object will remain identical."),
|
||||
_("Add a hinge to _PARAM0_ at _PARAM2_;_PARAM3_"),
|
||||
_("Joints"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Hinge X position"))
|
||||
.AddParameter("expression", _("Hinge Y position"))
|
||||
("Add a hinge to _PARAM0_ at _PARAM2_;_PARAM3_"),
|
||||
("Joints"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Hinge X position"))
|
||||
.AddParameter("expression", ("Hinge Y position"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("AddRevoluteJoint");
|
||||
|
||||
aut.AddAction("AddRevoluteJointBetweenObjects",
|
||||
_("Add a hinge between two objects"),
|
||||
_("Add a hinge that the object will rotate around."),
|
||||
_("Add a hinge between _PARAM0_ and _PARAM2_"),
|
||||
_("Joints"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("objectPtr", _("Object"))
|
||||
("Add a hinge between two objects"),
|
||||
("Add a hinge that the object will rotate around."),
|
||||
("Add a hinge between _PARAM0_ and _PARAM2_"),
|
||||
("Joints"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("objectPtr", ("Object"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter(
|
||||
"expression",
|
||||
_("X position of the hinge, from the first object mass center"),
|
||||
("X position of the hinge, from the first object mass center"),
|
||||
"",
|
||||
true)
|
||||
.SetDefaultValue("0")
|
||||
.AddParameter(
|
||||
"expression",
|
||||
_("Y position of the hinge, from the first object mass center"),
|
||||
("Y position of the hinge, from the first object mass center"),
|
||||
"",
|
||||
true)
|
||||
.SetDefaultValue("0")
|
||||
.SetFunctionName("AddRevoluteJointBetweenObjects");
|
||||
|
||||
aut.AddAction("ActAddGearJointBetweenObjects",
|
||||
_("Add a gear between two objects"),
|
||||
_("Add a virtual gear between two objects."),
|
||||
_("Add a gear between _PARAM0_ and _PARAM2_"),
|
||||
_("Joints"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("objectPtr", _("Object"))
|
||||
.AddParameter("expression", _("Ratio"), "", true)
|
||||
("Add a gear between two objects"),
|
||||
("Add a virtual gear between two objects."),
|
||||
("Add a gear between _PARAM0_ and _PARAM2_"),
|
||||
("Joints"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("objectPtr", ("Object"))
|
||||
.AddParameter("expression", ("Ratio"), "", true)
|
||||
.SetDefaultValue("1")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("AddGearJointBetweenObjects");
|
||||
|
||||
aut.AddAction("SetFreeRotation",
|
||||
_("Make object's rotation free"),
|
||||
_("Allows the object to rotate."),
|
||||
_("Allow _PARAM0_ to rotate"),
|
||||
_("Rotation"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Make object's rotation free"),
|
||||
("Allows the object to rotate."),
|
||||
("Allow _PARAM0_ to rotate"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetFreeRotation");
|
||||
|
||||
aut.AddCondition("IsFixedRotation",
|
||||
_("Fixed rotation"),
|
||||
_("Test if the object's rotation is fixed."),
|
||||
_("The rotation of _PARAM0_ is fixed."),
|
||||
_("Rotation"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Fixed rotation"),
|
||||
("Test if the object's rotation is fixed."),
|
||||
("The rotation of _PARAM0_ is fixed."),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("IsFixedRotation");
|
||||
|
||||
aut.AddAction("SetAsBullet",
|
||||
_("Treat object like a bullet."),
|
||||
_("Treat the object like a bullet, so it will have better "
|
||||
("Treat object like a bullet."),
|
||||
("Treat the object like a bullet, so it will have better "
|
||||
"collision handling."),
|
||||
_("Consider _PARAM0_ as a bullet"),
|
||||
_("Other"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Consider _PARAM0_ as a bullet"),
|
||||
("Other"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetAsBullet");
|
||||
|
||||
aut.AddAction("DontSetAsBullet",
|
||||
_("Do not treat object like a bullet"),
|
||||
_("Do not treat the object like a bullet, so it will use "
|
||||
("Do not treat object like a bullet"),
|
||||
("Do not treat the object like a bullet, so it will use "
|
||||
"standard collision handling."),
|
||||
_("Do not consider _PARAM0_ as a bullet."),
|
||||
_("Other"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Do not consider _PARAM0_ as a bullet."),
|
||||
("Other"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("DontSetAsBullet");
|
||||
|
||||
aut.AddCondition("IsBullet",
|
||||
_("Object is treated like a bullet"),
|
||||
_("Test if the object is treated like a bullet"),
|
||||
_("_PARAM0_ is considered as a bullet"),
|
||||
_("Other"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Object is treated like a bullet"),
|
||||
("Test if the object is treated like a bullet"),
|
||||
("_PARAM0_ is considered as a bullet"),
|
||||
("Other"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("IsBullet");
|
||||
|
||||
aut.AddAction("ApplyImpulse",
|
||||
_("Apply an impulse"),
|
||||
_("Apply an impulse to the object."),
|
||||
_("Apply to _PARAM0_ impulse _PARAM2_;_PARAM3_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("X component ( Newtons/Seconds )"))
|
||||
.AddParameter("expression", _("Y component ( Newtons/Seconds )"))
|
||||
("Apply an impulse"),
|
||||
("Apply an impulse to the object."),
|
||||
("Apply to _PARAM0_ impulse _PARAM2_;_PARAM3_"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("X component ( Newtons/Seconds )"))
|
||||
.AddParameter("expression", ("Y component ( Newtons/Seconds )"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyImpulse");
|
||||
|
||||
aut.AddAction("ApplyImpulseUsingPolarCoordinates",
|
||||
_("Apply an impulse (angle)"),
|
||||
_("Apply an impulse to an object, using an angle and a "
|
||||
("Apply an impulse (angle)"),
|
||||
("Apply an impulse to an object, using an angle and a "
|
||||
"length as coordinates."),
|
||||
_("Apply to _PARAM0_ impulse _PARAM3_ with angle: "
|
||||
("Apply to _PARAM0_ impulse _PARAM3_ with angle: "
|
||||
"_PARAM2_\302\260"), //\302\260 <=> DEGREE SIGN
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Angle"))
|
||||
.AddParameter("expression", _("Impulse value ( Newton/seconds )"))
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Angle"))
|
||||
.AddParameter("expression", ("Impulse value ( Newton/seconds )"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyImpulseUsingPolarCoordinates");
|
||||
|
||||
aut.AddAction(
|
||||
"ApplyImpulseTowardPosition",
|
||||
_("Apply an impulse toward a position"),
|
||||
_("Apply an impulse, directed toward a position, to the object."),
|
||||
_("Apply to _PARAM0_ impulse _PARAM4_ toward position "
|
||||
("Apply an impulse toward a position"),
|
||||
("Apply an impulse, directed toward a position, to the object."),
|
||||
("Apply to _PARAM0_ impulse _PARAM4_ toward position "
|
||||
"_PARAM2_;_PARAM3_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("X position"))
|
||||
.AddParameter("expression", _("Y position"))
|
||||
.AddParameter("expression", _("Impulse value ( Newton/seconds )"))
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("X position"))
|
||||
.AddParameter("expression", ("Y position"))
|
||||
.AddParameter("expression", ("Impulse value ( Newton/seconds )"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyImpulseTowardPosition");
|
||||
|
||||
aut.AddAction("ApplyForce",
|
||||
_("Add a force"),
|
||||
_("Add a force to the object"),
|
||||
_("Apply to _PARAM0_ force _PARAM2_;_PARAM3_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("X component ( Newtons )"))
|
||||
.AddParameter("expression", _("Y component ( Newtons )"))
|
||||
("Add a force"),
|
||||
("Add a force to the object"),
|
||||
("Apply to _PARAM0_ force _PARAM2_;_PARAM3_"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("X component ( Newtons )"))
|
||||
.AddParameter("expression", ("Y component ( Newtons )"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyForce");
|
||||
|
||||
aut.AddAction("ApplyForceUsingPolarCoordinates",
|
||||
_("Apply a force ( angle )"),
|
||||
_("Apply a force to an object, using an angle and a length "
|
||||
("Apply a force ( angle )"),
|
||||
("Apply a force to an object, using an angle and a length "
|
||||
"as coordinates."),
|
||||
_("Apply to _PARAM0_ force _PARAM3_ at angle _PARAM2_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Angle"))
|
||||
.AddParameter("expression", _("Length of the force ( Newtons )"))
|
||||
("Apply to _PARAM0_ force _PARAM3_ at angle _PARAM2_"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Angle"))
|
||||
.AddParameter("expression", ("Length of the force ( Newtons )"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyForceUsingPolarCoordinates");
|
||||
|
||||
aut.AddAction(
|
||||
"ApplyForceTowardPosition",
|
||||
_("Apply a force toward a position"),
|
||||
_("Apply a force, directed toward a position, to the object."),
|
||||
_("Add to _PARAM0_ force _PARAM4_ toward position "
|
||||
("Apply a force toward a position"),
|
||||
("Apply a force, directed toward a position, to the object."),
|
||||
("Add to _PARAM0_ force _PARAM4_ toward position "
|
||||
"_PARAM2_;_PARAM3_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("X position"))
|
||||
.AddParameter("expression", _("Y position"))
|
||||
.AddParameter("expression", _("Length of the force ( Newtons )"))
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("X position"))
|
||||
.AddParameter("expression", ("Y position"))
|
||||
.AddParameter("expression", ("Length of the force ( Newtons )"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyForceTowardPosition");
|
||||
|
||||
aut.AddAction("ApplyTorque",
|
||||
_("Add a torque (a rotation)"),
|
||||
_("Add a torque (a rotation) to the object."),
|
||||
_("Add to _PARAM0_ torque _PARAM2_"),
|
||||
_("Rotation"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Torque value"))
|
||||
("Add a torque (a rotation)"),
|
||||
("Add a torque (a rotation) to the object."),
|
||||
("Add to _PARAM0_ torque _PARAM2_"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Torque value"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("ApplyTorque");
|
||||
|
||||
aut.AddAction("SetLinearVelocity",
|
||||
_("Linear velocity"),
|
||||
_("Modify the velocity of an object."),
|
||||
_("Set linear velocity of _PARAM0_ to _PARAM2_;_PARAM3_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("X Coordinate"))
|
||||
.AddParameter("expression", _("Y Coordinate"))
|
||||
("Linear velocity"),
|
||||
("Modify the velocity of an object."),
|
||||
("Set linear velocity of _PARAM0_ to _PARAM2_;_PARAM3_"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("X Coordinate"))
|
||||
.AddParameter("expression", ("Y Coordinate"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetLinearVelocity");
|
||||
|
||||
aut.AddCondition(
|
||||
"LinearVelocityX",
|
||||
_("X component"),
|
||||
_("Compare the linear velocity on the X axis of the object."),
|
||||
_("the linear velocity on X axis"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("X component"),
|
||||
("Compare the linear velocity on the X axis of the object."),
|
||||
("the linear velocity on X axis"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
@@ -344,184 +344,184 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
aut.AddCondition(
|
||||
"LinearVelocityY",
|
||||
_("Y component"),
|
||||
_("Compare the linear velocity on the Y axis of the object."),
|
||||
_("the linear velocity on Y axis"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Y component"),
|
||||
("Compare the linear velocity on the Y axis of the object."),
|
||||
("the linear velocity on Y axis"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearVelocityY");
|
||||
|
||||
aut.AddCondition("LinearVelocity",
|
||||
_("Linear speed"),
|
||||
_("Compare the linear velocity of the object."),
|
||||
_("the linear velocity"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Linear speed"),
|
||||
("Compare the linear velocity of the object."),
|
||||
("the linear velocity"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearVelocity");
|
||||
|
||||
aut.AddAction("SetAngularVelocity",
|
||||
_("Angular speed"),
|
||||
_("Modify the angular velocity of the object."),
|
||||
_("Set angular speed of _PARAM0_ to _PARAM2_"),
|
||||
_("Rotation"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("New value"))
|
||||
("Angular speed"),
|
||||
("Modify the angular velocity of the object."),
|
||||
("Set angular speed of _PARAM0_ to _PARAM2_"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("New value"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetAngularVelocity");
|
||||
|
||||
aut.AddCondition("AngularVelocity",
|
||||
_("Angular speed"),
|
||||
_("Compare the angular speed of the object."),
|
||||
_("the angular speed"),
|
||||
_("Rotation"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Angular speed"),
|
||||
("Compare the angular speed of the object."),
|
||||
("the angular speed"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetAngularVelocity");
|
||||
|
||||
aut.AddCondition("LinearDamping",
|
||||
_("Linear damping"),
|
||||
_("Compare the linear damping of the object."),
|
||||
_("the linear damping"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Linear damping"),
|
||||
("Compare the linear damping of the object."),
|
||||
("the linear damping"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearDamping");
|
||||
|
||||
aut.AddCondition("CollisionWith",
|
||||
_("Collision"),
|
||||
_("Test if two objects are colliding.\nAttention! Only "
|
||||
("Collision"),
|
||||
("Test if two objects are colliding.\nAttention! Only "
|
||||
"objects specified in the first parameter will be taken "
|
||||
"into account by the next actions and conditions, if "
|
||||
"they are colliding with the other objects."),
|
||||
_("_PARAM0_ is in collision with a _PARAM2_"),
|
||||
("_PARAM0_ is in collision with a _PARAM2_"),
|
||||
"",
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("objectList", _("Object"))
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("objectList", ("Object"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("CollisionWith");
|
||||
|
||||
aut.AddAction("SetLinearDamping",
|
||||
_("Linear damping"),
|
||||
_("Modify the linear damping of the object."),
|
||||
_("Set linear damping of _PARAM0_ to _PARAM2_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Value"))
|
||||
("Linear damping"),
|
||||
("Modify the linear damping of the object."),
|
||||
("Set linear damping of _PARAM0_ to _PARAM2_"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Value"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetLinearDamping");
|
||||
|
||||
aut.AddCondition("AngularDamping",
|
||||
_("Angular damping"),
|
||||
_("Test the object's angular damping"),
|
||||
_("the angular damping"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Angular damping"),
|
||||
("Test the object's angular damping"),
|
||||
("the angular damping"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetAngularDamping");
|
||||
|
||||
aut.AddAction("SetAngularDamping",
|
||||
_("Angular damping"),
|
||||
_("Modify the angular damping of the object."),
|
||||
_("Set angular damping of _PARAM0_ to _PARAM2_"),
|
||||
_("Displacement"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Value"))
|
||||
("Angular damping"),
|
||||
("Modify the angular damping of the object."),
|
||||
("Set angular damping of _PARAM0_ to _PARAM2_"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Value"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetAngularDamping");
|
||||
|
||||
aut.AddAction("SetGravity",
|
||||
_("Gravity"),
|
||||
_("Modify the gravity"),
|
||||
_("Set gravity force to _PARAM2_;_PARAM3_"),
|
||||
_("Global options"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("X Coordinate"))
|
||||
.AddParameter("expression", _("Y Coordinate"))
|
||||
("Gravity"),
|
||||
("Modify the gravity"),
|
||||
("Set gravity force to _PARAM2_;_PARAM3_"),
|
||||
("Global options"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("X Coordinate"))
|
||||
.AddParameter("expression", ("Y Coordinate"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetGravity");
|
||||
|
||||
aut.AddAction("SetPolygonScaleX",
|
||||
_("Change the X scale of a collision polygon"),
|
||||
_("Change the X scale of the polygon. Use a value greater "
|
||||
("Change the X scale of a collision polygon"),
|
||||
("Change the X scale of the polygon. Use a value greater "
|
||||
"than 1 to enlarge the polygon, less than 1 to reduce it."),
|
||||
_("Change the X scale of the collision polygon of _PARAM0_ "
|
||||
("Change the X scale of the collision polygon of _PARAM0_ "
|
||||
"to _PARAM2_"),
|
||||
_("Collision polygon"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Scale"))
|
||||
("Collision polygon"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Scale"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetPolygonScaleX");
|
||||
|
||||
aut.AddAction("SetPolygonScaleY",
|
||||
_("Change the Y scale of a collision polygon"),
|
||||
_("Change the Y scale of the polygon. Use a value greater "
|
||||
("Change the Y scale of a collision polygon"),
|
||||
("Change the Y scale of the polygon. Use a value greater "
|
||||
"than 1 to enlarge the polygon, less than 1 to reduce it."),
|
||||
_("Change the Y scale of the collision polygon of _PARAM0_ Y "
|
||||
("Change the Y scale of the collision polygon of _PARAM0_ Y "
|
||||
"to _PARAM2_"),
|
||||
_("Collision polygon"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", _("Scale"))
|
||||
("Collision polygon"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddParameter("expression", ("Scale"))
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("SetPolygonScaleY");
|
||||
|
||||
aut.AddCondition(
|
||||
"GetPolygonScaleX",
|
||||
_("Collision polygon X scale"),
|
||||
_("Test the value of the X scale of the collision polygon."),
|
||||
_("the X scale of the collision polygon"),
|
||||
_("Collision polygon"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Collision polygon X scale"),
|
||||
("Test the value of the X scale of the collision polygon."),
|
||||
("the X scale of the collision polygon"),
|
||||
("Collision polygon"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
@@ -529,96 +529,96 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
|
||||
aut.AddCondition(
|
||||
"GetPolygonScaleY",
|
||||
_("Collision polygon Y scale"),
|
||||
_("Test the value of the Y scale of the collision polygon."),
|
||||
_("the Y scale of the collision polygon"),
|
||||
_("Collision polygon"),
|
||||
"res/physics24.png",
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Collision polygon Y scale"),
|
||||
("Test the value of the Y scale of the collision polygon."),
|
||||
("the Y scale of the collision polygon"),
|
||||
("Collision polygon"),
|
||||
"res/physics-deprecated24.png",
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.UseStandardRelationalOperatorParameters(
|
||||
"number", gd::ParameterOptions::MakeNewOptions())
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetPolygonScaleY");
|
||||
|
||||
aut.AddExpression("PolygonScaleX",
|
||||
_("Collision polygon X scale"),
|
||||
_("Collision polygon X scale"),
|
||||
_("Collision polygon"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Collision polygon X scale"),
|
||||
("Collision polygon X scale"),
|
||||
("Collision polygon"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetPolygonScaleX");
|
||||
|
||||
aut.AddExpression("PolygonScaleY",
|
||||
_("Collision polygon Y scale"),
|
||||
_("Collision polygon Y scale"),
|
||||
_("Collision polygon"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Collision polygon Y scale"),
|
||||
("Collision polygon Y scale"),
|
||||
("Collision polygon"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetPolygonScaleY");
|
||||
|
||||
aut.AddExpression("LinearVelocity",
|
||||
_("Linear speed"),
|
||||
_("Linear speed"),
|
||||
_("Displacement"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Linear speed"),
|
||||
("Linear speed"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearVelocity");
|
||||
|
||||
aut.AddExpression("LinearVelocityX",
|
||||
_("X component"),
|
||||
_("X component"),
|
||||
_("Displacement"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("X component"),
|
||||
("X component"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearVelocityX");
|
||||
|
||||
aut.AddExpression("LinearVelocityY",
|
||||
_("Y component"),
|
||||
_("Y component"),
|
||||
_("Displacement"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Y component"),
|
||||
("Y component"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearVelocityY");
|
||||
|
||||
aut.AddExpression("AngularVelocity",
|
||||
_("Angular speed"),
|
||||
_("Angular speed"),
|
||||
_("Rotation"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Angular speed"),
|
||||
("Angular speed"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetAngularVelocity");
|
||||
|
||||
aut.AddExpression("LinearDamping",
|
||||
_("Linear damping"),
|
||||
_("Linear damping"),
|
||||
_("Displacement"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Linear damping"),
|
||||
("Linear damping"),
|
||||
("Displacement"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetLinearDamping");
|
||||
|
||||
aut.AddExpression("AngularDamping",
|
||||
_("Angular damping"),
|
||||
_("Angular damping"),
|
||||
_("Rotation"),
|
||||
"res/physics16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PhysicsBehavior")
|
||||
("Angular damping"),
|
||||
("Angular damping"),
|
||||
("Rotation"),
|
||||
"res/physics-deprecated16.png")
|
||||
.AddParameter("object", ("Object"))
|
||||
.AddParameter("behavior", ("Behavior"), "PhysicsBehavior")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.SetFunctionName("GetAngularDamping");
|
||||
}
|
||||
|
@@ -50,7 +50,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Check if the object is moving (whether it is on the "
|
||||
"floor or in the air)."),
|
||||
_("_PARAM0_ is moving"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -64,7 +64,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Check if the object is moving (whether it is on the "
|
||||
"floor or in the air)."),
|
||||
_("_PARAM0_ is moving"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -75,7 +75,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Is on floor"),
|
||||
_("Check if the object is on a platform."),
|
||||
_("_PARAM0_ is on floor"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -87,7 +87,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Is on ladder"),
|
||||
_("Check if the object is on a ladder."),
|
||||
_("_PARAM0_ is on ladder"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -99,7 +99,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Is jumping"),
|
||||
_("Check if the object is jumping."),
|
||||
_("_PARAM0_ is jumping"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -114,7 +114,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"flagged as jumping and falling at the same time: at the end of a "
|
||||
"jump, the fall speed becomes higher than the jump speed."),
|
||||
_("_PARAM0_ is falling"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -125,7 +125,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Is grabbing platform ledge"),
|
||||
_("Check if the object is grabbing a platform ledge."),
|
||||
_("_PARAM0_ is grabbing a platform ledge"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -136,7 +136,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Gravity"),
|
||||
_("Compare the gravity applied on the object."),
|
||||
_("the gravity"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -153,7 +153,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Gravity"),
|
||||
_("Change the gravity applied on an object."),
|
||||
_("the gravity"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -171,7 +171,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum falling speed"),
|
||||
_("Compare the maximum falling speed of the object."),
|
||||
_("the maximum falling speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -187,7 +187,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum falling speed"),
|
||||
_("Change the maximum falling speed of an object."),
|
||||
_("the maximum falling speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -208,7 +208,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the ladder climbing speed (in pixels per "
|
||||
"second)."),
|
||||
_("the ladder climbing speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -224,7 +224,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Ladder climbing speed"),
|
||||
_("Change the ladder climbing speed."),
|
||||
_("the ladder climbing speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -241,7 +241,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Acceleration"),
|
||||
_("Compare the horizontal acceleration of the object."),
|
||||
_("the horizontal acceleration"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -258,7 +258,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Acceleration"),
|
||||
_("Change the horizontal acceleration of an object."),
|
||||
_("the horizontal acceleration"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -276,7 +276,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Deceleration"),
|
||||
_("Compare the horizontal deceleration of the object."),
|
||||
_("the horizontal deceleration"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -293,7 +293,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Deceleration"),
|
||||
_("Change the horizontal deceleration of an object."),
|
||||
_("the horizontal deceleration"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -311,7 +311,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum horizontal speed"),
|
||||
_("Compare the maximum horizontal speed of the object."),
|
||||
_("the maximum horizontal speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -326,7 +326,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum horizontal speed"),
|
||||
_("Change the maximum horizontal speed of an object."),
|
||||
_("the maximum horizontal speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -344,7 +344,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the jump speed of the object."
|
||||
"Its value is always positive."),
|
||||
_("the jump speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -361,7 +361,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the jump speed of an object. "
|
||||
"Its value is always positive."),
|
||||
_("the jump speed"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -380,7 +380,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"This is the time during which keeping the jump button held "
|
||||
"allow the initial jump speed to be maintained."),
|
||||
_("the jump sustain time"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -398,7 +398,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"This is the time during which keeping the jump button held "
|
||||
"allow the initial jump speed to be maintained."),
|
||||
_("the jump sustain time"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -417,7 +417,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"again this action everytime you want to allow the object to jump "
|
||||
"(apart if it's on the floor)."),
|
||||
_("Allow _PARAM0_ to jump again"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -432,7 +432,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"is made unable to jump while in mid air. This has no effect if "
|
||||
"the object is not in the air."),
|
||||
_("Forbid _PARAM0_ to air jump"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -445,7 +445,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"This action doesn't have any effect when the character is not "
|
||||
"jumping."),
|
||||
_("Abort the current jump of _PARAM0_"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -455,7 +455,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Can jump"),
|
||||
_("Check if the object can jump."),
|
||||
_("_PARAM0_ can jump"),
|
||||
"",
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -467,7 +467,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate left key press"),
|
||||
_("Simulate a press of the left key."),
|
||||
_("Simulate pressing Left for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -479,7 +479,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate right key press"),
|
||||
_("Simulate a press of the right key."),
|
||||
_("Simulate pressing Right for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -491,7 +491,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate up key press"),
|
||||
_("Simulate a press of the up key (used when on a ladder)."),
|
||||
_("Simulate pressing Up for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -504,7 +504,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate down key press"),
|
||||
_("Simulate a press of the down key (used when on a ladder)."),
|
||||
_("Simulate pressing Down for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -517,7 +517,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate ladder key press"),
|
||||
_("Simulate a press of the ladder key (used to grab a ladder)."),
|
||||
_("Simulate pressing Ladder key for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -530,7 +530,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate release ladder key press"),
|
||||
_("Simulate a press of the Release Ladder key (used to get off a ladder)."),
|
||||
_("Simulate pressing Release Ladder key for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -541,7 +541,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate jump key press"),
|
||||
_("Simulate a press of the jump key."),
|
||||
_("Simulate pressing Jump key for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -553,7 +553,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate a press of the release platform key (used when grabbing a "
|
||||
"platform ledge)."),
|
||||
_("Simulate pressing Release Platform key for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -568,7 +568,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate a press of a key.\nValid keys are Left, Right, "
|
||||
"Jump, Ladder, Release Ladder, Up, Down."),
|
||||
_("Simulate pressing _PARAM2_ key for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Platformer controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -583,7 +583,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Control pressed or simulated"),
|
||||
_("A control was applied from a default control or simulated by an action."),
|
||||
_("_PARAM0_ has the _PARAM2_ key pressed or simulated"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -598,7 +598,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("De/activate the use of default controls.\nIf deactivated, "
|
||||
"use the simulated actions to move the object."),
|
||||
_("Ignore default controls for _PARAM0_: _PARAM2_"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -612,7 +612,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Enable (or disable) the ability of the object to grab "
|
||||
"platforms when falling near to one."),
|
||||
_("Allow _PARAM0_ to grab platforms: _PARAM2_"),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -624,7 +624,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Can grab platforms"),
|
||||
_("Check if the object can grab the platforms."),
|
||||
_("_PARAM0_ can grab the platforms"),
|
||||
"Options",
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -637,7 +637,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the current falling speed of the object. Its "
|
||||
"value is always positive."),
|
||||
_("the current falling speed"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -656,7 +656,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"doesn't have any effect when the character "
|
||||
"is not falling or is in the first phase of a jump."),
|
||||
_("the current falling speed"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -672,7 +672,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the current jump speed of the object. Its "
|
||||
"value is always positive."),
|
||||
_("the current jump speed"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -691,7 +691,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"moves to the left with negative values and to the right with "
|
||||
"positive ones"),
|
||||
_("the current horizontal speed"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -709,7 +709,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"moves to the left "
|
||||
"with negative values and to the right with positive ones"),
|
||||
_("the current horizontal speed"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png",
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -725,7 +725,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Gravity"),
|
||||
_("Return the gravity applied on the object "
|
||||
"(in pixels per second per second)."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -735,7 +735,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum falling speed"),
|
||||
_("Return the maximum falling speed of the object "
|
||||
"(in pixels per second)."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -745,7 +745,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Ladder climbing speed"),
|
||||
_("Return the ladder climbing speed of the object "
|
||||
"(in pixels per second)."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -755,7 +755,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Acceleration"),
|
||||
_("Return the horizontal acceleration of the object "
|
||||
"(in pixels per second per second)."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -765,7 +765,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Deceleration"),
|
||||
_("Return the horizontal deceleration of the object "
|
||||
"(in pixels per second per second)."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -775,7 +775,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum horizontal speed"),
|
||||
_("Return the maximum horizontal speed of the object "
|
||||
"(in pixels per second)."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -786,7 +786,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Jump speed"),
|
||||
_("Return the jump speed of the object "
|
||||
"(in pixels per second). Its value is always positive."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -797,7 +797,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Return the jump sustain time of the object (in seconds)."
|
||||
"This is the time during which keeping the jump button held "
|
||||
"allow the initial jump speed to be maintained."),
|
||||
_("Options"),
|
||||
_("Platformer configuration"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior");
|
||||
@@ -806,7 +806,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Current fall speed"),
|
||||
_("Return the current fall speed of the object "
|
||||
"(in pixels per second). Its value is always positive."),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -817,17 +817,17 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Return the current horizontal speed of the object "
|
||||
"(in pixels per second). The object moves to the left "
|
||||
"with negative values and to the right with positive ones"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
.SetFunctionName("GetCurrentSpeed");
|
||||
|
||||
aut.AddExpression("CurrentJumpSpeed",
|
||||
_("Current jump speed"),
|
||||
_("Return the current jump speed of the object "
|
||||
"(in pixels per second). Its value is always positive."),
|
||||
_("Current jump speed"),
|
||||
_(""),
|
||||
_("Platformer state"),
|
||||
"CppPlatform/Extensions/platformerobjecticon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "PlatformerObjectBehavior")
|
||||
@@ -850,7 +850,7 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Change the platform type of the object: Platform, "
|
||||
"Jump-Through, or Ladder."),
|
||||
_("Set platform type of _PARAM0_ to _PARAM2_"),
|
||||
"",
|
||||
_("Platform"),
|
||||
"CppPlatform/Extensions/platformicon.png",
|
||||
"CppPlatform/Extensions/platformicon.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -863,10 +863,10 @@ void DeclarePlatformBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
}
|
||||
|
||||
extension.AddCondition("IsObjectOnGivenFloor",
|
||||
_("Is object on given floor"),
|
||||
_("Test if an object is on a given floor."),
|
||||
_("_PARAM0_ is on floor _PARAM2_"),
|
||||
"",
|
||||
_("Character is on given platform"),
|
||||
_("Check if a platformer character is on a given platform."),
|
||||
_("_PARAM0_ is on platform _PARAM2_"),
|
||||
_("Collision"),
|
||||
"CppPlatform/Extensions/platformicon.png",
|
||||
"CppPlatform/Extensions/platformicon.png")
|
||||
.AddParameter("objectList", _("Object"), "", false)
|
||||
|
@@ -48,6 +48,7 @@ describe('gdjs.ShapePainterRuntimeObject (using a PixiJS RuntimeGame with assets
|
||||
objects: [],
|
||||
instances: [],
|
||||
variables: [],
|
||||
usedResources: [],
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -67,9 +67,9 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'SetListenerPosition',
|
||||
_('Set position of the listener'),
|
||||
_('Sets the spatial position of the listener/player.'),
|
||||
_('Set the listener position to _PARAM0_, _PARAM1_, _PARAM2_'),
|
||||
_('Listener position'),
|
||||
_('Change the spatial position of the listener/player.'),
|
||||
_('Change the listener position to _PARAM0_, _PARAM1_, _PARAM2_'),
|
||||
'',
|
||||
'res/actions/son24.png',
|
||||
'res/actions/son.png'
|
||||
|
@@ -178,7 +178,7 @@ module.exports = {
|
||||
extension
|
||||
.addAction(
|
||||
'SetRichPresence',
|
||||
_('Change the Steam rich presence'),
|
||||
_('Steam rich presence'),
|
||||
_(
|
||||
"Changes an attribute of Steam's rich presence. Allows other player to see exactly what the player's currently doing in the game."
|
||||
),
|
||||
|
@@ -55,6 +55,7 @@ describe('gdjs.TextInputRuntimeObject (using a PixiJS RuntimeGame with DOM eleme
|
||||
objects: [],
|
||||
instances: [],
|
||||
variables: [],
|
||||
usedResources: [],
|
||||
});
|
||||
};
|
||||
|
||||
|
@@ -26,11 +26,12 @@ namespace gdjs {
|
||||
);
|
||||
};
|
||||
|
||||
class TextInputRuntimeObjectPixiRenderer {
|
||||
class TextInputRuntimeObjectPixiRenderer implements RendererObjectInterface {
|
||||
private _object: gdjs.TextInputRuntimeObject;
|
||||
private _input: HTMLInputElement | HTMLTextAreaElement | null = null;
|
||||
private _instanceContainer: gdjs.RuntimeInstanceContainer;
|
||||
private _runtimeGame: gdjs.RuntimeGame;
|
||||
private _isVisible = false;
|
||||
|
||||
constructor(
|
||||
runtimeObject: gdjs.TextInputRuntimeObject,
|
||||
@@ -113,14 +114,25 @@ namespace gdjs {
|
||||
this._destroyElement();
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
set visible(isVisible: boolean) {
|
||||
this._isVisible = isVisible;
|
||||
if (!this._input) return;
|
||||
this._input.style.display = isVisible ? 'initial' : 'none';
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
get visible(): boolean {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
updatePreRender() {
|
||||
if (!this._input) return;
|
||||
|
||||
// Hide the input entirely if the object is hidden.
|
||||
// Because this object is rendered as a DOM element (and not part of the PixiJS
|
||||
// scene graph), we have to do this manually.
|
||||
if (this._object.isHidden()) {
|
||||
this._input.style.display = 'none';
|
||||
if (!this._isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -102,7 +102,8 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
return null;
|
||||
// The renderer is not a Pixi Object but it implements visible.
|
||||
return this._renderer;
|
||||
}
|
||||
|
||||
updateFromObjectData(
|
||||
|
@@ -58,6 +58,7 @@ describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
setFramesPerSecond(runtimeScene, framePerSecond);
|
||||
return runtimeScene;
|
||||
|
@@ -38,12 +38,11 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
std::make_shared<TopDownMovementBehavior>(),
|
||||
std::make_shared<gd::BehaviorsSharedData>());
|
||||
|
||||
#if defined(GD_IDE_ONLY)
|
||||
aut.AddAction("SimulateLeftKey",
|
||||
_("Simulate left key press"),
|
||||
_("Simulate a press of left key."),
|
||||
_("Simulate pressing Left for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -55,7 +54,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate right key press"),
|
||||
_("Simulate a press of right key."),
|
||||
_("Simulate pressing Right for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -67,7 +66,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate up key press"),
|
||||
_("Simulate a press of up key."),
|
||||
_("Simulate pressing Up for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -79,7 +78,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate down key press"),
|
||||
_("Simulate a press of down key."),
|
||||
_("Simulate pressing Down for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -92,7 +91,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate control"),
|
||||
_("Simulate a press of a key.\nValid keys are Left, Right, Up, Down."),
|
||||
_("Simulate pressing _PARAM2_ key for _PARAM0_"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -108,7 +107,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("De/activate the use of default controls.\nIf deactivated, "
|
||||
"use the simulated actions to move the object."),
|
||||
_("Ignore default controls for _PARAM0_: _PARAM2_"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -121,7 +120,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Simulate stick control"),
|
||||
_("Simulate a stick control."),
|
||||
_("Simulate a stick control for _PARAM0_ with a _PARAM2_ angle and a _PARAM3_ force"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -135,7 +134,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Control pressed or simulated"),
|
||||
_("A control was applied from a default control or simulated by an action."),
|
||||
_("_PARAM0_ has the _PARAM2_ key pressed or simulated"),
|
||||
_("Controls"),
|
||||
_("Top-down state"),
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -148,7 +147,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("StickAngle",
|
||||
_("Stick angle"),
|
||||
_("Return the angle of the simulated stick input (in degrees)"),
|
||||
_("Controls"),
|
||||
_("Top-down controls"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior");
|
||||
@@ -157,7 +156,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Is moving"),
|
||||
_("Check if the object is moving."),
|
||||
_("_PARAM0_ is moving"),
|
||||
"",
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -168,7 +167,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Acceleration"),
|
||||
_("Change the acceleration of the object"),
|
||||
_("the acceleration"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -185,7 +184,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Acceleration"),
|
||||
_("Compare the acceleration of the object"),
|
||||
_("the acceleration"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -201,7 +200,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Deceleration"),
|
||||
_("Change the deceleration of the object"),
|
||||
_("the deceleration"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -218,7 +217,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Deceleration"),
|
||||
_("Compare the deceleration of the object"),
|
||||
_("the deceleration"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -234,7 +233,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum speed"),
|
||||
_("Change the maximum speed of the object"),
|
||||
_("the max. speed"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -250,7 +249,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Maximum speed"),
|
||||
_("Compare the maximum speed of the object"),
|
||||
_("the max. speed"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -266,7 +265,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Speed"),
|
||||
_("Compare the speed of the object"),
|
||||
_("the speed"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -281,7 +280,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Angular maximum speed"),
|
||||
_("Change the maximum angular speed of the object"),
|
||||
_("the max. angular speed"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -298,7 +297,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Angular maximum speed"),
|
||||
_("Compare the maximum angular speed of the object"),
|
||||
_("the max. angular speed"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -314,7 +313,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Rotation offset"),
|
||||
_("Change the rotation offset applied when moving the object"),
|
||||
_("the rotation offset"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -332,7 +331,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Rotation offset"),
|
||||
_("Compare the rotation offset applied when moving the object"),
|
||||
_("the rotation offset"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -344,12 +343,13 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
.MarkAsAdvanced()
|
||||
.SetFunctionName("GetAngleOffset");
|
||||
|
||||
// Deprecated
|
||||
aut.AddCondition(
|
||||
"Angle",
|
||||
_("Angle of movement"),
|
||||
_("Compare the angle of the top-down movement of the object."),
|
||||
_("the angle of movement"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -367,7 +367,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Angle of movement"),
|
||||
_("Compare the angle of the top-down movement of the object."),
|
||||
_("Angle of movement of _PARAM0_ is _PARAM2_ ± _PARAM3_°"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -380,7 +380,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the velocity of the top-down movement of the "
|
||||
"object on the X axis."),
|
||||
_("the speed of movement on X axis"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -396,7 +396,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Speed on the X axis"),
|
||||
_("Change the speed on the X axis of the movement"),
|
||||
_("the speed on the X axis of the movement"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -412,7 +412,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Compare the velocity of the top-down movement of the "
|
||||
"object on the Y axis."),
|
||||
_("the speed of movement on Y axis"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -428,7 +428,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Speed on the Y axis"),
|
||||
_("Change the speed on the Y axis of the movement"),
|
||||
_("the speed on the Y axis of the movement"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -443,7 +443,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Diagonal movement"),
|
||||
_("Allow or restrict diagonal movement"),
|
||||
_("Allow diagonal moves for _PARAM0_: _PARAM2_"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -455,7 +455,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Diagonal movement"),
|
||||
_("Check if the object is allowed to move diagonally"),
|
||||
_("Allow diagonal moves for _PARAM0_"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -467,7 +467,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Rotate the object"),
|
||||
_("Enable or disable rotation of the object"),
|
||||
_("Enable rotation of _PARAM0_: _PARAM2_"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -481,7 +481,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Object rotated"),
|
||||
_("Check if the object is rotated while traveling on its path."),
|
||||
_("_PARAM0_ is rotated when moving"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png",
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
@@ -492,7 +492,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("Acceleration",
|
||||
_("Acceleration"),
|
||||
_("Acceleration of the object"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -501,7 +501,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("Deceleration",
|
||||
_("Deceleration"),
|
||||
_("Deceleration of the object"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -510,7 +510,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("MaxSpeed",
|
||||
_("Maximum speed"),
|
||||
_("Maximum speed of the object"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -519,7 +519,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("Speed",
|
||||
_("Speed"),
|
||||
_("Speed of the object"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -528,7 +528,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("AngularMaxSpeed",
|
||||
_("Angular maximum speed"),
|
||||
_("Angular maximum speed of the object"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -537,7 +537,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("AngleOffset",
|
||||
_("Rotation offset"),
|
||||
_("Rotation offset applied to the object"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -546,7 +546,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("Angle",
|
||||
_("Angle of the movement"),
|
||||
_("Angle, in degrees, of the movement"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -555,7 +555,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("XVelocity",
|
||||
_("Speed on the X axis"),
|
||||
_("Speed on the X axis of the movement"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -564,7 +564,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
aut.AddExpression("YVelocity",
|
||||
_("Speed on the Y axis"),
|
||||
_("Speed on the Y axis of the movement"),
|
||||
_("Movement"),
|
||||
_("Top-down state"),
|
||||
"CppPlatform/Extensions/topdownmovementicon16.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -576,7 +576,7 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
_("Movement angle offset"),
|
||||
_("the movement angle offset"),
|
||||
_("the movement angle offset"),
|
||||
_("Movement"),
|
||||
_("Top-down configuration"),
|
||||
"CppPlatform/Extensions/topdownmovementicon24.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "TopDownMovementBehavior")
|
||||
@@ -584,5 +584,4 @@ void DeclareTopDownMovementBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"number",
|
||||
gd::ParameterOptions::MakeNewOptions().SetDescription(
|
||||
_("Angle (in degrees)")));
|
||||
#endif
|
||||
}
|
||||
|
@@ -32,6 +32,7 @@ describe('gdjs.TopDownMovementRuntimeBehavior', function () {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return timeDelta;
|
||||
|
@@ -203,7 +203,7 @@ module.exports = {
|
||||
object
|
||||
.addAction(
|
||||
'SetTime',
|
||||
_('Set time'),
|
||||
_('Current time'),
|
||||
_('Set the time of the video'),
|
||||
_('the time'),
|
||||
'',
|
||||
@@ -224,7 +224,7 @@ module.exports = {
|
||||
object
|
||||
.addAction(
|
||||
'SetVolume',
|
||||
_('Set volume'),
|
||||
_('Volume'),
|
||||
_('Set the volume of the video object.'),
|
||||
_('the volume'),
|
||||
'',
|
||||
|
@@ -39,6 +39,15 @@ SceneExtension::SceneExtension() {
|
||||
GetAllConditions()["DoesSceneExist"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.doesSceneExist");
|
||||
|
||||
GetAllActions()["PrioritizeLoadingOfScene"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.prioritizeLoadingOfScene");
|
||||
GetAllConditions()["AreSceneAssetsLoaded"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.areSceneAssetsLoaded");
|
||||
GetAllConditions()["SceneLoadingProgress"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.getSceneLoadingProgress");
|
||||
GetAllExpressions()["SceneLoadingProgress"].SetFunctionName(
|
||||
"gdjs.evtTools.runtimeScene.getSceneLoadingProgress");
|
||||
|
||||
StripUnimplementedInstructionsAndExpressions();
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/Project/SceneResourcesFinder.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
@@ -129,14 +130,26 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto projectUsedResources =
|
||||
gd::SceneResourcesFinder::FindProjectResources(exportedProject);
|
||||
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < exportedProject.GetLayoutsCount(); layoutIndex++) {
|
||||
auto &layout = exportedProject.GetLayout(layoutIndex);
|
||||
scenesUsedResources[layout.GetName()] =
|
||||
gd::SceneResourcesFinder::FindSceneResources(exportedProject,
|
||||
layout);
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use
|
||||
// stripped things like objects groups...)...
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
|
||||
//...and export it
|
||||
gd::SerializerElement noRuntimeGameOptions;
|
||||
helper.ExportProjectData(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", noRuntimeGameOptions);
|
||||
helper.ExportProjectData(fs, exportedProject, codeOutputDir + "/data.js",
|
||||
noRuntimeGameOptions, projectUsedResources,
|
||||
scenesUsedResources);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
// Export a WebManifest with project metadata
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
#include "GDCore/IDE/ExportedDependencyResolver.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/Project/SceneResourcesFinder.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
#include "GDCore/IDE/SceneNameMangler.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
@@ -187,6 +188,17 @@ bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
previousTime = LogTimeSpent("Events code export", previousTime);
|
||||
}
|
||||
|
||||
auto projectUsedResources =
|
||||
gd::SceneResourcesFinder::FindProjectResources(exportedProject);
|
||||
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < exportedProject.GetLayoutsCount(); layoutIndex++) {
|
||||
auto &layout = exportedProject.GetLayout(layoutIndex);
|
||||
scenesUsedResources[layout.GetName()] =
|
||||
gd::SceneResourcesFinder::FindSceneResources(exportedProject,
|
||||
layout);
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
@@ -234,8 +246,9 @@ bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
}
|
||||
|
||||
// Export the project
|
||||
ExportProjectData(
|
||||
fs, exportedProject, codeOutputDir + "/data.js", runtimeGameOptions);
|
||||
ExportProjectData(fs, exportedProject, codeOutputDir + "/data.js",
|
||||
runtimeGameOptions, projectUsedResources,
|
||||
scenesUsedResources);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
previousTime = LogTimeSpent("Project data export", previousTime);
|
||||
@@ -259,14 +272,17 @@ bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
|
||||
gd::String ExporterHelper::ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions) {
|
||||
const gd::SerializerElement &runtimeGameOptions,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources) {
|
||||
fs.MkDir(fs.DirNameFrom(filename));
|
||||
|
||||
// Save the project to JSON
|
||||
gd::SerializerElement rootElement;
|
||||
project.SerializeTo(rootElement);
|
||||
SerializeUsedResources(rootElement, projectUsedResources, scenesUsedResources);
|
||||
gd::String output =
|
||||
"gdjs.projectData = " + gd::Serializer::ToJSON(rootElement) + ";\n" +
|
||||
"gdjs.runtimeGameOptions = " +
|
||||
@@ -277,6 +293,35 @@ gd::String ExporterHelper::ExportProjectData(
|
||||
return "";
|
||||
}
|
||||
|
||||
void ExporterHelper::SerializeUsedResources(
|
||||
gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources) {
|
||||
|
||||
auto serializeUsedResources =
|
||||
[](gd::SerializerElement &element,
|
||||
std::set<gd::String> &usedResources) -> void {
|
||||
auto &resourcesElement = element.AddChild("usedResources");
|
||||
resourcesElement.ConsiderAsArrayOf("resourceReference");
|
||||
for (auto &resourceName : usedResources) {
|
||||
auto &resourceElement = resourcesElement.AddChild("resourceReference");
|
||||
resourceElement.SetAttribute("name", resourceName);
|
||||
}
|
||||
};
|
||||
|
||||
serializeUsedResources(rootElement, projectUsedResources);
|
||||
|
||||
auto &layoutsElement = rootElement.GetChild("layouts");
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < layoutsElement.GetChildrenCount(); layoutIndex++) {
|
||||
auto &layoutElement = layoutsElement.GetChild(layoutIndex);
|
||||
const auto layoutName = layoutElement.GetStringAttribute("name");
|
||||
|
||||
auto &layoutUsedResources = scenesUsedResources[layoutName];
|
||||
serializeUsedResources(layoutElement, layoutUsedResources);
|
||||
}
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportPixiIndexFile(
|
||||
const gd::Project &project,
|
||||
gd::String source,
|
||||
@@ -621,8 +666,12 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "inputmanager.js");
|
||||
InsertUnique(includesFiles, "jsonmanager.js");
|
||||
InsertUnique(includesFiles, "Model3DManager.js");
|
||||
InsertUnique(includesFiles, "ResourceLoader.js");
|
||||
InsertUnique(includesFiles, "ResourceCache.js");
|
||||
InsertUnique(includesFiles, "timemanager.js");
|
||||
InsertUnique(includesFiles, "polygon.js");
|
||||
InsertUnique(includesFiles, "ObjectSleepState.js");
|
||||
InsertUnique(includesFiles, "ObjectManager.js");
|
||||
InsertUnique(includesFiles, "runtimeobject.js");
|
||||
InsertUnique(includesFiles, "profiler.js");
|
||||
InsertUnique(includesFiles, "RuntimeInstanceContainer.js");
|
||||
|
@@ -3,12 +3,13 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#ifndef EXPORTER_HELPER_H
|
||||
#define EXPORTER_HELPER_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "GDCore/String.h"
|
||||
namespace gd {
|
||||
@@ -264,11 +265,12 @@ class ExporterHelper {
|
||||
* in gdjs.runtimeGameOptions \return Empty string if everything is ok,
|
||||
* description of the error otherwise.
|
||||
*/
|
||||
static gd::String ExportProjectData(
|
||||
gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions);
|
||||
static gd::String
|
||||
ExportProjectData(gd::AbstractFileSystem &fs, gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &layersUsedResources);
|
||||
|
||||
/**
|
||||
* \brief Copy all the resources of the project to to the export directory,
|
||||
@@ -474,7 +476,12 @@ class ExporterHelper {
|
||||
gdjsRoot; ///< The root directory of GDJS, used to copy runtime files.
|
||||
gd::String codeOutputDir; ///< The directory where JS code is outputted. Will
|
||||
///< be then copied to the final output directory.
|
||||
|
||||
private:
|
||||
static void SerializeUsedResources(
|
||||
gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &layersUsedResources);
|
||||
};
|
||||
|
||||
} // namespace gdjs
|
||||
#endif // EXPORTER_HELPER_H
|
||||
|
@@ -5,20 +5,21 @@
|
||||
*/
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Model3DManager');
|
||||
type OnProgressCallback = (loadedCount: integer, totalCount: integer) => void;
|
||||
|
||||
const resourceKinds: Array<ResourceKind> = ['model3D'];
|
||||
|
||||
/**
|
||||
* Load GLB files (using `Three.js`), using the "model3D" resources
|
||||
* registered in the game resources.
|
||||
*/
|
||||
export class Model3DManager {
|
||||
export class Model3DManager implements gdjs.ResourceManager {
|
||||
/**
|
||||
* Map associating a resource name to the loaded Three.js model.
|
||||
*/
|
||||
private _loadedThreeModels = new Map<String, THREE_ADDONS.GLTF>();
|
||||
private _loadedThreeModels = new gdjs.ResourceCache<THREE_ADDONS.GLTF>();
|
||||
private _downloadedArrayBuffers = new gdjs.ResourceCache<ArrayBuffer>();
|
||||
|
||||
_resourcesLoader: RuntimeGameResourcesLoader;
|
||||
_resources: Map<string, ResourceData>;
|
||||
_resourceLoader: gdjs.ResourceLoader;
|
||||
|
||||
_loader: THREE_ADDONS.GLTFLoader | null = null;
|
||||
_dracoLoader: THREE_ADDONS.DRACOLoader | null = null;
|
||||
@@ -28,15 +29,10 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
* @param resourcesLoader The resources loader of the game.
|
||||
* @param resourceLoader The resources loader of the game.
|
||||
*/
|
||||
constructor(
|
||||
resourceDataArray: ResourceData[],
|
||||
resourcesLoader: RuntimeGameResourcesLoader
|
||||
) {
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this.setResources(resourceDataArray);
|
||||
this._resourcesLoader = resourcesLoader;
|
||||
constructor(resourceLoader: gdjs.ResourceLoader) {
|
||||
this._resourceLoader = resourceLoader;
|
||||
|
||||
if (typeof THREE !== 'undefined') {
|
||||
this._loader = new THREE_ADDONS.GLTFLoader();
|
||||
@@ -69,17 +65,34 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
*/
|
||||
setResources(resourceDataArray: ResourceData[]): void {
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
if (resourceData.kind === 'model3D') {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
getResourceKinds(): ResourceKind[] {
|
||||
return resourceKinds;
|
||||
}
|
||||
|
||||
async processResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const loader = this._loader;
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
const data = this._downloadedArrayBuffers.get(resource);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
this._downloadedArrayBuffers.delete(resource);
|
||||
try {
|
||||
const gltf: THREE_ADDONS.GLTF = await loader.parseAsync(data, '');
|
||||
this._loadedThreeModels.set(resource, gltf);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"Can't fetch the 3D model file " + resource.file + ', error: ' + error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,42 +101,36 @@ namespace gdjs {
|
||||
*
|
||||
* Note that even if a file is already loaded, it will be reloaded (useful for hot-reloading,
|
||||
* as files can have been modified without the editor knowing).
|
||||
*
|
||||
* @param onProgress The function called after each file is loaded.
|
||||
* @param onComplete The function called when all file are loaded.
|
||||
*/
|
||||
async loadModels(onProgress: OnProgressCallback): Promise<integer> {
|
||||
const loader = this._loader;
|
||||
if (this._resources.size === 0 || !loader) {
|
||||
return 0;
|
||||
async loadResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
const loader = this._loader;
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
const url = this._resourceLoader.getFullUrl(resource.file);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
credentials: this._resourceLoader.checkIfCredentialsRequired(url)
|
||||
? 'include'
|
||||
: 'omit',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
const data = await response.arrayBuffer();
|
||||
this._downloadedArrayBuffers.set(resource, data);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"Can't fetch the 3D model file " + resource.file + ', error: ' + error
|
||||
);
|
||||
}
|
||||
|
||||
let loadedCount = 0;
|
||||
await Promise.all(
|
||||
[...this._resources.values()].map(async (resource) => {
|
||||
const url = this._resourcesLoader.getFullUrl(resource.file);
|
||||
loader.withCredentials = this._resourcesLoader.checkIfCredentialsRequired(
|
||||
url
|
||||
);
|
||||
try {
|
||||
const gltf: THREE_ADDONS.GLTF = await loader.loadAsync(
|
||||
url,
|
||||
(event) => {}
|
||||
);
|
||||
this._loadedThreeModels.set(resource.name, gltf);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"Can't fetch the 3D model file " +
|
||||
resource.file +
|
||||
', error: ' +
|
||||
error
|
||||
);
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, this._resources.size);
|
||||
})
|
||||
);
|
||||
return loadedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +142,9 @@ namespace gdjs {
|
||||
* @returns a 3D model if it exists.
|
||||
*/
|
||||
getModel(resourceName: string): THREE_ADDONS.GLTF {
|
||||
return this._loadedThreeModels.get(resourceName) || this._invalidModel;
|
||||
return (
|
||||
this._loadedThreeModels.getFromName(resourceName) || this._invalidModel
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
122
GDJS/Runtime/ObjectManager.ts
Normal file
122
GDJS/Runtime/ObjectManager.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
namespace gdjs {
|
||||
/**
|
||||
* Allow to do spacial searches on objects as fast as possible.
|
||||
*
|
||||
* Objects are put in an R-Tree only if they didn't move recently to avoid to
|
||||
* update the R-Tree too often.
|
||||
*/
|
||||
export class ObjectManager {
|
||||
private _allInstances: Array<RuntimeObject> = [];
|
||||
private _awakeInstances: Array<RuntimeObject> = [];
|
||||
private _rbush: RBush<RuntimeObject>;
|
||||
|
||||
constructor() {
|
||||
this._rbush = new RBush<RuntimeObject>();
|
||||
}
|
||||
|
||||
_destroy(): void {
|
||||
this._allInstances = [];
|
||||
this._awakeInstances = [];
|
||||
this._rbush.clear();
|
||||
}
|
||||
|
||||
search(
|
||||
searchArea: SearchArea,
|
||||
results: Array<RuntimeObject>
|
||||
): Array<RuntimeObject> {
|
||||
let instances = this._allInstances;
|
||||
if (instances.length >= 8) {
|
||||
this._rbush.search(searchArea, results);
|
||||
instances = this._awakeInstances;
|
||||
}
|
||||
for (const instance of instances) {
|
||||
// TODO Allow to use getAABB to optimize collision conditions
|
||||
const aabb = instance.getVisibilityAABB();
|
||||
if (
|
||||
!aabb ||
|
||||
(aabb.min[0] <= searchArea.maxX &&
|
||||
aabb.max[0] >= searchArea.minX &&
|
||||
aabb.min[1] <= searchArea.maxY &&
|
||||
aabb.max[1] >= searchArea.minY)
|
||||
) {
|
||||
results.push(instance);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private _onWakingUp(object: RuntimeObject): void {
|
||||
this._rbush.remove(object._rtreeAABB);
|
||||
this._awakeInstances.push(object);
|
||||
}
|
||||
|
||||
private _onFallenAsleep(object: RuntimeObject): void {
|
||||
// TODO Allow to use getAABB to optimize collision conditions
|
||||
const objectAABB = object.getVisibilityAABB();
|
||||
if (!objectAABB) {
|
||||
return;
|
||||
}
|
||||
this._rbush.remove(object._rtreeAABB);
|
||||
object._rtreeAABB.minX = objectAABB.min[0];
|
||||
object._rtreeAABB.minY = objectAABB.min[1];
|
||||
object._rtreeAABB.maxX = objectAABB.max[0];
|
||||
object._rtreeAABB.maxY = objectAABB.max[1];
|
||||
this._rbush.insert(object._rtreeAABB);
|
||||
}
|
||||
|
||||
updateAwakeObjects(): void {
|
||||
gdjs.ObjectSleepState.updateAwakeObjects(
|
||||
this._awakeInstances,
|
||||
(object) => object.getSpatialSearchSleepState(),
|
||||
(object) => this._onFallenAsleep(object),
|
||||
(object) => this._onWakingUp(object)
|
||||
);
|
||||
}
|
||||
|
||||
getAllInstances(): Array<RuntimeObject> {
|
||||
return this._allInstances;
|
||||
}
|
||||
|
||||
getAwakeInstances(): Array<RuntimeObject> {
|
||||
return this._awakeInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an object to the instances living in the container.
|
||||
* @param obj The object to be added.
|
||||
*/
|
||||
addObject(object: gdjs.RuntimeObject): void {
|
||||
this._allInstances.push(object);
|
||||
this._awakeInstances.push(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be called whenever an object must be removed from the container.
|
||||
* @param object The object to be removed.
|
||||
*/
|
||||
deleteObject(object: gdjs.RuntimeObject): boolean {
|
||||
const objId = object.id;
|
||||
let isObjectDeleted = false;
|
||||
for (let i = 0, len = this._allInstances.length; i < len; ++i) {
|
||||
if (this._allInstances[i].id == objId) {
|
||||
this._allInstances.splice(i, 1);
|
||||
isObjectDeleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO Maybe the state could be used but it would be more prone to errors.
|
||||
let isAwake = false;
|
||||
for (let i = 0, len = this._awakeInstances.length; i < len; ++i) {
|
||||
if (this._awakeInstances[i].id == objId) {
|
||||
this._awakeInstances.splice(i, 1);
|
||||
isAwake = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isAwake) {
|
||||
this._rbush.remove(object._rtreeAABB);
|
||||
}
|
||||
return isObjectDeleted;
|
||||
}
|
||||
}
|
||||
}
|
112
GDJS/Runtime/ObjectSleepState.ts
Normal file
112
GDJS/Runtime/ObjectSleepState.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2023-2023 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
namespace gdjs {
|
||||
export class ObjectSleepState {
|
||||
private static readonly framesBeforeSleep = 60;
|
||||
private _object: RuntimeObject;
|
||||
private _isNeedingToBeAwake: () => boolean;
|
||||
private _state: ObjectSleepState.State;
|
||||
private _lastActivityFrameIndex: integer;
|
||||
private _onWakingUpCallbacks: Array<(object: RuntimeObject) => void> = [];
|
||||
|
||||
constructor(
|
||||
object: RuntimeObject,
|
||||
isNeedingToBeAwake: () => boolean,
|
||||
initialSleepState: ObjectSleepState.State
|
||||
) {
|
||||
this._object = object;
|
||||
this._isNeedingToBeAwake = isNeedingToBeAwake;
|
||||
this._state = initialSleepState;
|
||||
this._lastActivityFrameIndex = this._object
|
||||
.getRuntimeScene()
|
||||
.getFrameIndex();
|
||||
}
|
||||
|
||||
canSleep(): boolean {
|
||||
return (
|
||||
this._state === gdjs.ObjectSleepState.State.CanSleepThisFrame ||
|
||||
this._object.getRuntimeScene().getFrameIndex() -
|
||||
this._lastActivityFrameIndex >=
|
||||
ObjectSleepState.framesBeforeSleep
|
||||
);
|
||||
}
|
||||
|
||||
isAwake(): boolean {
|
||||
return this._state !== gdjs.ObjectSleepState.State.ASleep;
|
||||
}
|
||||
|
||||
_forceToSleep(): void {
|
||||
if (!this.isAwake()) {
|
||||
return;
|
||||
}
|
||||
this._lastActivityFrameIndex = Number.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
wakeUp() {
|
||||
const object = this._object;
|
||||
this._lastActivityFrameIndex = object.getRuntimeScene().getFrameIndex();
|
||||
if (this.isAwake()) {
|
||||
return;
|
||||
}
|
||||
this._state = gdjs.ObjectSleepState.State.AWake;
|
||||
for (const onWakingUp of this._onWakingUpCallbacks) {
|
||||
onWakingUp(object);
|
||||
}
|
||||
}
|
||||
|
||||
registerOnWakingUp(onWakingUp: (object: RuntimeObject) => void) {
|
||||
this._onWakingUpCallbacks.push(onWakingUp);
|
||||
}
|
||||
|
||||
tryToSleep(): void {
|
||||
if (
|
||||
this._lastActivityFrameIndex !== Number.NEGATIVE_INFINITY &&
|
||||
this._isNeedingToBeAwake()
|
||||
) {
|
||||
this._lastActivityFrameIndex = this._object
|
||||
.getRuntimeScene()
|
||||
.getFrameIndex();
|
||||
}
|
||||
}
|
||||
|
||||
static updateAwakeObjects(
|
||||
awakeObjects: Array<RuntimeObject>,
|
||||
getSleepState: (object: RuntimeObject) => ObjectSleepState,
|
||||
onFallenAsleep: (object: RuntimeObject) => void,
|
||||
onWakingUp: (object: RuntimeObject) => void
|
||||
) {
|
||||
let writeIndex = 0;
|
||||
for (let readIndex = 0; readIndex < awakeObjects.length; readIndex++) {
|
||||
const object = awakeObjects[readIndex];
|
||||
const sleepState = getSleepState(object);
|
||||
sleepState.tryToSleep();
|
||||
if (sleepState.canSleep() || !sleepState.isAwake()) {
|
||||
if (sleepState.isAwake()) {
|
||||
// Avoid onWakingUp to be called if some managers didn't have time
|
||||
// to update their awake object list.
|
||||
sleepState._onWakingUpCallbacks.length = 0;
|
||||
}
|
||||
sleepState._state = gdjs.ObjectSleepState.State.ASleep;
|
||||
onFallenAsleep(object);
|
||||
sleepState._onWakingUpCallbacks.push(onWakingUp);
|
||||
} else {
|
||||
awakeObjects[writeIndex] = object;
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
awakeObjects.length = writeIndex;
|
||||
return awakeObjects;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ObjectSleepState {
|
||||
export enum State {
|
||||
ASleep,
|
||||
CanSleepThisFrame,
|
||||
AWake,
|
||||
}
|
||||
}
|
||||
}
|
54
GDJS/Runtime/ResourceCache.ts
Normal file
54
GDJS/Runtime/ResourceCache.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-present Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
namespace gdjs {
|
||||
/**
|
||||
* A cache of resources that helps ensuring that files are only downloaded
|
||||
* once.
|
||||
*/
|
||||
export class ResourceCache<C> {
|
||||
private _nameToContent = new Map<string, C>();
|
||||
private _fileToContent = new Map<string, C>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Gives a fast access to asset content when they were pre-loaded and
|
||||
* on-the-fly loading is not allowed.
|
||||
*/
|
||||
getFromName(name: string): C | null {
|
||||
return this._nameToContent.get(name) || null;
|
||||
}
|
||||
|
||||
get(resource: ResourceData): C | null {
|
||||
let existingContent = this._nameToContent.get(resource.name);
|
||||
if (existingContent) {
|
||||
return existingContent;
|
||||
}
|
||||
// When several assets use the same file, it avoids to download it again.
|
||||
existingContent = this._fileToContent.get(resource.file);
|
||||
if (existingContent) {
|
||||
this._nameToContent.set(resource.name, existingContent);
|
||||
return existingContent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set(resource: ResourceData, content: C) {
|
||||
this._nameToContent.set(resource.name, content);
|
||||
this._fileToContent.set(resource.file, content);
|
||||
}
|
||||
|
||||
delete(resource: ResourceData) {
|
||||
this._nameToContent.delete(resource.name);
|
||||
this._fileToContent.delete(resource.file);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._nameToContent.clear();
|
||||
this._fileToContent.clear();
|
||||
}
|
||||
}
|
||||
}
|
556
GDJS/Runtime/ResourceLoader.ts
Normal file
556
GDJS/Runtime/ResourceLoader.ts
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2023 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('ResourceLoader');
|
||||
|
||||
const addSearchParameterToUrl = (
|
||||
url: string,
|
||||
urlEncodedParameterName: string,
|
||||
urlEncodedValue: string
|
||||
) => {
|
||||
if (url.startsWith('data:') || url.startsWith('blob:')) {
|
||||
// blob/data protocol does not support search parameters, which are useless anyway.
|
||||
return url;
|
||||
}
|
||||
|
||||
const separator = url.indexOf('?') === -1 ? '?' : '&';
|
||||
return url + separator + urlEncodedParameterName + '=' + urlEncodedValue;
|
||||
};
|
||||
|
||||
const checkIfIsGDevelopCloudBucketUrl = (url: string): boolean => {
|
||||
return (
|
||||
url.startsWith('https://project-resources.gdevelop.io/') ||
|
||||
url.startsWith('https://project-resources-dev.gdevelop.io/')
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* A task of pre-loading resources used by a scene.
|
||||
*
|
||||
* A Promise can't be used instead of this class because a Promise will start
|
||||
* as soon as possible. It would flood the server with downloading requests
|
||||
* and make impossible to finely tune in which order scenes are actually
|
||||
* downloaded.
|
||||
*/
|
||||
class SceneLoadingTask {
|
||||
sceneName: string;
|
||||
private onProgressCallbacks: Array<(count: number, total: number) => void>;
|
||||
private onFinishCallbacks: Array<() => void>;
|
||||
private isFinished = false;
|
||||
|
||||
constructor(sceneName: string) {
|
||||
this.sceneName = sceneName;
|
||||
this.onProgressCallbacks = new Array<
|
||||
(count: number, total: number) => void
|
||||
>();
|
||||
this.onFinishCallbacks = new Array<() => void>();
|
||||
}
|
||||
|
||||
registerCallback(
|
||||
onFinish: () => void,
|
||||
onProgress?: (count: number, total: number) => void
|
||||
) {
|
||||
if (this.isFinished) {
|
||||
onFinish();
|
||||
return;
|
||||
}
|
||||
this.onFinishCallbacks.push(onFinish);
|
||||
if (onProgress) {
|
||||
this.onProgressCallbacks.push(onProgress);
|
||||
}
|
||||
}
|
||||
|
||||
onProgress(count: number, total: number) {
|
||||
for (const onProgress of this.onProgressCallbacks) {
|
||||
onProgress(count, total);
|
||||
}
|
||||
}
|
||||
|
||||
onFinish() {
|
||||
this.isFinished = true;
|
||||
for (const onFinish of this.onFinishCallbacks) {
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-load resources of any kind needed for a game or a scene.
|
||||
*/
|
||||
export class ResourceLoader {
|
||||
_runtimeGame: RuntimeGame;
|
||||
/**
|
||||
* All the resource of a game by resource name.
|
||||
*/
|
||||
private _resources: Map<string, ResourceData>;
|
||||
/**
|
||||
* Resources needed for any scene. Typically, they are resources from
|
||||
* global objects.
|
||||
*/
|
||||
private _globalResources: Array<string>;
|
||||
/**
|
||||
* Resources by scene names.
|
||||
*/
|
||||
private _sceneResources: Map<string, Array<string>>;
|
||||
/**
|
||||
* Keep track of which scene whose resources has already be pre-loaded.
|
||||
*/
|
||||
private _sceneNamesToLoad: Set<string>;
|
||||
/**
|
||||
* Keep track of which scene whose resources has already be loaded.
|
||||
*/
|
||||
private _sceneNamesToMakeReady: Set<string>;
|
||||
/**
|
||||
* A queue of scenes whose resources are still to be pre-loaded.
|
||||
*/
|
||||
private _sceneToLoadQueue: Array<SceneLoadingTask> = new Array<
|
||||
SceneLoadingTask
|
||||
>();
|
||||
/**
|
||||
* The resource managers that actually download and remember downloaded
|
||||
* content.
|
||||
*/
|
||||
_resourceManagersMap: Map<ResourceKind, ResourceManager>;
|
||||
private _imageManager: ImageManager;
|
||||
private _soundManager: SoundManager;
|
||||
private _fontManager: FontManager;
|
||||
private _jsonManager: JsonManager;
|
||||
private _model3DManager: Model3DManager;
|
||||
private _bitmapFontManager: BitmapFontManager;
|
||||
|
||||
/**
|
||||
* Only used by events.
|
||||
*/
|
||||
private currentLoadingSceneName: string = '';
|
||||
/**
|
||||
* Only used by events.
|
||||
*/
|
||||
private currentSceneLoadingProgress: float = 0;
|
||||
|
||||
/**
|
||||
* @param runtimeGame The game.
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
* @param globalResources The resources needed for any layer.
|
||||
* @param layoutDataArray The resources used by each layer.
|
||||
*/
|
||||
constructor(
|
||||
runtimeGame: RuntimeGame,
|
||||
resourceDataArray: ResourceData[],
|
||||
globalResources: Array<string>,
|
||||
layoutDataArray: Array<LayoutData>
|
||||
) {
|
||||
this._runtimeGame = runtimeGame;
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this._globalResources = globalResources;
|
||||
|
||||
// These 3 attributes are filled by `setResources`.
|
||||
this._sceneResources = new Map<string, Array<string>>();
|
||||
this._sceneNamesToLoad = new Set<string>();
|
||||
this._sceneNamesToMakeReady = new Set<string>();
|
||||
this.setResources(resourceDataArray, globalResources, layoutDataArray);
|
||||
|
||||
this._imageManager = new gdjs.ImageManager(this);
|
||||
this._soundManager = new gdjs.SoundManager(this);
|
||||
this._fontManager = new gdjs.FontManager(this);
|
||||
this._jsonManager = new gdjs.JsonManager(this);
|
||||
this._bitmapFontManager = new gdjs.BitmapFontManager(
|
||||
this,
|
||||
this._imageManager
|
||||
);
|
||||
this._model3DManager = new gdjs.Model3DManager(this);
|
||||
|
||||
const resourceManagers: Array<ResourceManager> = [
|
||||
this._imageManager,
|
||||
this._soundManager,
|
||||
this._fontManager,
|
||||
this._jsonManager,
|
||||
this._bitmapFontManager,
|
||||
this._model3DManager,
|
||||
];
|
||||
this._resourceManagersMap = new Map<ResourceKind, ResourceManager>();
|
||||
for (const resourceManager of resourceManagers) {
|
||||
for (const resourceKind of resourceManager.getResourceKinds()) {
|
||||
this._resourceManagersMap.set(resourceKind, resourceManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should
|
||||
* not be used otherwise.
|
||||
*/
|
||||
setResources(
|
||||
resourceDataArray: ResourceData[],
|
||||
globalResources: Array<string>,
|
||||
layoutDataArray: Array<LayoutData>
|
||||
): void {
|
||||
this._globalResources = globalResources;
|
||||
|
||||
this._sceneResources.clear();
|
||||
this._sceneNamesToLoad.clear();
|
||||
this._sceneNamesToMakeReady.clear();
|
||||
for (const layoutData of layoutDataArray) {
|
||||
this._sceneResources.set(
|
||||
layoutData.name,
|
||||
layoutData.usedResources.map((resource) => resource.name)
|
||||
);
|
||||
this._sceneNamesToLoad.add(layoutData.name);
|
||||
this._sceneNamesToMakeReady.add(layoutData.name);
|
||||
}
|
||||
// TODO Clearing the queue doesn't abort the running task, but it should
|
||||
// not matter as resource loading is really fast in preview mode.
|
||||
this._sceneToLoadQueue.length = 0;
|
||||
for (let index = layoutDataArray.length - 1; index >= 0; index--) {
|
||||
const layoutData = layoutDataArray[index];
|
||||
this._sceneToLoadQueue.push(new SceneLoadingTask(layoutData.name));
|
||||
}
|
||||
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
}
|
||||
|
||||
async loadAllResources(
|
||||
onProgress: (loadingCount: integer, totalCount: integer) => void
|
||||
): Promise<void> {
|
||||
let loadedCount = 0;
|
||||
await Promise.all(
|
||||
[...this._resources.values()].map(async (resource) => {
|
||||
await this._loadResource(resource);
|
||||
await this._processResource(resource);
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, this._resources.size);
|
||||
})
|
||||
);
|
||||
this._sceneNamesToLoad.clear();
|
||||
this._sceneNamesToMakeReady.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the resources that are needed to launch the first scene.
|
||||
*/
|
||||
async loadGlobalAndFirstSceneResources(
|
||||
firstSceneName: string,
|
||||
onProgress: (count: number, total: number) => void
|
||||
): Promise<void> {
|
||||
const sceneResources = this._sceneResources.get(firstSceneName);
|
||||
if (!sceneResources) {
|
||||
logger.warn(
|
||||
'Can\'t load resource for unknown scene: "' + firstSceneName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
let loadedCount = 0;
|
||||
const resources = [...this._globalResources, ...sceneResources.values()];
|
||||
await Promise.all(
|
||||
resources.map(async (resourceName) => {
|
||||
const resource = this._resources.get(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn('Unable to find resource "' + resourceName + '".');
|
||||
return;
|
||||
}
|
||||
await this._loadResource(resource);
|
||||
await this._processResource(resource);
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, resources.length);
|
||||
})
|
||||
);
|
||||
this._setSceneAssetsLoaded(firstSceneName);
|
||||
this._setSceneAssetsReady(firstSceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load each scene in order.
|
||||
*
|
||||
* This is done in background to try to avoid loading screens when changing
|
||||
* scenes.
|
||||
*/
|
||||
async loadAllSceneInBackground(): Promise<void> {
|
||||
while (this._sceneToLoadQueue.length > 0) {
|
||||
const task = this._sceneToLoadQueue[this._sceneToLoadQueue.length - 1];
|
||||
if (task === undefined) {
|
||||
continue;
|
||||
}
|
||||
this.currentLoadingSceneName = task.sceneName;
|
||||
if (!this.areSceneAssetsLoaded(task.sceneName)) {
|
||||
await this._doLoadSceneResources(
|
||||
task.sceneName,
|
||||
async (count, total) => task.onProgress(count, total)
|
||||
);
|
||||
// A scene may have been moved last while awaiting resources to be
|
||||
// downloaded (see _prioritizeScene).
|
||||
this._sceneToLoadQueue.splice(
|
||||
this._sceneToLoadQueue.findIndex((element) => element === task),
|
||||
1
|
||||
);
|
||||
task.onFinish();
|
||||
} else {
|
||||
this._sceneToLoadQueue.pop();
|
||||
}
|
||||
}
|
||||
this.currentLoadingSceneName = '';
|
||||
}
|
||||
|
||||
private async _doLoadSceneResources(
|
||||
sceneName: string,
|
||||
onProgress?: (count: number, total: number) => Promise<void>
|
||||
): Promise<void> {
|
||||
const sceneResources = this._sceneResources.get(sceneName);
|
||||
if (!sceneResources) {
|
||||
logger.warn(
|
||||
'Can\'t load resource for unknown scene: "' + sceneName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
let loadedCount = 0;
|
||||
await Promise.all(
|
||||
[...sceneResources.values()].map(async (resourceName) => {
|
||||
const resource = this._resources.get(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn('Unable to find resource "' + resourceName + '".');
|
||||
return;
|
||||
}
|
||||
await this._loadResource(resource);
|
||||
loadedCount++;
|
||||
this.currentSceneLoadingProgress = loadedCount / this._resources.size;
|
||||
onProgress && (await onProgress(loadedCount, this._resources.size));
|
||||
})
|
||||
);
|
||||
this._setSceneAssetsLoaded(sceneName);
|
||||
}
|
||||
|
||||
private async _loadResource(resource: ResourceData): Promise<void> {
|
||||
const resourceManager = this._resourceManagersMap.get(resource.kind);
|
||||
if (!resourceManager) {
|
||||
logger.warn(
|
||||
'Unknown resource kind: "' +
|
||||
resource.kind +
|
||||
'" for: "' +
|
||||
resource.name +
|
||||
'".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
await resourceManager.loadResource(resource.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and process a scene that is needed right away.
|
||||
*
|
||||
* The renderer will show a loading screen while its done.
|
||||
*/
|
||||
async loadAndProcessSceneResources(
|
||||
sceneName: string,
|
||||
onProgress?: (count: number, total: number) => Promise<void>
|
||||
): Promise<void> {
|
||||
if (this.areSceneAssetsReady(sceneName)) {
|
||||
return;
|
||||
}
|
||||
await this.loadSceneResources(sceneName, onProgress);
|
||||
|
||||
const sceneResources = this._sceneResources.get(sceneName);
|
||||
if (!sceneResources) {
|
||||
logger.warn(
|
||||
'Can\'t load resource for unknown scene: "' + sceneName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let parsedCount = 0;
|
||||
for (const resourceName of sceneResources) {
|
||||
const resource = this._resources.get(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn('Unable to find resource "' + resourceName + '".');
|
||||
continue;
|
||||
}
|
||||
await this._processResource(resource);
|
||||
parsedCount++;
|
||||
onProgress && (await onProgress(parsedCount, sceneResources.length));
|
||||
}
|
||||
this._setSceneAssetsReady(sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a scene resources without parsing them.
|
||||
*
|
||||
* When another scene resources are loading in background, it waits for
|
||||
* all its resources to be loaded before loading resources of the given
|
||||
* scene.
|
||||
*/
|
||||
async loadSceneResources(
|
||||
sceneName: string,
|
||||
onProgress?: (count: number, total: number) => void
|
||||
): Promise<void> {
|
||||
const task = this._prioritizeScene(sceneName);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!task) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
task.registerCallback(() => {
|
||||
resolve();
|
||||
}, onProgress);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a given scene at the end of the queue.
|
||||
*
|
||||
* When the scene that is currently loading in background is done,
|
||||
* this scene will be the next to be loaded.
|
||||
*/
|
||||
private _prioritizeScene(sceneName: string): SceneLoadingTask | null {
|
||||
const taskIndex = this._sceneToLoadQueue.findIndex(
|
||||
(task) => task.sceneName === sceneName
|
||||
);
|
||||
if (taskIndex < 0) {
|
||||
// The scene is already loaded.
|
||||
return null;
|
||||
}
|
||||
const task = this._sceneToLoadQueue[taskIndex];
|
||||
this._sceneToLoadQueue.splice(taskIndex, 1);
|
||||
this._sceneToLoadQueue.push(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
private async _processResource(resource: ResourceData): Promise<void> {
|
||||
const resourceManager = this._resourceManagersMap.get(resource.kind);
|
||||
if (!resourceManager) {
|
||||
logger.warn(
|
||||
'Unknown resource kind: "' +
|
||||
resource.kind +
|
||||
'" for: "' +
|
||||
resource.name +
|
||||
'".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
await resourceManager.processResource(resource.name);
|
||||
}
|
||||
|
||||
getSceneLoadingProgress(sceneName: string): float {
|
||||
return sceneName === this.currentLoadingSceneName
|
||||
? this.currentSceneLoadingProgress
|
||||
: this.areSceneAssetsLoaded(sceneName)
|
||||
? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true when all the resources of the given scene are loaded
|
||||
* (but maybe not parsed).
|
||||
*/
|
||||
areSceneAssetsLoaded(sceneName: string): boolean {
|
||||
return !this._sceneNamesToLoad.has(sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true when all the resources of the given scene are loaded and
|
||||
* parsed.
|
||||
*/
|
||||
areSceneAssetsReady(sceneName: string): boolean {
|
||||
return !this._sceneNamesToMakeReady.has(sceneName);
|
||||
}
|
||||
|
||||
private _setSceneAssetsLoaded(sceneName: string): void {
|
||||
this._sceneNamesToLoad.delete(sceneName);
|
||||
}
|
||||
|
||||
private _setSceneAssetsReady(sceneName: string): void {
|
||||
this._sceneNamesToMakeReady.delete(sceneName);
|
||||
}
|
||||
|
||||
getResource(resourceName: string): ResourceData | null {
|
||||
return this._resources.get(resourceName) || null;
|
||||
}
|
||||
|
||||
// Helper methods used when resources are loaded from an URL.
|
||||
|
||||
/**
|
||||
* Complete the given URL with any specific parameter required to access
|
||||
* the resource (this can be for example a token needed to access the resource).
|
||||
*/
|
||||
getFullUrl(url: string) {
|
||||
const { gdevelopResourceToken } = this._runtimeGame._options;
|
||||
if (!gdevelopResourceToken) return url;
|
||||
|
||||
if (!checkIfIsGDevelopCloudBucketUrl(url)) return url;
|
||||
|
||||
return addSearchParameterToUrl(
|
||||
url,
|
||||
'gd_resource_token',
|
||||
encodeURIComponent(gdevelopResourceToken)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the specified URL must be loaded with cookies ("credentials")
|
||||
* sent to grant access to them.
|
||||
*/
|
||||
checkIfCredentialsRequired(url: string) {
|
||||
if (this._runtimeGame._options.gdevelopResourceToken) return false;
|
||||
|
||||
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
|
||||
// i.e: its gdevelop.io cookie, to be passed.
|
||||
// Note that this is only useful during previews.
|
||||
if (checkIfIsGDevelopCloudBucketUrl(url)) return true;
|
||||
|
||||
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gdjs.SoundManager of the RuntimeGame.
|
||||
* @return The sound manager.
|
||||
*/
|
||||
getSoundManager(): gdjs.HowlerSoundManager {
|
||||
return this._soundManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gdjs.ImageManager of the RuntimeGame.
|
||||
* @return The image manager.
|
||||
*/
|
||||
getImageManager(): gdjs.PixiImageManager {
|
||||
return this._imageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gdjs.FontManager of the RuntimeGame.
|
||||
* @return The font manager.
|
||||
*/
|
||||
getFontManager(): gdjs.FontFaceObserverFontManager {
|
||||
return this._fontManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gdjs.BitmapFontManager of the RuntimeGame.
|
||||
* @return The bitmap font manager.
|
||||
*/
|
||||
getBitmapFontManager(): gdjs.BitmapFontManager {
|
||||
return this._bitmapFontManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON manager of the game, used to load JSON from game
|
||||
* resources.
|
||||
* @return The json manager for the game
|
||||
*/
|
||||
getJsonManager(): gdjs.JsonManager {
|
||||
return this._jsonManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 3D model manager of the game, used to load 3D model from game
|
||||
* resources.
|
||||
* @return The 3D model manager for the game
|
||||
*/
|
||||
getModel3DManager(): gdjs.Model3DManager {
|
||||
return this._model3DManager;
|
||||
}
|
||||
}
|
||||
}
|
33
GDJS/Runtime/ResourceManager.ts
Normal file
33
GDJS/Runtime/ResourceManager.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2023 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
namespace gdjs {
|
||||
/**
|
||||
* A resource managers that download and remember downloaded content for one
|
||||
* kind of resource.
|
||||
*/
|
||||
export interface ResourceManager {
|
||||
/**
|
||||
* Load the specified resource.
|
||||
*
|
||||
* This method will be run during the game. It should only do light tasks
|
||||
* like file downloading.
|
||||
*/
|
||||
loadResource(resourceName: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Process the specified resource.
|
||||
*
|
||||
* This method will only be run while loading screen is shown. It can do
|
||||
* heavy tasks like parsing data.
|
||||
*/
|
||||
processResource(resourceName: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Return the kind of resources handled by this manager.
|
||||
*/
|
||||
getResourceKinds(): Array<ResourceKind>;
|
||||
}
|
||||
}
|
@@ -34,7 +34,6 @@ namespace gdjs {
|
||||
|
||||
_layers: Hashtable<RuntimeLayer>;
|
||||
_orderedLayers: RuntimeLayer[]; // TODO: should this be a single structure with _layers, to enforce its usage?
|
||||
_layersCameraCoordinates: Record<string, [float, float, float, float]> = {};
|
||||
|
||||
// Options for the debug draw:
|
||||
_debugDrawEnabled: boolean = false;
|
||||
@@ -351,26 +350,6 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
_updateLayersCameraCoordinates(scale: float) {
|
||||
this._layersCameraCoordinates = this._layersCameraCoordinates || {};
|
||||
for (const name in this._layers.items) {
|
||||
if (this._layers.items.hasOwnProperty(name)) {
|
||||
const theLayer = this._layers.items[name];
|
||||
this._layersCameraCoordinates[name] = this._layersCameraCoordinates[
|
||||
name
|
||||
] || [0, 0, 0, 0];
|
||||
this._layersCameraCoordinates[name][0] =
|
||||
theLayer.getCameraX() - (theLayer.getCameraWidth() / 2) * scale;
|
||||
this._layersCameraCoordinates[name][1] =
|
||||
theLayer.getCameraY() - (theLayer.getCameraHeight() / 2) * scale;
|
||||
this._layersCameraCoordinates[name][2] =
|
||||
theLayer.getCameraX() + (theLayer.getCameraWidth() / 2) * scale;
|
||||
this._layersCameraCoordinates[name][3] =
|
||||
theLayer.getCameraY() + (theLayer.getCameraHeight() / 2) * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update effects of layers before rendering.
|
||||
*/
|
||||
@@ -625,6 +604,8 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
onObjectChangedOfLayer(object: RuntimeObject, oldLayer: RuntimeLayer) {}
|
||||
|
||||
/**
|
||||
* Get the layer with the given name
|
||||
* @param name The name of the layer
|
||||
|
@@ -351,6 +351,7 @@ namespace gdjs {
|
||||
'_renderer',
|
||||
'_gameRenderer',
|
||||
'_imageManager',
|
||||
'_rendererEffects',
|
||||
// Exclude PIXI textures:
|
||||
'baseTexture',
|
||||
'_baseTexture',
|
||||
|
@@ -311,77 +311,82 @@ namespace gdjs {
|
||||
return Promise.all(reloadPromises);
|
||||
}
|
||||
|
||||
_hotReloadRuntimeGame(
|
||||
async _hotReloadRuntimeGame(
|
||||
oldProjectData: ProjectData,
|
||||
newProjectData: ProjectData,
|
||||
changedRuntimeBehaviors: ChangedRuntimeBehavior[],
|
||||
runtimeGame: gdjs.RuntimeGame
|
||||
): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
// Update project data and re-load assets (sound/image/font/json managers
|
||||
// will take care of reloading only what is needed).
|
||||
runtimeGame.setProjectData(newProjectData);
|
||||
runtimeGame.loadAllAssets(() => {
|
||||
this._hotReloadVariablesContainer(
|
||||
oldProjectData.variables,
|
||||
newProjectData.variables,
|
||||
runtimeGame.getVariables()
|
||||
);
|
||||
|
||||
// Reload runtime scenes
|
||||
const sceneStack = runtimeGame.getSceneStack();
|
||||
sceneStack._stack.forEach((runtimeScene) => {
|
||||
const oldLayoutData = oldProjectData.layouts.filter(
|
||||
(layoutData) => layoutData.name === runtimeScene.getName()
|
||||
)[0];
|
||||
const newLayoutData = newProjectData.layouts.filter(
|
||||
(layoutData) => layoutData.name === runtimeScene.getName()
|
||||
)[0];
|
||||
if (oldLayoutData && newLayoutData) {
|
||||
this._hotReloadRuntimeScene(
|
||||
oldLayoutData,
|
||||
newLayoutData,
|
||||
changedRuntimeBehaviors,
|
||||
runtimeScene
|
||||
);
|
||||
} else {
|
||||
// A scene was removed. Not hot-reloading this.
|
||||
this._logs.push({
|
||||
kind: 'error',
|
||||
message:
|
||||
'Scene ' +
|
||||
oldLayoutData.name +
|
||||
' was removed. A fresh preview should be launched.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Reload changes in external layouts
|
||||
newProjectData.externalLayouts.forEach((newExternalLayoutData) => {
|
||||
const oldExternalLayoutData = oldProjectData.externalLayouts.filter(
|
||||
(externalLayoutData) =>
|
||||
externalLayoutData.name === newExternalLayoutData.name
|
||||
)[0];
|
||||
if (
|
||||
oldExternalLayoutData &&
|
||||
// Check if there are actual changes, to avoid useless work trying to
|
||||
// hot-reload all the scenes.
|
||||
!HotReloader.deepEqual(
|
||||
oldExternalLayoutData,
|
||||
newExternalLayoutData
|
||||
)
|
||||
) {
|
||||
sceneStack._stack.forEach((runtimeScene) => {
|
||||
this._hotReloadRuntimeSceneInstances(
|
||||
oldExternalLayoutData.instances,
|
||||
newExternalLayoutData.instances,
|
||||
runtimeScene
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
resolve();
|
||||
const sceneStack = runtimeGame.getSceneStack();
|
||||
const currentScene = sceneStack.getCurrentScene();
|
||||
if (!currentScene) {
|
||||
// It can't actually happen.
|
||||
this._logs.push({
|
||||
kind: 'error',
|
||||
message: "Can't hot-reload as no scene are opened.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Update project data and re-load assets (sound/image/font/json managers
|
||||
// will take care of reloading only what is needed).
|
||||
runtimeGame.setProjectData(newProjectData);
|
||||
await runtimeGame.loadFirstAssetsAndStartBackgroundLoading(
|
||||
currentScene.getName(),
|
||||
() => {}
|
||||
);
|
||||
this._hotReloadVariablesContainer(
|
||||
oldProjectData.variables,
|
||||
newProjectData.variables,
|
||||
runtimeGame.getVariables()
|
||||
);
|
||||
|
||||
// Reload runtime scenes
|
||||
sceneStack._stack.forEach((runtimeScene) => {
|
||||
const oldLayoutData = oldProjectData.layouts.filter(
|
||||
(layoutData) => layoutData.name === runtimeScene.getName()
|
||||
)[0];
|
||||
const newLayoutData = newProjectData.layouts.filter(
|
||||
(layoutData) => layoutData.name === runtimeScene.getName()
|
||||
)[0];
|
||||
if (oldLayoutData && newLayoutData) {
|
||||
this._hotReloadRuntimeScene(
|
||||
oldLayoutData,
|
||||
newLayoutData,
|
||||
changedRuntimeBehaviors,
|
||||
runtimeScene
|
||||
);
|
||||
} else {
|
||||
// A scene was removed. Not hot-reloading this.
|
||||
this._logs.push({
|
||||
kind: 'error',
|
||||
message:
|
||||
'Scene ' +
|
||||
oldLayoutData.name +
|
||||
' was removed. A fresh preview should be launched.',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Reload changes in external layouts
|
||||
newProjectData.externalLayouts.forEach((newExternalLayoutData) => {
|
||||
const oldExternalLayoutData = oldProjectData.externalLayouts.filter(
|
||||
(externalLayoutData) =>
|
||||
externalLayoutData.name === newExternalLayoutData.name
|
||||
)[0];
|
||||
if (
|
||||
oldExternalLayoutData &&
|
||||
// Check if there are actual changes, to avoid useless work trying to
|
||||
// hot-reload all the scenes.
|
||||
!HotReloader.deepEqual(oldExternalLayoutData, newExternalLayoutData)
|
||||
) {
|
||||
sceneStack._stack.forEach((runtimeScene) => {
|
||||
this._hotReloadRuntimeSceneInstances(
|
||||
oldExternalLayoutData.instances,
|
||||
newExternalLayoutData.instances,
|
||||
runtimeScene
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -380,7 +380,7 @@ namespace gdjs {
|
||||
.isMouseInsideCanvas();
|
||||
};
|
||||
|
||||
export const _cursorIsOnObject = function (
|
||||
const _cursorIsOnObject = function (
|
||||
obj: gdjs.RuntimeObject,
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
) {
|
||||
@@ -394,7 +394,7 @@ namespace gdjs {
|
||||
inverted: boolean
|
||||
) {
|
||||
return gdjs.evtTools.object.pickObjectsIf(
|
||||
gdjs.evtTools.input._cursorIsOnObject,
|
||||
_cursorIsOnObject,
|
||||
objectsLists,
|
||||
inverted,
|
||||
instanceContainer
|
||||
|
@@ -168,7 +168,7 @@ namespace gdjs {
|
||||
* @deprecated Use `JSON.stringify(variable.toJSObject())` instead.
|
||||
*/
|
||||
export const objectVariableStructureToJSON = function (
|
||||
object: gdjs.RuntimeObject,
|
||||
object: gdjs.RuntimeObject | null,
|
||||
variable: gdjs.Variable
|
||||
): string {
|
||||
return JSON.stringify(variable.toJSObject());
|
||||
@@ -205,7 +205,7 @@ namespace gdjs {
|
||||
*/
|
||||
export const jsonToObjectVariableStructure = function (
|
||||
jsonStr: string,
|
||||
object: gdjs.RuntimeObject,
|
||||
object: gdjs.RuntimeObject | null,
|
||||
variable: gdjs.Variable
|
||||
) {
|
||||
variable.fromJSON(jsonStr);
|
||||
|
@@ -326,6 +326,36 @@ namespace gdjs {
|
||||
): boolean => {
|
||||
return runtimeScene.getGame().hasScene(sceneName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload a scene assets as soon as possible in background.
|
||||
*/
|
||||
export const prioritizeLoadingOfScene = (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
sceneName: string
|
||||
): void => {
|
||||
runtimeScene.getGame().prioritizeLoadingOfScene(sceneName);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return The progress of assets loading in background for a scene (between 0 and 1).
|
||||
*/
|
||||
export const getSceneLoadingProgress = (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
sceneName: string
|
||||
): float => {
|
||||
return runtimeScene.getGame().getSceneLoadingProgress(sceneName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if scene assets have finished to load in background.
|
||||
*/
|
||||
export const areSceneAssetsLoaded = (
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
sceneName: string
|
||||
): boolean => {
|
||||
return runtimeScene.getGame().areSceneAssetsLoaded(sceneName);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -6,46 +6,29 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Font manager');
|
||||
|
||||
const resourceKinds: Array<ResourceKind> = ['font'];
|
||||
|
||||
/**
|
||||
* FontFaceObserverFontManager loads fonts (using `FontFace` or `fontfaceobserver` library)
|
||||
* from the game resources (see `loadFonts`), and allow to access to
|
||||
* the font families of the loaded fonts during the game (see `getFontFamily`).
|
||||
*/
|
||||
export class FontFaceObserverFontManager {
|
||||
_resourcesLoader: RuntimeGameResourcesLoader;
|
||||
_resources: Map<string, ResourceData>;
|
||||
export class FontFaceObserverFontManager implements gdjs.ResourceManager {
|
||||
_resourceLoader: gdjs.ResourceLoader;
|
||||
// Associate font resource names to the loaded font family
|
||||
_loadedFontFamily: { [key: string]: string } = {};
|
||||
// Associate font resource names to the resources, for faster access
|
||||
_loadedFonts: { [key: string]: ResourceData } = {};
|
||||
_filenameToFontFamily: { [key: string]: string } = {};
|
||||
_loadedFontFamily = new gdjs.ResourceCache<string>();
|
||||
_loadedFontFamilySet = new Set<string>();
|
||||
|
||||
/**
|
||||
* @param resources The resources data of the game.
|
||||
* @param resourcesLoader The resources loader of the game.
|
||||
* @param resourceLoader The resources loader of the game.
|
||||
*/
|
||||
constructor(
|
||||
resourceDataArray: ResourceData[],
|
||||
resourcesLoader: RuntimeGameResourcesLoader
|
||||
) {
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this.setResources(resourceDataArray);
|
||||
this._resourcesLoader = resourcesLoader;
|
||||
constructor(resourceLoader: gdjs.ResourceLoader) {
|
||||
this._resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
// Cache the result of transforming a filename to a font family - useful to avoid duplicates.
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param resources The resources data of the game.
|
||||
*/
|
||||
setResources(resourceDataArray: ResourceData[]): void {
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
if (resourceData.kind === 'font') {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
}
|
||||
getResourceKinds(): ResourceKind[] {
|
||||
return resourceKinds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,10 +41,7 @@ namespace gdjs {
|
||||
* or "Arial" if not loaded.
|
||||
*/
|
||||
getFontFamily(resourceName: string): string {
|
||||
if (this._loadedFontFamily[resourceName]) {
|
||||
return this._loadedFontFamily[resourceName];
|
||||
}
|
||||
return 'Arial';
|
||||
return this._loadedFontFamily.getFromName(resourceName) || 'Arial';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,10 +56,8 @@ namespace gdjs {
|
||||
* @returns The file of the font resource.
|
||||
*/
|
||||
getFontFile(resourceName: string): string {
|
||||
if (this._loadedFonts[resourceName]) {
|
||||
return this._loadedFonts[resourceName].file || '';
|
||||
}
|
||||
return resourceName;
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
return resource ? resource.file || '' : resourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,26 +70,20 @@ namespace gdjs {
|
||||
* @param filename The filename of the font.
|
||||
* @returns The font family to be used for this font resource.
|
||||
*/
|
||||
_getFontFamilyFromFilename(filename: string): string {
|
||||
if (this._filenameToFontFamily[filename]) {
|
||||
return this._filenameToFontFamily[filename];
|
||||
}
|
||||
|
||||
_getFontFamilyFromFilename(resource: ResourceData): string {
|
||||
// Replaces all non-alphanumeric characters with dashes to ensure no issues when
|
||||
// referring to this font family (see https://github.com/4ian/GDevelop/issues/1521).
|
||||
let baseSlugifiedName =
|
||||
'gdjs_font_' + filename.toLowerCase().replace(/[^\w]/gi, '-');
|
||||
'gdjs_font_' + resource.file.toLowerCase().replace(/[^\w]/gi, '-');
|
||||
|
||||
// Ensure the generated font family is unique.
|
||||
const slugifiedName = baseSlugifiedName;
|
||||
let uniqueSuffix = 2;
|
||||
while (!!this._filenameToFontFamily[baseSlugifiedName]) {
|
||||
while (this._loadedFontFamilySet.has(baseSlugifiedName)) {
|
||||
baseSlugifiedName = baseSlugifiedName + '-' + uniqueSuffix;
|
||||
uniqueSuffix++;
|
||||
}
|
||||
|
||||
// Cache the result to avoid collision with a similar slugified name for another filename.
|
||||
return (this._filenameToFontFamily[filename] = slugifiedName);
|
||||
return slugifiedName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,8 +103,8 @@ namespace gdjs {
|
||||
// @ts-ignore
|
||||
if (typeof FontFace !== 'undefined') {
|
||||
// Load the given font using CSS Font Loading API.
|
||||
return fetch(this._resourcesLoader.getFullUrl(src), {
|
||||
credentials: this._resourcesLoader.checkIfCredentialsRequired(src)
|
||||
return fetch(this._resourceLoader.getFullUrl(src), {
|
||||
credentials: this._resourceLoader.checkIfCredentialsRequired(src)
|
||||
? // Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
|
||||
// i.e: its gdevelop.io cookie, to be passed.
|
||||
'include'
|
||||
@@ -184,59 +156,46 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
async processResource(resourceName: string): Promise<void> {
|
||||
// Do nothing because fonts are light enough to be parsed in background.
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified resources, so that fonts are loaded and can then be
|
||||
* used by using the font family returned by getFontFamily.
|
||||
* @param onProgress Callback called each time a new file is loaded.
|
||||
*/
|
||||
async loadFonts(
|
||||
onProgress: (loadedCount: integer, totalCount: integer) => void
|
||||
): Promise<integer> {
|
||||
// Construct the list of files to be loaded.
|
||||
// For one loaded file, it can have one or more resources
|
||||
// that use it.
|
||||
const filesResources: { [key: string]: ResourceData[] } = {};
|
||||
for (const res of this._resources.values()) {
|
||||
if (res.file) {
|
||||
if (!!this._loadedFonts[res.name]) {
|
||||
continue;
|
||||
}
|
||||
filesResources[res.file] = filesResources[res.file]
|
||||
? filesResources[res.file].concat(res)
|
||||
: [res];
|
||||
}
|
||||
}
|
||||
const totalCount = Object.keys(filesResources).length;
|
||||
if (totalCount === 0) {
|
||||
return 0;
|
||||
async loadResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn('Unable to find font for resource "' + resourceName + '".');
|
||||
return;
|
||||
}
|
||||
|
||||
let loadingCount = 0;
|
||||
await Promise.all(
|
||||
Object.keys(filesResources).map(async (file) => {
|
||||
const fontFamily = this._getFontFamilyFromFilename(file);
|
||||
const fontResources = filesResources[file];
|
||||
try {
|
||||
await this._loadFont(fontFamily, file);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
'Error loading font resource "' +
|
||||
fontResources[0].name +
|
||||
'" (file: ' +
|
||||
file +
|
||||
'): ' +
|
||||
(error.message || 'Unknown error')
|
||||
);
|
||||
}
|
||||
fontResources.forEach((resource) => {
|
||||
this._loadedFontFamily[resource.name] = fontFamily;
|
||||
this._loadedFonts[resource.name] = resource;
|
||||
});
|
||||
loadingCount++;
|
||||
onProgress(loadingCount, totalCount);
|
||||
})
|
||||
);
|
||||
return totalCount;
|
||||
if (this._loadedFontFamily.get(resource)) {
|
||||
return;
|
||||
}
|
||||
const file = resource.file;
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fontFamily = this._getFontFamilyFromFilename(resource);
|
||||
// Cache the result to avoid collision with a similar slugified name for another filename.
|
||||
this._loadedFontFamily.set(resource, fontFamily);
|
||||
this._loadedFontFamilySet.add(fontFamily);
|
||||
try {
|
||||
await this._loadFont(fontFamily, file);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
'Error loading font resource "' +
|
||||
resource.name +
|
||||
'" (file: ' +
|
||||
file +
|
||||
'): ' +
|
||||
(error.message || 'Unknown error')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,8 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Audio manager');
|
||||
|
||||
const resourceKinds: Array<ResourceKind> = ['audio'];
|
||||
|
||||
const HowlParameters: HowlOptions = {
|
||||
preload: true,
|
||||
onplayerror: (_, error) =>
|
||||
@@ -364,9 +366,8 @@ namespace gdjs {
|
||||
* of all sounds being played.
|
||||
*/
|
||||
export class HowlerSoundManager {
|
||||
_loadedMusics: Record<string, Howl> = {};
|
||||
_loadedSounds: Record<string, Howl> = {};
|
||||
_resources: Map<string, ResourceData>;
|
||||
_loadedMusics = new gdjs.ResourceCache<Howl>();
|
||||
_loadedSounds = new gdjs.ResourceCache<Howl>();
|
||||
_availableResources: Record<string, ResourceData> = {};
|
||||
_globalVolume: float = 100;
|
||||
_sounds: Record<integer, HowlerSound> = {};
|
||||
@@ -378,19 +379,14 @@ namespace gdjs {
|
||||
_pausedSounds: HowlerSound[] = [];
|
||||
_paused: boolean = false;
|
||||
|
||||
_resourcesLoader: RuntimeGameResourcesLoader;
|
||||
_resourceLoader: gdjs.ResourceLoader;
|
||||
|
||||
/**
|
||||
* @param resources The resources data of the game.
|
||||
* @param resourcesLoader The resources loader of the game.
|
||||
* @param resourceLoader The resources loader of the game.
|
||||
*/
|
||||
constructor(
|
||||
resourceDataArray: ResourceData[],
|
||||
resourcesLoader: RuntimeGameResourcesLoader
|
||||
) {
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this.setResources(resourceDataArray);
|
||||
this._resourcesLoader = resourcesLoader;
|
||||
constructor(resourceLoader: gdjs.ResourceLoader) {
|
||||
this._resourceLoader = resourceLoader;
|
||||
|
||||
const that = this;
|
||||
document.addEventListener('deviceready', function () {
|
||||
@@ -437,18 +433,8 @@ namespace gdjs {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param resources The resources data of the game.
|
||||
*/
|
||||
setResources(resourceDataArray: ResourceData[]): void {
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
if (resourceData.kind === 'audio') {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
}
|
||||
getResourceKinds(): ResourceKind[] {
|
||||
return resourceKinds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,17 +458,19 @@ namespace gdjs {
|
||||
* file is associated to the given name, then the name will be considered as a
|
||||
* filename and will be returned.
|
||||
*
|
||||
* @return The associated filename
|
||||
* @return The associated resource
|
||||
*/
|
||||
private _getFileFromSoundName(soundName: string): string {
|
||||
if (
|
||||
this._availableResources.hasOwnProperty(soundName) &&
|
||||
this._availableResources[soundName].file
|
||||
) {
|
||||
return this._availableResources[soundName].file;
|
||||
}
|
||||
return soundName;
|
||||
}
|
||||
private _getAudioResource = (resourceName: string): ResourceData => {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
return resource && this.getResourceKinds().includes(resource.kind)
|
||||
? resource
|
||||
: ({
|
||||
file: resourceName,
|
||||
kind: 'audio',
|
||||
metadata: '',
|
||||
name: resourceName,
|
||||
} as ResourceData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Store the sound in the specified array, put it at the first index that
|
||||
@@ -524,18 +512,20 @@ namespace gdjs {
|
||||
loop: boolean,
|
||||
rate: float
|
||||
): HowlerSound {
|
||||
const soundFile = this._getFileFromSoundName(soundName);
|
||||
const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
|
||||
const resource = this._getAudioResource(soundName);
|
||||
|
||||
if (!cacheContainer.hasOwnProperty(soundFile)) {
|
||||
cacheContainer[soundFile] = new Howl(
|
||||
let howl = cacheContainer.get(resource);
|
||||
if (!howl) {
|
||||
const fileName = resource ? resource.file : soundName;
|
||||
howl = new Howl(
|
||||
Object.assign(
|
||||
{
|
||||
src: [this._resourcesLoader.getFullUrl(soundFile)],
|
||||
src: [this._resourceLoader.getFullUrl(fileName)],
|
||||
html5: isMusic,
|
||||
xhr: {
|
||||
withCredentials: this._resourcesLoader.checkIfCredentialsRequired(
|
||||
soundFile
|
||||
withCredentials: this._resourceLoader.checkIfCredentialsRequired(
|
||||
fileName
|
||||
),
|
||||
},
|
||||
// Cache the sound with no volume. This avoids a bug where it plays at full volume
|
||||
@@ -545,14 +535,10 @@ namespace gdjs {
|
||||
HowlParameters
|
||||
)
|
||||
);
|
||||
cacheContainer.set(resource, howl);
|
||||
}
|
||||
|
||||
return new gdjs.HowlerSound(
|
||||
cacheContainer[soundFile],
|
||||
volume,
|
||||
loop,
|
||||
rate
|
||||
);
|
||||
return new gdjs.HowlerSound(howl, volume, loop, rate);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,27 +547,32 @@ namespace gdjs {
|
||||
* @param isMusic True if a music, false if a sound.
|
||||
*/
|
||||
loadAudio(soundName: string, isMusic: boolean) {
|
||||
const soundFile = this._getFileFromSoundName(soundName);
|
||||
const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
|
||||
const resource = this._getAudioResource(soundName);
|
||||
|
||||
// Do not reload if it is already loaded.
|
||||
if (cacheContainer.hasOwnProperty(soundFile)) return;
|
||||
if (cacheContainer.get(resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cacheContainer[soundFile] = new Howl(
|
||||
Object.assign(
|
||||
{
|
||||
src: [this._resourcesLoader.getFullUrl(soundFile)],
|
||||
html5: isMusic,
|
||||
xhr: {
|
||||
withCredentials: this._resourcesLoader.checkIfCredentialsRequired(
|
||||
soundFile
|
||||
),
|
||||
cacheContainer.set(
|
||||
resource,
|
||||
new Howl(
|
||||
Object.assign(
|
||||
{
|
||||
src: [this._resourceLoader.getFullUrl(resource.file)],
|
||||
html5: isMusic,
|
||||
xhr: {
|
||||
withCredentials: this._resourceLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
),
|
||||
},
|
||||
// Cache the sound with no volume. This avoids a bug where it plays at full volume
|
||||
// for a split second before setting its correct volume.
|
||||
volume: 0,
|
||||
},
|
||||
// Cache the sound with no volume. This avoids a bug where it plays at full volume
|
||||
// for a split second before setting its correct volume.
|
||||
volume: 0,
|
||||
},
|
||||
HowlParameters
|
||||
HowlParameters
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -592,15 +583,17 @@ namespace gdjs {
|
||||
* @param isMusic True if a music, false if a sound.
|
||||
*/
|
||||
unloadAudio(soundName: string, isMusic: boolean) {
|
||||
const soundFile = this._getFileFromSoundName(soundName);
|
||||
const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
|
||||
const resource = this._getAudioResource(soundName);
|
||||
|
||||
if (!cacheContainer[soundFile]) return;
|
||||
const howl = cacheContainer.get(resource);
|
||||
if (!howl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure any sound using the howl is deleted so
|
||||
// that the howl can be garbage collected
|
||||
// and no weird "zombies" using the unloaded howl can exist.
|
||||
const howl = cacheContainer[soundFile];
|
||||
function clearContainer(howlerSoundContainer: HowlerSound[]) {
|
||||
for (let i in howlerSoundContainer) {
|
||||
if (
|
||||
@@ -620,8 +613,8 @@ namespace gdjs {
|
||||
clearContainer(Object.values(this._sounds));
|
||||
clearContainer(this._pausedSounds);
|
||||
|
||||
cacheContainer[soundFile].unload();
|
||||
delete cacheContainer[soundFile];
|
||||
howl.unload();
|
||||
cacheContainer.delete(resource);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,8 +631,8 @@ namespace gdjs {
|
||||
this._sounds = {};
|
||||
this._musics = {};
|
||||
this._pausedSounds.length = 0;
|
||||
this._loadedMusics = {};
|
||||
this._loadedSounds = {};
|
||||
this._loadedMusics.clear();
|
||||
this._loadedSounds.clear();
|
||||
}
|
||||
|
||||
playSound(soundName: string, loop: boolean, volume: float, pitch: float) {
|
||||
@@ -763,31 +756,24 @@ namespace gdjs {
|
||||
this._pausedSounds.length = 0;
|
||||
}
|
||||
|
||||
async preloadAudio(
|
||||
onProgress: (loadedCount: integer, totalCount: integer) => void,
|
||||
resources?: ResourceData[]
|
||||
): Promise<integer> {
|
||||
// Construct the list of files to be loaded.
|
||||
// For one loaded file, it can have one or more resources
|
||||
// that use it.
|
||||
const files = {};
|
||||
for (const res of resources || this._resources.values()) {
|
||||
if (res.file) {
|
||||
if (!!this._availableResources[res.name]) {
|
||||
continue;
|
||||
}
|
||||
async processResource(resourceName: string): Promise<void> {
|
||||
// Do nothing because sounds are light enough to be parsed in background.
|
||||
}
|
||||
|
||||
this._availableResources[res.name] = res;
|
||||
|
||||
files[res.file] = (files[res.file] || []).concat(res);
|
||||
}
|
||||
async loadResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find audio for resource "' + resourceName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (resource.file) {
|
||||
if (this._availableResources[resource.name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filesToLoad = Object.keys(files);
|
||||
const totalCount = filesToLoad.length;
|
||||
if (totalCount === 0) {
|
||||
// Nothing to load.
|
||||
return 0;
|
||||
this._availableResources[resource.name] = resource;
|
||||
}
|
||||
|
||||
const preloadAudioFile = (
|
||||
@@ -798,12 +784,12 @@ namespace gdjs {
|
||||
const container = isMusic ? this._loadedMusics : this._loadedSounds;
|
||||
container[file] = new Howl(
|
||||
Object.assign({}, HowlParameters, {
|
||||
src: [this._resourcesLoader.getFullUrl(file)],
|
||||
src: [this._resourceLoader.getFullUrl(file)],
|
||||
onload: resolve,
|
||||
onloaderror: (soundId: number, error?: string) => reject(error),
|
||||
html5: isMusic,
|
||||
xhr: {
|
||||
withCredentials: this._resourcesLoader.checkIfCredentialsRequired(
|
||||
withCredentials: this._resourceLoader.checkIfCredentialsRequired(
|
||||
file
|
||||
),
|
||||
},
|
||||
@@ -815,58 +801,49 @@ namespace gdjs {
|
||||
});
|
||||
};
|
||||
|
||||
let loadedCount: integer = 0;
|
||||
await Promise.all(
|
||||
filesToLoad.map(async (file) => {
|
||||
const fileData = files[file][0];
|
||||
const file = resource.file;
|
||||
if (resource.preloadAsMusic) {
|
||||
try {
|
||||
await preloadAudioFile(file, /* isMusic= */ true);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'There was an error while preloading an audio file: ' + error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileData.preloadAsMusic) {
|
||||
try {
|
||||
await preloadAudioFile(file, /* isMusic= */ true);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'There was an error while preloading an audio file: ' + error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (fileData.preloadAsSound) {
|
||||
try {
|
||||
await preloadAudioFile(file, /* isMusic= */ false);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'There was an error while preloading an audio file: ' + error
|
||||
);
|
||||
}
|
||||
} else if (fileData.preloadInCache) {
|
||||
// preloading as sound already does a XHR request, hence "else if"
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const sound = new XMLHttpRequest();
|
||||
sound.withCredentials = this._resourcesLoader.checkIfCredentialsRequired(
|
||||
file
|
||||
);
|
||||
sound.addEventListener('load', resolve);
|
||||
sound.addEventListener('error', (_) =>
|
||||
reject('XHR error: ' + file)
|
||||
);
|
||||
sound.addEventListener('abort', (_) =>
|
||||
reject('XHR abort: ' + file)
|
||||
);
|
||||
sound.open('GET', this._resourcesLoader.getFullUrl(file));
|
||||
sound.send();
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'There was an error while preloading an audio file: ' + error
|
||||
);
|
||||
}
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, totalCount);
|
||||
})
|
||||
);
|
||||
return totalCount;
|
||||
if (resource.preloadAsSound) {
|
||||
try {
|
||||
await preloadAudioFile(file, /* isMusic= */ false);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'There was an error while preloading an audio file: ' + error
|
||||
);
|
||||
}
|
||||
} else if (resource.preloadInCache) {
|
||||
// preloading as sound already does a XHR request, hence "else if"
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
const sound = new XMLHttpRequest();
|
||||
sound.withCredentials = this._resourceLoader.checkIfCredentialsRequired(
|
||||
file
|
||||
);
|
||||
sound.addEventListener('load', resolve);
|
||||
sound.addEventListener('error', (_) =>
|
||||
reject('XHR error: ' + file)
|
||||
);
|
||||
sound.addEventListener('abort', (_) =>
|
||||
reject('XHR abort: ' + file)
|
||||
);
|
||||
sound.open('GET', this._resourceLoader.getFullUrl(file));
|
||||
sound.send();
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
'There was an error while preloading an audio file: ' + error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ namespace gdjs {
|
||||
content: Object | null
|
||||
) => void;
|
||||
|
||||
const resourceKinds: Array<ResourceKind> = ['json', 'tilemap', 'tileset'];
|
||||
/**
|
||||
* JsonManager loads json files (using `XMLHttpRequest`), using the "json" resources
|
||||
* registered in the game resources.
|
||||
@@ -20,42 +21,22 @@ namespace gdjs {
|
||||
* You should properly handle errors, and give the developer/player a way to know
|
||||
* that loading failed.
|
||||
*/
|
||||
export class JsonManager {
|
||||
_resourcesLoader: RuntimeGameResourcesLoader;
|
||||
_resources: Map<string, ResourceData>;
|
||||
export class JsonManager implements gdjs.ResourceManager {
|
||||
_resourceLoader: ResourceLoader;
|
||||
|
||||
_loadedJsons: { [key: string]: Object } = {};
|
||||
_callbacks: { [key: string]: Array<JsonManagerRequestCallback> } = {};
|
||||
_loadedJsons = new gdjs.ResourceCache<Object>();
|
||||
_callbacks = new gdjs.ResourceCache<Array<JsonManagerRequestCallback>>();
|
||||
|
||||
/**
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
* @param resourcesLoader The resources loader of the game.
|
||||
* @param resourceLoader The resources loader of the game.
|
||||
*/
|
||||
constructor(
|
||||
resourceDataArray: ResourceData[],
|
||||
resourcesLoader: RuntimeGameResourcesLoader
|
||||
) {
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this.setResources(resourceDataArray);
|
||||
this._resourcesLoader = resourcesLoader;
|
||||
constructor(resourceLoader: gdjs.ResourceLoader) {
|
||||
this._resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
*/
|
||||
setResources(resourceDataArray: ResourceData[]): void {
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
if (
|
||||
resourceData.kind === 'json' ||
|
||||
resourceData.kind === 'tilemap' ||
|
||||
resourceData.kind === 'tileset'
|
||||
) {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
}
|
||||
getResourceKinds(): ResourceKind[] {
|
||||
return resourceKinds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,32 +44,25 @@ namespace gdjs {
|
||||
*
|
||||
* Note that even if a JSON is already loaded, it will be reloaded (useful for hot-reloading,
|
||||
* as JSON files can have been modified without the editor knowing).
|
||||
*
|
||||
* @param onProgress The function called after each json is loaded.
|
||||
*/
|
||||
async preloadJsons(
|
||||
onProgress: (loadedCount: integer, totalCount: integer) => void
|
||||
): Promise<integer> {
|
||||
const preloadedResources = [...this._resources.values()].filter(
|
||||
(resource) => !resource.disablePreload
|
||||
);
|
||||
async loadResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn('Unable to find json for resource "' + resourceName + '".');
|
||||
return;
|
||||
}
|
||||
if (resource.disablePreload) {
|
||||
return;
|
||||
}
|
||||
|
||||
let loadedCount = 0;
|
||||
await Promise.all(
|
||||
preloadedResources.map(async (resource) => {
|
||||
try {
|
||||
await this.loadJsonAsync(resource.name);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Error while preloading json resource ${resource.name}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, this._resources.size);
|
||||
})
|
||||
);
|
||||
return loadedCount;
|
||||
try {
|
||||
await this.loadJsonAsync(resource.name);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Error while preloading json resource ${resource.name}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
loadJsonAsync(resourceName: string): Promise<Object | null> {
|
||||
@@ -103,6 +77,17 @@ namespace gdjs {
|
||||
});
|
||||
}
|
||||
|
||||
private _getJsonResource = (resourceName: string): ResourceData | null => {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
return resource && this.getResourceKinds().includes(resource.kind)
|
||||
? resource
|
||||
: null;
|
||||
};
|
||||
|
||||
async processResource(resourceName: string): Promise<void> {
|
||||
// Do nothing because json are light enough to be parsed in background.
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the json file from the given resource name.
|
||||
* This method is asynchronous. When loaded, the `callback` is called with the error
|
||||
@@ -112,7 +97,7 @@ namespace gdjs {
|
||||
* @param callback The callback function called when json is loaded (or an error occurred).
|
||||
*/
|
||||
loadJson(resourceName: string, callback: JsonManagerRequestCallback): void {
|
||||
const resource = this._resources.get(resourceName);
|
||||
const resource = this._getJsonResource(resourceName);
|
||||
if (!resource) {
|
||||
callback(
|
||||
new Error(
|
||||
@@ -126,29 +111,30 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// Don't fetch again an object that is already in memory
|
||||
if (this._loadedJsons[resourceName]) {
|
||||
callback(null, this._loadedJsons[resourceName]);
|
||||
if (this._loadedJsons.get(resource)) {
|
||||
callback(null, this._loadedJsons.get(resource));
|
||||
return;
|
||||
}
|
||||
// Don't fetch again an object that is already being fetched.
|
||||
{
|
||||
const callbacks = this._callbacks[resourceName];
|
||||
const callbacks = this._callbacks.get(resource);
|
||||
if (callbacks) {
|
||||
callbacks.push(callback);
|
||||
return;
|
||||
} else {
|
||||
this._callbacks[resourceName] = [callback];
|
||||
this._callbacks.set(resource, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
const that = this;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json';
|
||||
xhr.withCredentials = this._resourcesLoader.checkIfCredentialsRequired(
|
||||
xhr.withCredentials = this._resourceLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
);
|
||||
xhr.open('GET', this._resourcesLoader.getFullUrl(resource.file));
|
||||
xhr.open('GET', this._resourceLoader.getFullUrl(resource.file));
|
||||
xhr.onload = function () {
|
||||
const callbacks = that._callbacks[resourceName];
|
||||
const callbacks = that._callbacks.get(resource);
|
||||
if (!callbacks) {
|
||||
return;
|
||||
}
|
||||
@@ -161,36 +147,36 @@ namespace gdjs {
|
||||
null
|
||||
);
|
||||
}
|
||||
delete that._callbacks[resourceName];
|
||||
that._callbacks.delete(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
that._loadedJsons[resourceName] = xhr.response;
|
||||
that._loadedJsons.set(resource, xhr.response);
|
||||
for (const callback of callbacks) {
|
||||
callback(null, xhr.response);
|
||||
}
|
||||
delete that._callbacks[resourceName];
|
||||
that._callbacks.delete(resource);
|
||||
};
|
||||
xhr.onerror = function () {
|
||||
const callbacks = that._callbacks[resourceName];
|
||||
const callbacks = that._callbacks.get(resource);
|
||||
if (!callbacks) {
|
||||
return;
|
||||
}
|
||||
for (const callback of callbacks) {
|
||||
callback(new Error('Network error'), null);
|
||||
}
|
||||
delete that._callbacks[resourceName];
|
||||
that._callbacks.delete(resource);
|
||||
};
|
||||
xhr.onabort = function () {
|
||||
const callbacks = that._callbacks[resourceName];
|
||||
const callbacks = that._callbacks.get(resource);
|
||||
if (!callbacks) {
|
||||
return;
|
||||
}
|
||||
for (const callback of callbacks) {
|
||||
callback(new Error('Request aborted'), null);
|
||||
}
|
||||
delete that._callbacks[resourceName];
|
||||
that._callbacks.delete(resource);
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
@@ -201,7 +187,7 @@ namespace gdjs {
|
||||
* @returns true if the content of the json resource is loaded. false otherwise.
|
||||
*/
|
||||
isJsonLoaded(resourceName: string): boolean {
|
||||
return !!this._loadedJsons[resourceName];
|
||||
return !!this._loadedJsons.getFromName(resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +198,7 @@ namespace gdjs {
|
||||
* @returns the content of the json resource, if loaded. `null` otherwise.
|
||||
*/
|
||||
getLoadedJson(resourceName: string): Object | null {
|
||||
return this._loadedJsons[resourceName] || null;
|
||||
return this._loadedJsons.getFromName(resourceName) || null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@ namespace gdjs {
|
||||
class LoadingScreenPixiRenderer {
|
||||
_pixiRenderer: PIXI.Renderer | null;
|
||||
_loadingScreenData: LoadingScreenData;
|
||||
_isFirstLayout: boolean;
|
||||
|
||||
_loadingScreenContainer: PIXI.Container;
|
||||
_backgroundSprite: PIXI.Sprite | null = null;
|
||||
@@ -40,9 +41,11 @@ namespace gdjs {
|
||||
constructor(
|
||||
runtimeGamePixiRenderer: gdjs.RuntimeGamePixiRenderer,
|
||||
imageManager: gdjs.PixiImageManager,
|
||||
loadingScreenData: LoadingScreenData
|
||||
loadingScreenData: LoadingScreenData,
|
||||
isFirstScene: boolean
|
||||
) {
|
||||
this._loadingScreenData = loadingScreenData;
|
||||
this._isFirstLayout = isFirstScene;
|
||||
this._loadingScreenContainer = new PIXI.Container();
|
||||
this._pixiRenderer = runtimeGamePixiRenderer.getPIXIRenderer();
|
||||
if (!this._pixiRenderer) {
|
||||
@@ -55,7 +58,10 @@ namespace gdjs {
|
||||
const backgroundTexture = imageManager.getOrLoadPIXITexture(
|
||||
loadingScreenData.backgroundImageResourceName
|
||||
);
|
||||
if (backgroundTexture !== imageManager.getInvalidPIXITexture()) {
|
||||
if (
|
||||
backgroundTexture !== imageManager.getInvalidPIXITexture() &&
|
||||
isFirstScene
|
||||
) {
|
||||
this._backgroundSprite = PIXI.Sprite.from(backgroundTexture);
|
||||
this._backgroundSprite.alpha = 0;
|
||||
this._backgroundSprite.anchor.x = 0.5;
|
||||
@@ -63,7 +69,7 @@ namespace gdjs {
|
||||
this._loadingScreenContainer.addChild(this._backgroundSprite);
|
||||
}
|
||||
|
||||
if (loadingScreenData.showGDevelopSplash) {
|
||||
if (loadingScreenData.showGDevelopSplash && isFirstScene) {
|
||||
this._gdevelopLogoSprite = PIXI.Sprite.from(gdjs.gdevelopLogo);
|
||||
this._gdevelopLogoSprite.alpha = 0;
|
||||
this._gdevelopLogoSprite.anchor.x = 0.5;
|
||||
@@ -137,6 +143,22 @@ namespace gdjs {
|
||||
requestAnimationFrame(() => this._render(performance.now()));
|
||||
}
|
||||
|
||||
this._renderIfNeeded(timeInMs);
|
||||
}
|
||||
|
||||
renderIfNeeded(): boolean {
|
||||
return this._renderIfNeeded(performance.now());
|
||||
}
|
||||
|
||||
private _renderIfNeeded(timeInMs: float): boolean {
|
||||
if (timeInMs - this._lastFrameTimeInMs < 1000 / 60) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this._pixiRenderer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const deltaTimeInMs = this._lastFrameTimeInMs
|
||||
? timeInMs - this._lastFrameTimeInMs
|
||||
: 0;
|
||||
@@ -226,12 +248,14 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
this._pixiRenderer.render(this._loadingScreenContainer);
|
||||
return true;
|
||||
}
|
||||
|
||||
unload(): Promise<void> {
|
||||
const totalElapsedTime = (performance.now() - this._startTimeInMs) / 1000;
|
||||
const remainingTime =
|
||||
this._loadingScreenData.minDuration - totalElapsedTime;
|
||||
(this._isFirstLayout ? this._loadingScreenData.minDuration : 0) -
|
||||
totalElapsedTime;
|
||||
this.setPercent(100);
|
||||
|
||||
// Ensure we have shown the loading screen for at least minDuration.
|
||||
|
@@ -33,13 +33,14 @@ namespace gdjs {
|
||||
return PIXI.BitmapFont.available[bitmapFontInstallKey];
|
||||
};
|
||||
|
||||
const resourceKinds: Array<ResourceKind> = ['bitmapFont'];
|
||||
|
||||
/**
|
||||
* PixiBitmapFontManager loads fnt/xml files (using `fetch`), from the "bitmapFont" resources of the game.
|
||||
*
|
||||
* It installs the "BitmapFont" with PixiJS to be used with PIXI.BitmapText.
|
||||
*/
|
||||
export class PixiBitmapFontManager {
|
||||
_resources: Map<string, ResourceData>;
|
||||
export class PixiBitmapFontManager implements gdjs.ResourceManager {
|
||||
private _imageManager: gdjs.PixiImageManager;
|
||||
|
||||
/** Pixi.BitmapFont used, indexed by their BitmapFont name. */
|
||||
@@ -52,26 +53,27 @@ namespace gdjs {
|
||||
private _pixiBitmapFontsToUninstall: string[] = [];
|
||||
|
||||
/** Loaded fonts data, indexed by resource name. */
|
||||
private _loadedFontsData: Record<string, any> = {};
|
||||
private _loadedFontsData = new gdjs.ResourceCache<any>();
|
||||
|
||||
private _defaultSlugFontName: string | null = null;
|
||||
|
||||
_resourcesLoader: RuntimeGameResourcesLoader;
|
||||
_resourceLoader: gdjs.ResourceLoader;
|
||||
|
||||
/**
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
* @param resourcesLoader The resources loader of the game.
|
||||
* @param resourceLoader The resources loader of the game.
|
||||
* @param imageManager The image manager to be used to get textures used by fonts.
|
||||
*/
|
||||
constructor(
|
||||
resourceDataArray: ResourceData[],
|
||||
resourcesLoader: RuntimeGameResourcesLoader,
|
||||
resourceLoader: gdjs.ResourceLoader,
|
||||
imageManager: gdjs.PixiImageManager
|
||||
) {
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this.setResources(resourceDataArray);
|
||||
this._imageManager = imageManager;
|
||||
this._resourcesLoader = resourcesLoader;
|
||||
this._resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
getResourceKinds(): ResourceKind[] {
|
||||
return resourceKinds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,19 +110,6 @@ namespace gdjs {
|
||||
return defaultBitmapFont;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
* @param resourceDataArray The resources data of the game.
|
||||
*/
|
||||
setResources(resourceDataArray: ResourceData[]): void {
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
if (resourceData.kind === 'bitmapFont') {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to specify that the bitmap font with the specified key is used by an object
|
||||
* (i.e: this is reference counting).
|
||||
@@ -219,7 +208,9 @@ namespace gdjs {
|
||||
// The Bitmap Font is not loaded, load it in memory.
|
||||
|
||||
// First get the font data:
|
||||
const fontData = this._loadedFontsData[bitmapFontResourceName];
|
||||
const fontData = this._loadedFontsData.getFromName(
|
||||
bitmapFontResourceName
|
||||
);
|
||||
if (!fontData) {
|
||||
logger.warn(
|
||||
'Could not find Bitmap Font for resource named "' +
|
||||
@@ -253,49 +244,50 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
async processResource(resourceName: string): Promise<void> {
|
||||
// Do nothing because fonts are light enough to be parsed in background.
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the "bitmapFont" resources of the game, so that they are ready
|
||||
* to be used when `obtainBitmapFont` is called.
|
||||
*/
|
||||
async loadBitmapFontData(
|
||||
onProgress: (count: integer, total: integer) => void
|
||||
): Promise<integer> {
|
||||
const preloadedResources = [...this._resources.values()].filter(
|
||||
(resource) => !resource.disablePreload
|
||||
);
|
||||
async loadResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find bitmap font for resource "' + resourceName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (this._loadedFontsData.get(resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let loadedCount = 0;
|
||||
await Promise.all(
|
||||
preloadedResources.map(async (bitmapFontResource) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
this._resourcesLoader.getFullUrl(bitmapFontResource.file),
|
||||
{
|
||||
credentials: this._resourcesLoader.checkIfCredentialsRequired(
|
||||
bitmapFontResource.file
|
||||
)
|
||||
? // Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
|
||||
// i.e: its gdevelop.io cookie, to be passed.
|
||||
'include'
|
||||
: // For other resources, use "same-origin" as done by default by fetch.
|
||||
'same-origin',
|
||||
}
|
||||
);
|
||||
const fontData = await response.text();
|
||||
this._loadedFontsData[bitmapFontResource.name] = fontData;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"Can't fetch the bitmap font file " +
|
||||
bitmapFontResource.file +
|
||||
', error: ' +
|
||||
error
|
||||
);
|
||||
try {
|
||||
const response = await fetch(
|
||||
this._resourceLoader.getFullUrl(resource.file),
|
||||
{
|
||||
credentials: this._resourceLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
)
|
||||
? // Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
|
||||
// i.e: its gdevelop.io cookie, to be passed.
|
||||
'include'
|
||||
: // For other resources, use "same-origin" as done by default by fetch.
|
||||
'same-origin',
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, preloadedResources.length);
|
||||
})
|
||||
);
|
||||
return loadedCount;
|
||||
);
|
||||
const fontData = await response.text();
|
||||
this._loadedFontsData.set(resource, fontData);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
"Can't fetch the bitmap font file " +
|
||||
resource.file +
|
||||
', error: ' +
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -34,21 +34,12 @@ namespace gdjs {
|
||||
}
|
||||
};
|
||||
|
||||
const findResourceWithNameAndKind = (
|
||||
resources: Map<string, ResourceData>,
|
||||
resourceName: string,
|
||||
kind: ResourceKind
|
||||
): ResourceData | null => {
|
||||
const resource = resources.get(resourceName);
|
||||
return resource && resource.kind === kind ? resource : null;
|
||||
};
|
||||
const resourceKinds: Array<ResourceKind> = ['image', 'video'];
|
||||
|
||||
/**
|
||||
* PixiImageManager loads and stores textures that can be used by the Pixi.js renderers.
|
||||
*/
|
||||
export class PixiImageManager {
|
||||
_resources: Map<string, ResourceData>;
|
||||
|
||||
export class PixiImageManager implements gdjs.ResourceManager {
|
||||
/**
|
||||
* The invalid texture is a 8x8 PNG file filled with magenta (#ff00ff), to be
|
||||
* easily spotted if rendered on screen.
|
||||
@@ -58,7 +49,7 @@ namespace gdjs {
|
||||
/**
|
||||
* Map associating a resource name to the loaded PixiJS texture.
|
||||
*/
|
||||
private _loadedTextures: Hashtable<PIXI.Texture<PIXI.Resource>>;
|
||||
private _loadedTextures = new gdjs.ResourceCache<PIXI.Texture>();
|
||||
|
||||
/**
|
||||
* Map associating a resource name to the loaded Three.js texture.
|
||||
@@ -66,39 +57,23 @@ namespace gdjs {
|
||||
private _loadedThreeTextures: Hashtable<THREE.Texture>;
|
||||
private _loadedThreeMaterials: Hashtable<THREE.Material>;
|
||||
|
||||
private _resourcesLoader: RuntimeGameResourcesLoader;
|
||||
private _resourceLoader: gdjs.ResourceLoader;
|
||||
|
||||
/**
|
||||
* @param resources The resources data of the game.
|
||||
* @param resourcesLoader The resources loader of the game.
|
||||
* @param resourceLoader The resources loader of the game.
|
||||
*/
|
||||
constructor(
|
||||
resourceDataArray: ResourceData[],
|
||||
resourcesLoader: RuntimeGameResourcesLoader
|
||||
) {
|
||||
this._resources = new Map<string, ResourceData>();
|
||||
this.setResources(resourceDataArray);
|
||||
this._resourcesLoader = resourcesLoader;
|
||||
constructor(resourceLoader: gdjs.ResourceLoader) {
|
||||
this._resourceLoader = resourceLoader;
|
||||
this._invalidTexture = PIXI.Texture.from(
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFElEQVQoU2P8z/D/PwMewDgyFAAApMMX8Zi0uXAAAAAASUVORK5CYIIA'
|
||||
);
|
||||
this._loadedTextures = new Hashtable();
|
||||
this._loadedThreeTextures = new Hashtable();
|
||||
this._loadedThreeMaterials = new Hashtable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the resources data of the game. Useful for hot-reloading, should not be used otherwise.
|
||||
*
|
||||
* @param resources The resources data of the game.
|
||||
*/
|
||||
setResources(resourceDataArray: ResourceData[]): void {
|
||||
this._resources.clear();
|
||||
for (const resourceData of resourceDataArray) {
|
||||
if (resourceData.kind === 'image' || resourceData.kind === 'video') {
|
||||
this._resources.set(resourceData.name, resourceData);
|
||||
}
|
||||
}
|
||||
getResourceKinds(): ResourceKind[] {
|
||||
return resourceKinds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,19 +83,28 @@ namespace gdjs {
|
||||
* @returns The requested texture, or a placeholder if not found.
|
||||
*/
|
||||
getPIXITexture(resourceName: string): PIXI.Texture {
|
||||
if (this._loadedTextures.containsKey(resourceName)) {
|
||||
const texture = this._loadedTextures.get(resourceName);
|
||||
if (texture.valid) {
|
||||
return texture;
|
||||
} else {
|
||||
logger.error(
|
||||
'Texture for ' +
|
||||
resourceName +
|
||||
' is not valid anymore (or never was).'
|
||||
);
|
||||
}
|
||||
const resource = this._getImageResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return this._invalidTexture;
|
||||
}
|
||||
return this._invalidTexture;
|
||||
|
||||
const existingTexture = this._loadedTextures.get(resource);
|
||||
if (!existingTexture) {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
if (!existingTexture.valid) {
|
||||
logger.error(
|
||||
'Texture for ' +
|
||||
resourceName +
|
||||
' is not valid anymore (or never was).'
|
||||
);
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
return existingTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,10 +116,18 @@ namespace gdjs {
|
||||
* @returns The requested texture, or a placeholder if not valid.
|
||||
*/
|
||||
getOrLoadPIXITexture(resourceName: string): PIXI.Texture {
|
||||
if (this._loadedTextures.containsKey(resourceName)) {
|
||||
const texture = this._loadedTextures.get(resourceName);
|
||||
if (texture.valid) {
|
||||
return texture;
|
||||
const resource = this._getImageResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
const existingTexture = this._loadedTextures.get(resource);
|
||||
if (existingTexture) {
|
||||
if (existingTexture.valid) {
|
||||
return existingTexture;
|
||||
} else {
|
||||
logger.error(
|
||||
'Texture for ' +
|
||||
@@ -146,29 +138,15 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
// Texture is not loaded, load it now from the resources list.
|
||||
const resource = findResourceWithNameAndKind(
|
||||
this._resources,
|
||||
resourceName,
|
||||
'image'
|
||||
);
|
||||
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
logger.log('Loading texture for resource "' + resourceName + '"...');
|
||||
const file = resource.file;
|
||||
const url = this._resourcesLoader.getFullUrl(file);
|
||||
const url = this._resourceLoader.getFullUrl(file);
|
||||
const texture = PIXI.Texture.from(url, {
|
||||
resourceOptions: {
|
||||
// Note that using `false`
|
||||
// to not having `crossorigin` at all would NOT work because the browser would taint the
|
||||
// loaded resource so that it can't be read/used in a canvas (it's only working for display `<img>` on screen).
|
||||
crossorigin: this._resourcesLoader.checkIfCredentialsRequired(file)
|
||||
crossorigin: this._resourceLoader.checkIfCredentialsRequired(file)
|
||||
? 'use-credentials'
|
||||
: 'anonymous',
|
||||
},
|
||||
@@ -185,7 +163,7 @@ namespace gdjs {
|
||||
}
|
||||
applyTextureSettings(texture, resource);
|
||||
|
||||
this._loadedTextures.put(resourceName, texture);
|
||||
this._loadedTextures.set(resource, texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -197,13 +175,15 @@ namespace gdjs {
|
||||
*/
|
||||
getThreeTexture(resourceName: string): THREE.Texture {
|
||||
const loadedThreeTexture = this._loadedThreeTextures.get(resourceName);
|
||||
if (loadedThreeTexture) return loadedThreeTexture;
|
||||
if (loadedThreeTexture) {
|
||||
return loadedThreeTexture;
|
||||
}
|
||||
|
||||
// Texture is not loaded, load it now from the PixiJS texture.
|
||||
// TODO (3D) - optimization: don't load the PixiJS Texture if not used by PixiJS.
|
||||
// TODO (3D) - optimization: Ideally we could even share the same WebGL texture.
|
||||
const pixiTexture = this.getPIXITexture(resourceName);
|
||||
const pixiRenderer = this._resourcesLoader._runtimeGame
|
||||
const pixiRenderer = this._resourceLoader._runtimeGame
|
||||
.getRenderer()
|
||||
.getPIXIRenderer();
|
||||
if (!pixiRenderer) throw new Error('No PIXI renderer was found.');
|
||||
@@ -224,11 +204,7 @@ namespace gdjs {
|
||||
threeTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
threeTexture.needsUpdate = true;
|
||||
|
||||
const resource = findResourceWithNameAndKind(
|
||||
this._resources,
|
||||
resourceName,
|
||||
'image'
|
||||
);
|
||||
const resource = this._getImageResource(resourceName);
|
||||
|
||||
applyThreeTextureSettings(threeTexture, resource);
|
||||
this._loadedThreeTextures.put(resourceName, threeTexture);
|
||||
@@ -278,12 +254,31 @@ namespace gdjs {
|
||||
* @param resourceName The name of the resource to get.
|
||||
*/
|
||||
getPIXIVideoTexture(resourceName: string) {
|
||||
if (this._loadedTextures.containsKey(resourceName)) {
|
||||
return this._loadedTextures.get(resourceName);
|
||||
if (resourceName === '') {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
return this._invalidTexture;
|
||||
const resource = this._getImageResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find video texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
const texture = this._loadedTextures.get(resource);
|
||||
if (!texture) {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
private _getImageResource = (resourceName: string): ResourceData | null => {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
return resource && this.getResourceKinds().includes(resource.kind)
|
||||
? resource
|
||||
: null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a PIXI texture which can be used as a placeholder when no
|
||||
* suitable texture can be found.
|
||||
@@ -292,87 +287,95 @@ namespace gdjs {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified resources, so that textures are loaded and can then be
|
||||
* used by calling `getPIXITexture`.
|
||||
*/
|
||||
async loadResource(resourceName: string): Promise<void> {
|
||||
const resource = this._resourceLoader.getResource(resourceName);
|
||||
if (!resource) {
|
||||
logger.warn(
|
||||
'Unable to find texture for resource "' + resourceName + '".'
|
||||
);
|
||||
return;
|
||||
}
|
||||
await this._loadTexture(resource);
|
||||
}
|
||||
|
||||
async processResource(resourceName: string): Promise<void> {
|
||||
// Do nothing because images are light enough to be parsed in background.
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the specified resources, so that textures are loaded and can then be
|
||||
* used by calling `getPIXITexture`.
|
||||
* @param onProgress Callback called each time a new file is loaded.
|
||||
*/
|
||||
async loadTextures(
|
||||
onProgress: (loadingCount: integer, totalCount: integer) => void
|
||||
): Promise<integer> {
|
||||
let loadedCount = 0;
|
||||
await Promise.all(
|
||||
[...this._resources.values()].map(async (resource) => {
|
||||
try {
|
||||
if (resource.kind === 'video') {
|
||||
// For videos, we want to preload them so they are available as soon as we want to use them.
|
||||
// We cannot use Pixi.assets.load() as it does not allow passing options (autoplay) to the resource loader.
|
||||
// Pixi.Texture.from() does not return a promise, so we need to ensure we look at the 'loaded' event of the baseTexture,
|
||||
// to continue, otherwise if we try to play the video too soon (at the beginning of scene for instance),
|
||||
// it will fail.
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const texture = PIXI.Texture.from(
|
||||
this._resourcesLoader.getFullUrl(resource.file),
|
||||
{
|
||||
resourceOptions: {
|
||||
crossorigin: this._resourcesLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
)
|
||||
? 'use-credentials'
|
||||
: 'anonymous',
|
||||
autoPlay: false,
|
||||
},
|
||||
}
|
||||
).on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
async _loadTexture(resource: ResourceData): Promise<void> {
|
||||
if (this._loadedTextures.get(resource)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (resource.kind === 'video') {
|
||||
// For videos, we want to preload them so they are available as soon as we want to use them.
|
||||
// We cannot use Pixi.assets.load() as it does not allow passing options (autoplay) to the resource loader.
|
||||
// Pixi.Texture.from() does not return a promise, so we need to ensure we look at the 'loaded' event of the baseTexture,
|
||||
// to continue, otherwise if we try to play the video too soon (at the beginning of scene for instance),
|
||||
// it will fail.
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const texture = PIXI.Texture.from(
|
||||
this._resourceLoader.getFullUrl(resource.file),
|
||||
{
|
||||
resourceOptions: {
|
||||
crossorigin: this._resourceLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
)
|
||||
? 'use-credentials'
|
||||
: 'anonymous',
|
||||
autoPlay: false,
|
||||
},
|
||||
}
|
||||
).on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
|
||||
const baseTexture = texture.baseTexture;
|
||||
const baseTexture = texture.baseTexture;
|
||||
|
||||
baseTexture
|
||||
.on('loaded', () => {
|
||||
this._loadedTextures.put(resource.name, texture);
|
||||
applyTextureSettings(texture, resource);
|
||||
resolve();
|
||||
})
|
||||
.on('error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// If the file has no extension, PIXI.assets.load cannot find
|
||||
// an adequate load parser and does not load the file although
|
||||
// we would like to force it to load (we are confident it's an image).
|
||||
// TODO: When PIXI v8+ is used, PIXI.Assets.load can be used because
|
||||
// loadParser can be forced in PIXI.Assets.load
|
||||
// (see https://github.com/pixijs/pixijs/blob/71ed56c569ebc6b53da19e3c49258a0a84892101/packages/assets/src/loader/Loader.ts#L68)
|
||||
const loadedTexture = PIXI.Texture.from(
|
||||
this._resourcesLoader.getFullUrl(resource.file),
|
||||
{
|
||||
resourceOptions: {
|
||||
autoLoad: false,
|
||||
crossorigin: this._resourcesLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
)
|
||||
? 'use-credentials'
|
||||
: 'anonymous',
|
||||
},
|
||||
}
|
||||
);
|
||||
await loadedTexture.baseTexture.resource.load();
|
||||
|
||||
this._loadedTextures.put(resource.name, loadedTexture);
|
||||
// TODO What if 2 assets share the same file with different settings?
|
||||
applyTextureSettings(loadedTexture, resource);
|
||||
baseTexture.on('loaded', () => {
|
||||
this._loadedTextures.set(resource, texture);
|
||||
applyTextureSettings(texture, resource);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// If the file has no extension, PIXI.assets.load cannot find
|
||||
// an adequate load parser and does not load the file although
|
||||
// we would like to force it to load (we are confident it's an image).
|
||||
// TODO: When PIXI v8+ is used, PIXI.Assets.load can be used because
|
||||
// loadParser can be forced in PIXI.Assets.load
|
||||
// (see https://github.com/pixijs/pixijs/blob/71ed56c569ebc6b53da19e3c49258a0a84892101/packages/assets/src/loader/Loader.ts#L68)
|
||||
const loadedTexture = PIXI.Texture.from(
|
||||
this._resourceLoader.getFullUrl(resource.file),
|
||||
{
|
||||
resourceOptions: {
|
||||
autoLoad: false,
|
||||
crossorigin: this._resourceLoader.checkIfCredentialsRequired(
|
||||
resource.file
|
||||
)
|
||||
? 'use-credentials'
|
||||
: 'anonymous',
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
logFileLoadingError(resource.file, error);
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, this._resources.size);
|
||||
})
|
||||
);
|
||||
return loadedCount;
|
||||
);
|
||||
await loadedTexture.baseTexture.resource.load();
|
||||
|
||||
this._loadedTextures.set(resource, loadedTexture);
|
||||
// TODO What if 2 assets share the same file with different settings?
|
||||
applyTextureSettings(loadedTexture, resource);
|
||||
}
|
||||
} catch (error) {
|
||||
logFileLoadingError(resource.file, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -184,15 +184,18 @@ namespace gdjs {
|
||||
return this._sprite.texture.frame.height;
|
||||
}
|
||||
|
||||
static getAnimationFrame(imageManager, imageName) {
|
||||
static getAnimationFrame(
|
||||
imageManager: gdjs.PixiImageManager,
|
||||
imageName: string
|
||||
) {
|
||||
return imageManager.getPIXITexture(imageName);
|
||||
}
|
||||
|
||||
static getAnimationFrameWidth(pixiTexture) {
|
||||
static getAnimationFrameWidth(pixiTexture: PIXI.Texture) {
|
||||
return pixiTexture.width;
|
||||
}
|
||||
|
||||
static getAnimationFrameHeight(pixiTexture) {
|
||||
static getAnimationFrameHeight(pixiTexture: PIXI.Texture) {
|
||||
return pixiTexture.height;
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,9 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Game manager');
|
||||
|
||||
const sleep = (ms: float) =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/** Identify a script file, with its content hash (useful for hot-reloading). */
|
||||
export type RuntimeGameOptionsScriptFile = {
|
||||
/** The path for this script file. */
|
||||
@@ -14,6 +17,9 @@ namespace gdjs {
|
||||
hash: number;
|
||||
};
|
||||
|
||||
const getGlobalResourceNames = (projectData: ProjectData): Array<string> =>
|
||||
projectData.usedResources.map((resource) => resource.name);
|
||||
|
||||
/** Options given to the game at startup. */
|
||||
export type RuntimeGameOptions = {
|
||||
/** if true, force fullscreen. */
|
||||
@@ -63,86 +69,15 @@ namespace gdjs {
|
||||
environment?: 'dev';
|
||||
};
|
||||
|
||||
const addSearchParameterToUrl = (
|
||||
url: string,
|
||||
urlEncodedParameterName: string,
|
||||
urlEncodedValue: string
|
||||
) => {
|
||||
if (url.startsWith('data:') || url.startsWith('blob:')) {
|
||||
// blob/data protocol does not support search parameters, which are useless anyway.
|
||||
return url;
|
||||
}
|
||||
|
||||
const separator = url.indexOf('?') === -1 ? '?' : '&';
|
||||
return url + separator + urlEncodedParameterName + '=' + urlEncodedValue;
|
||||
};
|
||||
|
||||
const checkIfIsGDevelopCloudBucketUrl = (url: string): boolean => {
|
||||
return (
|
||||
url.startsWith('https://project-resources.gdevelop.io/') ||
|
||||
url.startsWith('https://project-resources-dev.gdevelop.io/')
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gives helper methods used when resources are loaded from an URL.
|
||||
*/
|
||||
export class RuntimeGameResourcesLoader {
|
||||
_runtimeGame: RuntimeGame;
|
||||
|
||||
constructor(runtimeGame: RuntimeGame) {
|
||||
this._runtimeGame = runtimeGame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the given URL with any specific parameter required to access
|
||||
* the resource (this can be for example a token needed to access the resource).
|
||||
*/
|
||||
getFullUrl(url: string) {
|
||||
const { gdevelopResourceToken } = this._runtimeGame._options;
|
||||
if (!gdevelopResourceToken) return url;
|
||||
|
||||
if (!checkIfIsGDevelopCloudBucketUrl(url)) return url;
|
||||
|
||||
return addSearchParameterToUrl(
|
||||
url,
|
||||
'gd_resource_token',
|
||||
encodeURIComponent(gdevelopResourceToken)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the specified URL must be loaded with cookies ("credentials")
|
||||
* sent to grant access to them.
|
||||
*/
|
||||
checkIfCredentialsRequired(url: string) {
|
||||
if (this._runtimeGame._options.gdevelopResourceToken) return false;
|
||||
|
||||
// Any resource stored on the GDevelop Cloud buckets needs the "credentials" of the user,
|
||||
// i.e: its gdevelop.io cookie, to be passed.
|
||||
// Note that this is only useful during previews.
|
||||
if (checkIfIsGDevelopCloudBucketUrl(url)) return true;
|
||||
|
||||
// For other resources, use the default way of loading resources ("anonymous" or "same-site").
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a game being played.
|
||||
*/
|
||||
export class RuntimeGame {
|
||||
_resourcesLoader: RuntimeGameResourcesLoader;
|
||||
_resourcesLoader: gdjs.ResourceLoader;
|
||||
_variables: VariablesContainer;
|
||||
_data: ProjectData;
|
||||
_eventsBasedObjectDatas: Map<String, EventsBasedObjectData>;
|
||||
_imageManager: ImageManager;
|
||||
_soundManager: SoundManager;
|
||||
_fontManager: FontManager;
|
||||
_jsonManager: JsonManager;
|
||||
_model3DManager: Model3DManager;
|
||||
_effectsManager: EffectsManager;
|
||||
_bitmapFontManager: BitmapFontManager;
|
||||
_maxFPS: integer;
|
||||
_minFPS: integer;
|
||||
_gameResolutionWidth: integer;
|
||||
@@ -210,33 +145,12 @@ namespace gdjs {
|
||||
this._options = options || {};
|
||||
this._variables = new gdjs.VariablesContainer(data.variables);
|
||||
this._data = data;
|
||||
this._resourcesLoader = new gdjs.RuntimeGameResourcesLoader(this);
|
||||
|
||||
const resources = this._data.resources.resources;
|
||||
this._imageManager = new gdjs.ImageManager(
|
||||
resources,
|
||||
this._resourcesLoader
|
||||
);
|
||||
this._soundManager = new gdjs.SoundManager(
|
||||
resources,
|
||||
this._resourcesLoader
|
||||
);
|
||||
this._fontManager = new gdjs.FontManager(
|
||||
resources,
|
||||
this._resourcesLoader
|
||||
);
|
||||
this._jsonManager = new gdjs.JsonManager(
|
||||
resources,
|
||||
this._resourcesLoader
|
||||
);
|
||||
this._bitmapFontManager = new gdjs.BitmapFontManager(
|
||||
resources,
|
||||
this._resourcesLoader,
|
||||
this._imageManager
|
||||
);
|
||||
this._model3DManager = new gdjs.Model3DManager(
|
||||
resources,
|
||||
this._resourcesLoader
|
||||
this._resourcesLoader = new gdjs.ResourceLoader(
|
||||
this,
|
||||
data.resources.resources,
|
||||
getGlobalResourceNames(data),
|
||||
data.layouts
|
||||
);
|
||||
this._effectsManager = new gdjs.EffectsManager();
|
||||
this._maxFPS = this._data.properties.maxFPS;
|
||||
@@ -315,12 +229,11 @@ namespace gdjs {
|
||||
*/
|
||||
setProjectData(projectData: ProjectData): void {
|
||||
this._data = projectData;
|
||||
this._imageManager.setResources(this._data.resources.resources);
|
||||
this._soundManager.setResources(this._data.resources.resources);
|
||||
this._fontManager.setResources(this._data.resources.resources);
|
||||
this._jsonManager.setResources(this._data.resources.resources);
|
||||
this._bitmapFontManager.setResources(this._data.resources.resources);
|
||||
this._model3DManager.setResources(this._data.resources.resources);
|
||||
this._resourcesLoader.setResources(
|
||||
projectData.resources.resources,
|
||||
getGlobalResourceNames(projectData),
|
||||
projectData.layouts
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +261,7 @@ namespace gdjs {
|
||||
* @return The sound manager.
|
||||
*/
|
||||
getSoundManager(): gdjs.HowlerSoundManager {
|
||||
return this._soundManager;
|
||||
return this._resourcesLoader.getSoundManager();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,7 +269,7 @@ namespace gdjs {
|
||||
* @return The image manager.
|
||||
*/
|
||||
getImageManager(): gdjs.PixiImageManager {
|
||||
return this._imageManager;
|
||||
return this._resourcesLoader.getImageManager();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +277,7 @@ namespace gdjs {
|
||||
* @return The font manager.
|
||||
*/
|
||||
getFontManager(): gdjs.FontFaceObserverFontManager {
|
||||
return this._fontManager;
|
||||
return this._resourcesLoader.getFontManager();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,8 +285,25 @@ namespace gdjs {
|
||||
* @return The bitmap font manager.
|
||||
*/
|
||||
getBitmapFontManager(): gdjs.BitmapFontManager {
|
||||
// @ts-ignore
|
||||
return this._bitmapFontManager;
|
||||
return this._resourcesLoader.getBitmapFontManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON manager of the game, used to load JSON from game
|
||||
* resources.
|
||||
* @return The json manager for the game
|
||||
*/
|
||||
getJsonManager(): gdjs.JsonManager {
|
||||
return this._resourcesLoader.getJsonManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 3D model manager of the game, used to load 3D model from game
|
||||
* resources.
|
||||
* @return The 3D model manager for the game
|
||||
*/
|
||||
getModel3DManager(): gdjs.Model3DManager {
|
||||
return this._resourcesLoader.getModel3DManager();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,24 +315,6 @@ namespace gdjs {
|
||||
return this._inputManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON manager of the game, used to load JSON from game
|
||||
* resources.
|
||||
* @return The json manager for the game
|
||||
*/
|
||||
getJsonManager(): gdjs.JsonManager {
|
||||
return this._jsonManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 3D model manager of the game, used to load 3D model from game
|
||||
* resources.
|
||||
* @return The 3D model manager for the game
|
||||
*/
|
||||
getModel3DManager(): gdjs.Model3DManager {
|
||||
return this._model3DManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effects manager of the game, which allows to manage
|
||||
* effects on runtime objects or runtime layers.
|
||||
@@ -681,54 +593,151 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all assets, displaying progress in renderer.
|
||||
* Preload a scene assets as soon as possible in background.
|
||||
*/
|
||||
prioritizeLoadingOfScene(sceneName: string) {
|
||||
// Don't await the scene assets to be loaded.
|
||||
this._resourcesLoader.loadSceneResources(sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The progress of assets loading in background for a scene
|
||||
* (between 0 and 1).
|
||||
*/
|
||||
getSceneLoadingProgress(sceneName: string): number {
|
||||
return this._resourcesLoader.getSceneLoadingProgress(sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true when all the resources of the given scene are loaded
|
||||
* (but maybe not parsed).
|
||||
*/
|
||||
areSceneAssetsLoaded(sceneName: string): boolean {
|
||||
return this._resourcesLoader.areSceneAssetsLoaded(sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true when all the resources of the given scene are loaded and
|
||||
* parsed.
|
||||
*/
|
||||
areSceneAssetsReady(sceneName: string): boolean {
|
||||
return this._resourcesLoader.areSceneAssetsReady(sceneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all assets needed to display the 1st scene, displaying progress in
|
||||
* renderer.
|
||||
*/
|
||||
loadAllAssets(
|
||||
callback: () => void,
|
||||
progressCallback?: (progress: float) => void
|
||||
) {
|
||||
this.loadAllAssetsAsync(progressCallback).then(callback);
|
||||
this.loadFirstAssetsAndStartBackgroundLoading(
|
||||
this._getFirstSceneName(),
|
||||
progressCallback
|
||||
).then(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all assets, displaying progress in renderer.
|
||||
* Load all assets needed to display the 1st scene, displaying progress in
|
||||
* renderer.
|
||||
*
|
||||
* When a game is hot-reload, this method can be called with the current
|
||||
* scene.
|
||||
*/
|
||||
loadAllAssetsAsync = async (
|
||||
async loadFirstAssetsAndStartBackgroundLoading(
|
||||
firstSceneName: string,
|
||||
progressCallback?: (progress: float) => void
|
||||
) => {
|
||||
): Promise<void> {
|
||||
try {
|
||||
const loadingScreen = new gdjs.LoadingScreenRenderer(
|
||||
this.getRenderer(),
|
||||
this._imageManager,
|
||||
this._data.properties.loadingScreen
|
||||
await this._loadAssetsWithLoadingScreen(
|
||||
/* isFirstScene = */ true,
|
||||
async (onProgress) => {
|
||||
// TODO Is a setting needed?
|
||||
if (false) {
|
||||
await this._resourcesLoader.loadAllResources(onProgress);
|
||||
} else {
|
||||
await this._resourcesLoader.loadGlobalAndFirstSceneResources(
|
||||
firstSceneName,
|
||||
onProgress
|
||||
);
|
||||
// Don't await as it must not block the first scene from starting.
|
||||
this._resourcesLoader.loadAllSceneInBackground();
|
||||
}
|
||||
},
|
||||
progressCallback
|
||||
);
|
||||
const allAssetsTotal = this._data.resources.resources.length;
|
||||
let loadedAssets = 0;
|
||||
|
||||
const onProgress = (count: integer, total: integer) => {
|
||||
const percent = Math.floor(
|
||||
(100 * (loadedAssets + count)) / allAssetsTotal
|
||||
);
|
||||
loadingScreen.setPercent(percent);
|
||||
if (progressCallback) {
|
||||
progressCallback(percent);
|
||||
}
|
||||
};
|
||||
|
||||
loadedAssets += await this._imageManager.loadTextures(onProgress);
|
||||
loadedAssets += await this._soundManager.preloadAudio(onProgress);
|
||||
loadedAssets += await this._fontManager.loadFonts(onProgress);
|
||||
loadedAssets += await this._jsonManager.preloadJsons(onProgress);
|
||||
loadedAssets += await this._model3DManager.loadModels(onProgress);
|
||||
await this._bitmapFontManager.loadBitmapFontData(onProgress);
|
||||
await loadingScreen.unload();
|
||||
// TODO This is probably not necessary in case of hot reload.
|
||||
await gdjs.getAllAsynchronouslyLoadingLibraryPromise();
|
||||
} catch (e) {
|
||||
if (this._debuggerClient) this._debuggerClient.onUncaughtException(e);
|
||||
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all assets for a given scene, displaying progress in renderer.
|
||||
*/
|
||||
async loadSceneAssets(
|
||||
sceneName: string,
|
||||
progressCallback?: (progress: float) => void
|
||||
): Promise<void> {
|
||||
await this._loadAssetsWithLoadingScreen(
|
||||
/* isFirstLayout = */ false,
|
||||
async (onProgress) => {
|
||||
await this._resourcesLoader.loadAndProcessSceneResources(
|
||||
sceneName,
|
||||
onProgress
|
||||
);
|
||||
},
|
||||
progressCallback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load assets, displaying progress in renderer.
|
||||
*/
|
||||
private async _loadAssetsWithLoadingScreen(
|
||||
isFirstScene: boolean,
|
||||
loadAssets: (
|
||||
onProgress: (count: integer, total: integer) => Promise<void>
|
||||
) => Promise<void>,
|
||||
progressCallback?: (progress: float) => void
|
||||
): Promise<void> {
|
||||
this.pause(true);
|
||||
const loadingScreen = new gdjs.LoadingScreenRenderer(
|
||||
this.getRenderer(),
|
||||
this._resourcesLoader.getImageManager(),
|
||||
this._data.properties.loadingScreen,
|
||||
isFirstScene
|
||||
);
|
||||
|
||||
const onProgress = async (count: integer, total: integer) => {
|
||||
const percent = Math.floor((100 * count) / total);
|
||||
loadingScreen.setPercent(percent);
|
||||
if (progressCallback) {
|
||||
progressCallback(percent);
|
||||
}
|
||||
const hasRendered = loadingScreen.renderIfNeeded();
|
||||
if (hasRendered) {
|
||||
// Give a chance to draw calls from the renderer to be handled.
|
||||
await sleep(1);
|
||||
}
|
||||
};
|
||||
await loadAssets(onProgress);
|
||||
|
||||
await loadingScreen.unload();
|
||||
this.pause(false);
|
||||
}
|
||||
|
||||
private _getFirstSceneName(): string {
|
||||
const firstSceneName = this._data.firstLayout;
|
||||
return this.hasScene(firstSceneName)
|
||||
? firstSceneName
|
||||
: // @ts-ignore - no risk of null object.
|
||||
this.getSceneData().name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the game loop, to be called once assets are loaded.
|
||||
@@ -742,12 +751,8 @@ namespace gdjs {
|
||||
this._forceGameResolutionUpdate();
|
||||
|
||||
// Load the first scene
|
||||
const firstSceneName = this._data.firstLayout;
|
||||
this._sceneStack.push(
|
||||
this.hasScene(firstSceneName)
|
||||
? firstSceneName
|
||||
: // @ts-ignore - no risk of null object.
|
||||
this.getSceneData().name,
|
||||
this._getFirstSceneName(),
|
||||
this._injectExternalLayout
|
||||
);
|
||||
this._watermark.displayAtStartup();
|
||||
|
@@ -147,6 +147,8 @@ namespace gdjs {
|
||||
return true;
|
||||
};
|
||||
|
||||
type RuntimeObjectCallback = (object: gdjs.RuntimeObject) => void;
|
||||
|
||||
/**
|
||||
* RuntimeObject represents an object being used on a RuntimeScene.
|
||||
*
|
||||
@@ -164,9 +166,12 @@ namespace gdjs {
|
||||
layer: string = '';
|
||||
protected _nameId: integer;
|
||||
protected _livingOnScene: boolean = true;
|
||||
protected _spatialSearchSleepState: ObjectSleepState;
|
||||
|
||||
readonly id: integer;
|
||||
private destroyCallbacks = new Set<() => void>();
|
||||
// HitboxChanges happen a lot, an Array is faster to iterate.
|
||||
private hitBoxChangedCallbacks: Array<RuntimeObjectCallback> = [];
|
||||
_runtimeScene: gdjs.RuntimeInstanceContainer;
|
||||
|
||||
/**
|
||||
@@ -181,12 +186,16 @@ namespace gdjs {
|
||||
* not "thread safe" or "re-entrant algorithm" safe.
|
||||
*/
|
||||
pick: boolean = false;
|
||||
pickingId: integer = 0;
|
||||
|
||||
//Hit boxes:
|
||||
protected _defaultHitBoxes: gdjs.Polygon[] = [];
|
||||
protected hitBoxes: gdjs.Polygon[];
|
||||
protected hitBoxesDirty: boolean = true;
|
||||
// TODO use a different AABB for collision mask and rendered image.
|
||||
protected aabb: AABB = { min: [0, 0], max: [0, 0] };
|
||||
_rtreeAABB: SearchedItem<RuntimeObject>;
|
||||
|
||||
protected _isIncludedInParentCollisionMask = true;
|
||||
|
||||
//Variables:
|
||||
@@ -229,10 +238,11 @@ namespace gdjs {
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
objectData: ObjectData & any
|
||||
) {
|
||||
const scene = instanceContainer.getScene();
|
||||
this.name = objectData.name || '';
|
||||
this.type = objectData.type || '';
|
||||
this._nameId = RuntimeObject.getNameIdentifier(this.name);
|
||||
this.id = instanceContainer.getScene().createNewUniqueId();
|
||||
this.id = scene.createNewUniqueId();
|
||||
this._runtimeScene = instanceContainer;
|
||||
this._defaultHitBoxes.push(gdjs.Polygon.createRectangle(0, 0));
|
||||
this.hitBoxes = this._defaultHitBoxes;
|
||||
@@ -241,8 +251,20 @@ namespace gdjs {
|
||||
);
|
||||
this._totalForce = new gdjs.Force(0, 0, 0);
|
||||
this._behaviorsTable = new Hashtable();
|
||||
this._rtreeAABB = {
|
||||
source: this,
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxX: 0,
|
||||
maxY: 0,
|
||||
};
|
||||
this._spatialSearchSleepState = new gdjs.ObjectSleepState(
|
||||
this,
|
||||
() => !this.getVisibilityAABB(),
|
||||
gdjs.ObjectSleepState.State.CanSleepThisFrame
|
||||
);
|
||||
for (let i = 0; i < objectData.effects.length; ++i) {
|
||||
this._runtimeScene
|
||||
scene
|
||||
.getGame()
|
||||
.getEffectsManager()
|
||||
.initializeEffect(objectData.effects[i], this._rendererEffects, this);
|
||||
@@ -439,6 +461,14 @@ namespace gdjs {
|
||||
return false;
|
||||
}
|
||||
|
||||
getSpatialSearchSleepState(): ObjectSleepState {
|
||||
return this._spatialSearchSleepState;
|
||||
}
|
||||
|
||||
isAlive(): boolean {
|
||||
return this._livingOnScene;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an object from a scene.
|
||||
*
|
||||
@@ -486,6 +516,31 @@ namespace gdjs {
|
||||
|
||||
onDestroyed(): void {}
|
||||
|
||||
registerHitboxChangedCallback(callback: RuntimeObjectCallback) {
|
||||
if (this.hitBoxChangedCallbacks.includes(callback)) {
|
||||
return;
|
||||
}
|
||||
this.hitBoxChangedCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a signal that the object hitboxes are no longer up to date.
|
||||
*
|
||||
* The signal is propagated to parents so
|
||||
* {@link gdjs.RuntimeObject.hitBoxesDirty} should never be modified
|
||||
* directly.
|
||||
*/
|
||||
invalidateHitboxes(): void {
|
||||
// TODO EBO Check that no community extension set hitBoxesDirty to true
|
||||
// directly.
|
||||
this.hitBoxesDirty = true;
|
||||
this._spatialSearchSleepState.wakeUp();
|
||||
this._runtimeScene.onChildrenLocationChanged();
|
||||
for (const callback of this.hitBoxChangedCallbacks) {
|
||||
callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever the scene owning the object is paused.
|
||||
* This should *not* impact objects, but some may need to inform their renderer.
|
||||
@@ -570,20 +625,6 @@ namespace gdjs {
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a signal that the object hitboxes are no longer up to date.
|
||||
*
|
||||
* The signal is propagated to parents so
|
||||
* {@link gdjs.RuntimeObject.hitBoxesDirty} should never be modified
|
||||
* directly.
|
||||
*/
|
||||
invalidateHitboxes(): void {
|
||||
// TODO EBO Check that no community extension set hitBoxesDirty to true
|
||||
// directly.
|
||||
this.hitBoxesDirty = true;
|
||||
this._runtimeScene.onChildrenLocationChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the X position of the object.
|
||||
*
|
||||
@@ -758,6 +799,7 @@ namespace gdjs {
|
||||
oldLayer.getRenderer().remove3DRendererObject(rendererObject3D);
|
||||
newLayer.getRenderer().add3DRendererObject(rendererObject3D);
|
||||
}
|
||||
this._runtimeScene.onObjectChangedOfLayer(this, oldLayer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1410,7 +1452,7 @@ namespace gdjs {
|
||||
* @param multiplier Set the force multiplier
|
||||
*/
|
||||
addForceTowardObject(
|
||||
object: gdjs.RuntimeObject,
|
||||
object: gdjs.RuntimeObject | null,
|
||||
len: float,
|
||||
multiplier: integer
|
||||
): void {
|
||||
@@ -2276,10 +2318,12 @@ namespace gdjs {
|
||||
* @param angleInDegrees The angle between the object and the target, in degrees.
|
||||
*/
|
||||
putAroundObject(
|
||||
obj: gdjs.RuntimeObject,
|
||||
obj: gdjs.RuntimeObject | null,
|
||||
distance: float,
|
||||
angleInDegrees: float
|
||||
): void {
|
||||
if (!obj) return;
|
||||
|
||||
this.putAround(
|
||||
obj.getDrawableX() + obj.getCenterX(),
|
||||
obj.getDrawableY() + obj.getCenterY(),
|
||||
|
@@ -6,6 +6,7 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('RuntimeScene');
|
||||
const setupWarningLogger = new gdjs.Logger('RuntimeScene (setup warnings)');
|
||||
type SearchArea = { minX: float; minY: float; maxX: float; maxY: float };
|
||||
|
||||
/**
|
||||
* A scene being played, containing instances of objects rendered on screen.
|
||||
@@ -45,6 +46,17 @@ namespace gdjs {
|
||||
_cachedGameResolutionWidth: integer;
|
||||
_cachedGameResolutionHeight: integer;
|
||||
|
||||
private _frameIndex: integer = 0;
|
||||
|
||||
_layersCameraCoordinates: Record<string, SearchArea> = {};
|
||||
private _layerObjectManagers = new Map<string, ObjectManager>();
|
||||
/**
|
||||
* Objects that were rendered for the last frame.
|
||||
*
|
||||
* They keep to be hide back without iterating every objects from the scene.
|
||||
*/
|
||||
private _objectsInsideCamera: Record<string, Array<RuntimeObject>> = {};
|
||||
|
||||
/**
|
||||
* @param runtimeGame The game associated to this scene.
|
||||
*/
|
||||
@@ -81,6 +93,43 @@ namespace gdjs {
|
||||
this._orderedLayers.push(layer);
|
||||
}
|
||||
|
||||
addObject(object: gdjs.RuntimeObject): void {
|
||||
super.addObject(object);
|
||||
this._addObjectToLayerObjectManager(object);
|
||||
}
|
||||
|
||||
onObjectChangedOfLayer(object: RuntimeObject, oldLayer: RuntimeLayer) {
|
||||
this._removeObjectFromLayerObjectManager(object, oldLayer.getName());
|
||||
this._addObjectToLayerObjectManager(object);
|
||||
}
|
||||
|
||||
private _addObjectToLayerObjectManager(object: gdjs.RuntimeObject): void {
|
||||
const layerName = object.getLayer();
|
||||
let objectManager = this._layerObjectManagers.get(layerName);
|
||||
if (!objectManager) {
|
||||
objectManager = new gdjs.ObjectManager();
|
||||
this._layerObjectManagers.set(layerName, objectManager);
|
||||
}
|
||||
objectManager.addObject(object);
|
||||
}
|
||||
|
||||
markObjectForDeletion(object: gdjs.RuntimeObject): void {
|
||||
super.markObjectForDeletion(object);
|
||||
const layerName = object.getLayer();
|
||||
this._removeObjectFromLayerObjectManager(object, layerName);
|
||||
}
|
||||
|
||||
private _removeObjectFromLayerObjectManager(
|
||||
object: gdjs.RuntimeObject,
|
||||
layerName: string
|
||||
): void {
|
||||
let objectManager = this._layerObjectManagers.get(layerName);
|
||||
if (!objectManager) {
|
||||
return;
|
||||
}
|
||||
objectManager.deleteObject(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called when the canvas where the scene is rendered has been resized.
|
||||
* See gdjs.RuntimeGame.startGameLoop in particular.
|
||||
@@ -427,6 +476,7 @@ namespace gdjs {
|
||||
if (this._profiler) {
|
||||
this._profiler.endFrame();
|
||||
}
|
||||
this._frameIndex++;
|
||||
return !!this.getRequestedChange();
|
||||
}
|
||||
|
||||
@@ -437,6 +487,26 @@ namespace gdjs {
|
||||
this._renderer.render();
|
||||
}
|
||||
|
||||
_updateLayersCameraCoordinates(scale: float) {
|
||||
this._layersCameraCoordinates = this._layersCameraCoordinates || {};
|
||||
for (const name in this._layers.items) {
|
||||
if (this._layers.items.hasOwnProperty(name)) {
|
||||
const theLayer = this._layers.items[name];
|
||||
this._layersCameraCoordinates[name] = this._layersCameraCoordinates[
|
||||
name
|
||||
] || { minX: 0, minY: 0, maxX: 0, maxY: 0 };
|
||||
this._layersCameraCoordinates[name].minX =
|
||||
theLayer.getCameraX() - (theLayer.getCameraWidth() / 2) * scale;
|
||||
this._layersCameraCoordinates[name].minY =
|
||||
theLayer.getCameraY() - (theLayer.getCameraHeight() / 2) * scale;
|
||||
this._layersCameraCoordinates[name].maxX =
|
||||
theLayer.getCameraX() + (theLayer.getCameraWidth() / 2) * scale;
|
||||
this._layersCameraCoordinates[name].maxY =
|
||||
theLayer.getCameraY() + (theLayer.getCameraHeight() / 2) * scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update visibility of the renderers of objects
|
||||
* rendered on the scene ("culling"), update effects (of visible objects)
|
||||
@@ -446,50 +516,68 @@ namespace gdjs {
|
||||
* object is too far from the camera of its layer ("culling").
|
||||
*/
|
||||
_updateObjectsPreRender() {
|
||||
// Check awake objects only once every 64 frames.
|
||||
if ((this._frameIndex & 63) === 0) {
|
||||
for (const objectManager of this._layerObjectManagers.values()) {
|
||||
objectManager.updateAwakeObjects();
|
||||
}
|
||||
}
|
||||
if (this._timeManager.isFirstFrame()) {
|
||||
super._updateObjectsPreRender();
|
||||
return;
|
||||
} else {
|
||||
// After first frame, optimise rendering by setting only objects
|
||||
// near camera as visible.
|
||||
// TODO: For compatibility, pass a scale of `2`,
|
||||
// meaning that size of cameras will be multiplied by 2 and so objects
|
||||
// will be hidden if they are outside of this *larger* camera area.
|
||||
// This is useful for:
|
||||
// - objects not properly reporting their visibility AABB,
|
||||
// (so we have a "safety margin") but these objects should be fixed
|
||||
// instead.
|
||||
// - objects having effects rendering outside of their visibility AABB.
|
||||
|
||||
// TODO (3D) culling - add support for 3D object culling?
|
||||
this._updateLayersCameraCoordinates(2);
|
||||
const allInstancesList = this.getAdhocListOfAllInstances();
|
||||
for (let i = 0, len = allInstancesList.length; i < len; ++i) {
|
||||
const object = allInstancesList[i];
|
||||
const rendererObject = object.getRendererObject();
|
||||
if (rendererObject) {
|
||||
if (object.isHidden()) {
|
||||
rendererObject.visible = false;
|
||||
} else {
|
||||
const cameraCoords = this._layersCameraCoordinates[
|
||||
object.getLayer()
|
||||
];
|
||||
if (!cameraCoords) {
|
||||
continue;
|
||||
}
|
||||
const aabb = object.getVisibilityAABB();
|
||||
rendererObject.visible =
|
||||
// If no AABB is returned, the object should always be visible
|
||||
!aabb ||
|
||||
// If an AABB is there, it must be at least partially inside
|
||||
// the camera bounds.
|
||||
!(
|
||||
aabb.min[0] > cameraCoords[2] ||
|
||||
aabb.min[1] > cameraCoords[3] ||
|
||||
aabb.max[0] < cameraCoords[0] ||
|
||||
aabb.max[1] < cameraCoords[1]
|
||||
);
|
||||
}
|
||||
rendererObject.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// After first frame, optimise rendering by setting only objects
|
||||
// near camera as visible.
|
||||
// TODO: For compatibility, pass a scale of `2`,
|
||||
// meaning that size of cameras will be multiplied by 2 and so objects
|
||||
// will be hidden if they are outside of this *larger* camera area.
|
||||
// This is useful for:
|
||||
// - objects not properly reporting their visibility AABB,
|
||||
// (so we have a "safety margin") but these objects should be fixed
|
||||
// instead.
|
||||
// - objects having effects rendering outside of their visibility AABB.
|
||||
|
||||
// TODO (3D) culling - add support for 3D object culling?
|
||||
this._updateLayersCameraCoordinates(2);
|
||||
|
||||
// Reset objects that were visible last frame.
|
||||
for (const layerName in this._objectsInsideCamera) {
|
||||
for (const object of this._objectsInsideCamera[layerName]) {
|
||||
const rendererObject = object.getRendererObject();
|
||||
if (rendererObject) {
|
||||
rendererObject.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const layerName in this._layers.items) {
|
||||
const cameraAABB = this._layersCameraCoordinates[layerName];
|
||||
let objectsInsideCamera = this._objectsInsideCamera[layerName];
|
||||
if (objectsInsideCamera === undefined) {
|
||||
objectsInsideCamera = [];
|
||||
this._objectsInsideCamera[layerName] = objectsInsideCamera;
|
||||
}
|
||||
if (!cameraAABB) {
|
||||
continue;
|
||||
}
|
||||
const layerObjectManager = this._layerObjectManagers.get(layerName);
|
||||
if (!layerObjectManager) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find objects that are visible this frame.
|
||||
objectsInsideCamera.length = 0;
|
||||
layerObjectManager.search(cameraAABB, objectsInsideCamera);
|
||||
|
||||
for (const object of objectsInsideCamera) {
|
||||
const rendererObject = object.getRendererObject();
|
||||
if (rendererObject) {
|
||||
rendererObject.visible = !object.isHidden();
|
||||
|
||||
// Update effects, only for visible objects.
|
||||
if (rendererObject.visible) {
|
||||
@@ -734,6 +822,10 @@ namespace gdjs {
|
||||
sceneJustResumed(): boolean {
|
||||
return this._isJustResumed;
|
||||
}
|
||||
|
||||
getFrameIndex(): integer {
|
||||
return this._frameIndex;
|
||||
}
|
||||
}
|
||||
|
||||
//The flags to describe the change request by a scene:
|
||||
|
@@ -8,6 +8,7 @@ namespace gdjs {
|
||||
_runtimeGame: gdjs.RuntimeGame;
|
||||
_stack: gdjs.RuntimeScene[] = [];
|
||||
_wasFirstSceneLoaded: boolean = false;
|
||||
_isNextLayoutLoading: boolean = false;
|
||||
|
||||
/**
|
||||
* @param runtimeGame The runtime game that is using the scene stack
|
||||
@@ -31,7 +32,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
step(elapsedTime: float): boolean {
|
||||
if (this._stack.length === 0) {
|
||||
if (this._isNextLayoutLoading || this._stack.length === 0) {
|
||||
return false;
|
||||
}
|
||||
const currentScene = this._stack[this._stack.length - 1];
|
||||
@@ -91,13 +92,34 @@ namespace gdjs {
|
||||
* Pause the scene currently being played and start the new scene that is specified.
|
||||
* If `externalLayoutName` is set, also instantiate the objects from this external layout.
|
||||
*/
|
||||
push(newSceneName: string, externalLayoutName?: string): gdjs.RuntimeScene {
|
||||
push(
|
||||
newSceneName: string,
|
||||
externalLayoutName?: string
|
||||
): gdjs.RuntimeScene | null {
|
||||
// Tell the scene it's being paused
|
||||
const currentScene = this._stack[this._stack.length - 1];
|
||||
if (currentScene) {
|
||||
currentScene.onPause();
|
||||
}
|
||||
|
||||
// Avoid a risk of displaying an intermediate loading screen
|
||||
// during 1 frame.
|
||||
if (this._runtimeGame.areSceneAssetsReady(newSceneName)) {
|
||||
return this._loadNewScene(newSceneName, externalLayoutName);
|
||||
}
|
||||
|
||||
this._isNextLayoutLoading = true;
|
||||
this._runtimeGame.loadSceneAssets(newSceneName).then(() => {
|
||||
this._loadNewScene(newSceneName);
|
||||
this._isNextLayoutLoading = false;
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
private _loadNewScene(
|
||||
newSceneName: string,
|
||||
externalLayoutName?: string
|
||||
): gdjs.RuntimeScene {
|
||||
// Load the new one
|
||||
const newScene = new gdjs.RuntimeScene(this._runtimeGame);
|
||||
newScene.loadFromScene(this._runtimeGame.getSceneData(newSceneName));
|
||||
@@ -127,7 +149,7 @@ namespace gdjs {
|
||||
* Start the specified scene, replacing the one currently being played.
|
||||
* If `clear` is set to true, all running scenes are also removed from the stack of scenes.
|
||||
*/
|
||||
replace(newSceneName: string, clear?: boolean): gdjs.RuntimeScene {
|
||||
replace(newSceneName: string, clear?: boolean): gdjs.RuntimeScene | null {
|
||||
if (!!clear) {
|
||||
// Unload all the scenes
|
||||
while (this._stack.length !== 0) {
|
||||
|
@@ -1379,7 +1379,7 @@ namespace gdjs {
|
||||
* @param scene The scene containing the object
|
||||
* @deprecated
|
||||
*/
|
||||
turnTowardObject(obj: gdjs.RuntimeObject, scene: gdjs.RuntimeScene) {
|
||||
turnTowardObject(obj: gdjs.RuntimeObject | null, scene: gdjs.RuntimeScene) {
|
||||
if (obj === null) {
|
||||
return;
|
||||
}
|
||||
|
6
GDJS/Runtime/types/project-data.d.ts
vendored
6
GDJS/Runtime/types/project-data.d.ts
vendored
@@ -12,6 +12,7 @@ declare interface ProjectData {
|
||||
gdVersion: GdVersionData;
|
||||
properties: ProjectPropertiesData;
|
||||
resources: ResourcesData;
|
||||
usedResources: ResourceReference[];
|
||||
objects: ObjectData[];
|
||||
variables: RootVariableData[];
|
||||
layouts: LayoutData[];
|
||||
@@ -83,6 +84,7 @@ declare interface LayoutData {
|
||||
objects: ObjectData[];
|
||||
layers: LayerData[];
|
||||
behaviorsSharedData: BehaviorSharedData[];
|
||||
usedResources: ResourceReference[];
|
||||
}
|
||||
|
||||
declare interface EventsFunctionsExtensionData {
|
||||
@@ -264,6 +266,10 @@ declare interface ResourceData {
|
||||
preloadInCache?: boolean;
|
||||
}
|
||||
|
||||
declare interface ResourceReference {
|
||||
name: string;
|
||||
}
|
||||
|
||||
declare type ResourceKind =
|
||||
| 'audio'
|
||||
| 'image'
|
||||
|
19
GDJS/Runtime/types/rbush.d.ts
vendored
Normal file
19
GDJS/Runtime/types/rbush.d.ts
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
type SearchArea = { minX: float; minY: float; maxX: float; maxY: float };
|
||||
type SearchedItem<T> = {
|
||||
source: T;
|
||||
minX: float;
|
||||
minY: float;
|
||||
maxX: float;
|
||||
maxY: float;
|
||||
};
|
||||
|
||||
declare class RBush<T> {
|
||||
constructor(maxEntries?: number);
|
||||
search(bbox: SearchArea, result?: Array<T>): Array<T>;
|
||||
insert(item: SearchedItem<T>): RBush<T>;
|
||||
clear(): RBush<T>;
|
||||
remove(
|
||||
item: SearchedItem<T>,
|
||||
equalsFn?: (item: SearchedItem<T>, otherItem: SearchedItem<T>) => boolean
|
||||
): RBush<T>;
|
||||
}
|
@@ -49,8 +49,12 @@ module.exports = function (config) {
|
||||
'./newIDE/app/resources/GDJS/Runtime/fontfaceobserver-font-manager/fontfaceobserver-font-manager.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/jsonmanager.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/Model3DManager.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/ResourceLoader.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/ResourceCache.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/timemanager.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/polygon.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/ObjectSleepState.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/ObjectManager.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/runtimeobject.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/RuntimeInstanceContainer.js',
|
||||
'./newIDE/app/resources/GDJS/Runtime/runtimescene.js',
|
||||
|
@@ -64,6 +64,7 @@ gdjs.getPixiRuntimeGame = (settings) => {
|
||||
externalLayouts: [],
|
||||
resources: (settings && settings.resources) || { resources: [] },
|
||||
eventsFunctionsExtensions: [],
|
||||
usedResources: [],
|
||||
});
|
||||
|
||||
return runtimeGame;
|
||||
|
@@ -63,7 +63,23 @@ gdjs.getPixiRuntimeGameWithAssets = () => {
|
||||
revision: 0,
|
||||
},
|
||||
objects: [],
|
||||
layouts: [],
|
||||
layouts: [
|
||||
{
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: '',
|
||||
name: '',
|
||||
objects: [],
|
||||
layers: [],
|
||||
instances: [],
|
||||
behaviorsSharedData: [],
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
variables: [],
|
||||
usedResources: [],
|
||||
},
|
||||
],
|
||||
externalLayouts: [],
|
||||
resources: {
|
||||
resources: [
|
||||
@@ -76,6 +92,7 @@ gdjs.getPixiRuntimeGameWithAssets = () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
usedResources: [{ name: 'base/tests-utils/assets/64x64.jpg' }],
|
||||
// Used in CustomRuntimeObjects.js
|
||||
eventsFunctionsExtensions: [
|
||||
{
|
||||
|
40
GDJS/tests/tests/Extensions/MockedResourceLoader.js
Normal file
40
GDJS/tests/tests/Extensions/MockedResourceLoader.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* GDevelop JS Platform
|
||||
* Copyright 2013-2023 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
|
||||
* This project is released under the MIT License.
|
||||
*/
|
||||
// implements gdjs.ResourceManager
|
||||
gdjs.MockedResourceManager = class MockedResourceManager {
|
||||
|
||||
loadResourceCallbacks = new Map();
|
||||
|
||||
loadResource(resourceName) {
|
||||
const that = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
that.loadResourceCallbacks.set(resourceName, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
async processResource(resourceName) {}
|
||||
|
||||
/**
|
||||
* @param {string} resourceName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isResourceDownloadPending(resourceName) {
|
||||
return this.loadResourceCallbacks.has(resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} resourceName
|
||||
*/
|
||||
markPendingResourcesAsLoaded(resourceName) {
|
||||
const loadResourceCallback = this.loadResourceCallbacks.get(resourceName);
|
||||
loadResourceCallback();
|
||||
this.loadResourceCallbacks.delete(resourceName);
|
||||
}
|
||||
|
||||
getResourceKinds() {
|
||||
return ['fake-heavy-resource'];
|
||||
}
|
||||
}
|
@@ -3,12 +3,15 @@
|
||||
*/
|
||||
gdjs.TestRuntimeScene = class TestRuntimeScene extends gdjs.RuntimeScene {
|
||||
/**
|
||||
* @param {gdjs.RuntimeGame} runtimeGame
|
||||
* @param {gdjs.RuntimeGame} runtimeGame
|
||||
*/
|
||||
constructor(runtimeGame) {
|
||||
constructor(runtimeGame, layerNames = ['']) {
|
||||
super(runtimeGame);
|
||||
|
||||
this.addLayer({ name: '', cameras: [], effects: [] });
|
||||
for (const layerName of layerNames) {
|
||||
this.addLayer({ name: layerName, cameras: [], effects: [] });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -20,7 +20,32 @@
|
||||
/** @type {?float} */
|
||||
_customCenterY = null;
|
||||
|
||||
constructor(runtimeScene, objectData) {
|
||||
constructor(runtimeScene, objectData = {
|
||||
name: 'MySprite',
|
||||
type: '',
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
animations: [
|
||||
{
|
||||
name: 'animation',
|
||||
directions: [
|
||||
{
|
||||
sprites: [
|
||||
{
|
||||
originPoint: { x: 0, y: 0 },
|
||||
centerPoint: { x: 0, y: 0 },
|
||||
points: [
|
||||
{ name: 'Center', x: 0, y: 0 },
|
||||
{ name: 'Origin', x: 0, y: 0 },
|
||||
],
|
||||
hasCustomCollisionMask: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}) {
|
||||
// *ALWAYS* call the base gdjs.RuntimeObject constructor.
|
||||
super(runtimeScene, objectData);
|
||||
|
||||
@@ -50,10 +75,6 @@
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
getWidth() {
|
||||
return this._customWidth;
|
||||
}
|
||||
|
1
GDJS/tests/tests/effects.js
vendored
1
GDJS/tests/tests/effects.js
vendored
@@ -106,6 +106,7 @@ describe('gdjs.EffectsManager', () => {
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
|
||||
const runtimeLayer = runtimeScene.getLayer('');
|
||||
|
105
GDJS/tests/tests/runtimescene.culling.js
Normal file
105
GDJS/tests/tests/runtimescene.culling.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// @ts-check
|
||||
describe('gdjs.RuntimeScene culling tests', () => {
|
||||
|
||||
it('should hide objects moving outside of the screen', () => {
|
||||
const game = gdjs.getPixiRuntimeGame();
|
||||
const scene = new gdjs.TestRuntimeScene(game, ['', 'MyLayer']);
|
||||
|
||||
scene.getLayer('MyLayer').setCameraX(8000);
|
||||
|
||||
const object = new gdjs.TestSpriteRuntimeObject(scene);
|
||||
object.setUnscaledWidthAndHeight(100, 100);
|
||||
object.setCustomWidthAndHeight(100, 100);
|
||||
scene.addObject(object);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(true);
|
||||
|
||||
object.setY(8000);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(false);
|
||||
});
|
||||
|
||||
it('should show objects moving back in the screen', () => {
|
||||
const game = gdjs.getPixiRuntimeGame();
|
||||
const scene = new gdjs.TestRuntimeScene(game, ['']);
|
||||
|
||||
const object = new gdjs.TestSpriteRuntimeObject(scene);
|
||||
object.setUnscaledWidthAndHeight(100, 100);
|
||||
object.setCustomWidthAndHeight(100, 100);
|
||||
scene.addObject(object);
|
||||
object.setY(8000);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(false);
|
||||
|
||||
object.setY(200);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(true);
|
||||
});
|
||||
|
||||
it('should handle objects changing of layer', () => {
|
||||
const game = gdjs.getPixiRuntimeGame();
|
||||
const scene = new gdjs.TestRuntimeScene(game, ['']);
|
||||
|
||||
const object = new gdjs.TestSpriteRuntimeObject(scene);
|
||||
object.setUnscaledWidthAndHeight(100, 100);
|
||||
object.setCustomWidthAndHeight(100, 100);
|
||||
scene.addObject(object);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(true);
|
||||
|
||||
object.setLayer('MyLayer');
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(false);
|
||||
|
||||
object.setLayer('');
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(true);
|
||||
});
|
||||
|
||||
// This is important to avoid games with big levels to be CPU limited during
|
||||
// the 2 first seconds.
|
||||
it('should allow objects to sleep at the end of the 1st frame', () => {
|
||||
const game = gdjs.getPixiRuntimeGame();
|
||||
const scene = new gdjs.TestRuntimeScene(game, ['']);
|
||||
|
||||
const object = new gdjs.TestSpriteRuntimeObject(scene);
|
||||
object.setUnscaledWidthAndHeight(100, 100);
|
||||
object.setCustomWidthAndHeight(100, 100);
|
||||
scene.addObject(object);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
expect(object.getRendererObject().visible).to.be(true);
|
||||
expect(object.getSpatialSearchSleepState().isAwake()).to.be(false);
|
||||
|
||||
object.setY(200);
|
||||
expect(object.getSpatialSearchSleepState().isAwake()).to.be(true);
|
||||
});
|
||||
|
||||
it('should put objects to sleep when they don\'t move for 2 seconds', () => {
|
||||
const game = gdjs.getPixiRuntimeGame();
|
||||
const scene = new gdjs.TestRuntimeScene(game, ['']);
|
||||
|
||||
const object = new gdjs.TestSpriteRuntimeObject(scene);
|
||||
object.setUnscaledWidthAndHeight(100, 100);
|
||||
object.setCustomWidthAndHeight(100, 100);
|
||||
scene.addObject(object);
|
||||
|
||||
scene.renderAndStep(1000 / 60);
|
||||
object.setY(200);
|
||||
expect(object.getSpatialSearchSleepState().isAwake()).to.be(true);
|
||||
|
||||
// Objects can sleep every 64 frames if they haven't moved for 1 second.
|
||||
for (let index = 0; index < 60 + 64; index++) {
|
||||
scene.renderAndStep(1000 / 60);
|
||||
}
|
||||
expect(object.getSpatialSearchSleepState().isAwake()).to.be(false);
|
||||
expect(object.getRendererObject().visible).to.be(true);
|
||||
});
|
||||
});
|
@@ -46,6 +46,7 @@ describe('gdjs.RuntimeScene integration tests', function () {
|
||||
},
|
||||
],
|
||||
instances: [],
|
||||
usedResources: [],
|
||||
});
|
||||
|
||||
const object = runtimeScene.createObject('Object1');
|
||||
|
@@ -3,47 +3,62 @@
|
||||
/**
|
||||
* Tests for gdjs.SceneStack.
|
||||
*/
|
||||
describe('gdjs.SceneStack', function () {
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame({
|
||||
layouts: [
|
||||
{
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene2',
|
||||
name: 'Scene 1',
|
||||
objects: [],
|
||||
layers: [],
|
||||
instances: [],
|
||||
behaviorsSharedData: [],
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
variables: [],
|
||||
},
|
||||
{
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene2',
|
||||
name: 'Scene 2',
|
||||
objects: [],
|
||||
layers: [],
|
||||
instances: [],
|
||||
behaviorsSharedData: [],
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
variables: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
var sceneStack = runtimeGame._sceneStack;
|
||||
describe('gdjs.SceneStack', () => {
|
||||
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
const createSene = (name, usedResources) => {
|
||||
return {
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: name,
|
||||
name,
|
||||
objects: [],
|
||||
layers: [],
|
||||
instances: [],
|
||||
behaviorsSharedData: [],
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
variables: [],
|
||||
usedResources,
|
||||
};
|
||||
};
|
||||
|
||||
const gameSettings = {
|
||||
layouts: [
|
||||
createSene('Scene 1', []),
|
||||
createSene('Scene 2', [{ name: 'base/tests-utils/assets/64x64.jpg' }]),
|
||||
],
|
||||
resources: {
|
||||
resources: [
|
||||
{
|
||||
kind: 'image',
|
||||
name: 'base/tests-utils/assets/64x64.jpg',
|
||||
metadata: '',
|
||||
file: 'base/tests-utils/assets/64x64.jpg',
|
||||
userAdded: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
it('should support pushing, replacing and popping scenes', async () => {
|
||||
//@ts-ignore
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame(gameSettings);
|
||||
let sceneStack = runtimeGame._sceneStack;
|
||||
// Async asset loading is not tested here.
|
||||
await runtimeGame._resourcesLoader.loadAllResources(() => {});
|
||||
|
||||
it('should support pushing, replacing and popping scenes', function () {
|
||||
// Set up some scene callbacks.
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let firstLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastUnloadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastPausedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastResumedScene = null;
|
||||
|
||||
const onFirstRuntimeSceneLoaded = (runtimeScene) => {
|
||||
@@ -74,22 +89,22 @@ describe('gdjs.SceneStack', function () {
|
||||
expect(firstLoadedScene).to.be(null);
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(false);
|
||||
|
||||
var scene1 = sceneStack.push('Scene 1');
|
||||
let scene1 = sceneStack.push('Scene 1');
|
||||
expect(lastLoadedScene).to.be(scene1);
|
||||
expect(firstLoadedScene).to.be(scene1);
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(true);
|
||||
|
||||
var scene2 = sceneStack.push('Scene 2');
|
||||
let scene2 = sceneStack.push('Scene 2');
|
||||
expect(lastPausedScene).to.be(scene1);
|
||||
expect(lastLoadedScene).to.be(scene2);
|
||||
expect(firstLoadedScene).to.be(scene1); // Not changed
|
||||
|
||||
var scene3 = sceneStack.push('Scene 1');
|
||||
let scene3 = sceneStack.push('Scene 1');
|
||||
expect(lastPausedScene).to.be(scene2);
|
||||
expect(lastLoadedScene).to.be(scene3);
|
||||
expect(firstLoadedScene).to.be(scene1); // Not changed
|
||||
|
||||
var scene4 = sceneStack.replace('Scene 1');
|
||||
let scene4 = sceneStack.replace('Scene 1');
|
||||
expect(lastPausedScene).to.be(scene2); // Not changed
|
||||
expect(lastUnloadedScene).to.be(scene3);
|
||||
expect(lastLoadedScene).to.be(scene4);
|
||||
@@ -107,7 +122,7 @@ describe('gdjs.SceneStack', function () {
|
||||
expect(lastPausedScene).to.be(scene2); // Not changed
|
||||
expect(firstLoadedScene).to.be(scene1); // Not changed
|
||||
|
||||
var scene5 = sceneStack.replace('Scene 2', true);
|
||||
let scene5 = sceneStack.replace('Scene 2', true);
|
||||
expect(lastLoadedScene).to.be(scene5);
|
||||
expect(lastUnloadedScene).to.be(scene1);
|
||||
expect(lastPausedScene).to.be(scene2); // Not changed
|
||||
@@ -130,4 +145,390 @@ describe('gdjs.SceneStack', function () {
|
||||
gdjs._unregisterCallback(onRuntimeScenePaused);
|
||||
gdjs._unregisterCallback(onRuntimeSceneResumed);
|
||||
});
|
||||
|
||||
const gameSettingsWithHeavyResource = {
|
||||
layouts: [
|
||||
createSene('Scene 1', [{ name: 'fake-heavy-resource1.png' }]),
|
||||
createSene('Scene 2', [{ name: 'fake-heavy-resource2.png' }]),
|
||||
createSene('Scene 3', [{ name: 'fake-heavy-resource3.png' }]),
|
||||
createSene('Scene 4', [{ name: 'fake-heavy-resource4.png' }]),
|
||||
],
|
||||
resources: {
|
||||
resources: [
|
||||
{
|
||||
kind: 'fake-heavy-resource',
|
||||
name: 'fake-heavy-resource1.png',
|
||||
metadata: '',
|
||||
file: 'fake-heavy-resource1.png',
|
||||
userAdded: true,
|
||||
},
|
||||
{
|
||||
kind: 'fake-heavy-resource',
|
||||
name: 'fake-heavy-resource2.png',
|
||||
metadata: '',
|
||||
file: 'fake-heavy-resource2.png',
|
||||
userAdded: true,
|
||||
},
|
||||
{
|
||||
kind: 'fake-heavy-resource',
|
||||
name: 'fake-heavy-resource3.png',
|
||||
metadata: '',
|
||||
file: 'fake-heavy-resource3.png',
|
||||
userAdded: true,
|
||||
},
|
||||
{
|
||||
kind: 'fake-heavy-resource',
|
||||
name: 'fake-heavy-resource4.png',
|
||||
metadata: '',
|
||||
file: 'fake-heavy-resource4.png',
|
||||
userAdded: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
it('can start a layout when all its assets are already downloaded', async () => {
|
||||
const mockedResourceManager = new gdjs.MockedResourceManager();
|
||||
//@ts-ignore
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame(gameSettingsWithHeavyResource);
|
||||
runtimeGame._resourcesLoader._resourceManagersMap.set(
|
||||
//@ts-ignore
|
||||
'fake-heavy-resource',
|
||||
mockedResourceManager
|
||||
);
|
||||
let sceneStack = runtimeGame._sceneStack;
|
||||
|
||||
// Set up some scene callbacks.
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let firstLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastPausedScene = null;
|
||||
|
||||
const onFirstRuntimeSceneLoaded = (runtimeScene) => {
|
||||
firstLoadedScene = runtimeScene;
|
||||
};
|
||||
const onRuntimeSceneLoaded = (runtimeScene) => {
|
||||
lastLoadedScene = runtimeScene;
|
||||
};
|
||||
const onRuntimeScenePaused = (runtimeScene) => {
|
||||
lastPausedScene = runtimeScene;
|
||||
};
|
||||
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback(onFirstRuntimeSceneLoaded);
|
||||
gdjs.registerRuntimeSceneLoadedCallback(onRuntimeSceneLoaded);
|
||||
gdjs.registerRuntimeScenePausedCallback(onRuntimeScenePaused);
|
||||
|
||||
// The test do not await because test test will unblock
|
||||
// `loadFirstAssetsAsync` with `markPendingResourcesAsLoaded`.
|
||||
runtimeGame.loadFirstAssetsAndStartBackgroundLoading('Scene 1');
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource1.png'
|
||||
)
|
||||
).to.be(true);
|
||||
|
||||
// No layout has loaded.
|
||||
expect(lastLoadedScene).to.be(null);
|
||||
expect(firstLoadedScene).to.be(null);
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 1')).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
// Assets of the 1st layout are downloaded before the layout is pushed.
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource1.png'
|
||||
);
|
||||
await delay(10);
|
||||
sceneStack.push('Scene 1');
|
||||
|
||||
// The 1st layout is loaded
|
||||
expect(lastLoadedScene).not.to.be(null);
|
||||
expect(firstLoadedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(firstLoadedScene.getName()).to.be('Scene 1');
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 1')).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 1')).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 2')).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
// "Scene 2" is loading in background.
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource2.png'
|
||||
)
|
||||
).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
// Finish to load "Scene 2" assets.
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource2.png'
|
||||
);
|
||||
await delay(10);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 2')).to.be(true);
|
||||
|
||||
// The player triggers "Scene 2" to start.
|
||||
sceneStack.push('Scene 2');
|
||||
// "Scene 2" is loaded for the 1st time, assets are processed
|
||||
// asynchronously.
|
||||
await delay(10);
|
||||
expect(lastPausedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastPausedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 2');
|
||||
|
||||
// The player triggers "Scene 1" to start.
|
||||
sceneStack.push('Scene 1');
|
||||
// "Scene 1" has already been shown the scene change is done synchronously.
|
||||
expect(lastPausedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastPausedScene.getName()).to.be('Scene 2');
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 1');
|
||||
|
||||
// Remove all the global callbacks
|
||||
gdjs._unregisterCallback(onFirstRuntimeSceneLoaded);
|
||||
gdjs._unregisterCallback(onRuntimeSceneLoaded);
|
||||
gdjs._unregisterCallback(onRuntimeScenePaused);
|
||||
});
|
||||
|
||||
it('can start a layout while assets loading and wait them to finish', async () => {
|
||||
const mockedResourceManager = new gdjs.MockedResourceManager();
|
||||
//@ts-ignore
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame(gameSettingsWithHeavyResource);
|
||||
runtimeGame._resourcesLoader._resourceManagersMap.set(
|
||||
//@ts-ignore
|
||||
'fake-heavy-resource',
|
||||
mockedResourceManager
|
||||
);
|
||||
let sceneStack = runtimeGame._sceneStack;
|
||||
|
||||
// Set up some scene callbacks.
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let firstLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastPausedScene = null;
|
||||
|
||||
const onFirstRuntimeSceneLoaded = (runtimeScene) => {
|
||||
firstLoadedScene = runtimeScene;
|
||||
};
|
||||
const onRuntimeSceneLoaded = (runtimeScene) => {
|
||||
lastLoadedScene = runtimeScene;
|
||||
};
|
||||
const onRuntimeScenePaused = (runtimeScene) => {
|
||||
lastPausedScene = runtimeScene;
|
||||
};
|
||||
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback(onFirstRuntimeSceneLoaded);
|
||||
gdjs.registerRuntimeSceneLoadedCallback(onRuntimeSceneLoaded);
|
||||
gdjs.registerRuntimeScenePausedCallback(onRuntimeScenePaused);
|
||||
|
||||
// The test do not await because test test will unblock
|
||||
// `loadFirstAssetsAsync` with `markPendingResourcesAsLoaded`.
|
||||
runtimeGame.loadFirstAssetsAndStartBackgroundLoading('Scene 1');
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource1.png'
|
||||
)
|
||||
).to.be(true);
|
||||
|
||||
// No layout has loaded.
|
||||
expect(lastLoadedScene).to.be(null);
|
||||
expect(firstLoadedScene).to.be(null);
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 1')).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
// Assets of the 1st layout are downloaded before the layout is pushed.
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource1.png'
|
||||
);
|
||||
await delay(10);
|
||||
sceneStack.push('Scene 1');
|
||||
|
||||
// The 1st layout is loaded
|
||||
expect(lastLoadedScene).not.to.be(null);
|
||||
expect(firstLoadedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(firstLoadedScene.getName()).to.be('Scene 1');
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 1')).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
// The 2nd layout is loading in background.
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource2.png'
|
||||
)
|
||||
).to.be(true);
|
||||
|
||||
// The player triggers "Scene 2" to load.
|
||||
let scene2 = sceneStack.push('Scene 2');
|
||||
expect(scene2).to.be(null);
|
||||
await delay(10);
|
||||
|
||||
// The 2nd layout is not loaded because its assets are still being downloaded.
|
||||
//@ts-ignore
|
||||
expect(lastPausedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 1');
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource2.png'
|
||||
);
|
||||
await delay(10);
|
||||
|
||||
// The 2nd layout is now loaded.
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(true);
|
||||
expect(lastPausedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastPausedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 2');
|
||||
|
||||
// Remove all the global callbacks
|
||||
gdjs._unregisterCallback(onFirstRuntimeSceneLoaded);
|
||||
gdjs._unregisterCallback(onRuntimeSceneLoaded);
|
||||
gdjs._unregisterCallback(onRuntimeScenePaused);
|
||||
});
|
||||
|
||||
it('can start a layout which assets loading didn\'t stated yet and wait them to finish', async () => {
|
||||
const mockedResourceManager = new gdjs.MockedResourceManager();
|
||||
//@ts-ignore
|
||||
const runtimeGame = gdjs.getPixiRuntimeGame(gameSettingsWithHeavyResource);
|
||||
runtimeGame._resourcesLoader._resourceManagersMap.set(
|
||||
//@ts-ignore
|
||||
'fake-heavy-resource',
|
||||
mockedResourceManager
|
||||
);
|
||||
let sceneStack = runtimeGame._sceneStack;
|
||||
|
||||
// Set up some scene callbacks.
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let firstLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastLoadedScene = null;
|
||||
/** @type gdjs.RuntimeScene | null */
|
||||
let lastPausedScene = null;
|
||||
|
||||
const onFirstRuntimeSceneLoaded = (runtimeScene) => {
|
||||
firstLoadedScene = runtimeScene;
|
||||
};
|
||||
const onRuntimeSceneLoaded = (runtimeScene) => {
|
||||
lastLoadedScene = runtimeScene;
|
||||
};
|
||||
const onRuntimeScenePaused = (runtimeScene) => {
|
||||
lastPausedScene = runtimeScene;
|
||||
};
|
||||
|
||||
gdjs.registerFirstRuntimeSceneLoadedCallback(onFirstRuntimeSceneLoaded);
|
||||
gdjs.registerRuntimeSceneLoadedCallback(onRuntimeSceneLoaded);
|
||||
gdjs.registerRuntimeScenePausedCallback(onRuntimeScenePaused);
|
||||
|
||||
// The test do not await because test test will unblock
|
||||
// `loadFirstAssetsAsync` with `markPendingResourcesAsLoaded`.
|
||||
runtimeGame.loadFirstAssetsAndStartBackgroundLoading('Scene 1');
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource1.png'
|
||||
)
|
||||
).to.be(true);
|
||||
|
||||
// No layout has loaded.
|
||||
expect(lastLoadedScene).to.be(null);
|
||||
expect(firstLoadedScene).to.be(null);
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 1')).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 2')).to.be(false);
|
||||
|
||||
// Assets of the 1st layout are downloaded before the layout is pushed.
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource1.png'
|
||||
);
|
||||
await delay(10);
|
||||
sceneStack.push('Scene 1');
|
||||
|
||||
// The 1st layout is loaded
|
||||
expect(lastLoadedScene).not.to.be(null);
|
||||
expect(firstLoadedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(firstLoadedScene.getName()).to.be('Scene 1');
|
||||
expect(sceneStack.wasFirstSceneLoaded()).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 1')).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 2')).to.be(false);
|
||||
|
||||
// "Scene 2" is loaded on background but is blocked because
|
||||
// 'fake-heavy-resource2.png' take a lot of time to load.
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource2.png'
|
||||
)
|
||||
).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 2')).to.be(false);
|
||||
|
||||
// The player triggers "Scene 4" to load.
|
||||
let scene4 = sceneStack.push('Scene 4');
|
||||
expect(scene4).to.be(null);
|
||||
await delay(10);
|
||||
// "Scene 4" loading doesn't start yet as assets are currently downloading
|
||||
// for "Scene 2".
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource4.png'
|
||||
)
|
||||
).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 2')).to.be(false);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 4')).to.be(false);
|
||||
|
||||
// Finish to download "Scene 2" assets.
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource2.png'
|
||||
);
|
||||
await delay(10);
|
||||
|
||||
// "Scene 4" assets are now downloading.
|
||||
expect(
|
||||
mockedResourceManager.isResourceDownloadPending(
|
||||
'fake-heavy-resource4.png'
|
||||
)
|
||||
).to.be(true);
|
||||
|
||||
// "Scene 4" is not loaded because its assets are still being downloading.
|
||||
//@ts-ignore
|
||||
expect(lastPausedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 1');
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 2')).to.be(true);
|
||||
expect(runtimeGame.areSceneAssetsLoaded('Scene 4')).to.be(false);
|
||||
|
||||
mockedResourceManager.markPendingResourcesAsLoaded(
|
||||
'fake-heavy-resource4.png'
|
||||
);
|
||||
await delay(10);
|
||||
|
||||
// "Scene 4" is now loaded.
|
||||
expect(runtimeGame.areSceneAssetsReady('Scene 4')).to.be(true);
|
||||
expect(lastPausedScene).not.to.be(null);
|
||||
//@ts-ignore
|
||||
expect(lastPausedScene.getName()).to.be('Scene 1');
|
||||
//@ts-ignore
|
||||
expect(lastLoadedScene.getName()).to.be('Scene 4');
|
||||
|
||||
// Remove all the global callbacks
|
||||
gdjs._unregisterCallback(onFirstRuntimeSceneLoaded);
|
||||
gdjs._unregisterCallback(onRuntimeSceneLoaded);
|
||||
gdjs._unregisterCallback(onRuntimeScenePaused);
|
||||
});
|
||||
});
|
||||
|
@@ -12,8 +12,17 @@ if(NOT EMSCRIPTEN)
|
||||
endif()
|
||||
|
||||
# Compilation flags (https://emscripten.org/docs/tools_reference/emcc.html):
|
||||
add_compile_options(-O2) # Optimizations during compilation
|
||||
#add_compile_options(-g --profiling) # Uncomment for debugging + profiling support
|
||||
add_compile_options(-O3 -flto) # Optimizations during compilation
|
||||
# add_compile_options(-fwasm-exceptions) # Enable exceptions
|
||||
if(NOT DISABLE_EMSCRIPTEN_LINK_OPTIMIZATIONS)
|
||||
add_compile_options(-flto) # The compiler needs to know if there will be link time optimisations
|
||||
endif()
|
||||
|
||||
# Compiler debugging options
|
||||
#
|
||||
# add_compile_options(-fsanitize=address) # Uncomment to auto-detect occurences of memory bugs (memory leak, use after free, overflows, ...) - also enable linking below!
|
||||
# add_compile_options(-fsanitize=undefined) # Uncomment to auto-detect occurences of undefined behavior - also enable linking below!
|
||||
# add_compile_options(-g) # Uncomment for debugging support
|
||||
# add_compile_options(--profiling) # Uncomment for profiling support
|
||||
|
||||
# Common directories:
|
||||
@@ -64,7 +73,7 @@ if(DISABLE_EMSCRIPTEN_LINK_OPTIMIZATIONS)
|
||||
message(STATUS "Disabling optimization at link time for (slightly) faster build")
|
||||
target_link_libraries(GD "-O0")
|
||||
else()
|
||||
target_link_libraries(GD "-O2")
|
||||
target_link_libraries(GD "-O3 -flto")
|
||||
endif()
|
||||
target_link_libraries(GD "--post-js ${GD_base_dir}/GDevelop.js/Bindings/glue.js")
|
||||
target_link_libraries(GD "--post-js ${GD_base_dir}/GDevelop.js/Bindings/postjs.js")
|
||||
@@ -73,7 +82,17 @@ target_link_libraries(GD "-s EXPORT_NAME=\"initializeGDevelopJs\"") # Global fun
|
||||
target_link_libraries(GD "-s TOTAL_MEMORY=48MB") # Get some initial memory size that is a bit bigger than the default.
|
||||
target_link_libraries(GD "-s ALLOW_MEMORY_GROWTH=1")
|
||||
target_link_libraries(GD "-s ERROR_ON_UNDEFINED_SYMBOLS=0")
|
||||
target_link_libraries(GD "-s \"EXTRA_EXPORTED_RUNTIME_METHODS=['addOnPreMain', 'calledRun', 'UTF8ToString']\"")
|
||||
target_link_libraries(GD "-s \"EXPORTED_FUNCTIONS=['_free']\"")
|
||||
|
||||
# Linker debugging options
|
||||
#
|
||||
# target_link_libraries(GD "-s DEMANGLE_SUPPORT=1") # Demangle stack traces
|
||||
# target_link_libraries(GD "-s ASSERTIONS=1") # Basic runtime memory allocation checks (necessary for wasm exceptions stack traces)
|
||||
# target_link_libraries(GD "-s ASSERTIONS=2 -s SAFE_HEAP=1") # Uncomment to do runtime checks for memory allocations and access errors
|
||||
# target_link_libraries(GD "-fsanitize=address") # Uncomment to auto-detect occurences of memory bugs (memory leak, use after free, overflows, ...) - also enable compiling above!
|
||||
# target_link_libraries(GD "-fsanitize=undefined") # Uncomment to auto-detect occurences of undefined behavior - also enable compiling above!
|
||||
# target_link_libraries(--cpuprofiler) # Uncomment for interactive performance profiling
|
||||
# target_link_libraries(--memoryprofiler) # Uncomment for interactive memory profiling
|
||||
|
||||
# Even if we're building an "executable", prefix it by lib as it's used as a library.
|
||||
set_target_properties(GD PROPERTIES PREFIX "lib")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user