mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
1 Commits
feat/touch
...
refactor/g
Author | SHA1 | Date | |
---|---|---|---|
![]() |
994848bcdf |
@@ -329,6 +329,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
condition.SetParameters(parameters);
|
||||
}
|
||||
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
// Verify that there are no mismatches between object type in parameters.
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
@@ -356,11 +357,6 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(condition, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
gd::String objectName = condition.GetParameter(0).GetPlainString();
|
||||
@@ -492,16 +488,14 @@ gd::String EventsCodeGenerator::GenerateConditionsListCode(
|
||||
return outputCode;
|
||||
}
|
||||
|
||||
bool EventsCodeGenerator::CheckBehaviorParameters(
|
||||
void EventsCodeGenerator::CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos) {
|
||||
bool isAnyBehaviorMissing = false;
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
gd::ParameterMetadataTools::IterateOverParameters(
|
||||
instruction.GetParameters(), instrInfos.parameters,
|
||||
[this, &isAnyBehaviorMissing,
|
||||
&instrInfos](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
[this](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue,
|
||||
const gd::String &lastObjectName) {
|
||||
if (ParameterMetadata::IsBehavior(parameterMetadata.GetType())) {
|
||||
const gd::String &behaviorName = parameterValue.GetPlainString();
|
||||
const gd::String &actualBehaviorType =
|
||||
@@ -512,25 +506,13 @@ bool EventsCodeGenerator::CheckBehaviorParameters(
|
||||
|
||||
if (!expectedBehaviorType.empty() &&
|
||||
actualBehaviorType != expectedBehaviorType) {
|
||||
const auto &objectParameterMetadata =
|
||||
instrInfos.GetParameter(lastObjectIndex);
|
||||
// Event functions crash if some objects in a group are missing
|
||||
// the required behaviors, since they lose reference to the original
|
||||
// objects. Missing behaviors are considered "fatal" only for
|
||||
// ObjectList parameters, in order to minimize side effects on
|
||||
// built-in functions.
|
||||
if (objectParameterMetadata.GetType() == "objectList") {
|
||||
isAnyBehaviorMissing = true;
|
||||
}
|
||||
gd::ProjectDiagnostic projectDiagnostic(
|
||||
gd::ProjectDiagnostic::ErrorType::MissingBehavior, "",
|
||||
actualBehaviorType, expectedBehaviorType, lastObjectName);
|
||||
if (diagnosticReport)
|
||||
diagnosticReport->Add(projectDiagnostic);
|
||||
if (diagnosticReport) diagnosticReport->Add(projectDiagnostic);
|
||||
}
|
||||
}
|
||||
});
|
||||
return isAnyBehaviorMissing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -570,6 +552,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
action.SetParameters(parameters);
|
||||
}
|
||||
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
// Verify that there are no mismatches between object type in parameters.
|
||||
for (std::size_t pNb = 0; pNb < instrInfos.parameters.GetParametersCount(); ++pNb) {
|
||||
if (ParameterMetadata::IsObject(instrInfos.parameters.GetParameter(pNb).GetType())) {
|
||||
@@ -596,11 +579,6 @@ gd::String EventsCodeGenerator::GenerateActionCode(
|
||||
}
|
||||
}
|
||||
}
|
||||
bool isAnyBehaviorMissing =
|
||||
gd::EventsCodeGenerator::CheckBehaviorParameters(action, instrInfos);
|
||||
if (isAnyBehaviorMissing) {
|
||||
return "/* Missing behavior - skipped. */";
|
||||
}
|
||||
|
||||
// Call free function first if available
|
||||
if (instrInfos.IsObjectInstruction()) {
|
||||
@@ -791,7 +769,7 @@ gd::String EventsCodeGenerator::GenerateActionsListCode(
|
||||
} else {
|
||||
outputCode += actionCode;
|
||||
}
|
||||
outputCode += "}\n";
|
||||
outputCode += "}";
|
||||
}
|
||||
|
||||
return outputCode;
|
||||
|
@@ -837,7 +837,7 @@ protected:
|
||||
virtual gd::String GenerateGetBehaviorNameCode(
|
||||
const gd::String& behaviorName);
|
||||
|
||||
bool CheckBehaviorParameters(
|
||||
void CheckBehaviorParameters(
|
||||
const gd::Instruction &instruction,
|
||||
const gd::InstructionMetadata &instrInfos);
|
||||
|
||||
|
@@ -52,25 +52,10 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
.SetHidden();
|
||||
|
||||
extension
|
||||
.AddCondition(
|
||||
"KeyFromTextPressed",
|
||||
_("Key pressed"),
|
||||
_("Check if a key is pressed. This stays true as long as "
|
||||
"the key is held down. To check if a key was pressed during "
|
||||
"the frame, use \"Key just pressed\" instead."),
|
||||
_("_PARAM1_ key is pressed"),
|
||||
"",
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
.AddCodeOnlyParameter("currentScene", "")
|
||||
.AddParameter("keyboardKey", _("Key to check"))
|
||||
.MarkAsSimple();
|
||||
|
||||
extension
|
||||
.AddCondition("KeyFromTextJustPressed",
|
||||
_("Key just pressed"),
|
||||
_("Check if a key was just pressed."),
|
||||
_("_PARAM1_ key was just pressed"),
|
||||
.AddCondition("KeyFromTextPressed",
|
||||
_("Key pressed"),
|
||||
_("Check if a key is pressed"),
|
||||
_("_PARAM1_ key is pressed"),
|
||||
"",
|
||||
"res/conditions/keyboard24.png",
|
||||
"res/conditions/keyboard.png")
|
||||
@@ -81,7 +66,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsKeyboardExtension(
|
||||
extension
|
||||
.AddCondition("KeyFromTextReleased",
|
||||
_("Key released"),
|
||||
_("Check if a key was just released."),
|
||||
_("Check if a key was just released"),
|
||||
_("_PARAM1_ key is released"),
|
||||
"",
|
||||
"res/conditions/keyboard24.png",
|
||||
|
@@ -298,19 +298,6 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the behavior can be used on objects from event-based objects.
|
||||
*/
|
||||
bool IsRelevantForChildObjects() const { return isRelevantForChildObjects; }
|
||||
|
||||
/**
|
||||
* Set that behavior can't be used on objects from event-based objects.
|
||||
*/
|
||||
BehaviorMetadata &MarkAsIrrelevantForChildObjects() {
|
||||
isRelevantForChildObjects = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QuickCustomization::Visibility GetQuickCustomizationVisibility() const {
|
||||
return quickCustomizationVisibility;
|
||||
}
|
||||
@@ -406,7 +393,6 @@ class GD_CORE_API BehaviorMetadata : public InstructionOrExpressionContainerMeta
|
||||
mutable std::vector<gd::String> requiredBehaviors;
|
||||
bool isPrivate = false;
|
||||
bool isHidden = false;
|
||||
bool isRelevantForChildObjects = true;
|
||||
gd::String openFullEditorLabel;
|
||||
QuickCustomization::Visibility quickCustomizationVisibility = QuickCustomization::Visibility::Default;
|
||||
|
||||
|
@@ -194,8 +194,7 @@ void ParameterMetadataTools::IterateOverParameters(
|
||||
[&fn](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex) {
|
||||
const gd::String& lastObjectName) {
|
||||
fn(parameterMetadata, parameterValue, lastObjectName);
|
||||
});
|
||||
}
|
||||
@@ -206,10 +205,8 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex)> fn) {
|
||||
const gd::String& lastObjectName)> fn) {
|
||||
gd::String lastObjectName = "";
|
||||
size_t lastObjectIndex = 0;
|
||||
for (std::size_t pNb = 0; pNb < parametersMetadata.GetParametersCount();
|
||||
++pNb) {
|
||||
const gd::ParameterMetadata ¶meterMetadata =
|
||||
@@ -221,17 +218,15 @@ void ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
? Expression(parameterMetadata.GetDefaultValue())
|
||||
: parameterValue;
|
||||
|
||||
fn(parameterMetadata, parameterValueOrDefault, pNb, lastObjectName, lastObjectIndex);
|
||||
fn(parameterMetadata, parameterValueOrDefault, pNb, lastObjectName);
|
||||
|
||||
// Memorize the last object name. By convention, parameters that require
|
||||
// an object (mainly, "objectvar" and "behavior") should be placed after
|
||||
// the object in the list of parameters (if possible, just after).
|
||||
// Search "lastObjectName" in the codebase for other place where this
|
||||
// convention is enforced.
|
||||
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType())) {
|
||||
if (gd::ParameterMetadata::IsObject(parameterMetadata.GetType()))
|
||||
lastObjectName = parameterValueOrDefault.GetPlainString();
|
||||
lastObjectIndex = pNb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -64,8 +64,7 @@ class GD_CORE_API ParameterMetadataTools {
|
||||
std::function<void(const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName,
|
||||
size_t lastObjectIndex)> fn);
|
||||
const gd::String& lastObjectName)> fn);
|
||||
|
||||
/**
|
||||
* Iterate over the parameters of a FunctionCallNode.
|
||||
|
@@ -813,13 +813,6 @@ gd::String PlatformExtension::GetObjectFullType(const gd::String& extensionName,
|
||||
return extensionName + separator + objectName;
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetVariantFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
const gd::String& variantName) {
|
||||
const auto& separator = GetNamespaceSeparator();
|
||||
return extensionName + separator + objectName + separator + variantName;
|
||||
}
|
||||
|
||||
gd::String PlatformExtension::GetExtensionFromFullObjectType(
|
||||
const gd::String& type) {
|
||||
const auto separatorIndex =
|
||||
|
@@ -663,10 +663,6 @@ class GD_CORE_API PlatformExtension {
|
||||
static gd::String GetObjectFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName);
|
||||
|
||||
static gd::String GetVariantFullType(const gd::String& extensionName,
|
||||
const gd::String& objectName,
|
||||
const gd::String& variantName);
|
||||
|
||||
static gd::String GetExtensionFromFullObjectType(const gd::String& type);
|
||||
|
||||
static gd::String GetObjectNameFromFullObjectType(const gd::String& type);
|
||||
|
@@ -29,7 +29,7 @@ bool BehaviorParametersFiller::DoVisitInstruction(gd::Instruction &instruction,
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const gd::String &lastObjectName) {
|
||||
if (parameterMetadata.GetValueTypeMetadata().IsBehavior() &&
|
||||
parameterValue.GetPlainString().length() == 0) {
|
||||
|
||||
|
@@ -108,10 +108,12 @@ bool EventsBehaviorRenamer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (gd::ParameterMetadata::IsBehavior(type)) {
|
||||
|
@@ -183,10 +183,12 @@ bool EventsParameterReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
if (!gd::EventsParameterReplacer::CanContainParameter(
|
||||
parameterMetadata.GetValueTypeMetadata())) {
|
||||
return;
|
||||
|
@@ -217,10 +217,12 @@ bool EventsPropertyReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
bool shouldDeleteInstruction = false;
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
if (!gd::EventsPropertyReplacer::CanContainProperty(
|
||||
parameterMetadata.GetValueTypeMetadata())) {
|
||||
return;
|
||||
|
@@ -334,7 +334,7 @@ private:
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const gd::String &lastObjectName) {
|
||||
if (!gd::EventsObjectReplacer::CanContainObject(
|
||||
parameterMetadata.GetValueTypeMetadata())) {
|
||||
return;
|
||||
|
@@ -42,16 +42,18 @@ bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction&
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) ||
|
||||
!gd::VariableInstructionSwitcher::IsSwitchableVariableInstruction(
|
||||
instruction.GetType())) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
const auto variableName =
|
||||
gd::ExpressionVariableNameFinder::GetVariableName(
|
||||
@@ -70,11 +72,10 @@ bool EventsVariableInstructionTypeSwitcher::DoVisitInstruction(gd::Instruction&
|
||||
.GetObjectOrGroupVariablesContainer(lastObjectName);
|
||||
}
|
||||
} else if (type == "variableOrProperty") {
|
||||
variablesContainer =
|
||||
&GetProjectScopedContainers()
|
||||
.GetVariablesContainersList()
|
||||
.GetVariablesContainerFromVariableOrPropertyName(
|
||||
variableName);
|
||||
variablesContainer =
|
||||
&GetProjectScopedContainers()
|
||||
.GetVariablesContainersList()
|
||||
.GetVariablesContainerFromVariableOrPropertyName(variableName);
|
||||
} else {
|
||||
if (GetProjectScopedContainers().GetVariablesContainersList().Has(
|
||||
variableName)) {
|
||||
|
@@ -448,10 +448,12 @@ bool EventsVariableReplacer::DoVisitInstruction(gd::Instruction& instruction,
|
||||
bool shouldDeleteInstruction = false;
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterValue,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const gd::String& type = parameterMetadata.GetType();
|
||||
|
||||
if (!gd::ParameterMetadata::IsExpression("variable", type) &&
|
||||
|
@@ -150,7 +150,7 @@ bool ProjectElementRenamer::DoVisitInstruction(gd::Instruction &instruction,
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[&](const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterValue, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
const gd::String &lastObjectName) {
|
||||
if (parameterMetadata.GetType() == "layer") {
|
||||
if (parameterValue.GetPlainString().length() < 2) {
|
||||
// This is either the base layer or an invalid layer name.
|
||||
|
@@ -1,23 +0,0 @@
|
||||
#include "UsedObjectTypeFinder.h"
|
||||
|
||||
#include "GDCore/Events/Instruction.h"
|
||||
#include "GDCore/IDE/ProjectBrowserHelper.h"
|
||||
#include "GDCore/Project/Object.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
bool UsedObjectTypeFinder::ScanProject(gd::Project &project,
|
||||
const gd::String &objectType) {
|
||||
UsedObjectTypeFinder worker(project, objectType);
|
||||
gd::ProjectBrowserHelper::ExposeProjectObjects(project, worker);
|
||||
return worker.hasFoundObjectType;
|
||||
};
|
||||
|
||||
void UsedObjectTypeFinder::DoVisitObject(gd::Object &object) {
|
||||
if (!hasFoundObjectType && object.GetType() == objectType) {
|
||||
hasFoundObjectType = true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <set>
|
||||
|
||||
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
|
||||
#include "GDCore/Extensions/Metadata/SourceFileMetadata.h"
|
||||
#include "GDCore/Extensions/PlatformExtension.h"
|
||||
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryObjectsWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
class Project;
|
||||
class Object;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
class GD_CORE_API UsedObjectTypeFinder : public ArbitraryObjectsWorker {
|
||||
public:
|
||||
static bool ScanProject(gd::Project &project, const gd::String &objectType);
|
||||
|
||||
private:
|
||||
UsedObjectTypeFinder(gd::Project &project_, const gd::String &objectType_)
|
||||
: project(project_), objectType(objectType_){};
|
||||
gd::Project &project;
|
||||
const gd::String &objectType;
|
||||
bool hasFoundObjectType = false;
|
||||
|
||||
// Object Visitor
|
||||
void DoVisitObject(gd::Object &object) override;
|
||||
};
|
||||
|
||||
}; // namespace gd
|
@@ -76,7 +76,6 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
|
||||
double width = 0;
|
||||
double height = 0;
|
||||
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
|
||||
if (project.HasEventsBasedObject(object.GetType())) {
|
||||
SerializerElement &variantsElement =
|
||||
objectAssetElement.AddChild("variants");
|
||||
@@ -88,6 +87,7 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
height = variant->GetAreaMaxY() - variant->GetAreaMinY();
|
||||
}
|
||||
|
||||
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
|
||||
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
|
||||
project, object, variantsElement, alreadyUsedVariantIdentifiers);
|
||||
}
|
||||
@@ -114,24 +114,14 @@ void ObjectAssetSerializer::SerializeTo(
|
||||
resourceElement.SetAttribute("name", resource.GetName());
|
||||
}
|
||||
|
||||
std::unordered_set<gd::String> usedExtensionNames;
|
||||
usedExtensionNames.insert(extensionName);
|
||||
for (auto &usedVariantIdentifier : alreadyUsedVariantIdentifiers) {
|
||||
usedExtensionNames.insert(PlatformExtension::GetExtensionFromFullObjectType(
|
||||
usedVariantIdentifier));
|
||||
}
|
||||
SerializerElement &requiredExtensionsElement =
|
||||
objectAssetElement.AddChild("requiredExtensions");
|
||||
requiredExtensionsElement.ConsiderAsArrayOf("requiredExtension");
|
||||
for (auto &usedExtensionName : usedExtensionNames) {
|
||||
if (project.HasEventsFunctionsExtensionNamed(usedExtensionName)) {
|
||||
auto &extension = project.GetEventsFunctionsExtension(usedExtensionName);
|
||||
SerializerElement &requiredExtensionElement =
|
||||
requiredExtensionsElement.AddChild("requiredExtension");
|
||||
requiredExtensionElement.SetAttribute("extensionName", usedExtensionName);
|
||||
requiredExtensionElement.SetAttribute("extensionVersion",
|
||||
extension.GetVersion());
|
||||
}
|
||||
if (project.HasEventsFunctionsExtensionNamed(extensionName)) {
|
||||
SerializerElement &requiredExtensionElement =
|
||||
requiredExtensionsElement.AddChild("requiredExtension");
|
||||
requiredExtensionElement.SetAttribute("extensionName", extensionName);
|
||||
requiredExtensionElement.SetAttribute("extensionVersion", "1.0.0");
|
||||
}
|
||||
|
||||
// TODO This can be removed when the asset script no longer require it.
|
||||
|
@@ -227,11 +227,12 @@ bool ResourceWorkerInEventsWorker::DoVisitInstruction(gd::Instruction& instructi
|
||||
platform, instruction.GetType());
|
||||
|
||||
gd::ParameterMetadataTools::IterateOverParametersWithIndex(
|
||||
instruction.GetParameters(), metadata.GetParameters(),
|
||||
[this, &instruction](
|
||||
const gd::ParameterMetadata ¶meterMetadata,
|
||||
const gd::Expression ¶meterExpression, size_t parameterIndex,
|
||||
const gd::String &lastObjectName, size_t lastObjectIndex) {
|
||||
instruction.GetParameters(),
|
||||
metadata.GetParameters(),
|
||||
[this, &instruction](const gd::ParameterMetadata& parameterMetadata,
|
||||
const gd::Expression& parameterExpression,
|
||||
size_t parameterIndex,
|
||||
const gd::String& lastObjectName) {
|
||||
const String& parameterValue = parameterExpression.GetPlainString();
|
||||
if (parameterMetadata.GetType() == "fontResource") {
|
||||
gd::String updatedParameterValue = parameterValue;
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include "GDCore/CommonTools.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ResourcesAbsolutePathChecker.h"
|
||||
#include "GDCore/IDE/Project/ResourcesMergingHelper.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Tools/Localization.h"
|
||||
@@ -25,37 +26,42 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
bool preserveAbsoluteFilenames,
|
||||
bool preserveDirectoryStructure) {
|
||||
if (updateOriginalProject) {
|
||||
gd::ProjectResourcesCopier::AdaptFilePathsAndCopyAllResourcesTo(
|
||||
originalProject, fs, destinationDirectory, preserveAbsoluteFilenames,
|
||||
preserveDirectoryStructure);
|
||||
gd::ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
originalProject, originalProject, fs, destinationDirectory,
|
||||
preserveAbsoluteFilenames, preserveDirectoryStructure);
|
||||
} else {
|
||||
gd::Project clonedProject = originalProject;
|
||||
gd::ProjectResourcesCopier::AdaptFilePathsAndCopyAllResourcesTo(
|
||||
clonedProject, fs, destinationDirectory, preserveAbsoluteFilenames,
|
||||
preserveDirectoryStructure);
|
||||
gd::ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
originalProject, clonedProject, fs, destinationDirectory,
|
||||
preserveAbsoluteFilenames, preserveDirectoryStructure);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectResourcesCopier::AdaptFilePathsAndCopyAllResourcesTo(
|
||||
gd::Project& project,
|
||||
bool ProjectResourcesCopier::CopyAllResourcesTo(
|
||||
gd::Project& originalProject,
|
||||
gd::Project& clonedProject,
|
||||
AbstractFileSystem& fs,
|
||||
gd::String destinationDirectory,
|
||||
bool preserveAbsoluteFilenames,
|
||||
bool preserveDirectoryStructure) {
|
||||
|
||||
auto projectDirectory = fs.DirNameFrom(project.GetProjectFile());
|
||||
// Check if there are some resources with absolute filenames
|
||||
gd::ResourcesAbsolutePathChecker absolutePathChecker(originalProject.GetResourcesManager(), fs);
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(originalProject, absolutePathChecker);
|
||||
|
||||
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
|
||||
std::cout << "Copying all resources from " << projectDirectory << " to "
|
||||
<< destinationDirectory << "..." << std::endl;
|
||||
|
||||
// Get the resources to be copied
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
project.GetResourcesManager(), fs);
|
||||
clonedProject.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
resourcesMergingHelper.PreserveDirectoriesStructure(
|
||||
preserveDirectoryStructure);
|
||||
resourcesMergingHelper.PreserveAbsoluteFilenames(preserveAbsoluteFilenames);
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(project,
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
|
||||
resourcesMergingHelper);
|
||||
|
||||
// Copy resources
|
||||
|
@@ -50,10 +50,12 @@ class GD_CORE_API ProjectResourcesCopier {
|
||||
bool preserveDirectoryStructure = true);
|
||||
|
||||
private:
|
||||
static bool AdaptFilePathsAndCopyAllResourcesTo(
|
||||
gd::Project &project, gd::AbstractFileSystem &fs,
|
||||
gd::String destinationDirectory, bool preserveAbsoluteFilenames = true,
|
||||
bool preserveDirectoryStructure = true);
|
||||
static bool CopyAllResourcesTo(gd::Project& originalProject,
|
||||
gd::Project& clonedProject,
|
||||
gd::AbstractFileSystem& fs,
|
||||
gd::String destinationDirectory,
|
||||
bool preserveAbsoluteFilenames = true,
|
||||
bool preserveDirectoryStructure = true);
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
17
Core/GDCore/IDE/Project/ResourcesAbsolutePathChecker.cpp
Normal file
17
Core/GDCore/IDE/Project/ResourcesAbsolutePathChecker.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
|
||||
#include "ResourcesAbsolutePathChecker.h"
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
void ResourcesAbsolutePathChecker::ExposeFile(gd::String& resourceFilename) {
|
||||
if (fs.IsAbsolute(resourceFilename)) hasAbsoluteFilenames = true;
|
||||
}
|
||||
|
||||
} // namespace gd
|
48
Core/GDCore/IDE/Project/ResourcesAbsolutePathChecker.h
Normal file
48
Core/GDCore/IDE/Project/ResourcesAbsolutePathChecker.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* GDevelop Core
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/AbstractFileSystem.h"
|
||||
#include "GDCore/IDE/Project/ArbitraryResourceWorker.h"
|
||||
#include "GDCore/String.h"
|
||||
|
||||
namespace gd {
|
||||
|
||||
/**
|
||||
* \brief Helper used to check if a project has at least a resource with an
|
||||
* absolute filename.
|
||||
*
|
||||
* \see ArbitraryResourceWorker
|
||||
*
|
||||
* \ingroup IDE
|
||||
*/
|
||||
class GD_CORE_API ResourcesAbsolutePathChecker
|
||||
: public ArbitraryResourceWorker {
|
||||
public:
|
||||
ResourcesAbsolutePathChecker(gd::ResourcesManager &resourcesManager,
|
||||
AbstractFileSystem &fileSystem)
|
||||
: ArbitraryResourceWorker(resourcesManager), hasAbsoluteFilenames(false),
|
||||
fs(fileSystem){};
|
||||
virtual ~ResourcesAbsolutePathChecker(){};
|
||||
|
||||
/**
|
||||
* Return true if there is at least a resource with an absolute filename.
|
||||
*/
|
||||
bool HasResourceWithAbsoluteFilenames() const {
|
||||
return hasAbsoluteFilenames;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if there is a resource with an absolute path
|
||||
*/
|
||||
virtual void ExposeFile(gd::String& resource);
|
||||
|
||||
private:
|
||||
bool hasAbsoluteFilenames;
|
||||
AbstractFileSystem& fs;
|
||||
};
|
||||
|
||||
} // namespace gd
|
@@ -22,14 +22,6 @@ void ResourcesMergingHelper::ExposeFile(gd::String& resourceFilename) {
|
||||
resourceFullFilename = gd::AbstractFileSystem::NormalizeSeparator(
|
||||
resourceFullFilename); // Protect against \ on Linux.
|
||||
|
||||
if (shouldUseOriginalAbsoluteFilenames) {
|
||||
// There is no need to fill `newFilenames` and `oldFilenames` since the file
|
||||
// location stays the same.
|
||||
fs.MakeAbsolute(resourceFullFilename, baseDirectory);
|
||||
resourceFilename = resourceFullFilename;
|
||||
return;
|
||||
}
|
||||
|
||||
// In the case of absolute filenames that we don't want to preserve, or
|
||||
// in the case of copying files without preserving relative folders, the new
|
||||
// names will be generated from the filename alone (with collision protection).
|
||||
|
@@ -3,7 +3,8 @@
|
||||
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
|
||||
* reserved. This project is released under the MIT License.
|
||||
*/
|
||||
#pragma once
|
||||
#ifndef RESOURCESMERGINGHELPER_H
|
||||
#define RESOURCESMERGINGHELPER_H
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -57,15 +58,6 @@ public:
|
||||
preserveAbsoluteFilenames = preserveAbsoluteFilenames_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Set if the absolute filenames of original files must be used for
|
||||
* any resource.
|
||||
*/
|
||||
void SetShouldUseOriginalAbsoluteFilenames(
|
||||
bool shouldUseOriginalAbsoluteFilenames_ = true) {
|
||||
shouldUseOriginalAbsoluteFilenames = shouldUseOriginalAbsoluteFilenames_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Return a map containing the resources old absolute filename as key,
|
||||
* and the resources new filenames as value. The new filenames are relative to
|
||||
@@ -101,13 +93,10 @@ public:
|
||||
///< absolute (C:\MyFile.png will not be
|
||||
///< transformed into a relative filename
|
||||
///< (MyFile.png).
|
||||
/**
|
||||
* Set to true if the absolute filenames of original files must be used for
|
||||
* any resource.
|
||||
*/
|
||||
bool shouldUseOriginalAbsoluteFilenames = false;
|
||||
gd::AbstractFileSystem&
|
||||
fs; ///< The gd::AbstractFileSystem used to manipulate files.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
||||
#endif // RESOURCESMERGINGHELPER_H
|
||||
|
@@ -6,7 +6,6 @@
|
||||
#include "SceneResourcesFinder.h"
|
||||
|
||||
#include "GDCore/IDE/ResourceExposer.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
#include "GDCore/Project/Project.h"
|
||||
#include "GDCore/Serialization/SerializerElement.h"
|
||||
@@ -28,14 +27,6 @@ std::set<gd::String> SceneResourcesFinder::FindSceneResources(gd::Project &proje
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
std::set<gd::String> SceneResourcesFinder::FindEventsBasedObjectVariantResources(gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &variant) {
|
||||
gd::SceneResourcesFinder resourceWorker(project.GetResourcesManager());
|
||||
|
||||
gd::ResourceExposer::ExposeEventsBasedObjectVariantResources(project, variant, resourceWorker);
|
||||
return resourceWorker.resourceNames;
|
||||
}
|
||||
|
||||
void SceneResourcesFinder::AddUsedResource(gd::String &resourceName) {
|
||||
if (resourceName.empty()) {
|
||||
return;
|
||||
|
@@ -15,7 +15,6 @@ namespace gd {
|
||||
class Project;
|
||||
class Layout;
|
||||
class SerializerElement;
|
||||
class EventsBasedObjectVariant;
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
@@ -28,7 +27,7 @@ namespace gd {
|
||||
class SceneResourcesFinder : private gd::ArbitraryResourceWorker {
|
||||
public:
|
||||
/**
|
||||
* @brief Find resource usages in a given scene.
|
||||
* @brief Find resource usages in a given scenes.
|
||||
*
|
||||
* It doesn't include resources used globally.
|
||||
*/
|
||||
@@ -42,13 +41,6 @@ public:
|
||||
*/
|
||||
static std::set<gd::String> FindProjectResources(gd::Project &project);
|
||||
|
||||
/**
|
||||
* @brief Find resource usages in a given events-based object variant.
|
||||
*/
|
||||
static std::set<gd::String>
|
||||
FindEventsBasedObjectVariantResources(gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &variant);
|
||||
|
||||
virtual ~SceneResourcesFinder(){};
|
||||
|
||||
private:
|
||||
|
@@ -332,12 +332,6 @@ void ProjectBrowserHelper::ExposeLayoutObjects(gd::Layout &layout,
|
||||
worker.Launch(layout.GetObjects());
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeEventsBasedObjectVariantObjects(
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryObjectsWorker &worker) {
|
||||
worker.Launch(eventsBasedObjectVariant.GetObjects());
|
||||
}
|
||||
|
||||
void ProjectBrowserHelper::ExposeProjectFunctions(
|
||||
gd::Project &project, gd::ArbitraryEventsFunctionsWorker &worker) {
|
||||
|
||||
|
@@ -13,7 +13,6 @@ class EventsFunctionsExtension;
|
||||
class EventsFunction;
|
||||
class EventsBasedBehavior;
|
||||
class EventsBasedObject;
|
||||
class EventsBasedObjectVariant;
|
||||
class ArbitraryEventsWorker;
|
||||
class ArbitraryEventsWorkerWithContext;
|
||||
class ArbitraryEventsFunctionsWorker;
|
||||
@@ -208,17 +207,6 @@ public:
|
||||
static void ExposeLayoutObjects(gd::Layout &layout,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all ObjectContainers of the
|
||||
* events-based object variant.
|
||||
*
|
||||
* This should be the preferred way to traverse all the objects of an
|
||||
* events-based object variant.
|
||||
*/
|
||||
static void ExposeEventsBasedObjectVariantObjects(
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryObjectsWorker &worker);
|
||||
|
||||
/**
|
||||
* \brief Call the specified worker on all FunctionsContainers of the project
|
||||
* (global, layouts...)
|
||||
|
@@ -248,13 +248,12 @@ gd::String PropertyFunctionGenerator::GetStringifiedExtraInfo(
|
||||
gd::String arrayString;
|
||||
arrayString += "[";
|
||||
bool isFirst = true;
|
||||
for (const auto &choice : property.GetChoices()) {
|
||||
for (const gd::String &choice : property.GetExtraInfo()) {
|
||||
if (!isFirst) {
|
||||
arrayString += ",";
|
||||
}
|
||||
isFirst = false;
|
||||
// TODO Handle labels (and search "choice label")
|
||||
arrayString += "\"" + choice.GetValue() + "\"";
|
||||
arrayString += "\"" + choice + "\"";
|
||||
}
|
||||
arrayString += "]";
|
||||
return arrayString;
|
||||
|
@@ -116,34 +116,6 @@ void ResourceExposer::ExposeLayoutResources(
|
||||
project, layout, eventWorker);
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeEventsBasedObjectVariantResources(
|
||||
gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryResourceWorker &worker) {
|
||||
// Expose object configuration resources
|
||||
auto objectWorker = gd::GetResourceWorkerOnObjects(project, worker);
|
||||
gd::ProjectBrowserHelper::ExposeEventsBasedObjectVariantObjects(
|
||||
eventsBasedObjectVariant, objectWorker);
|
||||
|
||||
// Expose layer effect resources
|
||||
auto &layers = eventsBasedObjectVariant.GetLayers();
|
||||
for (std::size_t layerIndex = 0; layerIndex < layers.GetLayersCount();
|
||||
layerIndex++) {
|
||||
auto &layer = layers.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);
|
||||
}
|
||||
}
|
||||
// We don't check the events because it would cost too much to do it for every
|
||||
// variant. Resource usage in events-based object events and their
|
||||
// dependencies should be rare.
|
||||
}
|
||||
|
||||
void ResourceExposer::ExposeEffectResources(
|
||||
gd::Platform &platform,
|
||||
gd::Effect &effect,
|
||||
|
@@ -9,11 +9,10 @@ namespace gd {
|
||||
class Platform;
|
||||
class Project;
|
||||
class ArbitraryResourceWorker;
|
||||
class EventsBasedObjectVariant;
|
||||
class EventsFunctionsExtension;
|
||||
class Effect;
|
||||
class Layout;
|
||||
} // namespace gd
|
||||
} // namespace gd
|
||||
|
||||
namespace gd {
|
||||
|
||||
@@ -21,7 +20,7 @@ namespace gd {
|
||||
* \brief
|
||||
*/
|
||||
class GD_CORE_API ResourceExposer {
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* \brief Called ( e.g. during compilation ) so as to inventory internal
|
||||
* resources, sometimes update their filename or any other work or resources.
|
||||
@@ -51,14 +50,6 @@ public:
|
||||
gd::Layout &layout,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given events-based object variant.
|
||||
*/
|
||||
static void ExposeEventsBasedObjectVariantResources(
|
||||
gd::Project &project,
|
||||
gd::EventsBasedObjectVariant &eventsBasedObjectVariant,
|
||||
gd::ArbitraryResourceWorker &worker);
|
||||
|
||||
/**
|
||||
* @brief Expose the resources used in a given effect.
|
||||
*/
|
||||
|
@@ -37,7 +37,6 @@ void EventsBasedObjectVariant::SerializeTo(SerializerElement &element) const {
|
||||
|
||||
layers.SerializeLayersTo(element.AddChild("layers"));
|
||||
initialInstances.SerializeTo(element.AddChild("instances"));
|
||||
editorSettings.SerializeTo(element.AddChild("editionSettings"));
|
||||
}
|
||||
|
||||
void EventsBasedObjectVariant::UnserializeFrom(
|
||||
@@ -67,7 +66,6 @@ void EventsBasedObjectVariant::UnserializeFrom(
|
||||
layers.Reset();
|
||||
}
|
||||
initialInstances.UnserializeFrom(element.GetChild("instances"));
|
||||
editorSettings.UnserializeFrom(element.GetChild("editionSettings"));
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "GDCore/IDE/Dialogs/LayoutEditorCanvas/EditorSettings.h"
|
||||
#include "GDCore/Project/InitialInstancesContainer.h"
|
||||
#include "GDCore/Project/LayersContainer.h"
|
||||
#include "GDCore/Project/ObjectsContainer.h"
|
||||
@@ -200,19 +199,6 @@ public:
|
||||
const gd::String &GetAssetStoreOriginalName() const {
|
||||
return assetStoreOriginalName;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* \brief Get the user settings for the IDE.
|
||||
*/
|
||||
const gd::EditorSettings& GetAssociatedEditorSettings() const {
|
||||
return editorSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the user settings for the IDE.
|
||||
*/
|
||||
gd::EditorSettings& GetAssociatedEditorSettings() { return editorSettings; }
|
||||
|
||||
void SerializeTo(SerializerElement &element) const;
|
||||
|
||||
@@ -238,7 +224,6 @@ private:
|
||||
* store.
|
||||
*/
|
||||
gd::String assetStoreOriginalName;
|
||||
gd::EditorSettings editorSettings;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -60,18 +60,6 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
} else {
|
||||
SetHasCustomDepth(false);
|
||||
}
|
||||
if (element.HasChild("defaultWidth") ||
|
||||
element.HasAttribute("defaultWidth")) {
|
||||
defaultWidth = element.GetDoubleAttribute("defaultWidth");
|
||||
}
|
||||
if (element.HasChild("defaultHeight") ||
|
||||
element.HasAttribute("defaultHeight")) {
|
||||
defaultHeight = element.GetDoubleAttribute("defaultHeight");
|
||||
}
|
||||
if (element.HasChild("defaultDepth") ||
|
||||
element.HasAttribute("defaultDepth")) {
|
||||
defaultDepth = element.GetDoubleAttribute("defaultDepth");
|
||||
}
|
||||
SetZOrder(element.GetIntAttribute("zOrder", 0, "plan"));
|
||||
SetOpacity(element.GetIntAttribute("opacity", 255));
|
||||
SetLayer(element.GetStringAttribute("layer"));
|
||||
@@ -86,51 +74,45 @@ void InitialInstance::UnserializeFrom(const SerializerElement& element) {
|
||||
if (persistentUuid.empty()) ResetPersistentUuid();
|
||||
|
||||
numberProperties.clear();
|
||||
if (element.HasChild("numberProperties", "floatInfos")) {
|
||||
const SerializerElement& numberPropertiesElement =
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
|
||||
gd::String name =
|
||||
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
||||
double value =
|
||||
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
||||
const SerializerElement& numberPropertiesElement =
|
||||
element.GetChild("numberProperties", 0, "floatInfos");
|
||||
numberPropertiesElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < numberPropertiesElement.GetChildrenCount(); ++j) {
|
||||
gd::String name =
|
||||
numberPropertiesElement.GetChild(j).GetStringAttribute("name");
|
||||
double value =
|
||||
numberPropertiesElement.GetChild(j).GetDoubleAttribute("value");
|
||||
|
||||
// Compatibility with GD <= 5.1.164
|
||||
if (name == "z") {
|
||||
SetZ(value);
|
||||
} else if (name == "rotationX") {
|
||||
SetRotationX(value);
|
||||
} else if (name == "rotationY") {
|
||||
SetRotationY(value);
|
||||
} else if (name == "depth") {
|
||||
SetHasCustomDepth(true);
|
||||
SetCustomDepth(value);
|
||||
}
|
||||
// end of compatibility code
|
||||
else {
|
||||
numberProperties[name] = value;
|
||||
}
|
||||
// Compatibility with GD <= 5.1.164
|
||||
if (name == "z") {
|
||||
SetZ(value);
|
||||
} else if (name == "rotationX") {
|
||||
SetRotationX(value);
|
||||
} else if (name == "rotationY") {
|
||||
SetRotationY(value);
|
||||
} else if (name == "depth") {
|
||||
SetHasCustomDepth(true);
|
||||
SetCustomDepth(value);
|
||||
}
|
||||
// end of compatibility code
|
||||
else {
|
||||
numberProperties[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
stringProperties.clear();
|
||||
if (element.HasChild("stringProperties", "stringInfos")) {
|
||||
const SerializerElement& stringPropElement =
|
||||
element.GetChild("stringProperties", 0, "stringInfos");
|
||||
stringPropElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < stringPropElement.GetChildrenCount(); ++j) {
|
||||
gd::String name = stringPropElement.GetChild(j).GetStringAttribute("name");
|
||||
gd::String value =
|
||||
stringPropElement.GetChild(j).GetStringAttribute("value");
|
||||
stringProperties[name] = value;
|
||||
}
|
||||
const SerializerElement& stringPropElement =
|
||||
element.GetChild("stringProperties", 0, "stringInfos");
|
||||
stringPropElement.ConsiderAsArrayOf("property", "Info");
|
||||
for (std::size_t j = 0; j < stringPropElement.GetChildrenCount(); ++j) {
|
||||
gd::String name = stringPropElement.GetChild(j).GetStringAttribute("name");
|
||||
gd::String value =
|
||||
stringPropElement.GetChild(j).GetStringAttribute("value");
|
||||
stringProperties[name] = value;
|
||||
}
|
||||
|
||||
if (element.HasChild("initialVariables", "InitialVariables")) {
|
||||
GetVariables().UnserializeFrom(
|
||||
element.GetChild("initialVariables", 0, "InitialVariables"));
|
||||
}
|
||||
GetVariables().UnserializeFrom(
|
||||
element.GetChild("initialVariables", 0, "InitialVariables"));
|
||||
}
|
||||
|
||||
void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
@@ -151,8 +133,6 @@ void InitialInstance::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("width", GetCustomWidth());
|
||||
element.SetAttribute("height", GetCustomHeight());
|
||||
if (HasCustomDepth()) element.SetAttribute("depth", GetCustomDepth());
|
||||
// defaultWidth, defaultHeight and defaultDepth are not serialized
|
||||
// because they are evaluated by InGameEditor.
|
||||
if (IsLocked()) element.SetAttribute("locked", IsLocked());
|
||||
if (IsSealed()) element.SetAttribute("sealed", IsSealed());
|
||||
if (ShouldKeepRatio()) element.SetAttribute("keepRatio", ShouldKeepRatio());
|
||||
|
@@ -219,13 +219,6 @@ class GD_CORE_API InitialInstance {
|
||||
double GetCustomDepth() const { return depth; }
|
||||
void SetCustomDepth(double depth_) { depth = depth_; }
|
||||
|
||||
double GetDefaultWidth() const { return defaultWidth; }
|
||||
double GetDefaultHeight() const { return defaultHeight; }
|
||||
double GetDefaultDepth() const { return defaultDepth; }
|
||||
void SetDefaultWidth(double width_) { defaultWidth = width_; }
|
||||
void SetDefaultHeight(double height_) { defaultHeight = height_; }
|
||||
void SetDefaultDepth(double depth_) { defaultDepth = depth_; }
|
||||
|
||||
/**
|
||||
* \brief Return true if the instance is locked and cannot be moved in the
|
||||
* IDE.
|
||||
@@ -373,11 +366,7 @@ class GD_CORE_API InitialInstance {
|
||||
*/
|
||||
InitialInstance& ResetPersistentUuid();
|
||||
|
||||
/**
|
||||
* \brief Reset the persistent UUID used to recognize
|
||||
* the same initial instance between serialization.
|
||||
*/
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; }
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; }
|
||||
///@}
|
||||
|
||||
private:
|
||||
@@ -406,9 +395,6 @@ class GD_CORE_API InitialInstance {
|
||||
double width; ///< Instance custom width
|
||||
double height; ///< Instance custom height
|
||||
double depth; ///< Instance custom depth
|
||||
double defaultWidth = 0; ///< Instance default width as reported by InGameEditor
|
||||
double defaultHeight = 0; ///< Instance default height as reported by InGameEditor
|
||||
double defaultDepth = 0; ///< Instance default depth as reported by InGameEditor
|
||||
gd::VariablesContainer initialVariables; ///< Instance specific variables
|
||||
bool locked; ///< True if the instance is locked
|
||||
bool sealed; ///< True if the instance is sealed
|
||||
|
@@ -23,7 +23,6 @@ Layer::Layer()
|
||||
camera3DNearPlaneDistance(3),
|
||||
camera3DFarPlaneDistance(10000),
|
||||
camera3DFieldOfView(45),
|
||||
camera2DPlaneMaxDrawingDistance(5000),
|
||||
ambientLightColorR(200),
|
||||
ambientLightColorG(200),
|
||||
ambientLightColorB(200) {}
|
||||
@@ -57,8 +56,6 @@ void Layer::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("camera3DFarPlaneDistance",
|
||||
GetCamera3DFarPlaneDistance());
|
||||
element.SetAttribute("camera3DFieldOfView", GetCamera3DFieldOfView());
|
||||
element.SetAttribute("camera2DPlaneMaxDrawingDistance",
|
||||
GetCamera2DPlaneMaxDrawingDistance());
|
||||
|
||||
SerializerElement& camerasElement = element.AddChild("cameras");
|
||||
camerasElement.ConsiderAsArrayOf("camera");
|
||||
@@ -102,8 +99,6 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
|
||||
"camera3DFarPlaneDistance", 10000, "threeDFarPlaneDistance"));
|
||||
SetCamera3DFieldOfView(element.GetDoubleAttribute(
|
||||
"camera3DFieldOfView", 45, "threeDFieldOfView"));
|
||||
SetCamera2DPlaneMaxDrawingDistance(element.GetDoubleAttribute(
|
||||
"camera2DPlaneMaxDrawingDistance", 5000));
|
||||
|
||||
cameras.clear();
|
||||
SerializerElement& camerasElement = element.GetChild("cameras");
|
||||
|
@@ -182,8 +182,6 @@ class GD_CORE_API Layer {
|
||||
}
|
||||
double GetCamera3DFieldOfView() const { return camera3DFieldOfView; }
|
||||
void SetCamera3DFieldOfView(double angle) { camera3DFieldOfView = angle; }
|
||||
double GetCamera2DPlaneMaxDrawingDistance() const { return camera2DPlaneMaxDrawingDistance; }
|
||||
void SetCamera2DPlaneMaxDrawingDistance(double distance) { camera2DPlaneMaxDrawingDistance = distance; }
|
||||
///@}
|
||||
|
||||
/** \name Cameras
|
||||
@@ -294,7 +292,6 @@ class GD_CORE_API Layer {
|
||||
double camera3DNearPlaneDistance; ///< 3D camera frustum near plan distance
|
||||
double camera3DFarPlaneDistance; ///< 3D camera frustum far plan distance
|
||||
double camera3DFieldOfView; ///< 3D camera field of view (fov) in degrees
|
||||
double camera2DPlaneMaxDrawingDistance; ///< Max drawing distance of the 2D plane when in the 3D world
|
||||
unsigned int ambientLightColorR; ///< Ambient light color Red component
|
||||
unsigned int ambientLightColorG; ///< Ambient light color Green component
|
||||
unsigned int ambientLightColorB; ///< Ambient light color Blue component
|
||||
|
@@ -730,8 +730,6 @@ void Project::UnserializeFrom(const SerializerElement& element) {
|
||||
SetPackageName(propElement.GetStringAttribute("packageName"));
|
||||
SetTemplateSlug(propElement.GetStringAttribute("templateSlug"));
|
||||
SetOrientation(propElement.GetStringAttribute("orientation", "default"));
|
||||
SetEffectsHiddenInEditor(
|
||||
propElement.GetBoolAttribute("areEffectsHiddenInEditor", false));
|
||||
SetFolderProject(propElement.GetBoolAttribute("folderProject"));
|
||||
SetLastCompilationDirectory(propElement
|
||||
.GetChild("latestCompilationDirectory",
|
||||
@@ -1111,10 +1109,6 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
propElement.SetAttribute("packageName", packageName);
|
||||
propElement.SetAttribute("templateSlug", templateSlug);
|
||||
propElement.SetAttribute("orientation", orientation);
|
||||
if (areEffectsHiddenInEditor) {
|
||||
propElement.SetBoolAttribute("areEffectsHiddenInEditor",
|
||||
areEffectsHiddenInEditor);
|
||||
}
|
||||
platformSpecificAssets.SerializeTo(
|
||||
propElement.AddChild("platformSpecificAssets"));
|
||||
loadingScreen.SerializeTo(propElement.AddChild("loadingScreen"));
|
||||
@@ -1156,8 +1150,6 @@ void Project::SerializeTo(SerializerElement& element) const {
|
||||
// end of compatibility code
|
||||
|
||||
extensionProperties.SerializeTo(propElement.AddChild("extensionProperties"));
|
||||
|
||||
playableDevicesElement.AddChild("").SetStringValue("mobile");
|
||||
|
||||
SerializerElement& platformsElement = propElement.AddChild("platforms");
|
||||
platformsElement.ConsiderAsArrayOf("platform");
|
||||
@@ -1327,8 +1319,6 @@ void Project::Init(const gd::Project& game) {
|
||||
|
||||
sceneResourcesPreloading = game.sceneResourcesPreloading;
|
||||
sceneResourcesUnloading = game.sceneResourcesUnloading;
|
||||
|
||||
areEffectsHiddenInEditor = game.areEffectsHiddenInEditor;
|
||||
}
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -506,20 +506,6 @@ class GD_CORE_API Project {
|
||||
*/
|
||||
void SetCurrentPlatform(const gd::String& platformName);
|
||||
|
||||
/**
|
||||
* Check if the effects are shown.
|
||||
*/
|
||||
bool AreEffectsHiddenInEditor() const { return areEffectsHiddenInEditor; }
|
||||
|
||||
/**
|
||||
* Define the project as playable on a mobile.
|
||||
* \param enable True When false effects are not shown and a default light is
|
||||
* used for 3D layers.
|
||||
*/
|
||||
void SetEffectsHiddenInEditor(bool enable = true) {
|
||||
areEffectsHiddenInEditor = enable;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/** \name Factory method
|
||||
@@ -1179,9 +1165,6 @@ class GD_CORE_API Project {
|
||||
mutable unsigned int gdBuildVersion =
|
||||
0; ///< The GD build version used the last
|
||||
///< time the project was saved.
|
||||
bool areEffectsHiddenInEditor =
|
||||
false; ///< When false effects are not shown and a default light is used
|
||||
///< for 3D layers.
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -34,20 +34,6 @@ void PropertyDescriptor::SerializeTo(SerializerElement& element) const {
|
||||
}
|
||||
}
|
||||
|
||||
if (!choices.empty()
|
||||
// Compatibility with GD <= 5.5.239
|
||||
|| !extraInformation.empty()
|
||||
// end of compatibility code
|
||||
) {
|
||||
SerializerElement &choicesElement = element.AddChild("choices");
|
||||
choicesElement.ConsiderAsArrayOf("choice");
|
||||
for (const auto &choice : choices) {
|
||||
auto &choiceElement = choicesElement.AddChild("Choice");
|
||||
choiceElement.SetStringAttribute("value", choice.GetValue());
|
||||
choiceElement.SetStringAttribute("label", choice.GetLabel());
|
||||
}
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
element.AddChild("hidden").SetBoolValue(hidden);
|
||||
}
|
||||
@@ -70,9 +56,7 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
currentValue = element.GetChild("value").GetStringValue();
|
||||
type = element.GetChild("type").GetStringValue();
|
||||
if (type == "Number") {
|
||||
gd::String unitName = element.HasChild("unit")
|
||||
? element.GetChild("unit").GetStringValue()
|
||||
: "";
|
||||
gd::String unitName = element.GetChild("unit").GetStringValue();
|
||||
measurementUnit =
|
||||
gd::MeasurementUnit::HasDefaultMeasurementUnitNamed(unitName)
|
||||
? measurementUnit =
|
||||
@@ -96,26 +80,6 @@ void PropertyDescriptor::UnserializeFrom(const SerializerElement& element) {
|
||||
extraInformationElement.GetChild(i).GetStringValue());
|
||||
}
|
||||
|
||||
if (element.HasChild("choices")) {
|
||||
choices.clear();
|
||||
const SerializerElement &choicesElement = element.GetChild("choices");
|
||||
choicesElement.ConsiderAsArrayOf("choice");
|
||||
for (std::size_t i = 0; i < choicesElement.GetChildrenCount(); ++i) {
|
||||
auto &choiceElement = choicesElement.GetChild(i);
|
||||
AddChoice(choiceElement.GetStringAttribute("value"),
|
||||
choiceElement.GetStringAttribute("label"));
|
||||
}
|
||||
}
|
||||
// Compatibility with GD <= 5.5.239
|
||||
else if (type == "Choice") {
|
||||
choices.clear();
|
||||
for (auto &choiceValue : extraInformation) {
|
||||
AddChoice(choiceValue, choiceValue);
|
||||
}
|
||||
extraInformation.clear();
|
||||
}
|
||||
// end of compatibility code
|
||||
|
||||
hidden = element.HasChild("hidden")
|
||||
? element.GetChild("hidden").GetBoolValue()
|
||||
: false;
|
||||
|
@@ -116,11 +116,6 @@ class GD_CORE_API PropertyDescriptor {
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyDescriptor& ClearChoices() {
|
||||
choices.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PropertyDescriptor& AddChoice(const gd::String& value,
|
||||
const gd::String& label) {
|
||||
choices.push_back(PropertyDescriptorChoice(value, label));
|
||||
|
@@ -139,8 +139,8 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
.SetLabel("Dot shape")
|
||||
.SetDescription("The shape is used for collision.")
|
||||
.SetGroup("Movement");
|
||||
property.AddChoice("DotShape", "Dot shape");
|
||||
property.AddChoice("BoundingDisk", "Bounding disk");
|
||||
property.GetExtraInfo().push_back("Dot shape");
|
||||
property.GetExtraInfo().push_back("Bounding disk");
|
||||
|
||||
gd::PropertyFunctionGenerator::GenerateBehaviorGetterAndSetter(
|
||||
project, extension, behavior, property, false);
|
||||
@@ -157,7 +157,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
gd::EventsFunction::ExpressionAndCondition);
|
||||
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
|
||||
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
|
||||
"[\"DotShape\",\"BoundingDisk\"]");
|
||||
"[\"Dot shape\",\"Bounding disk\"]");
|
||||
}
|
||||
|
||||
SECTION("Can generate functions for a boolean property in a behavior") {
|
||||
@@ -386,8 +386,8 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
.SetLabel("Dot shape")
|
||||
.SetDescription("The shape is used for collision.")
|
||||
.SetGroup("Movement");
|
||||
property.AddChoice("DotShape", "Dot shape");
|
||||
property.AddChoice("BoundingDisk", "Bounding disk");
|
||||
property.GetExtraInfo().push_back("Dot shape");
|
||||
property.GetExtraInfo().push_back("Bounding disk");
|
||||
|
||||
gd::PropertyFunctionGenerator::GenerateObjectGetterAndSetter(
|
||||
project, extension, object, property);
|
||||
@@ -404,7 +404,7 @@ TEST_CASE("PropertyFunctionGenerator", "[common]") {
|
||||
gd::EventsFunction::ExpressionAndCondition);
|
||||
REQUIRE(getter.GetExpressionType().GetName() == "stringWithSelector");
|
||||
REQUIRE(getter.GetExpressionType().GetExtraInfo() ==
|
||||
"[\"DotShape\",\"BoundingDisk\"]");
|
||||
"[\"Dot shape\",\"Bounding disk\"]");
|
||||
}
|
||||
|
||||
SECTION("Can generate functions for a boolean property in an object") {
|
||||
|
@@ -147,9 +147,15 @@ namespace gdjs {
|
||||
if (initialInstanceData.depth !== undefined) {
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
this.flipZ(!!initialInstanceData.flippedZ);
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
if (initialInstanceData.flippedZ) {
|
||||
this.flipZ(initialInstanceData.flippedZ);
|
||||
}
|
||||
}
|
||||
|
||||
setX(x: float): void {
|
||||
@@ -316,18 +322,6 @@ namespace gdjs {
|
||||
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._originalWidth;
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._originalHeight;
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this._originalDepth;
|
||||
}
|
||||
|
||||
getWidth(): float {
|
||||
return this._width;
|
||||
}
|
||||
@@ -374,6 +368,31 @@ namespace gdjs {
|
||||
this.getRenderer().updateSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width of the object for a scale of 1.
|
||||
*
|
||||
* It can't be 0.
|
||||
*/
|
||||
_getOriginalWidth(): float {
|
||||
return this._originalWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the height of the object for a scale of 1.
|
||||
*
|
||||
* It can't be 0.
|
||||
*/
|
||||
_getOriginalHeight(): float {
|
||||
return this._originalHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the object size on the Z axis (called "depth") when the scale equals 1.
|
||||
*/
|
||||
_getOriginalDepth(): float {
|
||||
return this._originalDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width of the object for a scale of 1.
|
||||
*/
|
||||
|
@@ -11,8 +11,6 @@ namespace gdjs {
|
||||
this._object = runtimeObject;
|
||||
this._threeObject3D = threeObject3D;
|
||||
this._threeObject3D.rotation.order = 'ZYX';
|
||||
//@ts-ignore
|
||||
this._threeObject3D.gdjsRuntimeObject = runtimeObject;
|
||||
|
||||
instanceContainer
|
||||
.getLayer('')
|
||||
|
@@ -115,12 +115,6 @@ namespace gdjs {
|
||||
* Rotations around X and Y are not taken into account.
|
||||
*/
|
||||
getUnrotatedAABBMaxZ(): number;
|
||||
|
||||
/**
|
||||
* Return the depth of the object before any custom size is applied.
|
||||
* @return The depth of the object
|
||||
*/
|
||||
getOriginalDepth(): float;
|
||||
}
|
||||
|
||||
export interface Object3DDataContent {
|
||||
@@ -137,11 +131,7 @@ namespace gdjs {
|
||||
export namespace Base3DHandler {
|
||||
export const is3D = (
|
||||
object: gdjs.RuntimeObject
|
||||
): object is gdjs.RuntimeObject &
|
||||
gdjs.Base3DHandler &
|
||||
gdjs.Resizable &
|
||||
gdjs.Scalable &
|
||||
gdjs.Flippable => {
|
||||
): object is gdjs.RuntimeObject & gdjs.Base3DHandler => {
|
||||
//@ts-ignore We are checking if the methods are present.
|
||||
return object.getZ && object.setZ;
|
||||
};
|
||||
@@ -253,10 +243,6 @@ namespace gdjs {
|
||||
getUnrotatedAABBMaxZ(): number {
|
||||
return this.object.getUnrotatedAABBMaxZ();
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this.object.getOriginalDepth();
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.registerBehavior('Scene3D::Base3DBehavior', gdjs.Base3DBehavior);
|
||||
|
@@ -74,9 +74,15 @@ namespace gdjs {
|
||||
if (initialInstanceData.depth !== undefined) {
|
||||
this.setDepth(initialInstanceData.depth);
|
||||
}
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
this.flipZ(!!initialInstanceData.flippedZ);
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
if (initialInstanceData.flippedZ) {
|
||||
this.flipZ(initialInstanceData.flippedZ);
|
||||
}
|
||||
}
|
||||
|
||||
getNetworkSyncData(): CustomObject3DNetworkSyncDataType {
|
||||
@@ -309,10 +315,6 @@ namespace gdjs {
|
||||
return this._maxZ - this._minZ;
|
||||
}
|
||||
|
||||
getOriginalDepth(): float {
|
||||
return this._instanceContainer._getInitialInnerAreaDepth();
|
||||
}
|
||||
|
||||
override _updateUntransformedHitBoxes(): void {
|
||||
super._updateUntransformedHitBoxes();
|
||||
|
||||
|
@@ -2063,48 +2063,6 @@ module.exports = {
|
||||
.setType('number')
|
||||
.setGroup(_('Orientation'));
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
.addEffect('Skybox')
|
||||
.setFullName(_('Skybox'))
|
||||
.setDescription(
|
||||
_('Display a background on a cube surrounding the scene.')
|
||||
)
|
||||
.markAsNotWorkingForObjects()
|
||||
.markAsOnlyWorkingFor3D()
|
||||
.addIncludeFile('Extensions/3D/Skybox.js');
|
||||
const properties = effect.getProperties();
|
||||
properties
|
||||
.getOrCreate('rightFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Right face (X+)'));
|
||||
properties
|
||||
.getOrCreate('leftFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Left face (X-)'));
|
||||
properties
|
||||
.getOrCreate('bottomFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Bottom face (Y+)'));
|
||||
properties
|
||||
.getOrCreate('topFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Top face (Y-)'));
|
||||
properties
|
||||
.getOrCreate('frontFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Front face (Z+)'));
|
||||
properties
|
||||
.getOrCreate('backFaceResourceName')
|
||||
.setType('resource')
|
||||
.addExtraInfo('image')
|
||||
.setLabel(_('Back face (Z-)'));
|
||||
}
|
||||
{
|
||||
const effect = extension
|
||||
.addEffect('HueAndSaturation')
|
||||
|
@@ -278,9 +278,9 @@ namespace gdjs {
|
||||
rotationX,
|
||||
rotationY,
|
||||
rotationZ,
|
||||
this.getOriginalWidth(),
|
||||
this.getOriginalHeight(),
|
||||
this.getOriginalDepth(),
|
||||
this._getOriginalWidth(),
|
||||
this._getOriginalHeight(),
|
||||
this._getOriginalDepth(),
|
||||
keepAspectRatio
|
||||
);
|
||||
}
|
||||
|
@@ -1,102 +0,0 @@
|
||||
namespace gdjs {
|
||||
interface SkyboxFilterNetworkSyncData {}
|
||||
gdjs.PixiFiltersTools.registerFilterCreator(
|
||||
'Scene3D::Skybox',
|
||||
new (class implements gdjs.PixiFiltersTools.FilterCreator {
|
||||
makeFilter(
|
||||
target: EffectsTarget,
|
||||
effectData: EffectData
|
||||
): gdjs.PixiFiltersTools.Filter {
|
||||
if (typeof THREE === 'undefined') {
|
||||
return new gdjs.PixiFiltersTools.EmptyFilter();
|
||||
}
|
||||
return new (class implements gdjs.PixiFiltersTools.Filter {
|
||||
_cubeTexture: THREE.CubeTexture;
|
||||
_oldBackground:
|
||||
| THREE.CubeTexture
|
||||
| THREE.Texture
|
||||
| THREE.Color
|
||||
| null = null;
|
||||
_isEnabled: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this._cubeTexture = target
|
||||
.getRuntimeScene()
|
||||
.getGame()
|
||||
.getImageManager()
|
||||
.getThreeCubeTexture(
|
||||
effectData.stringParameters.rightFaceResourceName,
|
||||
effectData.stringParameters.leftFaceResourceName,
|
||||
effectData.stringParameters.topFaceResourceName,
|
||||
effectData.stringParameters.bottomFaceResourceName,
|
||||
effectData.stringParameters.frontFaceResourceName,
|
||||
effectData.stringParameters.backFaceResourceName
|
||||
);
|
||||
}
|
||||
|
||||
isEnabled(target: EffectsTarget): boolean {
|
||||
return this._isEnabled;
|
||||
}
|
||||
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
|
||||
if (this._isEnabled === enabled) {
|
||||
return true;
|
||||
}
|
||||
if (enabled) {
|
||||
return this.applyEffect(target);
|
||||
} else {
|
||||
return this.removeEffect(target);
|
||||
}
|
||||
}
|
||||
applyEffect(target: EffectsTarget): boolean {
|
||||
const scene = target.get3DRendererObject() as
|
||||
| THREE.Scene
|
||||
| null
|
||||
| undefined;
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
// TODO Add a background stack in LayerPixiRenderer to allow
|
||||
// filters to stack them.
|
||||
this._oldBackground = scene.background;
|
||||
scene.background = this._cubeTexture;
|
||||
if (!scene.environment) {
|
||||
scene.environment = this._cubeTexture;
|
||||
}
|
||||
this._isEnabled = true;
|
||||
return true;
|
||||
}
|
||||
removeEffect(target: EffectsTarget): boolean {
|
||||
const scene = target.get3DRendererObject() as
|
||||
| THREE.Scene
|
||||
| null
|
||||
| undefined;
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
scene.background = this._oldBackground;
|
||||
scene.environment = null;
|
||||
this._isEnabled = false;
|
||||
return true;
|
||||
}
|
||||
updatePreRender(target: gdjs.EffectsTarget): any {}
|
||||
updateDoubleParameter(parameterName: string, value: number): void {}
|
||||
getDoubleParameter(parameterName: string): number {
|
||||
return 0;
|
||||
}
|
||||
updateStringParameter(parameterName: string, value: string): void {}
|
||||
updateColorParameter(parameterName: string, value: number): void {}
|
||||
getColorParameter(parameterName: string): number {
|
||||
return 0;
|
||||
}
|
||||
updateBooleanParameter(parameterName: string, value: boolean): void {}
|
||||
getNetworkSyncData(): SkyboxFilterNetworkSyncData {
|
||||
return {};
|
||||
}
|
||||
updateFromNetworkSyncData(
|
||||
syncData: SkyboxFilterNetworkSyncData
|
||||
): void {}
|
||||
})();
|
||||
}
|
||||
})()
|
||||
);
|
||||
}
|
@@ -473,14 +473,7 @@ namespace gdjs {
|
||||
this._parentOldMaxY = instanceContainer.getUnrotatedViewportMaxY();
|
||||
}
|
||||
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
// Custom objects can be resized during the events step.
|
||||
// The anchor constraints must be applied on child-objects after the parent events.
|
||||
const isChildObject = instanceContainer !== instanceContainer.getScene();
|
||||
if (isChildObject) {
|
||||
this.doStepPreEvents(instanceContainer);
|
||||
}
|
||||
}
|
||||
doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}
|
||||
|
||||
private _convertCoords(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
|
@@ -218,11 +218,9 @@ namespace gdjs {
|
||||
this.setWrappingWidth(initialInstanceData.width);
|
||||
this.setWrapping(true);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
override onDestroyed(): void {
|
||||
|
@@ -388,9 +388,6 @@ namespace gdjs {
|
||||
|
||||
return new gdjs.PromiseTask(
|
||||
(async () => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
const scoreSavingState = (_scoreSavingStateByLeaderboard[
|
||||
leaderboardId
|
||||
] =
|
||||
@@ -426,9 +423,6 @@ namespace gdjs {
|
||||
) =>
|
||||
new gdjs.PromiseTask(
|
||||
(async () => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
const playerId = gdjs.playerAuthentication.getUserId();
|
||||
const playerToken = gdjs.playerAuthentication.getUserToken();
|
||||
if (!playerId || !playerToken) {
|
||||
@@ -753,9 +747,6 @@ namespace gdjs {
|
||||
leaderboardId: string,
|
||||
displayLoader: boolean
|
||||
) {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
// First ensure we're not trying to display multiple times the same leaderboard (in which case
|
||||
// we "de-duplicate" the request to display it).
|
||||
if (leaderboardId === _requestedLeaderboardId) {
|
||||
|
@@ -1841,9 +1841,6 @@ namespace gdjs {
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (isQuickJoiningTooFast()) {
|
||||
return;
|
||||
}
|
||||
@@ -1863,9 +1860,6 @@ namespace gdjs {
|
||||
displayLoader: boolean,
|
||||
openLobbiesPageIfFailure: boolean
|
||||
) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (isQuickJoiningTooFast()) {
|
||||
return;
|
||||
}
|
||||
@@ -1899,9 +1893,6 @@ namespace gdjs {
|
||||
export const openLobbiesWindow = async (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
isLobbiesWindowOpen(runtimeScene) ||
|
||||
gdjs.playerAuthentication.isAuthenticationWindowOpen()
|
||||
|
@@ -53,8 +53,6 @@ namespace gdjs {
|
||||
|
||||
_renderer: gdjs.PanelSpriteRuntimeObjectRenderer;
|
||||
|
||||
_objectData: PanelSpriteObjectData;
|
||||
|
||||
/**
|
||||
* @param instanceContainer The container the object belongs to.
|
||||
* @param panelSpriteObjectData The initial properties of the object
|
||||
@@ -64,7 +62,6 @@ namespace gdjs {
|
||||
panelSpriteObjectData: PanelSpriteObjectData
|
||||
) {
|
||||
super(instanceContainer, panelSpriteObjectData);
|
||||
this._objectData = panelSpriteObjectData;
|
||||
this._rBorder = panelSpriteObjectData.rightMargin;
|
||||
this._lBorder = panelSpriteObjectData.leftMargin;
|
||||
this._tBorder = panelSpriteObjectData.topMargin;
|
||||
@@ -87,7 +84,6 @@ namespace gdjs {
|
||||
oldObjectData: PanelSpriteObjectData,
|
||||
newObjectData: PanelSpriteObjectData
|
||||
): boolean {
|
||||
this._objectData = newObjectData;
|
||||
if (oldObjectData.width !== newObjectData.width) {
|
||||
this.setWidth(newObjectData.width);
|
||||
}
|
||||
@@ -167,11 +163,9 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,14 +244,6 @@ namespace gdjs {
|
||||
this.setHeight(newHeight);
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._objectData.width;
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._objectData.height;
|
||||
}
|
||||
|
||||
setOpacity(opacity: float): void {
|
||||
if (opacity < 0) {
|
||||
opacity = 0;
|
||||
|
@@ -531,7 +531,6 @@ module.exports = {
|
||||
physics2Behavior,
|
||||
sharedData
|
||||
)
|
||||
.markAsIrrelevantForChildObjects()
|
||||
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
|
||||
.addIncludeFile('Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.js')
|
||||
.addRequiredFile('Extensions/Physics2Behavior/Box2D_v2.3.1_min.wasm.wasm')
|
||||
|
@@ -679,7 +679,6 @@ module.exports = {
|
||||
behavior,
|
||||
sharedData
|
||||
)
|
||||
.markAsIrrelevantForChildObjects()
|
||||
.addIncludeFile(
|
||||
'Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.js'
|
||||
)
|
||||
|
@@ -37,8 +37,7 @@ void DeclarePhysicsBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
"res/physics-deprecated32.png",
|
||||
"PhysicsBehavior",
|
||||
std::make_shared<PhysicsBehavior>(),
|
||||
std::make_shared<ScenePhysicsDatas>())
|
||||
.MarkAsIrrelevantForChildObjects();
|
||||
std::make_shared<ScenePhysicsDatas>());
|
||||
|
||||
aut.AddAction("SetStatic",
|
||||
("Make the object static"),
|
||||
|
@@ -647,9 +647,6 @@ namespace gdjs {
|
||||
export const displayAuthenticationBanner = function (
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
if (_authenticationBanner) {
|
||||
// Banner already displayed, ensure it's visible.
|
||||
_authenticationBanner.style.opacity = '1';
|
||||
@@ -1045,10 +1042,6 @@ namespace gdjs {
|
||||
): gdjs.PromiseTask<{ status: 'logged' | 'errored' | 'dismissed' }> =>
|
||||
new gdjs.PromiseTask(
|
||||
new Promise((resolve) => {
|
||||
if (runtimeScene.getGame().isInGameEdition()) {
|
||||
resolve({ status: 'dismissed' });
|
||||
}
|
||||
|
||||
// Create the authentication container for the player to wait.
|
||||
const domElementContainer = runtimeScene
|
||||
.getGame()
|
||||
|
@@ -196,8 +196,12 @@ namespace gdjs {
|
||||
* @param initialInstanceData The extra parameters
|
||||
*/
|
||||
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
}
|
||||
|
||||
stepBehaviorsPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
|
||||
|
@@ -203,17 +203,13 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedSpineAtlas = this._loadedSpineAtlases.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
const loadedSpineAtlas = this._loadedSpineAtlases.get(resourceData);
|
||||
if (loadedSpineAtlas) {
|
||||
loadedSpineAtlas.dispose();
|
||||
this._loadedSpineAtlases.delete(resourceData);
|
||||
}
|
||||
|
||||
const loadingSpineAtlas = this._loadingSpineAtlases.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
const loadingSpineAtlas = this._loadingSpineAtlases.get(resourceData);
|
||||
if (loadingSpineAtlas) {
|
||||
loadingSpineAtlas.then((atl) => atl.dispose());
|
||||
this._loadingSpineAtlases.delete(resourceData);
|
||||
|
@@ -199,13 +199,15 @@ namespace gdjs {
|
||||
this.setSize(initialInstanceData.width, initialInstanceData.height);
|
||||
this.invalidateHitboxes();
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
}
|
||||
|
||||
getDrawableX(): number {
|
||||
|
@@ -141,9 +141,7 @@ namespace gdjs {
|
||||
);
|
||||
this._borderOpacity = objectData.content.borderOpacity;
|
||||
this._borderWidth = objectData.content.borderWidth;
|
||||
this._disabled = instanceContainer.getGame().isInGameEdition()
|
||||
? true
|
||||
: objectData.content.disabled;
|
||||
this._disabled = objectData.content.disabled;
|
||||
this._readOnly = objectData.content.readOnly;
|
||||
this._spellCheck =
|
||||
objectData.content.spellCheck !== undefined
|
||||
@@ -331,11 +329,9 @@ namespace gdjs {
|
||||
this.setHeight(initialInstanceData.height);
|
||||
this._renderer.updatePadding();
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
onScenePaused(runtimeScene: gdjs.RuntimeScene): void {
|
||||
@@ -565,9 +561,6 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
setDisabled(value: boolean) {
|
||||
if (this.getInstanceContainer().getGame().isInGameEdition()) {
|
||||
return;
|
||||
}
|
||||
this._disabled = value;
|
||||
this._renderer.updateDisabled();
|
||||
}
|
||||
|
@@ -334,9 +334,7 @@ namespace gdjs {
|
||||
return this._renderer.getRendererObject();
|
||||
}
|
||||
|
||||
override updatePreRender(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
override update(instanceContainer: gdjs.RuntimeInstanceContainer): void {
|
||||
this._renderer.ensureUpToDate();
|
||||
}
|
||||
|
||||
@@ -354,11 +352,9 @@ namespace gdjs {
|
||||
} else {
|
||||
this.setWrapping(false);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -205,11 +205,9 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
|
||||
// 4. Update position (calculations based on renderer's dimensions).
|
||||
this._renderer.updatePosition();
|
||||
@@ -417,14 +415,6 @@ namespace gdjs {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._renderer.getTileMapWidth();
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._renderer.getTileMapHeight();
|
||||
}
|
||||
|
||||
getScaleX(): float {
|
||||
return this._renderer.getScaleX();
|
||||
}
|
||||
|
@@ -193,11 +193,9 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTileMap(): void {
|
||||
@@ -339,14 +337,6 @@ namespace gdjs {
|
||||
this.setHeight(newHeight);
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._renderer.getTileMapWidth();
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._renderer.getTileMapHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scale of the object (or the geometric mean of the X and Y scale in case they are different).
|
||||
*
|
||||
|
@@ -42,8 +42,6 @@ namespace gdjs {
|
||||
|
||||
_renderer: gdjs.TiledSpriteRuntimeObjectRenderer;
|
||||
|
||||
_objectData: TiledSpriteObjectData;
|
||||
|
||||
/**
|
||||
* @param instanceContainer The container the object belongs to.
|
||||
* @param tiledSpriteObjectData The initial properties of the object
|
||||
@@ -53,7 +51,6 @@ namespace gdjs {
|
||||
tiledSpriteObjectData: TiledSpriteObjectData
|
||||
) {
|
||||
super(instanceContainer, tiledSpriteObjectData);
|
||||
this._objectData = tiledSpriteObjectData;
|
||||
this._renderer = new gdjs.TiledSpriteRuntimeObjectRenderer(
|
||||
this,
|
||||
instanceContainer,
|
||||
@@ -69,7 +66,6 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
updateFromObjectData(oldObjectData, newObjectData): boolean {
|
||||
this._objectData = newObjectData;
|
||||
if (oldObjectData.texture !== newObjectData.texture) {
|
||||
this.setTexture(newObjectData.texture, this.getRuntimeScene());
|
||||
}
|
||||
@@ -130,11 +126,9 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,14 +220,6 @@ namespace gdjs {
|
||||
this.setHeight(height);
|
||||
}
|
||||
|
||||
override getOriginalWidth(): float {
|
||||
return this._objectData.width;
|
||||
}
|
||||
|
||||
override getOriginalHeight(): float {
|
||||
return this._objectData.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the offset on the X-axis when displaying the image of the Tiled Sprite object.
|
||||
* @param xOffset The new offset on the X-axis.
|
||||
|
@@ -146,11 +146,9 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroyed(): void {
|
||||
|
@@ -145,19 +145,16 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionCode(
|
||||
gd::DiagnosticReport diagnosticReport;
|
||||
codeGenerator.SetDiagnosticReport(&diagnosticReport);
|
||||
|
||||
// Generate the code setting up the context of the function.
|
||||
gd::String fullPreludeCode = "let scopeInstanceContainer = null;\n" +
|
||||
codeGenerator.GenerateFreeEventsFunctionContext(
|
||||
eventsFunctionsExtension, eventsFunction,
|
||||
"runtimeScene.getOnceTriggers()");
|
||||
|
||||
gd::String output = GenerateEventsListCompleteFunctionCode(
|
||||
codeGenerator, codeGenerator.GetCodeNamespaceAccessor() + "func",
|
||||
codeGenerator.GenerateEventsFunctionParameterDeclarationsList(
|
||||
eventsFunction.GetParametersForEvents(
|
||||
eventsFunctionsExtension.GetEventsFunctions()),
|
||||
0, true),
|
||||
fullPreludeCode, eventsFunction.GetEvents(), "",
|
||||
codeGenerator.GenerateFreeEventsFunctionContext(
|
||||
eventsFunctionsExtension, eventsFunction,
|
||||
"runtimeScene.getOnceTriggers()"),
|
||||
eventsFunction.GetEvents(), "",
|
||||
codeGenerator.GenerateEventsFunctionReturn(eventsFunction));
|
||||
|
||||
// TODO: the editor should pass the diagnostic report and display it to the
|
||||
@@ -212,7 +209,6 @@ gd::String EventsCodeGenerator::GenerateBehaviorEventsFunctionCode(
|
||||
// TODO: this should be renamed to "instanceContainer" and have the code generation
|
||||
// adapted for this (rely less on `gdjs.RuntimeScene`, and more on `RuntimeInstanceContainer`).
|
||||
"var runtimeScene = this._runtimeScene;\n" +
|
||||
"let scopeInstanceContainer = null;\n" +
|
||||
// By convention of Behavior Events Function, the object is accessible
|
||||
// as a parameter called "Object", and thisObjectList is an array
|
||||
// containing it (for faster access, without having to go through the
|
||||
@@ -298,7 +294,6 @@ gd::String EventsCodeGenerator::GenerateObjectEventsFunctionCode(
|
||||
// TODO: this should be renamed to "instanceContainer" and have the code generation
|
||||
// adapted for this (rely less on `gdjs.RuntimeScene`, and more on `RuntimeInstanceContainer`).
|
||||
"var runtimeScene = this._instanceContainer;\n" +
|
||||
"let scopeInstanceContainer = this._instanceContainer;\n" +
|
||||
// By convention of Object Events Function, the object is accessible
|
||||
// as a parameter called "Object", and thisObjectList is an array
|
||||
// containing it (for faster access, without having to go through the
|
||||
@@ -629,11 +624,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
// the cost of creating/storing it for each events function might not
|
||||
// be worth it.
|
||||
" if (objectsList) {\n" +
|
||||
" const object = parentEventsFunctionContext && "
|
||||
// Check if `objectName` is not a child object and is passed by
|
||||
// parameter from a parent instance container.
|
||||
"!(scopeInstanceContainer && "
|
||||
"scopeInstanceContainer.isObjectRegistered(objectName)) ?\n" +
|
||||
" const object = parentEventsFunctionContext ?\n" +
|
||||
" "
|
||||
"parentEventsFunctionContext.createObject(objectsList.firstKey()) "
|
||||
":\n" +
|
||||
@@ -643,7 +634,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
" objectsList.get(objectsList.firstKey()).push(object);\n" +
|
||||
" "
|
||||
"eventsFunctionContext._objectArraysMap[objectName].push(object);\n" +
|
||||
" }\n" + " return object;\n" + " }\n" +
|
||||
" }\n" + " return object;" + " }\n" +
|
||||
// Unknown object, don't create anything:
|
||||
" return null;\n" +
|
||||
" },\n"
|
||||
@@ -655,11 +646,7 @@ gd::String EventsCodeGenerator::GenerateEventsFunctionContext(
|
||||
"eventsFunctionContext._objectsMap[objectName];\n" +
|
||||
" let count = 0;\n" + " if (objectsList) {\n" +
|
||||
" for(const objectName in objectsList.items)\n" +
|
||||
" count += parentEventsFunctionContext && "
|
||||
// Check if `objectName` is not a child object and is passed by
|
||||
// parameter from a parent instance container.
|
||||
"!(scopeInstanceContainer && "
|
||||
"scopeInstanceContainer.isObjectRegistered(objectName)) ?\n" +
|
||||
" count += parentEventsFunctionContext ?\n" +
|
||||
"parentEventsFunctionContext.getInstancesCountOnScene(objectName) "
|
||||
":\n" +
|
||||
" runtimeScene.getInstancesCountOnScene(objectName);\n" +
|
||||
|
@@ -18,8 +18,6 @@ KeyboardExtension::KeyboardExtension() {
|
||||
"gdjs.evtTools.input.wasKeyReleased");
|
||||
GetAllConditions()["KeyFromTextPressed"].SetFunctionName(
|
||||
"gdjs.evtTools.input.isKeyPressed");
|
||||
GetAllConditions()["KeyFromTextJustPressed"].SetFunctionName(
|
||||
"gdjs.evtTools.input.wasKeyJustPressed");
|
||||
GetAllConditions()["KeyFromTextReleased"].SetFunctionName(
|
||||
"gdjs.evtTools.input.wasKeyReleased");
|
||||
GetAllConditions()["AnyKeyPressed"].SetFunctionName(
|
||||
|
@@ -18,9 +18,6 @@
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/Project/SceneResourcesFinder.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
#include "GDCore/Project/Layout.h"
|
||||
@@ -50,7 +47,7 @@ Exporter::~Exporter() {}
|
||||
bool Exporter::ExportProjectForPixiPreview(
|
||||
const PreviewExportOptions &options) {
|
||||
ExporterHelper helper(fs, gdjsRoot, codeOutputDir);
|
||||
return helper.ExportProjectForPixiPreview(options, includesFiles);
|
||||
return helper.ExportProjectForPixiPreview(options);
|
||||
}
|
||||
|
||||
bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
@@ -83,7 +80,7 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
|
||||
// Prepare the export directory
|
||||
fs.MkDir(exportDir);
|
||||
includesFiles.clear();
|
||||
std::vector<gd::String> includesFiles;
|
||||
std::vector<gd::String> resourcesFiles;
|
||||
|
||||
// Export the resources (before generating events as some resources
|
||||
@@ -101,7 +98,6 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
helper.AddLibsInclude(
|
||||
/*pixiRenderers=*/true,
|
||||
usedExtensionsResult.Has3DObjects(),
|
||||
/*isInGameEditor=*/false,
|
||||
/*includeWebsocketDebuggerClient=*/false,
|
||||
/*includeWindowMessageDebuggerClient=*/false,
|
||||
/*includeMinimalDebuggerClient=*/false,
|
||||
@@ -124,7 +120,7 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
helper.ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export events
|
||||
if (!helper.ExportScenesEventsCode(exportedProject,
|
||||
if (!helper.ExportEventsCode(exportedProject,
|
||||
codeOutputDir,
|
||||
includesFiles,
|
||||
wholeProjectDiagnosticReport,
|
||||
@@ -134,10 +130,29 @@ 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");
|
||||
|
||||
helper.ExportIncludesAndLibs(includesFiles, exportDir, false);
|
||||
@@ -200,18 +215,4 @@ bool Exporter::ExportWholePixiProject(const ExportOptions &options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Exporter::SerializeProjectData(const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &projectDataElement) {
|
||||
ExporterHelper::SerializeProjectData(fs, project, options,
|
||||
projectDataElement);
|
||||
}
|
||||
|
||||
void Exporter::SerializeRuntimeGameOptions(
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &runtimeGameOptionsElement) {
|
||||
ExporterHelper::SerializeRuntimeGameOptions(
|
||||
fs, gdjsRoot, options, includesFiles, runtimeGameOptionsElement);
|
||||
}
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -16,7 +16,6 @@ class Project;
|
||||
class Layout;
|
||||
class ExternalLayout;
|
||||
class AbstractFileSystem;
|
||||
class SerializerElement;
|
||||
} // namespace gd
|
||||
namespace gdjs {
|
||||
struct PreviewExportOptions;
|
||||
@@ -65,33 +64,7 @@ class Exporter {
|
||||
codeOutputDir = codeOutputDir_;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Serialize a project without its events to JSON
|
||||
*
|
||||
* \param project The project to be exported
|
||||
* \param options The content of the extra configuration
|
||||
* \param projectDataElement The element where the project data is serialized
|
||||
*/
|
||||
void SerializeProjectData(const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &projectDataElement);
|
||||
|
||||
/**
|
||||
* \brief Serialize the content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions to JSON
|
||||
*
|
||||
* \warning `ExportProjectForPixiPreview` must be called first to serialize
|
||||
* the list of scripts files.
|
||||
*
|
||||
* \param options The content of the extra configuration
|
||||
* \param runtimeGameOptionsElement The element where the game options are
|
||||
* serialized
|
||||
*/
|
||||
void
|
||||
SerializeRuntimeGameOptions(const PreviewExportOptions &options,
|
||||
gd::SerializerElement &runtimeGameOptionsElement);
|
||||
|
||||
private:
|
||||
private:
|
||||
gd::AbstractFileSystem&
|
||||
fs; ///< The abstract file system to be used for exportation.
|
||||
gd::String lastError; ///< The last error that occurred.
|
||||
@@ -99,8 +72,6 @@ private:
|
||||
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.
|
||||
std::vector<gd::String>
|
||||
includesFiles; ///< The list of scripts files - useful for hot-reloading
|
||||
};
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -28,13 +28,10 @@
|
||||
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
|
||||
#include "GDCore/IDE/ExportedDependencyResolver.h"
|
||||
#include "GDCore/IDE/Project/ProjectResourcesCopier.h"
|
||||
#include "GDCore/IDE/Project/ResourcesMergingHelper.h"
|
||||
#include "GDCore/IDE/Project/SceneResourcesFinder.h"
|
||||
#include "GDCore/IDE/ProjectStripper.h"
|
||||
#include "GDCore/IDE/ResourceExposer.h"
|
||||
#include "GDCore/IDE/SceneNameMangler.h"
|
||||
#include "GDCore/Project/EventsBasedObject.h"
|
||||
#include "GDCore/Project/EventsBasedObjectVariant.h"
|
||||
#include "GDCore/Project/EventsFunctionsExtension.h"
|
||||
#include "GDCore/Project/ExternalEvents.h"
|
||||
#include "GDCore/Project/ExternalLayout.h"
|
||||
@@ -61,6 +58,7 @@ double GetTimeSpent(double previousTime) { return GetTimeNow() - previousTime; }
|
||||
double LogTimeSpent(const gd::String &name, double previousTime) {
|
||||
gd::LogStatus(name + " took " + gd::String::From(GetTimeSpent(previousTime)) +
|
||||
"ms");
|
||||
std::cout << std::endl;
|
||||
return GetTimeNow();
|
||||
}
|
||||
} // namespace
|
||||
@@ -106,297 +104,128 @@ ExporterHelper::ExporterHelper(gd::AbstractFileSystem &fileSystem,
|
||||
: fs(fileSystem), gdjsRoot(gdjsRoot_), codeOutputDir(codeOutputDir_) {};
|
||||
|
||||
bool ExporterHelper::ExportProjectForPixiPreview(
|
||||
const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles) {
|
||||
|
||||
if (options.isInGameEdition && !options.shouldReloadProjectData &&
|
||||
!options.shouldReloadLibraries && !options.shouldGenerateScenesEventsCode) {
|
||||
gd::LogStatus("Skip project export entirely");
|
||||
return "";
|
||||
}
|
||||
|
||||
const PreviewExportOptions &options) {
|
||||
double previousTime = GetTimeNow();
|
||||
fs.MkDir(options.exportPath);
|
||||
if (options.shouldClearExportFolder) {
|
||||
fs.ClearDir(options.exportPath);
|
||||
}
|
||||
includesFiles.clear();
|
||||
fs.ClearDir(options.exportPath);
|
||||
std::vector<gd::String> includesFiles;
|
||||
std::vector<gd::String> resourcesFiles;
|
||||
|
||||
// TODO Try to remove side effects to avoid the copy
|
||||
// that destroys the AST in cache.
|
||||
gd::Project exportedProject = options.project;
|
||||
const gd::Project &immutableProject = options.project;
|
||||
previousTime = LogTimeSpent("Project cloning", previousTime);
|
||||
const gd::Project &immutableProject = exportedProject;
|
||||
|
||||
if (options.isInGameEdition) {
|
||||
if (options.shouldReloadProjectData || options.shouldGenerateScenesEventsCode) {
|
||||
auto projectDirectory = fs.DirNameFrom(exportedProject.GetProjectFile());
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
exportedProject.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
resourcesMergingHelper.SetShouldUseOriginalAbsoluteFilenames();
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(exportedProject,
|
||||
resourcesMergingHelper);
|
||||
|
||||
previousTime = LogTimeSpent("Resource path resolving", previousTime);
|
||||
if (options.fullLoadingScreen) {
|
||||
// Use project properties fallback to set empty properties
|
||||
if (exportedProject.GetAuthorIds().empty() &&
|
||||
!options.fallbackAuthorId.empty()) {
|
||||
exportedProject.GetAuthorIds().push_back(options.fallbackAuthorId);
|
||||
}
|
||||
if (exportedProject.GetAuthorUsernames().empty() &&
|
||||
!options.fallbackAuthorUsername.empty()) {
|
||||
exportedProject.GetAuthorUsernames().push_back(
|
||||
options.fallbackAuthorUsername);
|
||||
}
|
||||
gd::LogStatus("Resource export is skipped");
|
||||
} else {
|
||||
// Export resources (*before* generating events as some resources filenames
|
||||
// may be updated)
|
||||
ExportResources(fs, exportedProject, options.exportPath);
|
||||
|
||||
previousTime = LogTimeSpent("Resource export", previousTime);
|
||||
// Most of the time, we skip the logo and minimum duration so that
|
||||
// the preview start as soon as possible.
|
||||
exportedProject.GetLoadingScreen()
|
||||
.ShowGDevelopLogoDuringLoadingScreen(false)
|
||||
.SetMinDuration(0);
|
||||
exportedProject.GetWatermark().ShowGDevelopWatermark(false);
|
||||
}
|
||||
|
||||
if (options.shouldReloadProjectData || options.shouldGenerateScenesEventsCode) {
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Stay compatible with text objects declaring their font as just a filename
|
||||
// without a font resource - by manually adding these resources.
|
||||
AddDeprecatedFontFilesToFontResources(
|
||||
fs, exportedProject.GetResourcesManager(), options.exportPath);
|
||||
// end of compatibility code
|
||||
// Export resources (*before* generating events as some resources filenames
|
||||
// may be updated)
|
||||
ExportResources(fs, exportedProject, options.exportPath);
|
||||
|
||||
previousTime = LogTimeSpent("Resource export", previousTime);
|
||||
|
||||
// Compatibility with GD <= 5.0-beta56
|
||||
// Stay compatible with text objects declaring their font as just a filename
|
||||
// without a font resource - by manually adding these resources.
|
||||
AddDeprecatedFontFilesToFontResources(
|
||||
fs, exportedProject.GetResourcesManager(), options.exportPath);
|
||||
// end of compatibility code
|
||||
|
||||
auto usedExtensionsResult =
|
||||
gd::UsedExtensionsFinder::ScanProject(exportedProject);
|
||||
|
||||
// Export engine libraries
|
||||
AddLibsInclude(/*pixiRenderers=*/true,
|
||||
usedExtensionsResult.Has3DObjects(),
|
||||
/*includeWebsocketDebuggerClient=*/
|
||||
!options.websocketDebuggerServerAddress.empty(),
|
||||
/*includeWindowMessageDebuggerClient=*/
|
||||
options.useWindowMessageDebuggerClient,
|
||||
/*includeMinimalDebuggerClient=*/
|
||||
options.useMinimalDebuggerClient,
|
||||
/*includeCaptureManager=*/
|
||||
!options.captureOptions.IsEmpty(),
|
||||
/*includeInAppTutorialMessage*/
|
||||
!options.inAppTutorialMessageInPreview.empty(),
|
||||
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
|
||||
includesFiles);
|
||||
|
||||
// Export files for free function, object and behaviors
|
||||
for (const auto &includeFile : usedExtensionsResult.GetUsedIncludeFiles()) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
for (const auto &requiredFile : usedExtensionsResult.GetUsedRequiredFiles()) {
|
||||
InsertUnique(resourcesFiles, requiredFile);
|
||||
}
|
||||
|
||||
std::vector<gd::SourceFileMetadata> noUsedSourceFiles;
|
||||
std::vector<gd::SourceFileMetadata> &usedSourceFiles = noUsedSourceFiles;
|
||||
if (options.shouldReloadLibraries) {
|
||||
auto usedExtensionsResult =
|
||||
gd::UsedExtensionsFinder::ScanProject(exportedProject);
|
||||
usedSourceFiles = usedExtensionsResult.GetUsedSourceFiles();
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
// Export engine libraries
|
||||
AddLibsInclude(/*pixiRenderers=*/true,
|
||||
/*pixiInThreeRenderers=*/
|
||||
usedExtensionsResult.Has3DObjects(),
|
||||
/*isInGameEdition=*/
|
||||
options.isInGameEdition,
|
||||
/*includeWebsocketDebuggerClient=*/
|
||||
!options.websocketDebuggerServerAddress.empty(),
|
||||
/*includeWindowMessageDebuggerClient=*/
|
||||
options.useWindowMessageDebuggerClient,
|
||||
/*includeMinimalDebuggerClient=*/
|
||||
options.useMinimalDebuggerClient,
|
||||
/*includeCaptureManager=*/
|
||||
!options.captureOptions.IsEmpty(),
|
||||
/*includeInAppTutorialMessage*/
|
||||
!options.inAppTutorialMessageInPreview.empty(),
|
||||
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
|
||||
includesFiles);
|
||||
previousTime = LogTimeSpent("Include files export", previousTime);
|
||||
|
||||
// Export files for free function, object and behaviors
|
||||
for (const auto &includeFile : usedExtensionsResult.GetUsedIncludeFiles()) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
for (const auto &requiredFile : usedExtensionsResult.GetUsedRequiredFiles()) {
|
||||
InsertUnique(resourcesFiles, requiredFile);
|
||||
}
|
||||
if (options.isInGameEdition) {
|
||||
// TODO Scan the objects and events of event-based objects
|
||||
// (it could be an alternative method ScanProjectAndEventsBasedObjects in
|
||||
// UsedExtensionsFinder).
|
||||
// This is already done by UsedExtensionsFinder, but maybe it shouldn't.
|
||||
|
||||
// Export all event-based objects because they can be edited even if they
|
||||
// are not used yet.
|
||||
for (std::size_t e = 0;
|
||||
e < exportedProject.GetEventsFunctionsExtensionsCount(); e++) {
|
||||
auto &eventsFunctionsExtension =
|
||||
exportedProject.GetEventsFunctionsExtension(e);
|
||||
|
||||
for (auto &&eventsBasedObjectUniquePtr :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects()
|
||||
.GetInternalVector()) {
|
||||
auto eventsBasedObject = eventsBasedObjectUniquePtr.get();
|
||||
|
||||
auto metadata = gd::MetadataProvider::GetExtensionAndObjectMetadata(
|
||||
exportedProject.GetCurrentPlatform(),
|
||||
gd::PlatformExtension::GetObjectFullType(
|
||||
eventsFunctionsExtension.GetName(),
|
||||
eventsBasedObject->GetName()));
|
||||
for (auto &&includeFile : metadata.GetMetadata().includeFiles) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
for (auto &behaviorType :
|
||||
metadata.GetMetadata().GetDefaultBehaviors()) {
|
||||
auto behaviorMetadata =
|
||||
gd::MetadataProvider::GetExtensionAndBehaviorMetadata(
|
||||
exportedProject.GetCurrentPlatform(), behaviorType);
|
||||
for (auto &&includeFile :
|
||||
behaviorMetadata.GetMetadata().includeFiles) {
|
||||
InsertUnique(includesFiles, includeFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export effects (after engine libraries as they auto-register themselves to
|
||||
// the engine)
|
||||
ExportEffectIncludes(exportedProject, includesFiles);
|
||||
|
||||
previousTime = LogTimeSpent("Include files export", previousTime);
|
||||
}
|
||||
else {
|
||||
gd::LogStatus("Include files export is skipped");
|
||||
}
|
||||
|
||||
if (options.shouldGenerateScenesEventsCode) {
|
||||
if (!options.projectDataOnlyExport) {
|
||||
gd::WholeProjectDiagnosticReport &wholeProjectDiagnosticReport =
|
||||
options.project.GetWholeProjectDiagnosticReport();
|
||||
wholeProjectDiagnosticReport.Clear();
|
||||
|
||||
// Generate events code
|
||||
if (!ExportScenesEventsCode(immutableProject,
|
||||
if (!ExportEventsCode(immutableProject,
|
||||
codeOutputDir,
|
||||
includesFiles,
|
||||
wholeProjectDiagnosticReport,
|
||||
true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
previousTime = LogTimeSpent("Events code export", previousTime);
|
||||
}
|
||||
else {
|
||||
gd::LogStatus("Events code export is skipped");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (options.shouldReloadProjectData) {
|
||||
|
||||
if (options.fullLoadingScreen) {
|
||||
// Use project properties fallback to set empty properties
|
||||
if (exportedProject.GetAuthorIds().empty() &&
|
||||
!options.fallbackAuthorId.empty()) {
|
||||
exportedProject.GetAuthorIds().push_back(options.fallbackAuthorId);
|
||||
}
|
||||
if (exportedProject.GetAuthorUsernames().empty() &&
|
||||
!options.fallbackAuthorUsername.empty()) {
|
||||
exportedProject.GetAuthorUsernames().push_back(
|
||||
options.fallbackAuthorUsername);
|
||||
}
|
||||
} else {
|
||||
// Most of the time, we skip the logo and minimum duration so that
|
||||
// the preview start as soon as possible.
|
||||
exportedProject.GetLoadingScreen()
|
||||
.ShowGDevelopLogoDuringLoadingScreen(false)
|
||||
.SetMinDuration(0);
|
||||
exportedProject.GetWatermark().ShowGDevelopWatermark(false);
|
||||
}
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(exportedProject);
|
||||
exportedProject.SetFirstLayout(options.layoutName);
|
||||
|
||||
gd::SerializerElement runtimeGameOptions;
|
||||
ExporterHelper::SerializeRuntimeGameOptions(fs, gdjsRoot, options,
|
||||
includesFiles, runtimeGameOptions);
|
||||
ExportProjectData(fs,
|
||||
exportedProject,
|
||||
codeOutputDir + "/data.js",
|
||||
runtimeGameOptions);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
previousTime = LogTimeSpent("Data stripping", previousTime);
|
||||
|
||||
previousTime = LogTimeSpent("Project data export", previousTime);
|
||||
}
|
||||
else {
|
||||
gd::LogStatus("Project data export is skipped");
|
||||
}
|
||||
|
||||
if (options.shouldReloadLibraries) {
|
||||
// Copy all the dependencies and their source maps
|
||||
ExportIncludesAndLibs(includesFiles, options.exportPath, true);
|
||||
ExportIncludesAndLibs(resourcesFiles, options.exportPath, true);
|
||||
|
||||
// TODO Build a full includesFiles list without actually doing export or
|
||||
// generation.
|
||||
if (options.shouldGenerateScenesEventsCode) {
|
||||
// Create the index file
|
||||
if (!ExportIndexFile(exportedProject, gdjsRoot + "/Runtime/index.html",
|
||||
options.exportPath, includesFiles, usedSourceFiles,
|
||||
options.nonRuntimeScriptsCacheBurst,
|
||||
"gdjs.runtimeGameOptions")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
previousTime = LogTimeSpent("Include and libs export", previousTime);
|
||||
} else {
|
||||
gd::LogStatus("Include and libs export is skipped");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::ExportProjectData(
|
||||
gd::AbstractFileSystem &fs, gd::Project &project, gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions) {
|
||||
fs.MkDir(fs.DirNameFrom(filename));
|
||||
|
||||
gd::SerializerElement projectDataElement;
|
||||
ExporterHelper::StriptAndSerializeProjectData(project, projectDataElement);
|
||||
|
||||
// Save the project to JSON
|
||||
gd::String output =
|
||||
"gdjs.projectData = " + gd::Serializer::ToJSON(projectDataElement) +
|
||||
";\ngdjs.runtimeGameOptions = " + gd::Serializer::ToJSON(runtimeGameOptions) +
|
||||
";\n";
|
||||
|
||||
if (!fs.WriteToFile(filename, output))
|
||||
return "Unable to write " + filename;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void ExporterHelper::SerializeRuntimeGameOptions(
|
||||
gd::AbstractFileSystem &fs, const gd::String &gdjsRoot,
|
||||
const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
gd::SerializerElement &runtimeGameOptions) {
|
||||
// Create the setup options passed to the gdjs.RuntimeGame
|
||||
gd::SerializerElement runtimeGameOptions;
|
||||
runtimeGameOptions.AddChild("isPreview").SetBoolValue(true);
|
||||
|
||||
auto &initialRuntimeGameStatus =
|
||||
runtimeGameOptions.AddChild("initialRuntimeGameStatus");
|
||||
initialRuntimeGameStatus.AddChild("sceneName")
|
||||
.SetStringValue(options.layoutName);
|
||||
if (options.isInGameEdition) {
|
||||
initialRuntimeGameStatus.AddChild("isInGameEdition").SetBoolValue(true);
|
||||
initialRuntimeGameStatus.AddChild("editorId").SetValue(options.editorId);
|
||||
if (!options.editorCamera3DCameraMode.empty()) {
|
||||
auto &editorCamera3D =
|
||||
initialRuntimeGameStatus.AddChild("editorCamera3D");
|
||||
editorCamera3D.AddChild("cameraMode").SetStringValue(
|
||||
options.editorCamera3DCameraMode);
|
||||
editorCamera3D.AddChild("positionX")
|
||||
.SetDoubleValue(options.editorCamera3DPositionX);
|
||||
editorCamera3D.AddChild("positionY")
|
||||
.SetDoubleValue(options.editorCamera3DPositionY);
|
||||
editorCamera3D.AddChild("positionZ")
|
||||
.SetDoubleValue(options.editorCamera3DPositionZ);
|
||||
editorCamera3D.AddChild("rotationAngle")
|
||||
.SetDoubleValue(options.editorCamera3DRotationAngle);
|
||||
editorCamera3D.AddChild("elevationAngle")
|
||||
.SetDoubleValue(options.editorCamera3DElevationAngle);
|
||||
editorCamera3D.AddChild("distance")
|
||||
.SetDoubleValue(options.editorCamera3DDistance);
|
||||
}
|
||||
}
|
||||
if (!options.externalLayoutName.empty()) {
|
||||
initialRuntimeGameStatus.AddChild("injectedExternalLayoutName")
|
||||
runtimeGameOptions.AddChild("injectExternalLayout")
|
||||
.SetValue(options.externalLayoutName);
|
||||
|
||||
if (options.isInGameEdition) {
|
||||
initialRuntimeGameStatus.AddChild("skipCreatingInstancesFromScene")
|
||||
.SetBoolValue(true);
|
||||
}
|
||||
}
|
||||
if (!options.eventsBasedObjectType.empty()) {
|
||||
initialRuntimeGameStatus.AddChild("eventsBasedObjectType")
|
||||
.SetValue(options.eventsBasedObjectType);
|
||||
initialRuntimeGameStatus.AddChild("eventsBasedObjectVariantName")
|
||||
.SetValue(options.eventsBasedObjectVariantName);
|
||||
}
|
||||
|
||||
runtimeGameOptions.AddChild("shouldReloadLibraries")
|
||||
.SetBoolValue(options.shouldReloadLibraries);
|
||||
runtimeGameOptions.AddChild("shouldGenerateScenesEventsCode")
|
||||
.SetBoolValue(options.shouldGenerateScenesEventsCode);
|
||||
|
||||
runtimeGameOptions.AddChild("projectDataOnlyExport")
|
||||
.SetBoolValue(options.projectDataOnlyExport);
|
||||
runtimeGameOptions.AddChild("nativeMobileApp")
|
||||
.SetBoolValue(options.nativeMobileApp);
|
||||
runtimeGameOptions.AddChild("websocketDebuggerServerAddress")
|
||||
@@ -468,93 +297,71 @@ void ExporterHelper::SerializeRuntimeGameOptions(
|
||||
|
||||
for (const auto &includeFile : includesFiles) {
|
||||
auto hashIt = options.includeFileHashes.find(includeFile);
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(fs, gdjsRoot, includeFile);
|
||||
gd::String scriptSrc = GetExportedIncludeFilename(includeFile);
|
||||
scriptFilesElement.AddChild("scriptFile")
|
||||
.SetStringAttribute("path", scriptSrc)
|
||||
.SetIntAttribute(
|
||||
"hash",
|
||||
hashIt != options.includeFileHashes.end() ? hashIt->second : 0);
|
||||
}
|
||||
|
||||
// Export the project
|
||||
ExportProjectData(fs,
|
||||
exportedProject,
|
||||
codeOutputDir + "/data.js",
|
||||
runtimeGameOptions,
|
||||
projectUsedResources,
|
||||
scenesUsedResources);
|
||||
includesFiles.push_back(codeOutputDir + "/data.js");
|
||||
|
||||
previousTime = LogTimeSpent("Project data export", previousTime);
|
||||
|
||||
// Copy all the dependencies and their source maps
|
||||
ExportIncludesAndLibs(includesFiles, options.exportPath, true);
|
||||
ExportIncludesAndLibs(resourcesFiles, options.exportPath, true);
|
||||
|
||||
// Create the index file
|
||||
if (!ExportIndexFile(exportedProject,
|
||||
gdjsRoot + "/Runtime/index.html",
|
||||
options.exportPath,
|
||||
includesFiles,
|
||||
usedExtensionsResult.GetUsedSourceFiles(),
|
||||
options.nonRuntimeScriptsCacheBurst,
|
||||
"gdjs.runtimeGameOptions"))
|
||||
return false;
|
||||
|
||||
previousTime = LogTimeSpent("Include and libs export", previousTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExporterHelper::SerializeProjectData(gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &rootElement) {
|
||||
gd::Project clonedProject = project;
|
||||
|
||||
// Replace all resource file paths with the one used in exported projects.
|
||||
auto projectDirectory = fs.DirNameFrom(project.GetProjectFile());
|
||||
gd::ResourcesMergingHelper resourcesMergingHelper(
|
||||
clonedProject.GetResourcesManager(), fs);
|
||||
resourcesMergingHelper.SetBaseDirectory(projectDirectory);
|
||||
if (options.isInGameEdition) {
|
||||
resourcesMergingHelper.SetShouldUseOriginalAbsoluteFilenames();
|
||||
} else {
|
||||
resourcesMergingHelper.PreserveDirectoriesStructure(false);
|
||||
resourcesMergingHelper.PreserveAbsoluteFilenames(false);
|
||||
}
|
||||
gd::ResourceExposer::ExposeWholeProjectResources(clonedProject,
|
||||
resourcesMergingHelper);
|
||||
|
||||
ExporterHelper::StriptAndSerializeProjectData(clonedProject, rootElement);
|
||||
}
|
||||
|
||||
void ExporterHelper::StriptAndSerializeProjectData(
|
||||
gd::Project &project, gd::SerializerElement &rootElement) {
|
||||
auto projectUsedResources =
|
||||
gd::SceneResourcesFinder::FindProjectResources(project);
|
||||
std::unordered_map<gd::String, std::set<gd::String>> scenesUsedResources;
|
||||
for (std::size_t layoutIndex = 0;
|
||||
layoutIndex < project.GetLayoutsCount(); layoutIndex++) {
|
||||
auto &layout = project.GetLayout(layoutIndex);
|
||||
scenesUsedResources[layout.GetName()] =
|
||||
gd::SceneResourcesFinder::FindSceneResources(project, layout);
|
||||
}
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
eventsBasedObjectVariantsUsedResources;
|
||||
for (std::size_t extensionIndex = 0;
|
||||
extensionIndex < project.GetEventsFunctionsExtensionsCount();
|
||||
extensionIndex++) {
|
||||
auto &eventsFunctionsExtension =
|
||||
project.GetEventsFunctionsExtension(extensionIndex);
|
||||
for (auto &&eventsBasedObject :
|
||||
eventsFunctionsExtension.GetEventsBasedObjects().GetInternalVector()) {
|
||||
|
||||
auto eventsBasedObjectType = gd::PlatformExtension::GetObjectFullType(
|
||||
eventsFunctionsExtension.GetName(), eventsBasedObject->GetName());
|
||||
eventsBasedObjectVariantsUsedResources[eventsBasedObjectType] =
|
||||
gd::SceneResourcesFinder::FindEventsBasedObjectVariantResources(
|
||||
project, eventsBasedObject->GetDefaultVariant());
|
||||
|
||||
for (auto &&eventsBasedObjectVariant :
|
||||
eventsBasedObject->GetVariants().GetInternalVector()) {
|
||||
|
||||
auto variantType = gd::PlatformExtension::GetVariantFullType(
|
||||
eventsFunctionsExtension.GetName(), eventsBasedObject->GetName(),
|
||||
eventsBasedObjectVariant->GetName());
|
||||
eventsBasedObjectVariantsUsedResources[variantType] =
|
||||
gd::SceneResourcesFinder::FindEventsBasedObjectVariantResources(
|
||||
project, *eventsBasedObjectVariant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Strip the project (*after* generating events as the events may use stripped
|
||||
// things (objects groups...))
|
||||
gd::ProjectStripper::StripProjectForExport(project);
|
||||
gd::String ExporterHelper::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>> &scenesUsedResources) {
|
||||
fs.MkDir(fs.DirNameFrom(filename));
|
||||
|
||||
// Save the project to JSON
|
||||
gd::SerializerElement rootElement;
|
||||
project.SerializeTo(rootElement);
|
||||
SerializeUsedResources(rootElement, projectUsedResources, scenesUsedResources,
|
||||
eventsBasedObjectVariantsUsedResources);
|
||||
SerializeUsedResources(
|
||||
rootElement, projectUsedResources, scenesUsedResources);
|
||||
gd::String output =
|
||||
"gdjs.projectData = " + gd::Serializer::ToJSON(rootElement) + ";\n" +
|
||||
"gdjs.runtimeGameOptions = " +
|
||||
gd::Serializer::ToJSON(runtimeGameOptions) + ";\n";
|
||||
|
||||
if (!fs.WriteToFile(filename, output)) return "Unable to write " + filename;
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void ExporterHelper::SerializeUsedResources(
|
||||
gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&eventsBasedObjectVariantsUsedResources) {
|
||||
std::unordered_map<gd::String, std::set<gd::String>> &scenesUsedResources) {
|
||||
auto serializeUsedResources =
|
||||
[](gd::SerializerElement &element,
|
||||
std::set<gd::String> &usedResources) -> void {
|
||||
@@ -578,41 +385,6 @@ void ExporterHelper::SerializeUsedResources(
|
||||
auto &layoutUsedResources = scenesUsedResources[layoutName];
|
||||
serializeUsedResources(layoutElement, layoutUsedResources);
|
||||
}
|
||||
|
||||
auto &extensionsElement = rootElement.GetChild("eventsFunctionsExtensions");
|
||||
for (std::size_t extensionIndex = 0;
|
||||
extensionIndex < extensionsElement.GetChildrenCount();
|
||||
extensionIndex++) {
|
||||
auto &extensionElement = extensionsElement.GetChild(extensionIndex);
|
||||
const auto extensionName = extensionElement.GetStringAttribute("name");
|
||||
|
||||
auto &objectsElement = extensionElement.GetChild("eventsBasedObjects");
|
||||
|
||||
for (std::size_t objectIndex = 0;
|
||||
objectIndex < objectsElement.GetChildrenCount(); objectIndex++) {
|
||||
auto &objectElement = objectsElement.GetChild(objectIndex);
|
||||
const auto objectName = objectElement.GetStringAttribute("name");
|
||||
|
||||
auto eventsBasedObjectType =
|
||||
gd::PlatformExtension::GetObjectFullType(extensionName, objectName);
|
||||
auto &objectUsedResources =
|
||||
eventsBasedObjectVariantsUsedResources[eventsBasedObjectType];
|
||||
serializeUsedResources(objectElement, objectUsedResources);
|
||||
|
||||
auto &variantsElement = objectElement.GetChild("variants");
|
||||
for (std::size_t variantIndex = 0;
|
||||
variantIndex < variantsElement.GetChildrenCount(); variantIndex++) {
|
||||
auto &variantElement = variantsElement.GetChild(variantIndex);
|
||||
const auto variantName = variantElement.GetStringAttribute("name");
|
||||
|
||||
auto variantType = gd::PlatformExtension::GetVariantFullType(
|
||||
extensionName, objectName, variantName);
|
||||
auto &variantUsedResources =
|
||||
eventsBasedObjectVariantsUsedResources[variantType];
|
||||
serializeUsedResources(variantElement, variantUsedResources);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportIndexFile(
|
||||
@@ -1003,7 +775,7 @@ bool ExporterHelper::CompleteIndexFile(
|
||||
gd::String codeFilesIncludes;
|
||||
for (auto &include : includesFiles) {
|
||||
gd::String scriptSrc =
|
||||
GetExportedIncludeFilename(fs, gdjsRoot, include, nonRuntimeScriptsCacheBurst);
|
||||
GetExportedIncludeFilename(include, nonRuntimeScriptsCacheBurst);
|
||||
|
||||
// Sanity check if the file exists - if not skip it to avoid
|
||||
// including it in the list of scripts.
|
||||
@@ -1029,7 +801,6 @@ bool ExporterHelper::CompleteIndexFile(
|
||||
|
||||
void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
bool pixiInThreeRenderers,
|
||||
bool isInGameEdition,
|
||||
bool includeWebsocketDebuggerClient,
|
||||
bool includeWindowMessageDebuggerClient,
|
||||
bool includeMinimalDebuggerClient,
|
||||
@@ -1106,7 +877,6 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "debugger-client/hot-reloader.js");
|
||||
InsertUnique(includesFiles, "debugger-client/abstract-debugger-client.js");
|
||||
InsertUnique(includesFiles, "debugger-client/InGameDebugger.js");
|
||||
InsertUnique(includesFiles, "InGameEditor/InGameEditor.js");
|
||||
}
|
||||
if (includeWebsocketDebuggerClient) {
|
||||
InsertUnique(includesFiles, "debugger-client/websocket-debugger-client.js");
|
||||
@@ -1119,16 +889,14 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "debugger-client/minimal-debugger-client.js");
|
||||
}
|
||||
|
||||
if (pixiInThreeRenderers || isInGameEdition) {
|
||||
if (pixiInThreeRenderers) {
|
||||
InsertUnique(includesFiles, "pixi-renderers/three.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/ThreeAddons.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/draco/gltf/draco_decoder.wasm");
|
||||
InsertUnique(includesFiles,
|
||||
"pixi-renderers/draco/gltf/draco_wasm_wrapper.js");
|
||||
// Extensions in JS may use it.
|
||||
InsertUnique(includesFiles, "Extensions/3D/Scene3DTools.js");
|
||||
}
|
||||
if (pixiRenderers || isInGameEdition) {
|
||||
if (pixiRenderers) {
|
||||
InsertUnique(includesFiles, "pixi-renderers/pixi.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/pixi-filters-tools.js");
|
||||
InsertUnique(includesFiles, "pixi-renderers/runtimegame-pixi-renderer.js");
|
||||
@@ -1152,12 +920,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
includesFiles,
|
||||
"fontfaceobserver-font-manager/fontfaceobserver-font-manager.js");
|
||||
}
|
||||
if (isInGameEdition) {
|
||||
// `InGameEditor` uses the `is3D` function.
|
||||
InsertUnique(includesFiles, "Extensions/3D/Base3DBehavior.js");
|
||||
InsertUnique(includesFiles, "Extensions/3D/HemisphereLight.js");
|
||||
}
|
||||
if (pixiInThreeRenderers || isInGameEdition) {
|
||||
if (pixiInThreeRenderers) {
|
||||
InsertUnique(includesFiles, "Extensions/3D/A_RuntimeObject3D.js");
|
||||
InsertUnique(includesFiles, "Extensions/3D/A_RuntimeObject3DRenderer.js");
|
||||
InsertUnique(includesFiles, "Extensions/3D/CustomRuntimeObject3D.js");
|
||||
@@ -1195,7 +958,7 @@ bool ExporterHelper::ExportEffectIncludes(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExporterHelper::ExportScenesEventsCode(
|
||||
bool ExporterHelper::ExportEventsCode(
|
||||
const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
@@ -1231,7 +994,6 @@ bool ExporterHelper::ExportScenesEventsCode(
|
||||
}
|
||||
|
||||
gd::String ExporterHelper::GetExportedIncludeFilename(
|
||||
gd::AbstractFileSystem &fs, const gd::String &gdjsRoot,
|
||||
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst) {
|
||||
auto addSearchParameterToUrl = [](const gd::String &url,
|
||||
const gd::String &urlEncodedParameterName,
|
||||
|
@@ -42,9 +42,9 @@ struct PreviewExportOptions {
|
||||
useWindowMessageDebuggerClient(false),
|
||||
useMinimalDebuggerClient(false),
|
||||
nativeMobileApp(false),
|
||||
projectDataOnlyExport(false),
|
||||
fullLoadingScreen(false),
|
||||
isDevelopmentEnvironment(false),
|
||||
isInGameEdition(false),
|
||||
nonRuntimeScriptsCacheBurst(0),
|
||||
inAppTutorialMessageInPreview(""),
|
||||
inAppTutorialMessagePositionInPreview(""),
|
||||
@@ -145,26 +145,6 @@ struct PreviewExportOptions {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the (optional) events-based object to be instantiated in the scene
|
||||
* at the beginning of the previewed game.
|
||||
*/
|
||||
PreviewExportOptions &SetEventsBasedObjectType(
|
||||
const gd::String &eventsBasedObjectType_) {
|
||||
eventsBasedObjectType = eventsBasedObjectType_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the (optional) events-based object variant to be instantiated in the scene
|
||||
* at the beginning of the previewed game.
|
||||
*/
|
||||
PreviewExportOptions &SetEventsBasedObjectVariantName(
|
||||
const gd::String &eventsBasedObjectVariantName_) {
|
||||
eventsBasedObjectVariantName = eventsBasedObjectVariantName_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the hash associated to an include file. Useful for the preview
|
||||
* hot-reload, to know if a file changed.
|
||||
@@ -176,34 +156,11 @@ struct PreviewExportOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the exported folder should be cleared befor the export.
|
||||
* \brief Set if the export should only export the project data, not
|
||||
* exporting events code.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldClearExportFolder(bool enable) {
|
||||
shouldClearExportFolder = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the `ProjectData` must be reloaded.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldReloadProjectData(bool enable) {
|
||||
shouldReloadProjectData = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if GDJS libraries must be reloaded.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldReloadLibraries(bool enable) {
|
||||
shouldReloadLibraries = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export should export events code.
|
||||
*/
|
||||
PreviewExportOptions &SetShouldGenerateScenesEventsCode(bool enable) {
|
||||
shouldGenerateScenesEventsCode = enable;
|
||||
PreviewExportOptions &SetProjectDataOnlyExport(bool enable) {
|
||||
projectDataOnlyExport = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -225,40 +182,6 @@ struct PreviewExportOptions {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export is made for being edited in the editor.
|
||||
*/
|
||||
PreviewExportOptions &SetIsInGameEdition(bool enable) {
|
||||
isInGameEdition = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the in-game editor identifier.
|
||||
*/
|
||||
PreviewExportOptions &SetEditorId(const gd::String &editorId_) {
|
||||
editorId = editorId_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the camera state to use in the in-game editor.
|
||||
*/
|
||||
PreviewExportOptions &
|
||||
SetEditorCameraState3D(const gd::String &cameraMode, double positionX,
|
||||
double positionY, double positionZ,
|
||||
double rotationAngle, double elevationAngle,
|
||||
double distance) {
|
||||
editorCamera3DCameraMode = cameraMode;
|
||||
editorCamera3DPositionX = positionX;
|
||||
editorCamera3DPositionY = positionY;
|
||||
editorCamera3DPositionZ = positionZ;
|
||||
editorCamera3DRotationAngle = rotationAngle;
|
||||
editorCamera3DElevationAngle = elevationAngle;
|
||||
editorCamera3DDistance = distance;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief If set to a non zero value, the exported script URLs will have an
|
||||
* extra search parameter added (with the given value) to ensure browser cache
|
||||
@@ -371,8 +294,6 @@ struct PreviewExportOptions {
|
||||
bool useMinimalDebuggerClient;
|
||||
gd::String layoutName;
|
||||
gd::String externalLayoutName;
|
||||
gd::String eventsBasedObjectType;
|
||||
gd::String eventsBasedObjectVariantName;
|
||||
gd::String fallbackAuthorUsername;
|
||||
gd::String fallbackAuthorId;
|
||||
gd::String playerId;
|
||||
@@ -382,21 +303,9 @@ struct PreviewExportOptions {
|
||||
gd::String inAppTutorialMessagePositionInPreview;
|
||||
bool nativeMobileApp;
|
||||
std::map<gd::String, int> includeFileHashes;
|
||||
bool shouldClearExportFolder = true;
|
||||
bool shouldReloadProjectData = true;
|
||||
bool shouldReloadLibraries = true;
|
||||
bool shouldGenerateScenesEventsCode = true;
|
||||
bool projectDataOnlyExport;
|
||||
bool fullLoadingScreen;
|
||||
bool isDevelopmentEnvironment;
|
||||
bool isInGameEdition;
|
||||
gd::String editorId;
|
||||
gd::String editorCamera3DCameraMode;
|
||||
double editorCamera3DPositionX = 0;
|
||||
double editorCamera3DPositionY = 0;
|
||||
double editorCamera3DPositionZ = 0;
|
||||
double editorCamera3DRotationAngle = 0;
|
||||
double editorCamera3DElevationAngle = 0;
|
||||
double editorCamera3DDistance = 0;
|
||||
unsigned int nonRuntimeScriptsCacheBurst;
|
||||
gd::String electronRemoteRequirePath;
|
||||
gd::String gdevelopResourceToken;
|
||||
@@ -470,50 +379,23 @@ class ExporterHelper {
|
||||
const gd::String &GetLastError() const { return lastError; };
|
||||
|
||||
/**
|
||||
* \brief Export a project without its events and options to 2 JS variables
|
||||
* \brief Export a project to JSON
|
||||
*
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param project The project to be exported.
|
||||
* \param filename The filename where export the project
|
||||
* \param runtimeGameOptions The content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions
|
||||
*
|
||||
* \return Empty string if everything is ok,
|
||||
* in gdjs.runtimeGameOptions \return Empty string if everything is ok,
|
||||
* description of the error otherwise.
|
||||
*/
|
||||
static gd::String
|
||||
ExportProjectData(gd::AbstractFileSystem &fs, gd::Project &project,
|
||||
gd::String filename,
|
||||
const gd::SerializerElement &runtimeGameOptions);
|
||||
|
||||
/**
|
||||
* \brief Serialize a project without its events to JSON
|
||||
*
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param project The project to be exported.
|
||||
* \param projectDataElement The element where the project data is serialized
|
||||
*/
|
||||
static void SerializeProjectData(gd::AbstractFileSystem &fs,
|
||||
const gd::Project &project,
|
||||
const PreviewExportOptions &options,
|
||||
gd::SerializerElement &projectDataElement);
|
||||
|
||||
/**
|
||||
* \brief Serialize the content of the extra configuration to store
|
||||
* in gdjs.runtimeGameOptions to JSON
|
||||
*
|
||||
* \param fs The abstract file system to use to write the file
|
||||
* \param options The content of the extra configuration
|
||||
* \param includesFiles The list of scripts files - useful for hot-reloading
|
||||
* \param runtimeGameOptionsElement The element where the game options are
|
||||
* serialized
|
||||
*/
|
||||
static void
|
||||
SerializeRuntimeGameOptions(gd::AbstractFileSystem &fs,
|
||||
const gd::String &gdjsRoot,
|
||||
const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
gd::SerializerElement &runtimeGameOptionsElement);
|
||||
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,
|
||||
@@ -534,7 +416,6 @@ class ExporterHelper {
|
||||
*/
|
||||
void AddLibsInclude(bool pixiRenderers,
|
||||
bool pixiInThreeRenderers,
|
||||
bool isInGameEdition,
|
||||
bool includeWebsocketDebuggerClient,
|
||||
bool includeWindowMessageDebuggerClient,
|
||||
bool includeMinimalDebuggerClient,
|
||||
@@ -572,7 +453,7 @@ class ExporterHelper {
|
||||
* includesFiles A reference to a vector that will be filled with JS files to
|
||||
* be exported along with the project. ( including "codeX.js" files ).
|
||||
*/
|
||||
bool ExportScenesEventsCode(
|
||||
bool ExportEventsCode(
|
||||
const gd::Project &project,
|
||||
gd::String outputDir,
|
||||
std::vector<gd::String> &includesFiles,
|
||||
@@ -697,17 +578,14 @@ class ExporterHelper {
|
||||
* a browser pointing to the preview.
|
||||
*
|
||||
* \param options The options to generate the preview.
|
||||
* \param includesFiles The list of scripts files - useful for hot-reloading
|
||||
*/
|
||||
bool ExportProjectForPixiPreview(const PreviewExportOptions &options,
|
||||
std::vector<gd::String> &includesFiles);
|
||||
bool ExportProjectForPixiPreview(const PreviewExportOptions &options);
|
||||
|
||||
/**
|
||||
* \brief Given an include file, returns the name of the file to reference
|
||||
* in the exported game.
|
||||
*/
|
||||
static gd::String GetExportedIncludeFilename(
|
||||
gd::AbstractFileSystem &fs, const gd::String &gdjsRoot,
|
||||
gd::String GetExportedIncludeFilename(
|
||||
const gd::String &include, unsigned int nonRuntimeScriptsCacheBurst = 0);
|
||||
|
||||
/**
|
||||
@@ -734,22 +612,11 @@ class ExporterHelper {
|
||||
///< 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,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&eventsBasedObjectVariantsUsedResources);
|
||||
|
||||
/**
|
||||
* \brief Stript a project and serialize it to JSON
|
||||
*
|
||||
* \param project The project to be exported.
|
||||
*/
|
||||
static void
|
||||
StriptAndSerializeProjectData(gd::Project &project,
|
||||
gd::SerializerElement &rootElement);
|
||||
static void SerializeUsedResources(
|
||||
gd::SerializerElement &rootElement,
|
||||
std::set<gd::String> &projectUsedResources,
|
||||
std::unordered_map<gd::String, std::set<gd::String>>
|
||||
&layersUsedResources);
|
||||
};
|
||||
|
||||
} // namespace gdjs
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<name>GDJS_PROJECTNAME</name>
|
||||
<content src="index.html" />
|
||||
<plugin name="cordova-plugin-whitelist" version="1" />
|
||||
<plugin name="cordova-plugin-screen-orientation" version="3.0.4" />
|
||||
<plugin name="cordova-plugin-screen-orientation" version="3.0.2" />
|
||||
<access origin="*" />
|
||||
<allow-intent href="http://*/*" />
|
||||
<allow-intent href="https://*/*" />
|
||||
@@ -67,4 +67,4 @@
|
||||
<!-- Keep cordova-plugin-ionic-webview plugin last as it has a deployment-target to 11, which
|
||||
affects the installation of other plugins.-->
|
||||
<plugin name="cordova-plugin-ionic-webview" version="5.0.1" />
|
||||
</widget>
|
||||
</widget>
|
@@ -13,7 +13,7 @@ namespace gdjs {
|
||||
export type CustomObjectConfiguration = ObjectConfiguration & {
|
||||
animatable?: SpriteAnimationData[];
|
||||
variant: string;
|
||||
childrenContent?: { [objectName: string]: ObjectConfiguration & any };
|
||||
childrenContent: { [objectName: string]: ObjectConfiguration & any };
|
||||
isInnerAreaFollowingParentSize: boolean;
|
||||
};
|
||||
|
||||
@@ -110,19 +110,37 @@ namespace gdjs {
|
||||
);
|
||||
return;
|
||||
}
|
||||
const usedVariantData: EventsBasedObjectVariantData | null =
|
||||
this.getRuntimeScene()
|
||||
.getGame()
|
||||
.getEventsBasedObjectVariantData(
|
||||
customObjectData.type,
|
||||
customObjectData.variant
|
||||
);
|
||||
if (!usedVariantData) {
|
||||
// This can't actually happen.
|
||||
logger.error(
|
||||
`Unknown variant "${customObjectData.variant}" for object "${customObjectData.type}".`
|
||||
);
|
||||
return;
|
||||
|
||||
if (!eventsBasedObjectData.defaultVariant) {
|
||||
eventsBasedObjectData.defaultVariant = {
|
||||
...eventsBasedObjectData,
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
// Legacy events-based objects don't have any instance in their default
|
||||
// variant since there wasn't a graphical editor at the time.
|
||||
// In this case, the editor doesn't allow to choose a variant, but a
|
||||
// variant may have stayed after a user rolled back the extension.
|
||||
// This variant must be ignored to match what the editor shows.
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
eventsBasedObjectData.defaultVariant.instances.length == 0;
|
||||
let usedVariantData: EventsBasedObjectVariantData =
|
||||
eventsBasedObjectData.defaultVariant;
|
||||
if (
|
||||
customObjectData.variant &&
|
||||
!isForcedToOverrideEventsBasedObjectChildrenConfiguration
|
||||
) {
|
||||
for (
|
||||
let variantIndex = 0;
|
||||
variantIndex < eventsBasedObjectData.variants.length;
|
||||
variantIndex++
|
||||
) {
|
||||
const variantData = eventsBasedObjectData.variants[variantIndex];
|
||||
if (variantData.name === customObjectData.variant) {
|
||||
usedVariantData = variantData;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._isInnerAreaFollowingParentSize =
|
||||
@@ -152,7 +170,8 @@ namespace gdjs {
|
||||
override reinitialize(objectData: ObjectData & CustomObjectConfiguration) {
|
||||
super.reinitialize(objectData);
|
||||
|
||||
this._reinitializeContentFromObjectData(objectData);
|
||||
this._reinitializeRenderer();
|
||||
this._initializeFromObjectData(objectData);
|
||||
|
||||
// When changing the variant, the instance is like a new instance.
|
||||
// We call again `onCreated` at the end, like done by the constructor
|
||||
@@ -160,14 +179,6 @@ namespace gdjs {
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
private _reinitializeContentFromObjectData(
|
||||
objectData: ObjectData & CustomObjectConfiguration
|
||||
) {
|
||||
this._reinitializeRenderer();
|
||||
this._instanceContainer._unloadContent();
|
||||
this._initializeFromObjectData(objectData);
|
||||
}
|
||||
|
||||
override updateFromObjectData(
|
||||
oldObjectData: ObjectData & CustomObjectConfiguration,
|
||||
newObjectData: ObjectData & CustomObjectConfiguration
|
||||
@@ -195,7 +206,8 @@ namespace gdjs {
|
||||
this._instanceContainer._initialInnerArea.max[1] !==
|
||||
this._innerArea.max[1]);
|
||||
|
||||
this._reinitializeContentFromObjectData(newObjectData);
|
||||
this._reinitializeRenderer();
|
||||
this._initializeFromObjectData(newObjectData);
|
||||
|
||||
// The generated code calls the onCreated super implementation at the end.
|
||||
this.onCreated();
|
||||
@@ -249,13 +261,15 @@ namespace gdjs {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
this.setOpacity(
|
||||
initialInstanceData.opacity === undefined
|
||||
? 255
|
||||
: initialInstanceData.opacity
|
||||
);
|
||||
this.flipX(!!initialInstanceData.flippedX);
|
||||
this.flipY(!!initialInstanceData.flippedY);
|
||||
if (initialInstanceData.opacity !== undefined) {
|
||||
this.setOpacity(initialInstanceData.opacity);
|
||||
}
|
||||
if (initialInstanceData.flippedX) {
|
||||
this.flipX(initialInstanceData.flippedX);
|
||||
}
|
||||
if (initialInstanceData.flippedY) {
|
||||
this.flipY(initialInstanceData.flippedY);
|
||||
}
|
||||
}
|
||||
|
||||
override onDeletedFromScene(): void {
|
||||
@@ -286,13 +300,8 @@ namespace gdjs {
|
||||
if (profiler) {
|
||||
profiler.end(this.type);
|
||||
}
|
||||
}
|
||||
|
||||
override stepBehaviorsPostEvents(
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
super.stepBehaviorsPostEvents(instanceContainer);
|
||||
this._instanceContainer._stepBehaviorsPostEvents();
|
||||
this._instanceContainer._updateObjectsPostEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,20 +603,6 @@ namespace gdjs {
|
||||
return this._unrotatedAABB.max[1];
|
||||
}
|
||||
|
||||
getOriginalWidth(): float {
|
||||
return (
|
||||
this._instanceContainer.getInitialUnrotatedViewportMaxX() -
|
||||
this._instanceContainer.getInitialUnrotatedViewportMinX()
|
||||
);
|
||||
}
|
||||
|
||||
getOriginalHeight(): float {
|
||||
return (
|
||||
this._instanceContainer.getInitialUnrotatedViewportMaxY() -
|
||||
this._instanceContainer.getInitialUnrotatedViewportMinY()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the internal width of the object according to its children.
|
||||
*/
|
||||
|
@@ -16,7 +16,6 @@ namespace gdjs {
|
||||
_parent: gdjs.RuntimeInstanceContainer;
|
||||
/** The object that is built with the instances of this container. */
|
||||
_customObject: gdjs.CustomRuntimeObject;
|
||||
// TODO Remove this attribute
|
||||
_isLoaded: boolean = false;
|
||||
/**
|
||||
* The default size defined by users in the custom object initial instances editor.
|
||||
@@ -47,28 +46,15 @@ namespace gdjs {
|
||||
this._debuggerRenderer = new gdjs.DebuggerRenderer(this);
|
||||
}
|
||||
|
||||
// TODO `_layers` and `_orderedLayers` should not be used directly.
|
||||
|
||||
addLayer(layerData: LayerData) {
|
||||
if (this._layers.containsKey(layerData.name)) {
|
||||
return;
|
||||
}
|
||||
// This code is duplicated with `RuntimeScene.addLayer` because it avoids
|
||||
// to expose a method to build a layer.
|
||||
const layer = new gdjs.RuntimeCustomObjectLayer(layerData, this);
|
||||
this._layers.put(layerData.name, layer);
|
||||
this._orderedLayers.push(layer);
|
||||
}
|
||||
|
||||
_unloadContent() {
|
||||
this.onDeletedFromScene(this._parent);
|
||||
// At this point, layer renderers are already removed by
|
||||
// `CustomRuntimeObject._reinitializeRenderer`.
|
||||
// It's not great to do this here, but it allows to keep it private.
|
||||
this._layers.clear();
|
||||
this._orderedLayers.length = 0;
|
||||
}
|
||||
|
||||
createObject(objectName: string): gdjs.RuntimeObject | null {
|
||||
const result = super.createObject(objectName);
|
||||
this._customObject.onChildrenLocationChanged();
|
||||
@@ -77,14 +63,21 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Load the container from the given initial configuration.
|
||||
* @param customObjectData An object containing the parent object data.
|
||||
* @param eventsBasedObjectVariantData An object containing the container data.
|
||||
* @param customObjectData An object containing the container data.
|
||||
* @see gdjs.RuntimeGame#getSceneAndExtensionsData
|
||||
*/
|
||||
loadFrom(
|
||||
customObjectData: ObjectData & CustomObjectConfiguration,
|
||||
eventsBasedObjectVariantData: EventsBasedObjectVariantData
|
||||
) {
|
||||
if (this._isLoaded) {
|
||||
this.onDeletedFromScene(this._parent);
|
||||
}
|
||||
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
!eventsBasedObjectVariantData.name &&
|
||||
eventsBasedObjectVariantData.instances.length == 0;
|
||||
|
||||
this._setOriginalInnerArea(eventsBasedObjectVariantData);
|
||||
|
||||
// Registering objects
|
||||
@@ -94,21 +87,19 @@ namespace gdjs {
|
||||
++i
|
||||
) {
|
||||
const childObjectData = eventsBasedObjectVariantData.objects[i];
|
||||
// The children configuration override only applies to the default variant.
|
||||
if (
|
||||
customObjectData.childrenContent &&
|
||||
gdjs.CustomRuntimeObjectInstanceContainer.hasChildrenConfigurationOverriding(
|
||||
customObjectData,
|
||||
eventsBasedObjectVariantData
|
||||
)
|
||||
(!eventsBasedObjectVariantData.name ||
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration)
|
||||
) {
|
||||
this.registerObject({
|
||||
...childObjectData,
|
||||
// The custom object overrides its variant configuration with
|
||||
// a legacy children configuration.
|
||||
// The custom object overrides its events-based object configuration.
|
||||
...customObjectData.childrenContent[childObjectData.name],
|
||||
});
|
||||
} else {
|
||||
// The custom object follows its variant configuration.
|
||||
// The custom object follows its events-based object configuration.
|
||||
this.registerObject(childObjectData);
|
||||
}
|
||||
}
|
||||
@@ -163,28 +154,6 @@ namespace gdjs {
|
||||
this._isLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the custom object has a children configuration overriding that
|
||||
* should be used instead of the variant's objects configurations.
|
||||
* @param customObjectData An object containing the parent object data.
|
||||
* @param eventsBasedObjectVariantData An object containing the container data.
|
||||
* @returns
|
||||
*/
|
||||
static hasChildrenConfigurationOverriding(
|
||||
customObjectData: CustomObjectConfiguration,
|
||||
eventsBasedObjectVariantData: EventsBasedObjectVariantData
|
||||
): boolean {
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
!eventsBasedObjectVariantData.name &&
|
||||
eventsBasedObjectVariantData.instances.length == 0;
|
||||
|
||||
// The children configuration override only applies to the default variant.
|
||||
return customObjectData.childrenContent
|
||||
? !eventsBasedObjectVariantData.name ||
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration
|
||||
: false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize `_initialInnerArea` if it doesn't exist.
|
||||
* `_initialInnerArea` is shared by every instance to save memory.
|
||||
@@ -192,10 +161,7 @@ namespace gdjs {
|
||||
private _setOriginalInnerArea(
|
||||
eventsBasedObjectData: EventsBasedObjectVariantData
|
||||
) {
|
||||
if (
|
||||
eventsBasedObjectData.instances.length > 0 ||
|
||||
this.getGame().isInGameEdition()
|
||||
) {
|
||||
if (eventsBasedObjectData.instances.length > 0) {
|
||||
if (!eventsBasedObjectData._initialInnerArea) {
|
||||
eventsBasedObjectData._initialInnerArea = {
|
||||
min: [
|
||||
@@ -375,12 +341,6 @@ namespace gdjs {
|
||||
return this._initialInnerArea ? this._initialInnerArea.max[1] : 0;
|
||||
}
|
||||
|
||||
_getInitialInnerAreaDepth(): float {
|
||||
return this._initialInnerArea
|
||||
? this._initialInnerArea.max[2] - this._initialInnerArea.min[2]
|
||||
: 0;
|
||||
}
|
||||
|
||||
getViewportWidth(): float {
|
||||
return this._customObject.getUnscaledWidth();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -164,17 +164,14 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedThreeModel = this._loadedThreeModels.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
const loadedThreeModel = this._loadedThreeModels.get(resourceData);
|
||||
if (loadedThreeModel) {
|
||||
loadedThreeModel.scene.clear();
|
||||
this._loadedThreeModels.delete(resourceData);
|
||||
}
|
||||
|
||||
const downloadedArrayBuffer = this._downloadedArrayBuffers.getFromName(
|
||||
resourceData.name
|
||||
);
|
||||
const downloadedArrayBuffer =
|
||||
this._downloadedArrayBuffers.get(resourceData);
|
||||
if (downloadedArrayBuffer) {
|
||||
this._downloadedArrayBuffers.delete(resourceData);
|
||||
}
|
||||
|
@@ -282,27 +282,6 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
async loadResources(
|
||||
resourceNames: Array<string>,
|
||||
onProgress: (loadingCount: integer, totalCount: integer) => void
|
||||
): Promise<void> {
|
||||
let loadedCount = 0;
|
||||
await processAndRetryIfNeededWithPromisePool(
|
||||
resourceNames,
|
||||
maxForegroundConcurrency,
|
||||
maxAttempt,
|
||||
async (resourceName) => {
|
||||
const resource = this._resources.get(resourceName);
|
||||
if (resource) {
|
||||
await this._loadResource(resource);
|
||||
await this._processResource(resource);
|
||||
}
|
||||
loadedCount++;
|
||||
onProgress(loadedCount, this._resources.size);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the resources that are needed to launch the first scene.
|
||||
*/
|
||||
@@ -572,23 +551,6 @@ namespace gdjs {
|
||||
// TODO: mark the scene as unloaded so it's not automatically loaded again eagerly.
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called when hot-reloading resources.
|
||||
*/
|
||||
unloadAllResources(): void {
|
||||
debugLogger.log(`Unloading of all resources was requested.`);
|
||||
for (const resource of this._resources.values()) {
|
||||
const resourceManager = this._resourceManagersMap.get(resource.kind);
|
||||
if (resourceManager) {
|
||||
resourceManager.unloadResource(resource);
|
||||
}
|
||||
}
|
||||
for (const sceneLoadingState of this._sceneLoadingStates.values()) {
|
||||
sceneLoadingState.status = 'not-loaded';
|
||||
}
|
||||
debugLogger.log(`Unloading of all resources finished.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a given scene at the end of the queue.
|
||||
*
|
||||
@@ -690,9 +652,6 @@ namespace gdjs {
|
||||
* the resource (this can be for example a token needed to access the resource).
|
||||
*/
|
||||
getFullUrl(url: string) {
|
||||
if (this._runtimeGame.isInGameEdition()) {
|
||||
url = addSearchParameterToUrl(url, 'cache', '' + Date.now());
|
||||
}
|
||||
const { gdevelopResourceToken } = this._runtimeGame._options;
|
||||
if (!gdevelopResourceToken) return url;
|
||||
|
||||
|
@@ -575,18 +575,10 @@ namespace gdjs {
|
||||
this._cacheOrClearRemovedInstances();
|
||||
}
|
||||
|
||||
_updateObjectsForInGameEditor() {
|
||||
const allInstancesList = this.getAdhocListOfAllInstances();
|
||||
for (let i = 0, len = allInstancesList.length; i < len; ++i) {
|
||||
const obj = allInstancesList[i];
|
||||
obj.update(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call each behavior stepPostEvents method.
|
||||
* Update the objects (update positions, time management...)
|
||||
*/
|
||||
_stepBehaviorsPostEvents() {
|
||||
_updateObjectsPostEvents() {
|
||||
this._cacheOrClearRemovedInstances();
|
||||
|
||||
// It is *mandatory* to create and iterate on a external list of all objects, as the behaviors
|
||||
@@ -620,7 +612,7 @@ namespace gdjs {
|
||||
getObjects(name: string): gdjs.RuntimeObject[] | undefined {
|
||||
if (!this._instances.containsKey(name)) {
|
||||
logger.info(
|
||||
'RuntimeInstanceContainer.getObjects: No instances called "' +
|
||||
'RuntimeScene.getObjects: No instances called "' +
|
||||
name +
|
||||
'"! Adding it.'
|
||||
);
|
||||
|
@@ -55,14 +55,13 @@ namespace gdjs {
|
||||
_timeScale: float = 1;
|
||||
_defaultZOrder: integer = 0;
|
||||
_hidden: boolean;
|
||||
_initialLayerData: LayerData;
|
||||
_initialEffectsData: Array<EffectData>;
|
||||
|
||||
// TODO EBO Don't store scene layer related data in layers used by custom objects.
|
||||
// (both these 3D settings and the lighting layer properties below).
|
||||
_initialCamera3DFieldOfView: float;
|
||||
_initialCamera3DFarPlaneDistance: float;
|
||||
_initialCamera3DNearPlaneDistance: float;
|
||||
_initialCamera2DPlaneMaxDrawingDistance: float;
|
||||
|
||||
_runtimeScene: gdjs.RuntimeInstanceContainer;
|
||||
_effectsManager: gdjs.EffectsManager;
|
||||
@@ -95,9 +94,7 @@ namespace gdjs {
|
||||
layerData.camera3DNearPlaneDistance || 0.1;
|
||||
this._initialCamera3DFarPlaneDistance =
|
||||
layerData.camera3DFarPlaneDistance || 2000;
|
||||
this._initialCamera2DPlaneMaxDrawingDistance =
|
||||
layerData.camera2DPlaneMaxDrawingDistance || 5000;
|
||||
this._initialLayerData = layerData;
|
||||
this._initialEffectsData = layerData.effects || [];
|
||||
this._runtimeScene = instanceContainer;
|
||||
this._effectsManager = instanceContainer.getGame().getEffectsManager();
|
||||
this._isLightingLayer = layerData.isLightingLayer;
|
||||
@@ -399,9 +396,6 @@ namespace gdjs {
|
||||
getInitialCamera3DFarPlaneDistance(): float {
|
||||
return this._initialCamera3DFarPlaneDistance;
|
||||
}
|
||||
getInitialCamera2DPlaneMaxDrawingDistance(): float {
|
||||
return this._initialCamera2DPlaneMaxDrawingDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the initial effects data for the layer. Only to
|
||||
@@ -409,7 +403,7 @@ namespace gdjs {
|
||||
* @deprecated
|
||||
*/
|
||||
getInitialEffectsData(): EffectData[] {
|
||||
return this._initialLayerData.effects || [];
|
||||
return this._initialEffectsData;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -107,12 +107,8 @@ namespace gdjs {
|
||||
exception: Error,
|
||||
runtimeGame: gdjs.RuntimeGame
|
||||
) => {
|
||||
const currentScene = runtimeGame.isInGameEdition()
|
||||
? runtimeGame.getInGameEditor()?.getCurrentScene()
|
||||
: runtimeGame.getSceneStack().getCurrentScene();
|
||||
const sceneNames = runtimeGame.isInGameEdition()
|
||||
? [currentScene?.getName()]
|
||||
: runtimeGame.getSceneStack().getAllSceneNames();
|
||||
const sceneNames = runtimeGame.getSceneStack().getAllSceneNames();
|
||||
const currentScene = runtimeGame.getSceneStack().getCurrentScene();
|
||||
return {
|
||||
type: 'javascript-uncaught-exception',
|
||||
exception,
|
||||
@@ -120,7 +116,6 @@ namespace gdjs {
|
||||
playerId: runtimeGame.getPlayerId(),
|
||||
sessionId: runtimeGame.getSessionId(),
|
||||
isPreview: runtimeGame.isPreview(),
|
||||
isInGameEdition: runtimeGame.isInGameEdition(),
|
||||
gdevelop: {
|
||||
previewContext: runtimeGame.getAdditionalOptions().previewContext,
|
||||
isNativeMobileApp: runtimeGame.getAdditionalOptions().nativeMobileApp,
|
||||
@@ -238,242 +233,42 @@ namespace gdjs {
|
||||
protected handleCommand(data: any) {
|
||||
const that = this;
|
||||
const runtimeGame = this._runtimegame;
|
||||
const inGameEditor = runtimeGame.getInGameEditor();
|
||||
if (!data || !data.command) {
|
||||
// Not a command that's meant to be handled by the debugger, return silently to
|
||||
// avoid polluting the console.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (data.command === 'play') {
|
||||
runtimeGame.pause(false);
|
||||
} else if (data.command === 'pause') {
|
||||
runtimeGame.pause(true);
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'refresh') {
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'getStatus') {
|
||||
that.sendRuntimeGameStatus();
|
||||
} else if (data.command === 'set') {
|
||||
that.set(data.path, data.newValue);
|
||||
} else if (data.command === 'call') {
|
||||
that.call(data.path, data.args);
|
||||
} else if (data.command === 'profiler.start') {
|
||||
runtimeGame.startCurrentSceneProfiler(function (stoppedProfiler) {
|
||||
that.sendProfilerOutput(
|
||||
stoppedProfiler.getFramesAverageMeasures(),
|
||||
stoppedProfiler.getStats()
|
||||
);
|
||||
that.sendProfilerStopped();
|
||||
});
|
||||
that.sendProfilerStarted();
|
||||
} else if (data.command === 'profiler.stop') {
|
||||
runtimeGame.stopCurrentSceneProfiler();
|
||||
} else if (data.command === 'hotReload') {
|
||||
this._hasLoggedUncaughtException = false;
|
||||
that._hotReloader
|
||||
.hotReload({
|
||||
projectData: data.payload.projectData,
|
||||
runtimeGameOptions: data.payload.runtimeGameOptions,
|
||||
shouldReloadResources:
|
||||
data.payload.shouldReloadResources || false,
|
||||
})
|
||||
.then((logs) => {
|
||||
that.sendHotReloaderLogs(logs);
|
||||
});
|
||||
} else if (data.command === 'hotReloadObjects') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
that._hotReloader.hotReloadRuntimeSceneObjects(
|
||||
data.payload.updatedObjects,
|
||||
editedInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'hotReloadLayers') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
inGameEditor.onLayersDataChange(
|
||||
data.payload.layers,
|
||||
data.payload.areEffectsHidden
|
||||
);
|
||||
runtimeGame._data.areEffectsHiddenInEditor =
|
||||
data.payload.areEffectsHidden;
|
||||
that._hotReloader.hotReloadRuntimeSceneLayers(
|
||||
data.payload.layers,
|
||||
editedInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'setBackgroundColor') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
const backgroundColor = data.payload.backgroundColor;
|
||||
if (
|
||||
backgroundColor &&
|
||||
editedInstanceContainer instanceof gdjs.RuntimeScene
|
||||
) {
|
||||
const sceneData = runtimeGame.getSceneData(
|
||||
editedInstanceContainer.getScene().getName()
|
||||
);
|
||||
if (sceneData) {
|
||||
editedInstanceContainer._backgroundColor =
|
||||
gdjs.rgbToHexNumber(
|
||||
backgroundColor[0],
|
||||
backgroundColor[1],
|
||||
backgroundColor[2]
|
||||
);
|
||||
sceneData.r = backgroundColor[0];
|
||||
sceneData.v = backgroundColor[1];
|
||||
sceneData.b = backgroundColor[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'hotReloadAllInstances') {
|
||||
if (inGameEditor) {
|
||||
const editedInstanceContainer =
|
||||
inGameEditor.getEditedInstanceContainer();
|
||||
if (editedInstanceContainer) {
|
||||
that._hotReloader.hotReloadRuntimeInstances(
|
||||
inGameEditor.getEditedInstanceDataList(),
|
||||
data.payload.instances,
|
||||
editedInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'switchForInGameEdition') {
|
||||
if (!this._runtimegame.isInGameEdition()) return;
|
||||
|
||||
const sceneName = data.sceneName || null;
|
||||
const eventsBasedObjectType = data.eventsBasedObjectType || null;
|
||||
if (!sceneName && !eventsBasedObjectType) {
|
||||
logger.warn(
|
||||
'No scene name specified, switchForInGameEdition aborted'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (inGameEditor) {
|
||||
const wasPaused = this._runtimegame.isPaused();
|
||||
this._runtimegame.pause(true);
|
||||
inGameEditor.switchToSceneOrVariant(
|
||||
data.editorId || null,
|
||||
sceneName,
|
||||
data.externalLayoutName || null,
|
||||
eventsBasedObjectType,
|
||||
data.eventsBasedObjectVariantName || null,
|
||||
data.editorCamera3D || null
|
||||
);
|
||||
this._runtimegame.pause(wasPaused);
|
||||
}
|
||||
} else if (data.command === 'setVisibleStatus') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setVisibleStatus(data.visible);
|
||||
}
|
||||
} else if (data.command === 'updateInstances') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.reloadInstances(data.payload.instances);
|
||||
}
|
||||
} else if (data.command === 'addInstances') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.addInstances(data.payload.instances);
|
||||
inGameEditor.setSelectedObjects(
|
||||
data.payload.instances.map((instance) => instance.persistentUuid)
|
||||
);
|
||||
if (data.payload.moveUnderCursor) {
|
||||
inGameEditor.moveSelectionUnderCursor();
|
||||
}
|
||||
}
|
||||
} else if (data.command === 'deleteSelection') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.deleteSelection();
|
||||
}
|
||||
} else if (data.command === 'dragNewInstance') {
|
||||
const gameCoords = runtimeGame
|
||||
.getRenderer()
|
||||
.convertPageToGameCoords(data.x, data.y);
|
||||
runtimeGame
|
||||
.getInputManager()
|
||||
.onMouseMove(gameCoords[0], gameCoords[1]);
|
||||
|
||||
if (inGameEditor)
|
||||
inGameEditor.dragNewInstance({
|
||||
name: data.name,
|
||||
dropped: data.dropped,
|
||||
});
|
||||
} else if (data.command === 'cancelDragNewInstance') {
|
||||
if (inGameEditor) inGameEditor.cancelDragNewInstance();
|
||||
} else if (data.command === 'setInstancesEditorSettings') {
|
||||
if (inGameEditor)
|
||||
inGameEditor.updateInstancesEditorSettings(
|
||||
data.payload.instancesEditorSettings
|
||||
);
|
||||
} else if (data.command === 'zoomToInitialPosition') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomToInitialPosition(data.payload.visibleScreenArea);
|
||||
}
|
||||
} else if (data.command === 'zoomToFitContent') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomToFitContent(data.payload.visibleScreenArea);
|
||||
}
|
||||
} else if (data.command === 'setSelectedLayer') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setSelectedLayerName(data.payload.layerName);
|
||||
}
|
||||
} else if (data.command === 'zoomToFitSelection') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomToFitSelection(data.payload.visibleScreenArea);
|
||||
}
|
||||
} else if (data.command === 'zoomBy') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.zoomBy(data.payload.zoomFactor);
|
||||
}
|
||||
} else if (data.command === 'setZoom') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setZoom(data.payload.zoom);
|
||||
}
|
||||
} else if (data.command === 'setSelectedInstances') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.setSelectedObjects(data.payload.instanceUuids);
|
||||
}
|
||||
} else if (data.command === 'centerViewOnLastSelectedInstance') {
|
||||
if (inGameEditor) {
|
||||
// TODO: use data.payload.visibleScreenArea
|
||||
inGameEditor.centerViewOnLastSelectedInstance();
|
||||
}
|
||||
} else if (data.command === 'updateInnerArea') {
|
||||
if (inGameEditor) {
|
||||
inGameEditor.updateInnerArea(
|
||||
data.payload.areaMinX,
|
||||
data.payload.areaMinY,
|
||||
data.payload.areaMinZ,
|
||||
data.payload.areaMaxX,
|
||||
data.payload.areaMaxY,
|
||||
data.payload.areaMaxZ
|
||||
);
|
||||
}
|
||||
} else if (data.command === 'getSelectionAABB') {
|
||||
if (inGameEditor) {
|
||||
this.sendSelectionAABB(data.messageId);
|
||||
}
|
||||
} else if (data.command === 'hardReload') {
|
||||
// This usually means that the preview was modified so much that an entire reload
|
||||
// is needed, or that the runtime itself could have been modified.
|
||||
this.launchHardReload();
|
||||
} else {
|
||||
logger.info(
|
||||
'Unknown command "' + data.command + '" received by the debugger.'
|
||||
if (data.command === 'play') {
|
||||
runtimeGame.pause(false);
|
||||
} else if (data.command === 'pause') {
|
||||
runtimeGame.pause(true);
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'refresh') {
|
||||
that.sendRuntimeGameDump();
|
||||
} else if (data.command === 'set') {
|
||||
that.set(data.path, data.newValue);
|
||||
} else if (data.command === 'call') {
|
||||
that.call(data.path, data.args);
|
||||
} else if (data.command === 'profiler.start') {
|
||||
runtimeGame.startCurrentSceneProfiler(function (stoppedProfiler) {
|
||||
that.sendProfilerOutput(
|
||||
stoppedProfiler.getFramesAverageMeasures(),
|
||||
stoppedProfiler.getStats()
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.onUncaughtException(error);
|
||||
that.sendProfilerStopped();
|
||||
});
|
||||
that.sendProfilerStarted();
|
||||
} else if (data.command === 'profiler.stop') {
|
||||
runtimeGame.stopCurrentSceneProfiler();
|
||||
} else if (data.command === 'hotReload') {
|
||||
that._hotReloader.hotReload().then((logs) => {
|
||||
that.sendHotReloaderLogs(logs);
|
||||
});
|
||||
} else {
|
||||
logger.info(
|
||||
'Unknown command "' + data.command + '" received by the debugger.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,12 +330,9 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
onUncaughtException(exception: Error): void {
|
||||
logger.error('Uncaught exception: ', exception, exception.stack);
|
||||
logger.error('Uncaught exception: ' + exception);
|
||||
|
||||
const runtimeGame = this._runtimegame;
|
||||
if (!runtimeGame.isInGameEdition()) {
|
||||
this._inGameDebugger.setUncaughtException(exception);
|
||||
}
|
||||
this._inGameDebugger.setUncaughtException(exception);
|
||||
|
||||
if (!this._hasLoggedUncaughtException) {
|
||||
// Only log an uncaught exception once, to avoid spamming the debugger server
|
||||
@@ -643,20 +435,6 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
sendRuntimeGameStatus(): void {
|
||||
const currentScene = this._runtimegame.getSceneStack().getCurrentScene();
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'status',
|
||||
payload: {
|
||||
isPaused: this._runtimegame.isPaused(),
|
||||
isInGameEdition: this._runtimegame.isInGameEdition(),
|
||||
sceneName: currentScene ? currentScene.getName() : null,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all the relevant data from the {@link RuntimeGame} instance and send it to the server.
|
||||
*/
|
||||
@@ -737,10 +515,7 @@ namespace gdjs {
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'hotReloader.logs',
|
||||
payload: {
|
||||
isInGameEdition: this._runtimegame.isInGameEdition(),
|
||||
logs,
|
||||
},
|
||||
payload: logs,
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -769,152 +544,26 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
|
||||
sendInstanceChanges(changes: {
|
||||
isSendingBackSelectionForDefaultSize: boolean;
|
||||
updatedInstances: Array<InstanceData>;
|
||||
addedInstances: Array<InstanceData>;
|
||||
selectedInstances: Array<InstancePersistentUuidData>;
|
||||
removedInstances: Array<InstancePersistentUuidData>;
|
||||
}): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Callback called when the game is paused.
|
||||
*/
|
||||
sendGamePaused(): void {
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'updateInstances',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: changes,
|
||||
command: 'game.paused',
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendOpenContextMenu(cursorX: float, cursorY: float): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Callback called when the game is resumed.
|
||||
*/
|
||||
sendGameResumed(): void {
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'openContextMenu',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: { cursorX, cursorY },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendCameraState(cameraState: EditorCameraState): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'setCameraState',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: cameraState,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendUndo(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'undo',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendRedo(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'redo',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendCopy(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'copy',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendPaste(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'paste',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendCut(): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'cut',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
payload: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
sendSelectionAABB(messageId: number): void {
|
||||
const inGameEditor = this._runtimegame.getInGameEditor();
|
||||
if (!inGameEditor) {
|
||||
return;
|
||||
}
|
||||
const selectionAABB = inGameEditor.getSelectionAABB();
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'selectionAABB',
|
||||
editorId: inGameEditor.getEditorId(),
|
||||
messageId,
|
||||
payload: selectionAABB
|
||||
? {
|
||||
minX: selectionAABB.min[0],
|
||||
minY: selectionAABB.min[1],
|
||||
minZ: selectionAABB.min[2],
|
||||
maxX: selectionAABB.max[0],
|
||||
maxY: selectionAABB.max[1],
|
||||
maxZ: selectionAABB.max[2],
|
||||
}
|
||||
: {
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
minZ: 0,
|
||||
maxX: 0,
|
||||
maxY: 0,
|
||||
maxZ: 0,
|
||||
},
|
||||
command: 'game.resumed',
|
||||
payload: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -938,43 +587,5 @@ namespace gdjs {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
launchHardReload(): void {
|
||||
try {
|
||||
const reloadUrl = new URL(location.href);
|
||||
|
||||
// Construct the initial status to be restored.
|
||||
const initialRuntimeGameStatus =
|
||||
this._runtimegame.getAdditionalOptions().initialRuntimeGameStatus;
|
||||
// We use empty strings to avoid `null` to become `"null"`.
|
||||
const runtimeGameStatus: RuntimeGameStatus = {
|
||||
editorId: initialRuntimeGameStatus?.editorId || '',
|
||||
isPaused: this._runtimegame.isPaused(),
|
||||
isInGameEdition: this._runtimegame.isInGameEdition(),
|
||||
sceneName: initialRuntimeGameStatus?.sceneName || '',
|
||||
injectedExternalLayoutName:
|
||||
initialRuntimeGameStatus?.injectedExternalLayoutName || '',
|
||||
skipCreatingInstancesFromScene:
|
||||
initialRuntimeGameStatus?.skipCreatingInstancesFromScene || false,
|
||||
eventsBasedObjectType:
|
||||
initialRuntimeGameStatus?.eventsBasedObjectType || '',
|
||||
eventsBasedObjectVariantName:
|
||||
initialRuntimeGameStatus?.eventsBasedObjectVariantName || '',
|
||||
editorCamera3D: this._runtimegame.getInGameEditor()?.getCameraState(),
|
||||
};
|
||||
|
||||
reloadUrl.searchParams.set(
|
||||
'runtimeGameStatus',
|
||||
JSON.stringify(runtimeGameStatus)
|
||||
);
|
||||
location.replace(reloadUrl);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
'Could not reload the game with the new initial status',
|
||||
error
|
||||
);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -144,30 +144,18 @@ namespace gdjs {
|
||||
});
|
||||
}
|
||||
|
||||
async hotReload({
|
||||
shouldReloadResources,
|
||||
projectData: newProjectData,
|
||||
runtimeGameOptions: newRuntimeGameOptions,
|
||||
}: {
|
||||
shouldReloadResources: boolean;
|
||||
projectData: ProjectData;
|
||||
runtimeGameOptions: RuntimeGameOptions;
|
||||
}): Promise<HotReloaderLog[]> {
|
||||
hotReload(): Promise<HotReloaderLog[]> {
|
||||
logger.info('Hot reload started');
|
||||
const wasPaused = this._runtimeGame.isPaused();
|
||||
this._runtimeGame.pause(true);
|
||||
this._logs = [];
|
||||
|
||||
// Save old data of the project, to be used to compute
|
||||
// the difference between the old and new project data:
|
||||
|
||||
const oldProjectData: ProjectData = gdjs.projectData;
|
||||
gdjs.projectData = newProjectData;
|
||||
|
||||
const oldRuntimeGameOptions = gdjs.runtimeGameOptions;
|
||||
gdjs.runtimeGameOptions = newRuntimeGameOptions;
|
||||
|
||||
const oldScriptFiles =
|
||||
oldRuntimeGameOptions.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
const oldScriptFiles = gdjs.runtimeGameOptions
|
||||
.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
|
||||
oldScriptFiles.forEach((scriptFile) => {
|
||||
this._alreadyLoadedScriptFiles[scriptFile.path] = true;
|
||||
@@ -179,102 +167,76 @@ namespace gdjs {
|
||||
gdjs.behaviorsTypes.items[behaviorTypeName];
|
||||
}
|
||||
|
||||
if (gdjs.inAppTutorialMessage) {
|
||||
gdjs.inAppTutorialMessage.displayInAppTutorialMessage(
|
||||
this._runtimeGame,
|
||||
newRuntimeGameOptions.inAppTutorialMessageInPreview,
|
||||
newRuntimeGameOptions.inAppTutorialMessagePositionInPreview || ''
|
||||
);
|
||||
}
|
||||
// Reload projectData and runtimeGameOptions stored by convention in data.js:
|
||||
return this._reloadScript('data.js').then(() => {
|
||||
const newProjectData: ProjectData = gdjs.projectData;
|
||||
|
||||
const newScriptFiles =
|
||||
newRuntimeGameOptions.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
const shouldGenerateScenesEventsCode =
|
||||
!!newRuntimeGameOptions.shouldGenerateScenesEventsCode;
|
||||
const shouldReloadLibraries =
|
||||
!!newRuntimeGameOptions.shouldReloadLibraries;
|
||||
const newRuntimeGameOptions: RuntimeGameOptions =
|
||||
gdjs.runtimeGameOptions;
|
||||
|
||||
// Reload the changed scripts, which will have the side effects of re-running
|
||||
// the new scripts, potentially replacing the code of the free functions from
|
||||
// extensions (which is fine) and registering updated behaviors (which will
|
||||
// need to be re-instantiated in runtime objects).
|
||||
try {
|
||||
if (shouldReloadLibraries) {
|
||||
await this.reloadScriptFiles(
|
||||
newProjectData,
|
||||
oldScriptFiles,
|
||||
newScriptFiles,
|
||||
shouldGenerateScenesEventsCode
|
||||
if (gdjs.inAppTutorialMessage) {
|
||||
gdjs.inAppTutorialMessage.displayInAppTutorialMessage(
|
||||
this._runtimeGame,
|
||||
newRuntimeGameOptions.inAppTutorialMessageInPreview,
|
||||
newRuntimeGameOptions.inAppTutorialMessagePositionInPreview || ''
|
||||
);
|
||||
}
|
||||
const newRuntimeGameStatus =
|
||||
newRuntimeGameOptions.initialRuntimeGameStatus;
|
||||
if (
|
||||
newRuntimeGameStatus &&
|
||||
newRuntimeGameStatus.editorId &&
|
||||
newRuntimeGameStatus.isInGameEdition
|
||||
) {
|
||||
if (shouldReloadResources) {
|
||||
// Unloading all resources will force them to be loaded again,
|
||||
// which is sufficient for ensuring they are up-to-date as
|
||||
// resources will be loaded with a 'cache bursting' parameter.
|
||||
this._runtimeGame._resourcesLoader.unloadAllResources();
|
||||
}
|
||||
// The editor don't need to hot-reload the current scene because the
|
||||
// editor always stays in the initial state.
|
||||
this._runtimeGame.setProjectData(newProjectData);
|
||||
await this._runtimeGame.loadFirstAssetsAndStartBackgroundLoading(
|
||||
newRuntimeGameStatus.sceneName || newProjectData.firstLayout,
|
||||
() => {}
|
||||
);
|
||||
const inGameEditor = this._runtimeGame.getInGameEditor();
|
||||
if (inGameEditor) {
|
||||
await inGameEditor.switchToSceneOrVariant(
|
||||
newRuntimeGameStatus.editorId || null,
|
||||
newRuntimeGameStatus.sceneName,
|
||||
newRuntimeGameStatus.injectedExternalLayoutName,
|
||||
newRuntimeGameStatus.eventsBasedObjectType,
|
||||
newRuntimeGameStatus.eventsBasedObjectVariantName,
|
||||
newRuntimeGameStatus.editorCamera3D || null
|
||||
|
||||
const newScriptFiles =
|
||||
newRuntimeGameOptions.scriptFiles as RuntimeGameOptionsScriptFile[];
|
||||
const projectDataOnlyExport =
|
||||
!!newRuntimeGameOptions.projectDataOnlyExport;
|
||||
|
||||
// Reload the changed scripts, which will have the side effects of re-running
|
||||
// the new scripts, potentially replacing the code of the free functions from
|
||||
// extensions (which is fine) and registering updated behaviors (which will
|
||||
// need to be re-instantiated in runtime objects).
|
||||
return this.reloadScriptFiles(
|
||||
newProjectData,
|
||||
oldScriptFiles,
|
||||
newScriptFiles,
|
||||
projectDataOnlyExport
|
||||
)
|
||||
.then(() => {
|
||||
const changedRuntimeBehaviors =
|
||||
this._computeChangedRuntimeBehaviors(
|
||||
oldBehaviorConstructors,
|
||||
gdjs.behaviorsTypes.items
|
||||
);
|
||||
return this._hotReloadRuntimeGame(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
changedRuntimeBehaviors,
|
||||
this._runtimeGame
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const changedRuntimeBehaviors = this._computeChangedRuntimeBehaviors(
|
||||
oldBehaviorConstructors,
|
||||
gdjs.behaviorsTypes.items
|
||||
);
|
||||
await this._hotReloadRuntimeGame(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
changedRuntimeBehaviors,
|
||||
this._runtimeGame
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
const errorTarget = error.target;
|
||||
if (errorTarget instanceof HTMLScriptElement) {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message: 'Unable to reload script: ' + errorTarget.src,
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorTarget = error.target;
|
||||
if (errorTarget instanceof HTMLScriptElement) {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message: 'Unable to reload script: ' + errorTarget.src,
|
||||
});
|
||||
} else {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message:
|
||||
'Unexpected error happened while hot-reloading: ' +
|
||||
error.message +
|
||||
'\n' +
|
||||
error.stack,
|
||||
});
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
logger.info(
|
||||
'Hot reload finished with logs:',
|
||||
this._logs.map((log) => '\n' + log.kind + ': ' + log.message)
|
||||
);
|
||||
this._runtimeGame.pause(false);
|
||||
return this._logs;
|
||||
});
|
||||
} else {
|
||||
this._logs.push({
|
||||
kind: 'fatal',
|
||||
message:
|
||||
'Unexpected error happened while hot-reloading: ' +
|
||||
error.message +
|
||||
'\n' +
|
||||
error.stack,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
'Hot reload finished with logs:',
|
||||
this._logs.map((log) => '\n' + log.kind + ': ' + log.message)
|
||||
);
|
||||
this._runtimeGame.pause(wasPaused);
|
||||
return this._logs;
|
||||
});
|
||||
}
|
||||
|
||||
_computeChangedRuntimeBehaviors(
|
||||
@@ -319,12 +281,12 @@ namespace gdjs {
|
||||
newProjectData: ProjectData,
|
||||
oldScriptFiles: RuntimeGameOptionsScriptFile[],
|
||||
newScriptFiles: RuntimeGameOptionsScriptFile[],
|
||||
shouldGenerateScenesEventsCode: boolean
|
||||
projectDataOnlyExport: boolean
|
||||
): Promise<void[]> {
|
||||
const reloadPromises: Array<Promise<void>> = [];
|
||||
|
||||
// Reload events, only if they were exported.
|
||||
if (shouldGenerateScenesEventsCode) {
|
||||
if (!projectDataOnlyExport) {
|
||||
newProjectData.layouts.forEach((_layoutData, index) => {
|
||||
reloadPromises.push(this._reloadScript('code' + index + '.js'));
|
||||
});
|
||||
@@ -364,7 +326,7 @@ namespace gdjs {
|
||||
)[0];
|
||||
|
||||
// A file may be removed because of a partial preview.
|
||||
if (!newScriptFile && !shouldGenerateScenesEventsCode) {
|
||||
if (!newScriptFile && !projectDataOnlyExport) {
|
||||
this._logs.push({
|
||||
kind: 'warning',
|
||||
message: 'Script file ' + oldScriptFile.path + ' was removed.',
|
||||
@@ -732,16 +694,6 @@ namespace gdjs {
|
||||
runtimeScene.setEventsGeneratedCodeFunction(newLayoutData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the children object data into every custom object data.
|
||||
*
|
||||
* At the runtime, this is done at the object instantiation.
|
||||
* For hot-reloading, it's done before hands to optimize.
|
||||
*
|
||||
* @param projectData The project data
|
||||
* @param objectDatas The object datas to modify
|
||||
* @returns
|
||||
*/
|
||||
static resolveCustomObjectConfigurations(
|
||||
projectData: ProjectData,
|
||||
objectDatas: ObjectData[]
|
||||
@@ -765,43 +717,27 @@ namespace gdjs {
|
||||
if (!eventsBasedObjectData) {
|
||||
return objectData;
|
||||
}
|
||||
|
||||
const customObjectConfiguration = objectData as ObjectData &
|
||||
CustomObjectConfiguration;
|
||||
const eventsBasedObjectVariantData =
|
||||
gdjs.RuntimeGame._getEventsBasedObjectVariantData(
|
||||
eventsBasedObjectData,
|
||||
customObjectConfiguration.variant
|
||||
);
|
||||
|
||||
// Apply the legacy children configuration overriding if any.
|
||||
const mergedChildObjectDataList =
|
||||
gdjs.CustomRuntimeObjectInstanceContainer.hasChildrenConfigurationOverriding(
|
||||
customObjectConfiguration,
|
||||
eventsBasedObjectVariantData
|
||||
)
|
||||
? eventsBasedObjectData.objects.map((objectData) =>
|
||||
customObjectConfiguration.childrenContent
|
||||
? {
|
||||
...objectData,
|
||||
...customObjectConfiguration.childrenContent[
|
||||
objectData.name
|
||||
],
|
||||
}
|
||||
: objectData
|
||||
)
|
||||
customObjectConfiguration.childrenContent
|
||||
? eventsBasedObjectData.objects.map((objectData) => ({
|
||||
...objectData,
|
||||
...customObjectConfiguration.childrenContent[objectData.name],
|
||||
}))
|
||||
: eventsBasedObjectData.objects;
|
||||
|
||||
const mergedObjectConfiguration = {
|
||||
// ObjectData doesn't have an `objects` nor `instances` attribute.
|
||||
...eventsBasedObjectData,
|
||||
...objectData,
|
||||
// ObjectData doesn't have an `objects` attribute.
|
||||
// This is a small optimization to avoid to create an
|
||||
// InstanceContainerData for each instance to hot-reload their inner
|
||||
// scene (see `_hotReloadRuntimeInstanceContainer` call from
|
||||
// `_hotReloadRuntimeSceneInstances`).
|
||||
...eventsBasedObjectData,
|
||||
...eventsBasedObjectVariantData,
|
||||
objects: mergedChildObjectDataList,
|
||||
// It must be the last one to ensure the object name won't be overridden.
|
||||
...objectData,
|
||||
};
|
||||
return mergedObjectConfiguration;
|
||||
});
|
||||
@@ -815,12 +751,6 @@ namespace gdjs {
|
||||
changedRuntimeBehaviors: ChangedRuntimeBehavior[],
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
if (!oldLayoutData.objects || newLayoutData.objects) {
|
||||
// It can happen when `hotReloadRuntimeInstances` is executed.
|
||||
// `hotReloadRuntimeInstances` doesn't resolve the custom objects
|
||||
// because it can only modify the 1st level of instances.
|
||||
return;
|
||||
}
|
||||
const oldObjectDataList = HotReloader.resolveCustomObjectConfigurations(
|
||||
oldProjectData,
|
||||
oldLayoutData.objects
|
||||
@@ -991,62 +921,16 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
hotReloadRuntimeSceneObjects(
|
||||
updatedObjects: Array<ObjectData>,
|
||||
// runtimeInstanceContainer gives an access as a map.
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
const oldObjects: Array<ObjectData | null> = updatedObjects.map(
|
||||
(objectData) =>
|
||||
runtimeInstanceContainer._objects.get(objectData.name) || null
|
||||
);
|
||||
|
||||
const projectData: ProjectData = this._runtimeGame._data;
|
||||
const newObjectDataList = HotReloader.resolveCustomObjectConfigurations(
|
||||
projectData,
|
||||
updatedObjects
|
||||
);
|
||||
|
||||
this._hotReloadRuntimeSceneObjects(
|
||||
oldObjects,
|
||||
newObjectDataList,
|
||||
runtimeInstanceContainer
|
||||
);
|
||||
// Update the GameData
|
||||
for (let index = 0; index < updatedObjects.length; index++) {
|
||||
const oldObjectData = oldObjects[index];
|
||||
// When the object is new, the hot-reload call `registerObject`
|
||||
// so `_objects` is already updated.
|
||||
if (oldObjectData) {
|
||||
// In gdjs.CustomRuntimeObjectInstanceContainer.loadFrom, object can
|
||||
// be registered with a different instance from the ProjectData. This
|
||||
// is only done for children of a custom object with a children overriding.
|
||||
// In the case of the editor, the fake custom object used for editing
|
||||
// variants has no children overriding (see
|
||||
// gdjs.RuntimeGame._createSceneWithCustomObject).
|
||||
// Thus, the oldObjectData is always the one from the ProjectData.
|
||||
HotReloader.assignOrDelete(oldObjectData, updatedObjects[index]);
|
||||
} else {
|
||||
console.warn(
|
||||
`Can't update object data for "${updatedObjects[index].name}" because it doesn't exist.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hotReloadRuntimeSceneObjects(
|
||||
oldObjects: Array<ObjectData | null>,
|
||||
oldObjects: ObjectData[],
|
||||
newObjects: ObjectData[],
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
oldObjects.forEach((oldObjectData) => {
|
||||
if (!oldObjectData) {
|
||||
return;
|
||||
}
|
||||
const name = oldObjectData.name;
|
||||
const newObjectData = newObjects.find(
|
||||
const newObjectData = newObjects.filter(
|
||||
(objectData) => objectData.name === name
|
||||
);
|
||||
)[0];
|
||||
|
||||
// Note: if an object is renamed in the editor, it will be considered as removed,
|
||||
// and the new object name as a new object to register.
|
||||
@@ -1068,9 +952,9 @@ namespace gdjs {
|
||||
});
|
||||
newObjects.forEach((newObjectData) => {
|
||||
const name = newObjectData.name;
|
||||
const oldObjectData = oldObjects.find(
|
||||
(layerData) => layerData && layerData.name === name
|
||||
);
|
||||
const oldObjectData = oldObjects.filter(
|
||||
(layerData) => layerData.name === name
|
||||
)[0];
|
||||
if (
|
||||
(!oldObjectData || oldObjectData.type !== newObjectData.type) &&
|
||||
!runtimeInstanceContainer.isObjectRegistered(name)
|
||||
@@ -1308,31 +1192,6 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
|
||||
hotReloadRuntimeSceneLayers(
|
||||
newLayers: LayerData[],
|
||||
runtimeInstanceContainer: gdjs.RuntimeInstanceContainer
|
||||
): void {
|
||||
const layerNames = [];
|
||||
runtimeInstanceContainer.getAllLayerNames(layerNames);
|
||||
const oldLayers = layerNames.map((layerName) =>
|
||||
runtimeInstanceContainer.hasLayer(layerName)
|
||||
? runtimeInstanceContainer.getLayer(layerName)._initialLayerData
|
||||
: null
|
||||
);
|
||||
this._hotReloadRuntimeSceneLayers(
|
||||
oldLayers.filter(Boolean) as LayerData[],
|
||||
newLayers,
|
||||
runtimeInstanceContainer
|
||||
);
|
||||
// Update the GameData
|
||||
for (let index = 0; index < newLayers.length; index++) {
|
||||
const oldLayer = oldLayers[index];
|
||||
if (oldLayer) {
|
||||
HotReloader.assignOrDelete(oldLayer, newLayers[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_hotReloadRuntimeSceneLayers(
|
||||
oldLayers: LayerData[],
|
||||
newLayers: LayerData[],
|
||||
@@ -1414,8 +1273,6 @@ namespace gdjs {
|
||||
newLayer.effects,
|
||||
runtimeLayer
|
||||
);
|
||||
|
||||
runtimeLayer._initialLayerData = newLayer;
|
||||
}
|
||||
|
||||
_hotReloadRuntimeLayerEffects(
|
||||
@@ -1500,28 +1357,6 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
hotReloadRuntimeInstances(
|
||||
oldInstances: InstanceData[],
|
||||
newInstances: InstanceData[],
|
||||
runtimeInstanceContainer: RuntimeInstanceContainer
|
||||
): void {
|
||||
const projectData: ProjectData = gdjs.projectData;
|
||||
const objects: Array<ObjectData> = [];
|
||||
runtimeInstanceContainer._objects.values(objects);
|
||||
projectData.layouts;
|
||||
this._hotReloadRuntimeSceneInstances(
|
||||
projectData,
|
||||
projectData,
|
||||
[],
|
||||
objects,
|
||||
objects,
|
||||
oldInstances,
|
||||
newInstances,
|
||||
runtimeInstanceContainer
|
||||
);
|
||||
gdjs.copyArray(newInstances, oldInstances);
|
||||
}
|
||||
|
||||
_hotReloadRuntimeSceneInstances(
|
||||
oldProjectData: ProjectData,
|
||||
newProjectData: ProjectData,
|
||||
@@ -1588,9 +1423,6 @@ namespace gdjs {
|
||||
);
|
||||
} else {
|
||||
// Reload objects that were created at runtime.
|
||||
// This is a subset of what is done by `_hotReloadRuntimeInstance`.
|
||||
// Since the instance doesn't exist in the editor, it's properties
|
||||
// can't be updated, only the object changes are applied.
|
||||
|
||||
// Update variables
|
||||
this._hotReloadVariablesContainer(
|
||||
@@ -1599,7 +1431,6 @@ namespace gdjs {
|
||||
runtimeObject.getVariables()
|
||||
);
|
||||
|
||||
// Update the content of custom object
|
||||
if (runtimeObject instanceof gdjs.CustomRuntimeObject) {
|
||||
const childrenInstanceContainer =
|
||||
runtimeObject.getChildrenContainer();
|
||||
@@ -1612,18 +1443,15 @@ namespace gdjs {
|
||||
CustomObjectConfiguration &
|
||||
InstanceContainerData;
|
||||
|
||||
// Variant swapping is handled by `CustomRuntimeObject.updateFromObjectData`.
|
||||
if (newCustomObjectData.variant === oldCustomObjectData.variant) {
|
||||
// Reload the content of custom objects that were created at runtime.
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
}
|
||||
// Reload the content of custom objects that were created at runtime.
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1685,16 +1513,22 @@ namespace gdjs {
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (gdjs.Base3DHandler && gdjs.Base3DHandler.is3D(runtimeObject)) {
|
||||
if (oldInstance.z !== newInstance.z) {
|
||||
runtimeObject.setZ(newInstance.z || 0);
|
||||
if (oldInstance.z !== newInstance.z && newInstance.z !== undefined) {
|
||||
runtimeObject.setZ(newInstance.z);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (oldInstance.rotationX !== newInstance.rotationX) {
|
||||
runtimeObject.setRotationX(newInstance.rotationX || 0);
|
||||
if (
|
||||
oldInstance.rotationX !== newInstance.rotationX &&
|
||||
newInstance.rotationX !== undefined
|
||||
) {
|
||||
runtimeObject.setRotationX(newInstance.rotationX);
|
||||
somethingChanged = true;
|
||||
}
|
||||
if (oldInstance.rotationY !== newInstance.rotationY) {
|
||||
runtimeObject.setRotationY(newInstance.rotationY || 0);
|
||||
if (
|
||||
oldInstance.rotationY !== newInstance.rotationY &&
|
||||
newInstance.rotationY !== undefined
|
||||
) {
|
||||
runtimeObject.setRotationY(newInstance.rotationY);
|
||||
somethingChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -1749,6 +1583,8 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
if (runtimeObject instanceof gdjs.CustomRuntimeObject) {
|
||||
const childrenInstanceContainer = runtimeObject.getChildrenContainer();
|
||||
|
||||
// The `objects` attribute is already resolved by `resolveCustomObjectConfigurations()`.
|
||||
const oldCustomObjectData = oldObjectData as ObjectData &
|
||||
CustomObjectConfiguration &
|
||||
@@ -1757,19 +1593,14 @@ namespace gdjs {
|
||||
CustomObjectConfiguration &
|
||||
InstanceContainerData;
|
||||
|
||||
// Variant swapping is handled by `CustomRuntimeObject.updateFromObjectData`.
|
||||
if (newCustomObjectData.variant === oldCustomObjectData.variant) {
|
||||
const childrenInstanceContainer =
|
||||
runtimeObject.getChildrenContainer();
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
}
|
||||
this._hotReloadRuntimeInstanceContainer(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
oldCustomObjectData,
|
||||
newCustomObjectData,
|
||||
changedRuntimeBehaviors,
|
||||
childrenInstanceContainer
|
||||
);
|
||||
}
|
||||
|
||||
// Update variables
|
||||
@@ -1896,23 +1727,5 @@ namespace gdjs {
|
||||
// true if both NaN, false otherwise
|
||||
return a !== a && b !== b;
|
||||
}
|
||||
|
||||
static assignOrDelete(
|
||||
target: any,
|
||||
source: any,
|
||||
ignoreKeys: string[] = []
|
||||
): void {
|
||||
Object.assign(target, source);
|
||||
for (const key in target) {
|
||||
if (ignoreKeys.includes(key)) {
|
||||
continue;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
||||
if (source[key] === undefined) {
|
||||
delete target[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -56,19 +56,6 @@ namespace gdjs {
|
||||
};
|
||||
this._ws.onclose = function close() {
|
||||
logger.info('Debugger connection closed');
|
||||
|
||||
if (that._runtimegame.isInGameEdition()) {
|
||||
// Sometimes, for example if the editor is launched for a long time and the device goes to sleep,
|
||||
// the WebSocket connection between the editor and the game is closed. When we are in in-game edition,
|
||||
// we can't afford to lose the connection because it means the editor is unusable.
|
||||
// In this case, we hard reload the game to re-establish a new connection.
|
||||
setTimeout(() => {
|
||||
logger.info(
|
||||
'Debugger connection closed while in in-game edition - this is suspicious so hard reloading to re-establish a new connection.'
|
||||
);
|
||||
that.launchHardReload();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
this._ws.onerror = function errored(error) {
|
||||
logger.warn('Debugger client error:', error);
|
||||
|
@@ -11,13 +11,7 @@ namespace gdjs {
|
||||
constructor(runtimeGame: RuntimeGame) {
|
||||
super(runtimeGame);
|
||||
|
||||
// Opener is either the `opener` for popups, or the `parent` if the game
|
||||
// is running as an iframe (notably: in-game edition).
|
||||
this._opener = window.opener || null;
|
||||
if (!this._opener && window.parent !== window) {
|
||||
this._opener = window.parent;
|
||||
}
|
||||
|
||||
if (!this._opener) {
|
||||
logger.info("`window.opener` not existing, the debugger won't work.");
|
||||
return;
|
||||
|
@@ -153,7 +153,7 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key is pressed (i.e: just pressed or held down).
|
||||
* Return true if the specified key is pressed
|
||||
*
|
||||
*/
|
||||
export const isKeyPressed = function (
|
||||
@@ -170,22 +170,8 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key was just pressed (i.e: it started being pressed
|
||||
* during this frame).
|
||||
*/
|
||||
export const wasKeyJustPressed = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
key: string
|
||||
) {
|
||||
return instanceContainer
|
||||
.getGame()
|
||||
.getInputManager()
|
||||
.wasKeyJustPressed(gdjs.evtTools.input.keysNameToCode[key]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the specified key was just released (i.e: it stopped being pressed
|
||||
* during this frame).
|
||||
* Return true if the specified key was just released
|
||||
*
|
||||
*/
|
||||
export const wasKeyReleased = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer,
|
||||
@@ -201,7 +187,7 @@ namespace gdjs {
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the name of the last key pressed in the game.
|
||||
* Return the name of the last key pressed in the game
|
||||
*/
|
||||
export const lastPressedKey = function (
|
||||
instanceContainer: gdjs.RuntimeInstanceContainer
|
||||
|
@@ -207,7 +207,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const resource = this._loadedFontFamily.getFromName(resourceData.name);
|
||||
const resource = this._loadedFontFamily.get(resourceData);
|
||||
if (resource) {
|
||||
this._loadedFontFamily.delete(resourceData);
|
||||
}
|
||||
|
@@ -941,12 +941,12 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const musicRes = this._loadedMusics.getFromName(resourceData.name);
|
||||
const musicRes = this._loadedMusics.get(resourceData);
|
||||
if (musicRes) {
|
||||
this.unloadAudio(resourceData.name, true);
|
||||
}
|
||||
|
||||
const soundRes = this._loadedSounds.getFromName(resourceData.name);
|
||||
const soundRes = this._loadedSounds.get(resourceData);
|
||||
if (soundRes) {
|
||||
this.unloadAudio(resourceData.name, false);
|
||||
}
|
||||
|
@@ -23,38 +23,33 @@ namespace gdjs {
|
||||
* variants and should default to their left variant values
|
||||
* if location is not specified.
|
||||
*/
|
||||
private static _DEFAULT_LEFT_VARIANT_KEYS: integer[] = [16, 17, 18, 91];
|
||||
private _pressedKeys: Hashtable<boolean>;
|
||||
private _justPressedKeys: Hashtable<boolean>;
|
||||
private _releasedKeys: Hashtable<boolean>;
|
||||
private _lastPressedKey: float = 0;
|
||||
private _pressedMouseButtons: Array<boolean>;
|
||||
private _releasedMouseButtons: Array<boolean>;
|
||||
static _DEFAULT_LEFT_VARIANT_KEYS: integer[] = [16, 17, 18, 91];
|
||||
_pressedKeys: Hashtable<boolean>;
|
||||
_releasedKeys: Hashtable<boolean>;
|
||||
_lastPressedKey: float = 0;
|
||||
_pressedMouseButtons: Array<boolean>;
|
||||
_releasedMouseButtons: Array<boolean>;
|
||||
/**
|
||||
* The cursor X position (moved by mouse and touch events).
|
||||
*/
|
||||
private _cursorX: float = 0;
|
||||
_cursorX: float = 0;
|
||||
/**
|
||||
* The cursor Y position (moved by mouse and touch events).
|
||||
*/
|
||||
private _cursorY: float = 0;
|
||||
_cursorY: float = 0;
|
||||
/**
|
||||
* The mouse X position (only moved by mouse events).
|
||||
*/
|
||||
private _mouseX: float = 0;
|
||||
_mouseX: float = 0;
|
||||
/**
|
||||
* The mouse Y position (only moved by mouse events).
|
||||
*/
|
||||
private _mouseY: float = 0;
|
||||
private _isMouseInsideCanvas: boolean = true;
|
||||
private _wheelDeltaX: float = 0;
|
||||
private _wheelDeltaY: float = 0;
|
||||
private _wheelDeltaZ: float = 0;
|
||||
|
||||
_mouseY: float = 0;
|
||||
_isMouseInsideCanvas: boolean = true;
|
||||
_mouseWheelDelta: float = 0;
|
||||
// TODO Remove _touches when there is no longer SpritePanelButton 1.2.0
|
||||
// extension in the wild.
|
||||
// @ts-ignore
|
||||
private _touches = {
|
||||
_touches = {
|
||||
firstKey: (): string | number | null => {
|
||||
for (const key in this._mouseOrTouches.items) {
|
||||
// Exclude mouse key.
|
||||
@@ -65,27 +60,25 @@ namespace gdjs {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
private _mouseOrTouches: Hashtable<Touch>;
|
||||
_mouseOrTouches: Hashtable<Touch>;
|
||||
//Identifiers of the touches that started during/before the frame.
|
||||
private _startedTouches: Array<integer> = [];
|
||||
_startedTouches: Array<integer> = [];
|
||||
|
||||
//Identifiers of the touches that ended during/before the frame.
|
||||
private _endedTouches: Array<integer> = [];
|
||||
private _touchSimulateMouse: boolean = true;
|
||||
_endedTouches: Array<integer> = [];
|
||||
_touchSimulateMouse: boolean = true;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private _lastStartedTouchIndex = 0;
|
||||
_lastStartedTouchIndex = 0;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
private _lastEndedTouchIndex = 0;
|
||||
_lastEndedTouchIndex = 0;
|
||||
|
||||
constructor() {
|
||||
this._pressedKeys = new Hashtable();
|
||||
this._justPressedKeys = new Hashtable();
|
||||
this._releasedKeys = new Hashtable();
|
||||
this._pressedMouseButtons = new Array(5);
|
||||
this._releasedMouseButtons = new Array(5);
|
||||
@@ -101,7 +94,7 @@ namespace gdjs {
|
||||
* @param keyCode The raw key code
|
||||
* @param location The location
|
||||
*/
|
||||
static getLocationAwareKeyCode(
|
||||
_getLocationAwareKeyCode(
|
||||
keyCode: number,
|
||||
location: number | null | undefined
|
||||
): integer {
|
||||
@@ -126,12 +119,11 @@ namespace gdjs {
|
||||
* @param location The location of the event.
|
||||
*/
|
||||
onKeyPressed(keyCode: number, location?: number): void {
|
||||
const locationAwareKeyCode = InputManager.getLocationAwareKeyCode(
|
||||
const locationAwareKeyCode = this._getLocationAwareKeyCode(
|
||||
keyCode,
|
||||
location
|
||||
);
|
||||
this._pressedKeys.put(locationAwareKeyCode, true);
|
||||
this._justPressedKeys.put(locationAwareKeyCode, true);
|
||||
this._lastPressedKey = locationAwareKeyCode;
|
||||
}
|
||||
|
||||
@@ -143,39 +135,14 @@ namespace gdjs {
|
||||
* @param location The location of the event.
|
||||
*/
|
||||
onKeyReleased(keyCode: number, location?: number): void {
|
||||
const locationAwareKeyCode = InputManager.getLocationAwareKeyCode(
|
||||
const locationAwareKeyCode = this._getLocationAwareKeyCode(
|
||||
keyCode,
|
||||
location
|
||||
);
|
||||
this._pressedKeys.put(locationAwareKeyCode, false);
|
||||
this._justPressedKeys.put(locationAwareKeyCode, false);
|
||||
this._releasedKeys.put(locationAwareKeyCode, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all keys that are currently pressed.
|
||||
* Note: if you want to discard pressed keys without considering them as
|
||||
* released, check `clearAllPressedKeys` instead.
|
||||
*/
|
||||
releaseAllPressedKeys(): void {
|
||||
for (const locationAwareKeyCode in this._pressedKeys.items) {
|
||||
this._pressedKeys.put(locationAwareKeyCode, false);
|
||||
this._justPressedKeys.put(locationAwareKeyCode, false);
|
||||
this._releasedKeys.put(locationAwareKeyCode, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all stored pressed keys without making the keys go through
|
||||
* the release state.
|
||||
* Note: prefer to use `releaseAllPressedKeys` instead, as it corresponds
|
||||
* to a normal key release.
|
||||
*/
|
||||
clearAllPressedKeys(): void {
|
||||
this._pressedKeys.clear();
|
||||
this._justPressedKeys.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location-aware code of the last key that was pressed.
|
||||
* @return The location-aware code of the last key pressed.
|
||||
@@ -185,21 +152,14 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the key corresponding to the location-aware keyCode is pressed
|
||||
* (either it was just pressed or is still held down).
|
||||
* Return true if the key corresponding to the location-aware keyCode is pressed.
|
||||
* @param locationAwareKeyCode The location-aware key code to be tested.
|
||||
*/
|
||||
isKeyPressed(locationAwareKeyCode: number): boolean {
|
||||
return !!this._pressedKeys.get(locationAwareKeyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the key corresponding to the location-aware keyCode
|
||||
* was just pressed during the last frame.
|
||||
* @param locationAwareKeyCode The location-aware key code to be tested.
|
||||
*/
|
||||
wasKeyJustPressed(locationAwareKeyCode: number): boolean {
|
||||
return !!this._justPressedKeys.get(locationAwareKeyCode);
|
||||
return (
|
||||
this._pressedKeys.containsKey(locationAwareKeyCode) &&
|
||||
this._pressedKeys.get(locationAwareKeyCode)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +167,10 @@ namespace gdjs {
|
||||
* @param locationAwareKeyCode The location-aware key code to be tested.
|
||||
*/
|
||||
wasKeyReleased(locationAwareKeyCode: number) {
|
||||
return !!this._releasedKeys.get(locationAwareKeyCode);
|
||||
return (
|
||||
this._releasedKeys.containsKey(locationAwareKeyCode) &&
|
||||
this._releasedKeys.get(locationAwareKeyCode)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,19 +303,6 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any mouse button is pressed.
|
||||
* @return true if any mouse button is pressed.
|
||||
*/
|
||||
anyMouseButtonPressed(): boolean {
|
||||
for (const buttonCode in this._pressedMouseButtons) {
|
||||
if (this._pressedMouseButtons[buttonCode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_setMouseButtonPressed(buttonCode: number): void {
|
||||
this._pressedMouseButtons[buttonCode] = true;
|
||||
this._releasedMouseButtons[buttonCode] = false;
|
||||
@@ -398,37 +348,17 @@ namespace gdjs {
|
||||
|
||||
/**
|
||||
* Should be called whenever the mouse wheel is used
|
||||
* @param wheelDeltaY The mouse wheel delta
|
||||
* @param wheelDelta The mouse wheel delta
|
||||
*/
|
||||
onMouseWheel(
|
||||
wheelDeltaY: number,
|
||||
wheelDeltaX: number,
|
||||
wheelDeltaZ: number
|
||||
): void {
|
||||
this._wheelDeltaY = wheelDeltaY;
|
||||
if (wheelDeltaX !== undefined) this._wheelDeltaX = wheelDeltaX;
|
||||
if (wheelDeltaZ !== undefined) this._wheelDeltaZ = wheelDeltaZ;
|
||||
onMouseWheel(wheelDelta: number): void {
|
||||
this._mouseWheelDelta = wheelDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta on Y axis.
|
||||
* Return the mouse wheel delta
|
||||
*/
|
||||
getMouseWheelDelta(): float {
|
||||
return this._wheelDeltaY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta on X axis.
|
||||
*/
|
||||
getMouseWheelDeltaX(): float {
|
||||
return this._wheelDeltaX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mouse wheel delta on Z axis.
|
||||
*/
|
||||
getMouseWheelDeltaZ(): float {
|
||||
return this._wheelDeltaZ;
|
||||
return this._mouseWheelDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -614,11 +544,8 @@ namespace gdjs {
|
||||
this._startedTouches.length = 0;
|
||||
this._endedTouches.length = 0;
|
||||
this._releasedKeys.clear();
|
||||
this._justPressedKeys.clear();
|
||||
this._releasedMouseButtons.length = 0;
|
||||
this._wheelDeltaX = 0;
|
||||
this._wheelDeltaY = 0;
|
||||
this._wheelDeltaZ = 0;
|
||||
this._mouseWheelDelta = 0;
|
||||
this._lastStartedTouchIndex = 0;
|
||||
this._lastEndedTouchIndex = 0;
|
||||
}
|
||||
@@ -637,6 +564,14 @@ namespace gdjs {
|
||||
return this.getMouseWheelDelta() < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all stored pressed keys without making the keys go through
|
||||
* the release state.
|
||||
*/
|
||||
clearAllPressedKeys(): void {
|
||||
this._pressedKeys.clear();
|
||||
}
|
||||
|
||||
static _allTouchIds: Array<integer> = [];
|
||||
}
|
||||
}
|
||||
|
@@ -210,12 +210,12 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedJson = this._loadedJsons.getFromName(resourceData.name);
|
||||
const loadedJson = this._loadedJsons.get(resourceData);
|
||||
if (loadedJson) {
|
||||
this._loadedJsons.delete(resourceData);
|
||||
}
|
||||
|
||||
const callback = this._callbacks.getFromName(resourceData.name);
|
||||
const callback = this._callbacks.get(resourceData);
|
||||
if (callback) {
|
||||
this._callbacks.delete(resourceData);
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -7,178 +7,6 @@
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('LayerPixiRenderer');
|
||||
|
||||
const FRUSTUM_EDGES: Array<[number, number]> = [
|
||||
// near plane edges
|
||||
[0, 1],
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
[3, 0],
|
||||
// far plane edges
|
||||
[4, 5],
|
||||
[5, 6],
|
||||
[6, 7],
|
||||
[7, 4],
|
||||
// near↔far connections
|
||||
[0, 4],
|
||||
[1, 5],
|
||||
[2, 6],
|
||||
[3, 7],
|
||||
];
|
||||
|
||||
/** Normalized Device Coordinates corners for near (-1) and far (+1) planes (Three.js NDC: z=-1 near, z=+1 far). */
|
||||
const NDC_CORNERS: Array<Array<float>> = [
|
||||
// near
|
||||
[-1, -1, -1],
|
||||
[+1, -1, -1],
|
||||
[+1, +1, -1],
|
||||
[-1, +1, -1],
|
||||
// far
|
||||
[-1, -1, +1],
|
||||
[+1, -1, +1],
|
||||
[+1, +1, +1],
|
||||
[-1, +1, +1],
|
||||
];
|
||||
|
||||
/** Sort convex polygon vertices around centroid to get consistent winding. */
|
||||
const sortConvexPolygon = (points: THREE.Vector3[]): THREE.Vector3[] => {
|
||||
if (points.length <= 2) return points;
|
||||
const cx = points.reduce((s, p) => s + p.x, 0) / points.length;
|
||||
const cy = points.reduce((s, p) => s + p.y, 0) / points.length;
|
||||
return points
|
||||
.map((p) => ({ p, a: Math.atan2(p.y - cy, p.x - cx) }))
|
||||
.sort((u, v) => u.a - v.a)
|
||||
.map((u) => u.p);
|
||||
};
|
||||
|
||||
/**
|
||||
* Intersect a frustum edge segment [a,b] with plane Z=0.
|
||||
* Returns point or null if no intersection on the segment.
|
||||
*/
|
||||
const intersectSegmentWithZ0 = (
|
||||
a: THREE.Vector3,
|
||||
b: THREE.Vector3,
|
||||
eps = 1e-9
|
||||
): THREE.Vector3 | null => {
|
||||
const az = a.z,
|
||||
bz = b.z;
|
||||
const dz = bz - az;
|
||||
|
||||
// If both z on same side and not on plane, no crossing.
|
||||
if (Math.abs(dz) < eps) {
|
||||
// Segment is (almost) parallel to plane.
|
||||
if (Math.abs(az) < eps && Math.abs(bz) < eps) {
|
||||
// Entire segment lies on plane: return endpoints (handled by caller via dedup).
|
||||
// Here we return null and let caller add endpoints if needed.
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Solve a.z + t*(b.z - a.z) = 0 ⇒ t = -a.z / (b.z - a.z)
|
||||
const t = -az / dz;
|
||||
if (t < -eps || t > 1 + eps) {
|
||||
// Intersection beyond the segment bounds.
|
||||
return null;
|
||||
}
|
||||
|
||||
const p = new THREE.Vector3(
|
||||
a.x + t * (b.x - a.x),
|
||||
a.y + t * (b.y - a.y),
|
||||
0
|
||||
);
|
||||
return p;
|
||||
};
|
||||
|
||||
/** Remove near-duplicate points. */
|
||||
const dedupPoints = (
|
||||
points: THREE.Vector3[],
|
||||
eps = 1e-6
|
||||
): THREE.Vector3[] => {
|
||||
const out: THREE.Vector3[] = [];
|
||||
for (const p of points) {
|
||||
const exists = out.some(
|
||||
(q) => Math.abs(p.x - q.x) < eps && Math.abs(p.y - q.y) < eps
|
||||
);
|
||||
if (!exists) out.push(p);
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute the convex polygon of the camera frustum clipped by plane Z=0.
|
||||
* Returns ordered vertices (world coords, z=0). Empty array if no intersection.
|
||||
*/
|
||||
const clipFrustumAgainstZ0 = (camera: THREE.Camera): THREE.Vector3[] => {
|
||||
camera.updateMatrixWorld(true);
|
||||
|
||||
// Get the 8 corners of the camera frustum in world coordinates.
|
||||
const corners = NDC_CORNERS.map((ndc) =>
|
||||
new THREE.Vector3(ndc[0], ndc[1], ndc[2]).unproject(camera)
|
||||
);
|
||||
if (corners.length !== 8) return [];
|
||||
|
||||
const hits: THREE.Vector3[] = [];
|
||||
|
||||
// 1) Add vertices that already lie on the plane (z≈0).
|
||||
for (const v of corners) {
|
||||
if (Math.abs(v.z) < 1e-9) {
|
||||
hits.push(new THREE.Vector3(v.x, v.y, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Intersect each frustum edge with plane Z=0.
|
||||
for (const [i, j] of FRUSTUM_EDGES) {
|
||||
const a = corners[i],
|
||||
b = corners[j];
|
||||
const p = intersectSegmentWithZ0(a, b);
|
||||
if (p) hits.push(p);
|
||||
}
|
||||
|
||||
// Deduplicate and order.
|
||||
const unique = dedupPoints(hits);
|
||||
if (unique.length < 3) return [];
|
||||
return sortConvexPolygon(unique);
|
||||
};
|
||||
|
||||
/**
|
||||
* Intersect the ray going through a normalized device coordinate (nx, ny)
|
||||
* with the plane Z=0. Returns the hit point in THREE world coords (z=0)
|
||||
* or null if the ray doesn't intersect the plane in front of the camera.
|
||||
*/
|
||||
const projectNDCToZ0 = (
|
||||
camera: THREE.Camera,
|
||||
nx: number,
|
||||
ny: number
|
||||
): THREE.Vector3 | null => {
|
||||
if (!camera) return null;
|
||||
|
||||
camera.updateMatrixWorld(true);
|
||||
|
||||
const origin = new THREE.Vector3();
|
||||
const dir = new THREE.Vector3();
|
||||
const p = new THREE.Vector3(nx, ny, 0.5);
|
||||
|
||||
if (camera instanceof THREE.OrthographicCamera) {
|
||||
// For ortho, unproject a point on the camera plane, and use forward dir.
|
||||
p.z = 0; // on the camera plane
|
||||
p.unproject(camera); // gives a point on the camera plane in world coords
|
||||
origin.copy(p);
|
||||
camera.getWorldDirection(dir);
|
||||
} else {
|
||||
// Perspective: unproject a point on the frustum plane, build a ray.
|
||||
p.unproject(camera);
|
||||
origin.copy(camera.position);
|
||||
dir.copy(p).sub(origin).normalize();
|
||||
}
|
||||
|
||||
const dz = dir.z;
|
||||
if (Math.abs(dz) < 1e-8) return null; // parallel
|
||||
const t = -origin.z / dz;
|
||||
if (t <= 0) return null; // behind the camera => not visible
|
||||
|
||||
return origin.addScaledVector(dir, t).setZ(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* The renderer for a gdjs.Layer using Pixi.js.
|
||||
*/
|
||||
@@ -219,7 +47,6 @@ namespace gdjs {
|
||||
private _threePlaneGeometry: THREE.PlaneGeometry | null = null;
|
||||
private _threePlaneMaterial: THREE.ShaderMaterial | null = null;
|
||||
private _threePlaneMesh: THREE.Mesh | null = null;
|
||||
private _threePlaneMeshDebugOutline: THREE.LineSegments | null = null;
|
||||
|
||||
/**
|
||||
* Pixi doesn't sort children with zIndex == 0.
|
||||
@@ -272,9 +99,6 @@ namespace gdjs {
|
||||
// The layer is now fully initialized. Adapt the 3D camera position
|
||||
// (which we could not do before in `_setup3DRendering`).
|
||||
this._update3DCameraAspectAndPosition();
|
||||
|
||||
// Uncomment to show the outline of the 2D rendering plane.
|
||||
// this.show2DRenderingPlaneDebugOutline(true);
|
||||
}
|
||||
|
||||
onGameResolutionResized() {
|
||||
@@ -310,10 +134,6 @@ namespace gdjs {
|
||||
return this._threeScene;
|
||||
}
|
||||
|
||||
getThreeGroup(): THREE.Group | null {
|
||||
return this._threeGroup;
|
||||
}
|
||||
|
||||
getThreeCamera():
|
||||
| THREE.PerspectiveCamera
|
||||
| THREE.OrthographicCamera
|
||||
@@ -465,10 +285,6 @@ namespace gdjs {
|
||||
'Tried to setup PixiJS plane for 2D rendering in 3D for a layer that is already set up.'
|
||||
);
|
||||
|
||||
this.set2DPlaneMaxDrawingDistance(
|
||||
this._layer.getInitialCamera2DPlaneMaxDrawingDistance()
|
||||
);
|
||||
|
||||
// If we have both 2D and 3D objects to be rendered, create a render texture that PixiJS will use
|
||||
// to render, and that will be projected on a plane by Three.js
|
||||
this._createPixiRenderTexture(pixiRenderer);
|
||||
@@ -572,298 +388,30 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the drawing of an outline of the 2D rendering plane.
|
||||
* Useful to visually see where the 2D rendering is done in the 3D world.
|
||||
* Update the position of the PIXI container. To be called after each change
|
||||
* made to position, zoom or rotation of the camera.
|
||||
*/
|
||||
show2DRenderingPlaneDebugOutline(enable: boolean) {
|
||||
if (!this._threePlaneMesh) return;
|
||||
if (enable && !this._threePlaneMeshDebugOutline) {
|
||||
// Add rectangle outline around the plane.
|
||||
const edges = new THREE.EdgesGeometry(this._threePlaneGeometry);
|
||||
const lineMaterial = new THREE.LineBasicMaterial({
|
||||
color: 0xff0000,
|
||||
});
|
||||
this._threePlaneMeshDebugOutline = new THREE.LineSegments(
|
||||
edges,
|
||||
lineMaterial
|
||||
);
|
||||
|
||||
// Attach the outline to the plane so it follows position/scale/rotation.
|
||||
this._threePlaneMesh.add(this._threePlaneMeshDebugOutline);
|
||||
}
|
||||
if (!enable && this._threePlaneMeshDebugOutline) {
|
||||
this._threePlaneMesh.remove(this._threePlaneMeshDebugOutline);
|
||||
this._threePlaneMeshDebugOutline = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Maximum size of the 2D plane, in pixels. */
|
||||
private _2DPlaneMaxDrawingDistance: number = 5000;
|
||||
/** Tilt degrees below which the 2D plane is not clamped. */
|
||||
private _2DPlaneClampFreeTiltDeg: number = 0.1;
|
||||
/** Tilt degrees below which the 2D plane is fully clamped. */
|
||||
private _2DPlaneClampHardTiltDeg: number = 6;
|
||||
private _2DPlaneClampRampPower: number = 1.5; // 1 = linear, >1 = smoother
|
||||
|
||||
/**
|
||||
* Set the maximum "drawing distance", in pixels, of the 2D when in the 3D world.
|
||||
* This corresponds to the "height" of the 2D plane.
|
||||
* Used when the 3D camera is tilted on the X or Y axis (instead of looking down the Z axis,
|
||||
* as it's done by default for 2D games).
|
||||
* This is useful to avoid the 2D plane being too big when the camera is tilted.
|
||||
*/
|
||||
set2DPlaneMaxDrawingDistance(h: number) {
|
||||
this._2DPlaneMaxDrawingDistance = Math.max(0, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tilt degrees below which the 2D plane is not clamped.
|
||||
*/
|
||||
set2DPlaneClampFreeTiltDegrees(d: number) {
|
||||
this._2DPlaneClampFreeTiltDeg = Math.max(0, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tilt degrees below which the 2D plane is clamped (see `set2DPlaneMaxDrawingDistance`).
|
||||
*/
|
||||
set2DPlaneClampHardTiltDegrees(d: number) {
|
||||
this._2DPlaneClampHardTiltDeg = Math.max(0, d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ramp power of the 2D plane clamping (see `set2DPlaneMaxDrawingDistance`). Used
|
||||
* for smoother transition between clamped and unclamped.
|
||||
*/
|
||||
set2DPlaneClampRampPower(p: number) {
|
||||
this._2DPlaneClampRampPower = Math.max(0.1, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the 2D plane, in the world coordinates.
|
||||
*/
|
||||
private _get2DPlaneSize(): [number, number] {
|
||||
if (!this._threeCamera) return [0, 0];
|
||||
|
||||
// Compute the intersection of the frustrum of the camera on the Z=0 plane.
|
||||
// In theory, that's where the entire 2D rendering should be displayed.
|
||||
const poly = clipFrustumAgainstZ0(this._threeCamera);
|
||||
|
||||
if (poly.length === 0) {
|
||||
// No intersection at all: Z=0 not in view.
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
// Compute the axis-aligned bounds on Z=0 (world units) of the polygon,
|
||||
// so we can compute the size of the plane doing the 2D rendering.
|
||||
let minX = Infinity,
|
||||
maxX = -Infinity,
|
||||
minY = Infinity,
|
||||
maxY = -Infinity;
|
||||
for (const p of poly) {
|
||||
if (p.x < minX) minX = p.x;
|
||||
if (p.x > maxX) maxX = p.x;
|
||||
if (p.y < minY) minY = p.y;
|
||||
if (p.y > maxY) maxY = p.y;
|
||||
}
|
||||
let boxW = Math.max(1e-8, maxX - minX);
|
||||
let boxH = Math.max(1e-8, maxY - minY);
|
||||
|
||||
// Keep 2D layer aspect ratio (so texture isn't stretched).
|
||||
const targetAspect = this._layer.getWidth() / this._layer.getHeight();
|
||||
const boxAspect = boxW / boxH;
|
||||
if (boxAspect < targetAspect) {
|
||||
boxW = targetAspect * boxH;
|
||||
} else {
|
||||
boxH = boxW / targetAspect;
|
||||
}
|
||||
|
||||
// Decide if we should cap based on camera tilt (X/Y) ---
|
||||
const forward = new THREE.Vector3();
|
||||
this._threeCamera.getWorldDirection(forward);
|
||||
// |forward.z| ≈ 1 -> no tilt (look mostly perpendicular to Z=0).
|
||||
// |forward.z| ≈ 0 -> grazing the horizon (strong tilt).
|
||||
|
||||
const freeCos = Math.cos(
|
||||
THREE.MathUtils.degToRad(this._2DPlaneClampFreeTiltDeg)
|
||||
);
|
||||
const hardCos = Math.cos(
|
||||
THREE.MathUtils.degToRad(this._2DPlaneClampHardTiltDeg)
|
||||
);
|
||||
const tiltCos = Math.abs(forward.z);
|
||||
|
||||
// Map tiltCos ∈ [hardCos, freeCos] to w ∈ [1, 0]
|
||||
let w = 0;
|
||||
if (tiltCos <= hardCos)
|
||||
w = 1; // fully clamped
|
||||
else if (tiltCos >= freeCos)
|
||||
w = 0; // no clamp
|
||||
else w = (freeCos - tiltCos) / (freeCos - hardCos);
|
||||
|
||||
// Ease it
|
||||
w = Math.pow(w, this._2DPlaneClampRampPower);
|
||||
|
||||
// Interpolate Infinity→base via 1/w (bounded):
|
||||
const BIG = 1e12; // “practically infinite”
|
||||
const denom = Math.max(w, 1e-6);
|
||||
const effectiveMaxH = Math.min(
|
||||
BIG,
|
||||
this._2DPlaneMaxDrawingDistance / denom
|
||||
);
|
||||
|
||||
// Apply the max height.
|
||||
if (effectiveMaxH < BIG) {
|
||||
const clampedH = Math.max(1e-8, Math.min(boxH, effectiveMaxH));
|
||||
if (clampedH !== boxH) {
|
||||
boxH = clampedH;
|
||||
boxW = targetAspect * boxH; // keep aspect
|
||||
}
|
||||
}
|
||||
|
||||
return [boxW, boxH];
|
||||
}
|
||||
|
||||
private _get2DPlanePosition(boxH: number): [number, number] {
|
||||
if (!this._threeCamera) return [0, 0];
|
||||
|
||||
// Choose the plane position (anchor to bottom of screen, heading-invariant) ---
|
||||
const bottomLeft = projectNDCToZ0(this._threeCamera, -1, -1);
|
||||
const bottomRight = projectNDCToZ0(this._threeCamera, +1, -1);
|
||||
|
||||
let cx: number, cy: number;
|
||||
|
||||
if (bottomLeft && bottomRight) {
|
||||
// Midpoint of the bottom-of-screen segment on Z=0:
|
||||
const mx = 0.5 * (bottomLeft.x + bottomRight.x);
|
||||
const my = 0.5 * (bottomLeft.y + bottomRight.y);
|
||||
|
||||
// Tangent along the bottom line (unit):
|
||||
let dx = bottomRight.x - bottomLeft.x;
|
||||
let dy = bottomRight.y - bottomLeft.y;
|
||||
const len = Math.hypot(dx, dy) || 1;
|
||||
dx /= len;
|
||||
dy /= len;
|
||||
|
||||
// Inward normal n = +90° rotation of d in XY plane:
|
||||
// d = (dx, dy) -> n = (-dy, dx)
|
||||
let nx = -dy;
|
||||
let ny = dx;
|
||||
|
||||
// Ensure n points "into the screen":
|
||||
const midIn = projectNDCToZ0(this._threeCamera, 0, -0.5);
|
||||
if (midIn) {
|
||||
const vx = midIn.x - mx;
|
||||
const vy = midIn.y - my;
|
||||
if (vx * nx + vy * ny < 0) {
|
||||
nx = -nx;
|
||||
ny = -ny;
|
||||
}
|
||||
}
|
||||
|
||||
// Place the plane so its bottom edge lies on the bottom-of-screen line:
|
||||
cx = mx + nx * (boxH * 0.5);
|
||||
cy = my + ny * (boxH * 0.5);
|
||||
} else {
|
||||
// Fallback to the camera center projected on Z=0 if bottom line not visible:
|
||||
const centerRay = projectNDCToZ0(this._threeCamera, 0, 0);
|
||||
if (centerRay) {
|
||||
cx = centerRay.x;
|
||||
cy = centerRay.y;
|
||||
} else {
|
||||
// Fallback to the camera position if the center ray is not visible:
|
||||
cx = this._threeCamera.position.x;
|
||||
cy = this._threeCamera.position.y;
|
||||
}
|
||||
}
|
||||
return [cx, cy];
|
||||
}
|
||||
|
||||
updatePosition(): void {
|
||||
// Update the 3D camera position and rotation.
|
||||
if (this._threeCamera) {
|
||||
const angle = -gdjs.toRad(this._layer.getCameraRotation());
|
||||
this._threeCamera.position.x = this._layer.getCameraX();
|
||||
this._threeCamera.position.y = -this._layer.getCameraY(); // scene is mirrored on Y
|
||||
this._threeCamera.rotation.z = angle;
|
||||
|
||||
if (this._threeCamera instanceof THREE.OrthographicCamera) {
|
||||
this._threeCamera.zoom = this._layer.getCameraZoom();
|
||||
this._threeCamera.updateProjectionMatrix();
|
||||
this._threeCamera.position.z = this._layer.getCameraZ(null);
|
||||
} else {
|
||||
this._threeCamera.position.z = this._layer.getCameraZ(
|
||||
this._threeCamera.fov
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let effectivePixiZoom = 1;
|
||||
const angle = -gdjs.toRad(this._layer.getCameraRotation());
|
||||
const angleCosValue = Math.cos(angle);
|
||||
const angleSinValue = Math.sin(angle);
|
||||
const zoomFactor = this._layer.getCameraZoom();
|
||||
this._pixiContainer.rotation = angle;
|
||||
this._pixiContainer.scale.x = zoomFactor;
|
||||
this._pixiContainer.scale.y = zoomFactor;
|
||||
const cosValue = Math.cos(angle);
|
||||
const sinValue = Math.sin(angle);
|
||||
const centerX =
|
||||
this._layer.getCameraX() * zoomFactor * cosValue -
|
||||
this._layer.getCameraY() * zoomFactor * sinValue;
|
||||
const centerY =
|
||||
this._layer.getCameraX() * zoomFactor * sinValue +
|
||||
this._layer.getCameraY() * zoomFactor * cosValue;
|
||||
this._pixiContainer.position.x = this._layer.getWidth() / 2 - centerX;
|
||||
this._pixiContainer.position.y = this._layer.getHeight() / 2 - centerY;
|
||||
|
||||
// Update the 2D plane in the 3D world position, size and rotation,
|
||||
// and update the 2D Pixi container position, size and rotation.
|
||||
if (this._threeCamera && this._threePlaneMesh) {
|
||||
const [boxW, boxH] = this._get2DPlaneSize();
|
||||
|
||||
if (boxW === 0 || boxH === 0) {
|
||||
// No size means the 2D plane is not visible.
|
||||
this._threePlaneMesh.visible = false;
|
||||
} else {
|
||||
this._threePlaneMesh.visible = true;
|
||||
|
||||
const [cx, cy] = this._get2DPlanePosition(boxH);
|
||||
|
||||
// Update the 2D plane size, position and rotation (so 2D remains upright).
|
||||
// Plane size (geometry is 1×1).
|
||||
this._threePlaneMesh.scale.set(boxW, boxH, 1);
|
||||
this._threePlaneMesh.position.set(cx, -cy, 0);
|
||||
this._threePlaneMesh.rotation.set(0, 0, -angle);
|
||||
|
||||
// Update the 2D Pixi container size and rotation to match the "zoom" (which comes from the 2D plane size)
|
||||
// rotation and position.
|
||||
effectivePixiZoom = this._layer.getWidth() / boxW; // == height/boxH
|
||||
this._pixiContainer.scale.set(effectivePixiZoom, effectivePixiZoom);
|
||||
this._pixiContainer.rotation = angle;
|
||||
|
||||
const followX = cx;
|
||||
const followY = -cy;
|
||||
const centerX2d =
|
||||
followX * effectivePixiZoom * angleCosValue -
|
||||
followY * effectivePixiZoom * angleSinValue;
|
||||
const centerY2d =
|
||||
followX * effectivePixiZoom * angleSinValue +
|
||||
followY * effectivePixiZoom * angleCosValue;
|
||||
this._pixiContainer.position.x =
|
||||
this._layer.getWidth() / 2 - centerX2d;
|
||||
this._pixiContainer.position.y =
|
||||
this._layer.getHeight() / 2 - centerY2d;
|
||||
}
|
||||
}
|
||||
|
||||
// 2D only (no 3D rendering and so no 2D plane in the 3D world):
|
||||
// Update the 2D Pixi container position, size and rotation.
|
||||
if (!this._threeCamera || !this._threePlaneMesh) {
|
||||
effectivePixiZoom = this._layer.getCameraZoom();
|
||||
this._pixiContainer.rotation = angle;
|
||||
this._pixiContainer.scale.x = effectivePixiZoom;
|
||||
this._pixiContainer.scale.y = effectivePixiZoom;
|
||||
const centerX =
|
||||
this._layer.getCameraX() * effectivePixiZoom * angleCosValue -
|
||||
this._layer.getCameraY() * effectivePixiZoom * angleSinValue;
|
||||
const centerY =
|
||||
this._layer.getCameraX() * effectivePixiZoom * angleSinValue +
|
||||
this._layer.getCameraY() * effectivePixiZoom * angleCosValue;
|
||||
this._pixiContainer.position.x = this._layer.getWidth() / 2 - centerX;
|
||||
this._pixiContainer.position.y = this._layer.getHeight() / 2 - centerY;
|
||||
}
|
||||
|
||||
// Pixel rounding for the Pixi rendering (be it for 2D only
|
||||
// or for the 2D rendering shown in the 2D plane in the 3D world).
|
||||
if (
|
||||
this._layer.getRuntimeScene().getGame().getPixelsRounding() &&
|
||||
(angleCosValue === 0 || angleSinValue === 0) &&
|
||||
Number.isInteger(effectivePixiZoom)
|
||||
(cosValue === 0 || sinValue === 0) &&
|
||||
Number.isInteger(zoomFactor)
|
||||
) {
|
||||
// Camera rounding is important for pixel perfect games.
|
||||
// Otherwise, the camera position fractional part is added to
|
||||
@@ -919,12 +467,39 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._threeCamera) {
|
||||
// TODO (3D) - improvement: handle camera rounding like down for PixiJS?
|
||||
this._threeCamera.position.x = this._layer.getCameraX();
|
||||
this._threeCamera.position.y = -this._layer.getCameraY(); // Inverted because the scene is mirrored on Y axis.
|
||||
this._threeCamera.rotation.z = angle;
|
||||
|
||||
if (this._threeCamera instanceof THREE.OrthographicCamera) {
|
||||
this._threeCamera.zoom = this._layer.getCameraZoom();
|
||||
this._threeCamera.updateProjectionMatrix();
|
||||
this._threeCamera.position.z = this._layer.getCameraZ(null);
|
||||
} else {
|
||||
this._threeCamera.position.z = this._layer.getCameraZ(
|
||||
this._threeCamera.fov
|
||||
);
|
||||
}
|
||||
|
||||
if (this._threePlaneMesh) {
|
||||
// Adapt the plane size so that it covers the whole screen.
|
||||
this._threePlaneMesh.scale.x = this._layer.getWidth() / zoomFactor;
|
||||
this._threePlaneMesh.scale.y = this._layer.getHeight() / zoomFactor;
|
||||
|
||||
// Adapt the plane position so that it's always displayed on the whole screen.
|
||||
this._threePlaneMesh.position.x = this._threeCamera.position.x;
|
||||
this._threePlaneMesh.position.y = -this._threeCamera.position.y; // Inverted because the scene is mirrored on Y axis.
|
||||
this._threePlaneMesh.rotation.z = -angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateResolution() {
|
||||
if (this._threeEffectComposer) {
|
||||
const game = this._layer.getRuntimeScene().getGame();
|
||||
this._threeEffectComposer.setPixelRatio(window.devicePixelRatio);
|
||||
this._threeEffectComposer.setSize(
|
||||
game.getGameResolutionWidth(),
|
||||
game.getGameResolutionHeight()
|
||||
|
@@ -309,7 +309,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
unloadResource(resourceData: ResourceData): void {
|
||||
const loadedFont = this._loadedFontsData.getFromName(resourceData.name);
|
||||
const loadedFont = this._loadedFontsData.get(resourceData);
|
||||
if (loadedFont) {
|
||||
this._loadedFontsData.delete(resourceData);
|
||||
}
|
||||
|
@@ -56,11 +56,6 @@ namespace gdjs {
|
||||
*/
|
||||
private _loadedThreeTextures: Hashtable<THREE.Texture>;
|
||||
private _loadedThreeMaterials = new ThreeMaterialCache();
|
||||
private _loadedThreeCubeTextures = new Map<string, THREE.CubeTexture>();
|
||||
private _loadedThreeCubeTextureKeysByResourceName = new ArrayMap<
|
||||
string,
|
||||
string
|
||||
>();
|
||||
|
||||
private _diskTextures = new Map<float, PIXI.Texture>();
|
||||
private _rectangleTextures = new Map<string, PIXI.Texture>();
|
||||
@@ -103,10 +98,6 @@ namespace gdjs {
|
||||
if (!existingTexture) {
|
||||
return this._invalidTexture;
|
||||
}
|
||||
if (existingTexture.destroyed) {
|
||||
logger.error('Texture for ' + resourceName + ' is not valid anymore.');
|
||||
return this._invalidTexture;
|
||||
}
|
||||
if (!existingTexture.valid) {
|
||||
logger.error(
|
||||
'Texture for ' +
|
||||
@@ -190,25 +181,7 @@ namespace gdjs {
|
||||
if (loadedThreeTexture) {
|
||||
return loadedThreeTexture;
|
||||
}
|
||||
const image = this._getImageSource(resourceName);
|
||||
|
||||
const threeTexture = new THREE.Texture(image);
|
||||
threeTexture.magFilter = THREE.LinearFilter;
|
||||
threeTexture.minFilter = THREE.LinearFilter;
|
||||
threeTexture.wrapS = THREE.RepeatWrapping;
|
||||
threeTexture.wrapT = THREE.RepeatWrapping;
|
||||
threeTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
threeTexture.needsUpdate = true;
|
||||
|
||||
const resource = this._getImageResource(resourceName);
|
||||
|
||||
applyThreeTextureSettings(threeTexture, resource);
|
||||
this._loadedThreeTextures.put(resourceName, threeTexture);
|
||||
|
||||
return threeTexture;
|
||||
}
|
||||
|
||||
private _getImageSource(resourceName: string): HTMLImageElement {
|
||||
// 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.
|
||||
@@ -225,86 +198,21 @@ namespace gdjs {
|
||||
`Can't load texture for resource "${resourceName}" as it's not an image.`
|
||||
);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the three.js texture associated to the specified resource name.
|
||||
* Returns a placeholder texture if not found.
|
||||
* @param xPositiveResourceName The name of the resource
|
||||
* @returns The requested cube texture, or a placeholder if not found.
|
||||
*/
|
||||
getThreeCubeTexture(
|
||||
xPositiveResourceName: string,
|
||||
xNegativeResourceName: string,
|
||||
yPositiveResourceName: string,
|
||||
yNegativeResourceName: string,
|
||||
zPositiveResourceName: string,
|
||||
zNegativeResourceName: string
|
||||
): THREE.CubeTexture {
|
||||
const key =
|
||||
xPositiveResourceName +
|
||||
'|' +
|
||||
xNegativeResourceName +
|
||||
'|' +
|
||||
yPositiveResourceName +
|
||||
'|' +
|
||||
yNegativeResourceName +
|
||||
'|' +
|
||||
zPositiveResourceName +
|
||||
'|' +
|
||||
zNegativeResourceName;
|
||||
const loadedThreeTexture = this._loadedThreeCubeTextures.get(key);
|
||||
if (loadedThreeTexture) {
|
||||
return loadedThreeTexture;
|
||||
}
|
||||
const threeTexture = new THREE.Texture(image);
|
||||
threeTexture.magFilter = THREE.LinearFilter;
|
||||
threeTexture.minFilter = THREE.LinearFilter;
|
||||
threeTexture.wrapS = THREE.RepeatWrapping;
|
||||
threeTexture.wrapT = THREE.RepeatWrapping;
|
||||
threeTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
threeTexture.needsUpdate = true;
|
||||
|
||||
const cubeTexture = new THREE.CubeTexture();
|
||||
// Faces on X axis need to be swapped.
|
||||
cubeTexture.images[0] = this._getImageSource(xNegativeResourceName);
|
||||
cubeTexture.images[1] = this._getImageSource(xPositiveResourceName);
|
||||
// Faces on Y keep the same order.
|
||||
cubeTexture.images[2] = this._getImageSource(yPositiveResourceName);
|
||||
cubeTexture.images[3] = this._getImageSource(yNegativeResourceName);
|
||||
// Faces on Z keep the same order.
|
||||
cubeTexture.images[4] = this._getImageSource(zPositiveResourceName);
|
||||
cubeTexture.images[5] = this._getImageSource(zNegativeResourceName);
|
||||
// The images also need to be mirrored horizontally by users.
|
||||
const resource = this._getImageResource(resourceName);
|
||||
|
||||
cubeTexture.magFilter = THREE.LinearFilter;
|
||||
cubeTexture.minFilter = THREE.LinearFilter;
|
||||
cubeTexture.colorSpace = THREE.SRGBColorSpace;
|
||||
cubeTexture.needsUpdate = true;
|
||||
applyThreeTextureSettings(threeTexture, resource);
|
||||
this._loadedThreeTextures.put(resourceName, threeTexture);
|
||||
|
||||
const resource = this._getImageResource(xPositiveResourceName);
|
||||
applyThreeTextureSettings(cubeTexture, resource);
|
||||
this._loadedThreeCubeTextures.set(key, cubeTexture);
|
||||
this._loadedThreeCubeTextureKeysByResourceName.add(
|
||||
xPositiveResourceName,
|
||||
key
|
||||
);
|
||||
this._loadedThreeCubeTextureKeysByResourceName.add(
|
||||
xNegativeResourceName,
|
||||
key
|
||||
);
|
||||
this._loadedThreeCubeTextureKeysByResourceName.add(
|
||||
yPositiveResourceName,
|
||||
key
|
||||
);
|
||||
this._loadedThreeCubeTextureKeysByResourceName.add(
|
||||
yNegativeResourceName,
|
||||
key
|
||||
);
|
||||
this._loadedThreeCubeTextureKeysByResourceName.add(
|
||||
zPositiveResourceName,
|
||||
key
|
||||
);
|
||||
this._loadedThreeCubeTextureKeysByResourceName.add(
|
||||
zNegativeResourceName,
|
||||
key
|
||||
);
|
||||
|
||||
return cubeTexture;
|
||||
return threeTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,11 +482,6 @@ namespace gdjs {
|
||||
for (const threeTexture of threeTextures) {
|
||||
threeTexture.dispose();
|
||||
}
|
||||
for (const cubeTexture of this._loadedThreeCubeTextures.values()) {
|
||||
cubeTexture.dispose();
|
||||
}
|
||||
this._loadedThreeCubeTextures.clear();
|
||||
this._loadedThreeCubeTextureKeysByResourceName.clear();
|
||||
|
||||
this._loadedThreeMaterials.disposeAll();
|
||||
|
||||
@@ -625,51 +528,12 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
this._loadedThreeMaterials.dispose(resourceName);
|
||||
|
||||
const cubeTextureKeys =
|
||||
this._loadedThreeCubeTextureKeysByResourceName.getValuesFor(
|
||||
resourceName
|
||||
);
|
||||
if (cubeTextureKeys) {
|
||||
for (const cubeTextureKey of cubeTextureKeys) {
|
||||
const cubeTexture = this._loadedThreeCubeTextures.get(cubeTextureKey);
|
||||
if (cubeTexture) {
|
||||
cubeTexture.dispose();
|
||||
this._loadedThreeCubeTextures.delete(cubeTextureKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayMap<K, V> {
|
||||
map = new Map<K, Array<V>>();
|
||||
|
||||
getValuesFor(key: K): Array<V> | undefined {
|
||||
return this.map.get(key);
|
||||
}
|
||||
|
||||
add(key: K, value: V): void {
|
||||
let values = this.map.get(key);
|
||||
if (!values) {
|
||||
values = [];
|
||||
this.map.set(key, values);
|
||||
}
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
deleteValuesFor(key: K): void {
|
||||
this.map.delete(key);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class ThreeMaterialCache {
|
||||
private _flaggedMaterials = new Map<string, THREE.Material>();
|
||||
private _materialFlaggedKeys = new ArrayMap<string, string>();
|
||||
private _materialFlaggedKeys = new Map<string, Array<string>>();
|
||||
|
||||
/**
|
||||
* Return the three.js material associated to the specified resource name
|
||||
@@ -720,7 +584,12 @@ namespace gdjs {
|
||||
forceBasicMaterial ? 1 : 0
|
||||
}|${vertexColors ? 1 : 0}`;
|
||||
this._flaggedMaterials.set(cacheKey, material);
|
||||
this._materialFlaggedKeys.add(resourceName, cacheKey);
|
||||
let flaggedKeys = this._materialFlaggedKeys.get(resourceName);
|
||||
if (!flaggedKeys) {
|
||||
flaggedKeys = [];
|
||||
this._materialFlaggedKeys.set(resourceName, flaggedKeys);
|
||||
}
|
||||
flaggedKeys.push(cacheKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -729,7 +598,7 @@ namespace gdjs {
|
||||
* @param resourceName The name of the resource
|
||||
*/
|
||||
dispose(resourceName: string): void {
|
||||
const flaggedKeys = this._materialFlaggedKeys.getValuesFor(resourceName);
|
||||
const flaggedKeys = this._materialFlaggedKeys.get(resourceName);
|
||||
if (flaggedKeys) {
|
||||
for (const flaggedKey of flaggedKeys) {
|
||||
const threeMaterial = this._flaggedMaterials.get(flaggedKey);
|
||||
@@ -739,7 +608,7 @@ namespace gdjs {
|
||||
this._flaggedMaterials.delete(flaggedKey);
|
||||
}
|
||||
}
|
||||
this._materialFlaggedKeys.deleteValuesFor(resourceName);
|
||||
this._materialFlaggedKeys.delete(resourceName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -101,7 +101,6 @@ namespace gdjs {
|
||||
this._threeRenderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||||
this._threeRenderer.useLegacyLights = true;
|
||||
this._threeRenderer.autoClear = false;
|
||||
this._threeRenderer.pixelRatio = window.devicePixelRatio;
|
||||
this._threeRenderer.setSize(
|
||||
this._game.getGameResolutionWidth(),
|
||||
this._game.getGameResolutionHeight()
|
||||
@@ -658,14 +657,6 @@ namespace gdjs {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (e.repeat) {
|
||||
// If `repeat` is true, this is not the first press of the key.
|
||||
// We only communicate the changes of states ("first" key down, key up)
|
||||
// to the manager, which then tracks the state of the key:
|
||||
// pressed, just pressed or released.
|
||||
return;
|
||||
}
|
||||
|
||||
manager.onKeyPressed(e.keyCode, e.location);
|
||||
};
|
||||
document.onkeyup = function (e) {
|
||||
@@ -762,7 +753,7 @@ namespace gdjs {
|
||||
};
|
||||
// @ts-ignore
|
||||
canvas.onwheel = function (event) {
|
||||
manager.onMouseWheel(-event.deltaY, event.deltaX, event.deltaZ);
|
||||
manager.onMouseWheel(-event.deltaY);
|
||||
};
|
||||
|
||||
// Touches:
|
||||
@@ -785,7 +776,6 @@ namespace gdjs {
|
||||
touch.pageY
|
||||
);
|
||||
manager.onTouchMove(touch.identifier, pos[0], pos[1]);
|
||||
manager.onTouchMove(touch.identifier, pos[0], pos[1]);
|
||||
// This works because touch events are sent
|
||||
// when they continue outside of the canvas.
|
||||
if (manager.isSimulatingMouseWithTouch()) {
|
||||
|
@@ -18,7 +18,6 @@ namespace gdjs {
|
||||
rendered2DLayersCount: 0,
|
||||
rendered3DLayersCount: 0,
|
||||
};
|
||||
private _backgroundColor: THREE.Color | null = null;
|
||||
|
||||
constructor(
|
||||
runtimeScene: gdjs.RuntimeScene,
|
||||
@@ -115,10 +114,9 @@ namespace gdjs {
|
||||
const runtimeLayerRenderingType = runtimeLayer.getRenderingType();
|
||||
const layerHas3DObjectsToRender = runtimeLayerRenderer.has3DObjects();
|
||||
if (
|
||||
!this._runtimeScene.getGame().isInGameEdition() &&
|
||||
(runtimeLayerRenderingType ===
|
||||
runtimeLayerRenderingType ===
|
||||
gdjs.RuntimeLayerRenderingType.TWO_D ||
|
||||
!layerHas3DObjectsToRender)
|
||||
!layerHas3DObjectsToRender
|
||||
) {
|
||||
// Render a layer with 2D rendering (PixiJS) only if layer is configured as is
|
||||
// or if there is no 3D object to render.
|
||||
@@ -211,23 +209,15 @@ namespace gdjs {
|
||||
);
|
||||
threeRenderer.resetState();
|
||||
if (this._runtimeScene.getClearCanvas()) threeRenderer.clear();
|
||||
if (!this._backgroundColor) {
|
||||
this._backgroundColor = new THREE.Color();
|
||||
}
|
||||
this._backgroundColor.set(
|
||||
threeScene.background = new THREE.Color(
|
||||
this._runtimeScene.getBackgroundColor()
|
||||
);
|
||||
if (!threeScene.background) {
|
||||
threeScene.background = this._backgroundColor;
|
||||
}
|
||||
|
||||
isFirstRender = false;
|
||||
} else {
|
||||
// It's important to set the background to null, as maybe the first rendered
|
||||
// layer has changed and so the Three.js scene background must be reset.
|
||||
if (threeScene.background === this._backgroundColor) {
|
||||
threeScene.background = null;
|
||||
}
|
||||
threeScene.background = null;
|
||||
}
|
||||
|
||||
// Clear the depth as each layer is independent and display on top of the previous one,
|
||||
|
@@ -41,71 +41,18 @@ namespace gdjs {
|
||||
return supportedCompressionMethods;
|
||||
};
|
||||
|
||||
/**
|
||||
* The desired status of the game, used for previews or in-game edition.
|
||||
* Either stored in the options generated by the preview or in the URL
|
||||
* in case of a hard reload.
|
||||
*/
|
||||
export type RuntimeGameStatus = {
|
||||
isPaused: boolean;
|
||||
isInGameEdition: boolean;
|
||||
sceneName: string | null;
|
||||
injectedExternalLayoutName: string | null;
|
||||
skipCreatingInstancesFromScene: boolean;
|
||||
eventsBasedObjectType: string | null;
|
||||
eventsBasedObjectVariantName: string | null;
|
||||
editorId: string | null;
|
||||
editorCamera3D?: EditorCameraState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the desired status of the game from the URL. Only useful for previews
|
||||
* when hard reloaded.
|
||||
*/
|
||||
const readRuntimeGameStatusFromUrl = (): RuntimeGameStatus | null => {
|
||||
try {
|
||||
const url = new URL(location.href);
|
||||
const runtimeGameStatus = url.searchParams.get('runtimeGameStatus');
|
||||
if (!runtimeGameStatus) return null;
|
||||
|
||||
const parsedRuntimeGameStatus = JSON.parse(runtimeGameStatus);
|
||||
return {
|
||||
isPaused: !!parsedRuntimeGameStatus.isPaused,
|
||||
isInGameEdition: !!parsedRuntimeGameStatus.isInGameEdition,
|
||||
sceneName: '' + parsedRuntimeGameStatus.sceneName,
|
||||
injectedExternalLayoutName:
|
||||
'' + parsedRuntimeGameStatus.injectedExternalLayoutName,
|
||||
skipCreatingInstancesFromScene:
|
||||
!!parsedRuntimeGameStatus.skipCreatingInstancesFromScene,
|
||||
eventsBasedObjectType: parsedRuntimeGameStatus.eventsBasedObjectType,
|
||||
eventsBasedObjectVariantName:
|
||||
parsedRuntimeGameStatus.eventsBasedObjectVariantName,
|
||||
editorId: parsedRuntimeGameStatus.editorId,
|
||||
editorCamera3D: parsedRuntimeGameStatus.editorCamera3D,
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/** Options given to the game at startup. */
|
||||
export type RuntimeGameOptions = {
|
||||
/** if true, force fullscreen. */
|
||||
forceFullscreen?: boolean;
|
||||
|
||||
/** if true, game is run as a preview launched from an editor. */
|
||||
isPreview?: boolean;
|
||||
|
||||
/** if set, the status of the game to be restored. */
|
||||
initialRuntimeGameStatus?: RuntimeGameStatus;
|
||||
|
||||
/** The name of the external layout to create in the scene at position 0;0. */
|
||||
injectExternalLayout?: string;
|
||||
/** Script files, used for hot-reloading. */
|
||||
scriptFiles?: Array<RuntimeGameOptionsScriptFile>;
|
||||
/** if true, export is a partial preview without reloading libraries. */
|
||||
shouldReloadLibraries?: boolean;
|
||||
/** if true, export is a partial preview without generating events. */
|
||||
shouldGenerateScenesEventsCode?: boolean;
|
||||
|
||||
/** if true, export is a partial preview without events. */
|
||||
projectDataOnlyExport?: boolean;
|
||||
/** if true, preview is launched from GDevelop native mobile app. */
|
||||
nativeMobileApp?: boolean;
|
||||
/** The address of the debugger server, to reach out using WebSocket. */
|
||||
@@ -192,13 +139,7 @@ namespace gdjs {
|
||||
_gameResolutionHeight: integer;
|
||||
_originalWidth: float;
|
||||
_originalHeight: float;
|
||||
_resizeMode:
|
||||
| ''
|
||||
| 'scaleOuter'
|
||||
| 'adaptWidth'
|
||||
| 'adaptHeight'
|
||||
| 'native'
|
||||
| string;
|
||||
_resizeMode: 'adaptWidth' | 'adaptHeight' | string;
|
||||
_adaptGameResolutionAtRuntime: boolean;
|
||||
_scaleMode: 'linear' | 'nearest';
|
||||
_pixelsRounding: boolean;
|
||||
@@ -230,8 +171,12 @@ namespace gdjs {
|
||||
_hasJustResumed: boolean = false;
|
||||
|
||||
//Inputs :
|
||||
private _inputManager: InputManager;
|
||||
_inputManager: InputManager;
|
||||
|
||||
/**
|
||||
* Allow to specify an external layout to insert in the first scene.
|
||||
*/
|
||||
_injectExternalLayout: any;
|
||||
_options: RuntimeGameOptions;
|
||||
|
||||
/**
|
||||
@@ -249,7 +194,6 @@ namespace gdjs {
|
||||
_sessionMetricsInitialized: boolean = false;
|
||||
_disableMetrics: boolean = false;
|
||||
_isPreview: boolean;
|
||||
_isInGameEdition: boolean;
|
||||
|
||||
/**
|
||||
* The capture manager, used to manage captures (screenshots, videos, etc...).
|
||||
@@ -259,27 +203,12 @@ namespace gdjs {
|
||||
/** True if the RuntimeGame has been disposed and should not be used anymore. */
|
||||
_wasDisposed: boolean = false;
|
||||
|
||||
_inGameEditor: InGameEditor | null;
|
||||
|
||||
/**
|
||||
* @param data The object (usually stored in data.json) containing the full project data
|
||||
* @param options The game options
|
||||
*/
|
||||
constructor(data: ProjectData, options?: RuntimeGameOptions) {
|
||||
this._options = options || {};
|
||||
|
||||
this._isPreview = this._options.isPreview || false;
|
||||
if (this._isPreview) {
|
||||
// Check if we need to restore the state from the URL, which is used
|
||||
// when a preview is hard reloaded (search for `hardReload`).
|
||||
const runtimeGameStatusFromUrl = readRuntimeGameStatusFromUrl();
|
||||
if (runtimeGameStatusFromUrl) {
|
||||
this._options.initialRuntimeGameStatus = runtimeGameStatusFromUrl;
|
||||
}
|
||||
}
|
||||
this._isInGameEdition =
|
||||
this._options.initialRuntimeGameStatus?.isInGameEdition || false;
|
||||
|
||||
this._variables = new gdjs.VariablesContainer(data.variables);
|
||||
this._variablesByExtensionName = new Map<
|
||||
string,
|
||||
@@ -308,12 +237,7 @@ namespace gdjs {
|
||||
getGlobalResourceNames(data),
|
||||
data.layouts
|
||||
);
|
||||
this._inGameEditor = this._isInGameEdition
|
||||
? new gdjs.InGameEditor(this, data)
|
||||
: null;
|
||||
this._debuggerClient = gdjs.DebuggerClient
|
||||
? new gdjs.DebuggerClient(this)
|
||||
: null;
|
||||
|
||||
this._effectsManager = new gdjs.EffectsManager();
|
||||
this._maxFPS = this._data.properties.maxFPS;
|
||||
this._minFPS = this._data.properties.minFPS;
|
||||
@@ -341,12 +265,17 @@ namespace gdjs {
|
||||
);
|
||||
this._sceneStack = new gdjs.SceneStack(this);
|
||||
this._inputManager = new gdjs.InputManager();
|
||||
this._injectExternalLayout = this._options.injectExternalLayout || '';
|
||||
this._debuggerClient = gdjs.DebuggerClient
|
||||
? new gdjs.DebuggerClient(this)
|
||||
: null;
|
||||
this._captureManager = gdjs.CaptureManager
|
||||
? new gdjs.CaptureManager(
|
||||
this._renderer,
|
||||
this._options.captureOptions || {}
|
||||
)
|
||||
: null;
|
||||
this._isPreview = this._options.isPreview || false;
|
||||
this._sessionId = null;
|
||||
this._playerId = null;
|
||||
|
||||
@@ -382,9 +311,6 @@ namespace gdjs {
|
||||
* @param projectData The object (usually stored in data.json) containing the full project data
|
||||
*/
|
||||
setProjectData(projectData: ProjectData): void {
|
||||
if (this._inGameEditor) {
|
||||
this._inGameEditor.onProjectDataChange(projectData);
|
||||
}
|
||||
this._data = projectData;
|
||||
this._updateSceneAndExtensionsData();
|
||||
this._resourcesLoader.setResources(
|
||||
@@ -559,55 +485,6 @@ namespace gdjs {
|
||||
return eventsBasedObjectData;
|
||||
}
|
||||
|
||||
getEventsBasedObjectVariantData(
|
||||
type: string,
|
||||
variantName: string
|
||||
): EventsBasedObjectVariantData | null {
|
||||
const eventsBasedObjectData = this.getEventsBasedObjectData(type);
|
||||
if (!eventsBasedObjectData) {
|
||||
return null;
|
||||
}
|
||||
return gdjs.RuntimeGame._getEventsBasedObjectVariantData(
|
||||
eventsBasedObjectData,
|
||||
variantName
|
||||
);
|
||||
}
|
||||
|
||||
static _getEventsBasedObjectVariantData(
|
||||
eventsBasedObjectData: EventsBasedObjectData,
|
||||
variantName: string
|
||||
): EventsBasedObjectVariantData {
|
||||
if (!eventsBasedObjectData.defaultVariant) {
|
||||
eventsBasedObjectData.defaultVariant = {
|
||||
...eventsBasedObjectData,
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
// Legacy events-based objects don't have any instance in their default
|
||||
// variant since there wasn't a graphical editor at the time.
|
||||
// In this case, the editor doesn't allow to choose a variant, but a
|
||||
// variant may have stayed after a user rolled back the extension.
|
||||
// This variant must be ignored to match what the editor shows.
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
eventsBasedObjectData.defaultVariant.instances.length == 0;
|
||||
if (isForcedToOverrideEventsBasedObjectChildrenConfiguration) {
|
||||
return eventsBasedObjectData.defaultVariant;
|
||||
}
|
||||
let usedVariantData: EventsBasedObjectVariantData =
|
||||
eventsBasedObjectData.defaultVariant;
|
||||
for (
|
||||
let variantIndex = 0;
|
||||
variantIndex < eventsBasedObjectData.variants.length;
|
||||
variantIndex++
|
||||
) {
|
||||
const variantData = eventsBasedObjectData.variants[variantIndex];
|
||||
if (variantData.name === variantName) {
|
||||
usedVariantData = variantData;
|
||||
}
|
||||
}
|
||||
return usedVariantData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data associated to a scene.
|
||||
*
|
||||
@@ -646,22 +523,6 @@ namespace gdjs {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data associated to a scene.
|
||||
*
|
||||
* @param name The name of the scene.
|
||||
* @return The data associated to the scene or null if not found.
|
||||
*/
|
||||
getSceneData(sceneName: string): LayoutData | null {
|
||||
for (let i = 0, len = this._data.layouts.length; i < len; ++i) {
|
||||
const sceneData = this._data.layouts[i];
|
||||
if (sceneData.name == sceneName) {
|
||||
return sceneData;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data associated to an external layout.
|
||||
*
|
||||
@@ -733,7 +594,7 @@ namespace gdjs {
|
||||
|
||||
this._gameResolutionWidth = width;
|
||||
this._gameResolutionHeight = height;
|
||||
if (this._adaptGameResolutionAtRuntime || this._isInGameEdition) {
|
||||
if (this._adaptGameResolutionAtRuntime) {
|
||||
if (
|
||||
gdjs.RuntimeGameRenderer &&
|
||||
gdjs.RuntimeGameRenderer.getWindowInnerWidth &&
|
||||
@@ -745,10 +606,7 @@ namespace gdjs {
|
||||
gdjs.RuntimeGameRenderer.getWindowInnerHeight();
|
||||
|
||||
// Enlarge either the width or the eight to fill the inner window space.
|
||||
if (this._isInGameEdition) {
|
||||
this._gameResolutionWidth = windowInnerWidth;
|
||||
this._gameResolutionHeight = windowInnerHeight;
|
||||
} else if (this._resizeMode === 'adaptWidth') {
|
||||
if (this._resizeMode === 'adaptWidth') {
|
||||
this._gameResolutionWidth =
|
||||
(this._gameResolutionHeight * windowInnerWidth) /
|
||||
windowInnerHeight;
|
||||
@@ -877,9 +735,9 @@ namespace gdjs {
|
||||
if (this._paused === enable) return;
|
||||
|
||||
this._paused = enable;
|
||||
if (this._inGameEditor) this._inGameEditor.activate(enable);
|
||||
if (this._debuggerClient) {
|
||||
this._debuggerClient.sendRuntimeGameStatus();
|
||||
if (this._paused) this._debuggerClient.sendGamePaused();
|
||||
else this._debuggerClient.sendGameResumed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1056,16 +914,11 @@ namespace gdjs {
|
||||
await loadAssets(onProgress);
|
||||
|
||||
await loadingScreen.unload();
|
||||
|
||||
if (!this._isInGameEdition) {
|
||||
this.pause(false);
|
||||
}
|
||||
this.pause(false);
|
||||
}
|
||||
|
||||
private _getFirstSceneName(): string {
|
||||
const firstSceneName =
|
||||
this._options.initialRuntimeGameStatus?.sceneName ||
|
||||
this._data.firstLayout;
|
||||
const firstSceneName = this._data.firstLayout;
|
||||
return this.hasScene(firstSceneName)
|
||||
? firstSceneName
|
||||
: // There is always at least a scene
|
||||
@@ -1085,41 +938,10 @@ namespace gdjs {
|
||||
this._forceGameResolutionUpdate();
|
||||
|
||||
// Load the first scene
|
||||
const sceneName = this._getFirstSceneName();
|
||||
const externalLayoutName =
|
||||
this._options.initialRuntimeGameStatus?.injectedExternalLayoutName ||
|
||||
null;
|
||||
if (this._inGameEditor) {
|
||||
const eventsBasedObjectType =
|
||||
this._options.initialRuntimeGameStatus?.eventsBasedObjectType ||
|
||||
null;
|
||||
const eventsBasedObjectVariantName =
|
||||
this._options.initialRuntimeGameStatus
|
||||
?.eventsBasedObjectVariantName || null;
|
||||
const editorId =
|
||||
this._options.initialRuntimeGameStatus?.editorId || null;
|
||||
const editorCamera3D =
|
||||
this._options.initialRuntimeGameStatus?.editorCamera3D || null;
|
||||
this._inGameEditor.switchToSceneOrVariant(
|
||||
editorId,
|
||||
sceneName,
|
||||
externalLayoutName,
|
||||
eventsBasedObjectType,
|
||||
eventsBasedObjectVariantName,
|
||||
editorCamera3D
|
||||
);
|
||||
} else {
|
||||
if (sceneName) {
|
||||
this.getSceneStack().replace({
|
||||
sceneName,
|
||||
externalLayoutName:
|
||||
externalLayoutName === null ? undefined : externalLayoutName,
|
||||
skipCreatingInstancesFromScene: !!externalLayoutName,
|
||||
clear: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._sceneStack.push(
|
||||
this._getFirstSceneName(),
|
||||
this._injectExternalLayout
|
||||
);
|
||||
this._watermark.displayAtStartup();
|
||||
|
||||
//Uncomment to profile the first x frames of the game.
|
||||
@@ -1145,33 +967,15 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
// The standard game loop
|
||||
let lastFrameSceneName: string | null = null;
|
||||
let accumulatedElapsedTime = 0;
|
||||
this._hasJustResumed = false;
|
||||
this._renderer.startGameLoop((lastCallElapsedTime) => {
|
||||
try {
|
||||
// Watch the scene name to automatically update debugger when a scene is changed.
|
||||
if (this._debuggerClient) {
|
||||
const currentScene = (
|
||||
this._inGameEditor || this.getSceneStack()
|
||||
).getCurrentScene();
|
||||
if (
|
||||
currentScene &&
|
||||
currentScene.getName() !== lastFrameSceneName
|
||||
) {
|
||||
lastFrameSceneName = currentScene.getName();
|
||||
this._debuggerClient.sendRuntimeGameStatus();
|
||||
}
|
||||
if (this._paused) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the game is edited, update the target framerate according to interactions.
|
||||
// Do it now (before frame skip), so that if a user interaction happens
|
||||
// we don't wait for a frame to pass at the current, probably very slow framerate.
|
||||
if (this._paused && this._inGameEditor) {
|
||||
this._inGameEditor.updateTargetFramerate(lastCallElapsedTime);
|
||||
}
|
||||
|
||||
// Skip the frame if we rendering frames too fast.
|
||||
// Skip the frame if we rendering frames too fast
|
||||
accumulatedElapsedTime += lastCallElapsedTime;
|
||||
if (
|
||||
this._maxFPS > 0 &&
|
||||
@@ -1188,36 +992,17 @@ namespace gdjs {
|
||||
|
||||
// Manage resize events.
|
||||
if (this._notifyScenesForGameResolutionResize) {
|
||||
if (this._inGameEditor) {
|
||||
this._inGameEditor.onGameResolutionResized();
|
||||
} else {
|
||||
this._sceneStack.onGameResolutionResized();
|
||||
}
|
||||
this._sceneStack.onGameResolutionResized();
|
||||
this._notifyScenesForGameResolutionResize = false;
|
||||
}
|
||||
|
||||
// Render and possibly step the game.
|
||||
if (this._paused) {
|
||||
if (this._inGameEditor) {
|
||||
// The game is paused for edition: the in-game editor runs and render
|
||||
// the scene.
|
||||
this._inGameEditor.updateAndRender();
|
||||
} else {
|
||||
// The game is paused (for debugging): the rendering of the scene is done,
|
||||
// but the game logic is not executed (no full "step").
|
||||
this._sceneStack.renderWithoutStep();
|
||||
}
|
||||
} else {
|
||||
// The game is not paused (and so, not edited): both the rendering
|
||||
// and game logic (a full "step") is executed.
|
||||
if (!this._sceneStack.step(elapsedTime)) {
|
||||
return false; // Return if game asked to be stopped.
|
||||
}
|
||||
// Render and step the scene.
|
||||
if (this._sceneStack.step(elapsedTime)) {
|
||||
this.getInputManager().onFrameEnded();
|
||||
this._hasJustResumed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
this.getInputManager().onFrameEnded();
|
||||
return true;
|
||||
return false;
|
||||
} catch (e) {
|
||||
if (this._debuggerClient)
|
||||
this._debuggerClient.onUncaughtException(e);
|
||||
@@ -1538,37 +1323,6 @@ namespace gdjs {
|
||||
return this._isPreview;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the game loop is paused, for debugging/edition purposes.
|
||||
* @returns true if the current game is paused
|
||||
*/
|
||||
isPaused(): boolean {
|
||||
return this._paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the game should display in-game edition tools or not.
|
||||
* @returns true if the current game is being edited.
|
||||
*/
|
||||
isInGameEdition(): boolean {
|
||||
return this._isInGameEdition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return in-game editor.
|
||||
*/
|
||||
getInGameEditor(): InGameEditor | null {
|
||||
return this._inGameEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum FPS of the game.
|
||||
* @param maximumFps The maximum FPS.
|
||||
*/
|
||||
setMaximumFps(maximumFps: integer) {
|
||||
this._maxFPS = maximumFps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the game should call GDevelop development APIs or not.
|
||||
*
|
||||
|
@@ -1380,22 +1380,6 @@ namespace gdjs {
|
||||
return this.hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width of the object before any custom size is applied.
|
||||
* @return The width of the object
|
||||
*/
|
||||
getOriginalWidth(): float {
|
||||
return this.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the width of the object before any custom size is applied.
|
||||
* @return The width of the object
|
||||
*/
|
||||
getOriginalHeight(): float {
|
||||
return this.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width of the object, if applicable.
|
||||
* @param width The new width in pixels.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user