Skip to content

Commit 896f56e

Browse files
authored
Allow to switch between variants of custom objects (#7403)
- Variants allows to restyle custom objects - They can be customized with the graphical editor - The asset store will progressively use them notably for UI elements (buttons, sliders)
1 parent cee43ce commit 896f56e

File tree

128 files changed

+4123
-1006
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+4123
-1006
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* GDevelop Core
3+
* Copyright 2008-2016 Florian Rival ([email protected]). All rights
4+
* reserved. This project is released under the MIT License.
5+
*/
6+
#include "EventsBasedObjectVariantHelper.h"
7+
8+
#include "GDCore/Project/EventsBasedObject.h"
9+
#include "GDCore/Project/InitialInstancesContainer.h"
10+
#include "GDCore/Project/Object.h"
11+
#include "GDCore/Project/ObjectGroup.h"
12+
#include "GDCore/Project/ObjectsContainer.h"
13+
#include "GDCore/Project/ObjectsContainersList.h"
14+
#include "GDCore/Project/Project.h"
15+
#include "GDCore/Project/Variable.h"
16+
#include "GDCore/Project/VariablesContainer.h"
17+
#include "GDCore/String.h"
18+
19+
namespace gd {
20+
21+
void EventsBasedObjectVariantHelper::ComplyVariantsToEventsBasedObject(
22+
const gd::Project &project, gd::EventsBasedObject &eventsBasedObject) {
23+
auto &defaultObjects = eventsBasedObject.GetDefaultVariant().GetObjects();
24+
25+
for (const auto &variant :
26+
eventsBasedObject.GetVariants().GetInternalVector()) {
27+
auto &objects = variant->GetObjects();
28+
29+
// Delete extra objects
30+
for (auto it = objects.GetObjects().begin();
31+
it != objects.GetObjects().end(); ++it) {
32+
const auto &objectName = it->get()->GetName();
33+
if (!defaultObjects.HasObjectNamed(objectName)) {
34+
variant->GetInitialInstances().RemoveInitialInstancesOfObject(
35+
objectName);
36+
// Do it in last because it unalloc objectName.
37+
objects.RemoveObject(objectName);
38+
--it;
39+
}
40+
}
41+
for (const auto &defaultObject : defaultObjects.GetObjects()) {
42+
const auto &objectName = defaultObject->GetName();
43+
const auto &defaultVariables = defaultObject->GetVariables();
44+
const auto &defaultBehaviors = defaultObject->GetAllBehaviorContents();
45+
46+
// Copy missing objects
47+
if (!objects.HasObjectNamed(objectName)) {
48+
objects.InsertObject(*defaultObject,
49+
defaultObjects.GetObjectPosition(objectName));
50+
objects.AddMissingObjectsInRootFolder();
51+
continue;
52+
}
53+
// Change object types
54+
auto &object = objects.GetObject(objectName);
55+
if (object.GetType() != defaultObject->GetType()) {
56+
// Keep a copy of the old object.
57+
auto oldObject = objects.GetObject(objectName);
58+
objects.RemoveObject(objectName);
59+
objects.InsertObject(*defaultObject,
60+
defaultObjects.GetObjectPosition(objectName));
61+
object.CopyWithoutConfiguration(oldObject);
62+
objects.AddMissingObjectsInRootFolder();
63+
}
64+
65+
// Copy missing behaviors
66+
auto &behaviors = object.GetAllBehaviorContents();
67+
for (const auto &pair : defaultBehaviors) {
68+
const auto &behaviorName = pair.first;
69+
const auto &defaultBehavior = pair.second;
70+
71+
if (object.HasBehaviorNamed(behaviorName) &&
72+
object.GetBehavior(behaviorName).GetTypeName() !=
73+
defaultBehavior->GetTypeName()) {
74+
object.RemoveBehavior(behaviorName);
75+
}
76+
if (!object.HasBehaviorNamed(behaviorName)) {
77+
auto *behavior = object.AddNewBehavior(
78+
project, defaultBehavior->GetTypeName(), behaviorName);
79+
gd::SerializerElement element;
80+
defaultBehavior->SerializeTo(element);
81+
behavior->UnserializeFrom(element);
82+
}
83+
}
84+
// Delete extra behaviors
85+
for (auto it = behaviors.begin(); it != behaviors.end(); ++it) {
86+
const auto &behaviorName = it->first;
87+
if (!defaultObject->HasBehaviorNamed(behaviorName)) {
88+
object.RemoveBehavior(behaviorName);
89+
--it;
90+
}
91+
}
92+
93+
// Sort and copy missing variables
94+
auto &variables = object.GetVariables();
95+
for (size_t defaultVariableIndex = 0;
96+
defaultVariableIndex < defaultVariables.Count();
97+
defaultVariableIndex++) {
98+
const auto &variableName =
99+
defaultVariables.GetNameAt(defaultVariableIndex);
100+
const auto &defaultVariable =
101+
defaultVariables.Get(defaultVariableIndex);
102+
103+
auto variableIndex = variables.GetPosition(variableName);
104+
if (variableIndex == gd::String::npos) {
105+
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
106+
} else {
107+
variables.Move(variableIndex, defaultVariableIndex);
108+
}
109+
if (variables.Get(variableName).GetType() != defaultVariable.GetType()) {
110+
variables.Remove(variableName);
111+
variables.Insert(variableName, defaultVariable, defaultVariableIndex);
112+
}
113+
}
114+
// Remove extra variables
115+
auto variableToRemoveCount = variables.Count() - defaultVariables.Count();
116+
for (size_t iteration = 0; iteration < variableToRemoveCount;
117+
iteration++) {
118+
variables.Remove(variables.GetNameAt(variables.Count() - 1));
119+
}
120+
121+
// Remove extra instance variables
122+
variant->GetInitialInstances().IterateOverInstances(
123+
[&objectName,
124+
&defaultVariables](gd::InitialInstance &initialInstance) {
125+
if (initialInstance.GetObjectName() != objectName) {
126+
return false;
127+
}
128+
auto &instanceVariables = initialInstance.GetVariables();
129+
for (size_t instanceVariableIndex = 0;
130+
instanceVariableIndex < instanceVariables.Count();
131+
instanceVariableIndex++) {
132+
const auto &variableName =
133+
defaultVariables.GetNameAt(instanceVariableIndex);
134+
135+
if (!defaultVariables.Has(variableName)) {
136+
instanceVariables.Remove(variableName);
137+
}
138+
}
139+
return false;
140+
});
141+
}
142+
auto &defaultObjectGroups =
143+
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjectGroups();
144+
auto &objectGroups = variant->GetObjects().GetObjectGroups();
145+
auto objectGroupsCount = objectGroups.Count();
146+
// Clear groups
147+
for (size_t index = 0; index < objectGroupsCount; index++) {
148+
objectGroups.Remove(objectGroups.Get(0).GetName());
149+
}
150+
// Copy groups
151+
for (size_t index = 0; index < defaultObjectGroups.Count(); index++) {
152+
objectGroups.Insert(defaultObjectGroups.Get(index), index);
153+
}
154+
}
155+
}
156+
157+
} // namespace gd
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* GDevelop Core
3+
* Copyright 2008-2016 Florian Rival ([email protected]). All rights
4+
* reserved. This project is released under the MIT License.
5+
*/
6+
#pragma once
7+
8+
namespace gd {
9+
class EventsBasedObject;
10+
class Project;
11+
} // namespace gd
12+
13+
namespace gd {
14+
15+
class GD_CORE_API EventsBasedObjectVariantHelper {
16+
public:
17+
/**
18+
* @brief Apply the changes done on events-based object children to all its
19+
* variants.
20+
*/
21+
static void
22+
ComplyVariantsToEventsBasedObject(const gd::Project &project,
23+
gd::EventsBasedObject &eventsBasedObject);
24+
};
25+
26+
} // namespace gd

Core/GDCore/IDE/ObjectAssetSerializer.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "GDCore/IDE/Project/ResourcesRenamer.h"
1818
#include "GDCore/Project/Behavior.h"
1919
#include "GDCore/Project/CustomBehavior.h"
20+
#include "GDCore/Project/CustomObjectConfiguration.h"
2021
#include "GDCore/Project/EventsFunctionsExtension.h"
2122
#include "GDCore/Project/Layout.h"
2223
#include "GDCore/Project/Object.h"
@@ -75,6 +76,16 @@ void ObjectAssetSerializer::SerializeTo(
7576

7677
cleanObject->SerializeTo(objectAssetElement.AddChild("object"));
7778

79+
if (project.HasEventsBasedObject(object.GetType())) {
80+
SerializerElement &variantsElement =
81+
objectAssetElement.AddChild("variants");
82+
variantsElement.ConsiderAsArrayOf("variant");
83+
84+
std::unordered_set<gd::String> alreadyUsedVariantIdentifiers;
85+
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
86+
project, object, variantsElement, alreadyUsedVariantIdentifiers);
87+
}
88+
7889
SerializerElement &resourcesElement =
7990
objectAssetElement.AddChild("resources");
8091
resourcesElement.ConsiderAsArrayOf("resource");
@@ -108,4 +119,46 @@ void ObjectAssetSerializer::SerializeTo(
108119
objectAssetElement.AddChild("customization");
109120
customizationElement.ConsiderAsArrayOf("empty");
110121
}
122+
123+
void ObjectAssetSerializer::SerializeUsedVariantsTo(
124+
gd::Project &project, const gd::Object &object,
125+
SerializerElement &variantsElement,
126+
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers) {
127+
128+
if (!project.HasEventsBasedObject(object.GetType())) {
129+
return;
130+
}
131+
const auto *customObjectConfiguration =
132+
dynamic_cast<const gd::CustomObjectConfiguration *>(
133+
&object.GetConfiguration());
134+
if (customObjectConfiguration
135+
->IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() ||
136+
customObjectConfiguration
137+
->IsForcedToOverrideEventsBasedObjectChildrenConfiguration()) {
138+
return;
139+
}
140+
const auto &variantName = customObjectConfiguration->GetVariantName();
141+
const auto &variantIdentifier =
142+
object.GetType() + gd::PlatformExtension::GetNamespaceSeparator() +
143+
variantName;
144+
auto insertResult = alreadyUsedVariantIdentifiers.insert(variantIdentifier);
145+
if (insertResult.second) {
146+
const auto &eventsBasedObject =
147+
project.GetEventsBasedObject(object.GetType());
148+
const auto &variants = eventsBasedObject.GetVariants();
149+
const auto &variant = variants.HasVariantNamed(variantName)
150+
? variants.GetVariant(variantName)
151+
: eventsBasedObject.GetDefaultVariant();
152+
153+
SerializerElement &pairElement = variantsElement.AddChild("variant");
154+
pairElement.SetAttribute("objectType", object.GetType());
155+
SerializerElement &variantElement = pairElement.AddChild("variant");
156+
variant.SerializeTo(variantElement);
157+
// TODO Recursivity
158+
for (auto &object : variant.GetObjects().GetObjects()) {
159+
gd::ObjectAssetSerializer::SerializeUsedVariantsTo(
160+
project, *object, variantsElement, alreadyUsedVariantIdentifiers);
161+
}
162+
}
163+
}
111164
} // namespace gd

Core/GDCore/IDE/ObjectAssetSerializer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#pragma once
77
#include <map>
88
#include <vector>
9+
#include <unordered_set>
910

1011
#include "GDCore/String.h"
1112

@@ -52,6 +53,11 @@ class GD_CORE_API ObjectAssetSerializer {
5253
ObjectAssetSerializer(){};
5354

5455
static gd::String GetObjectExtensionName(const gd::Object &object);
56+
57+
static void SerializeUsedVariantsTo(
58+
gd::Project &project, const gd::Object &object,
59+
SerializerElement &variantsElement,
60+
std::unordered_set<gd::String> &alreadyUsedVariantIdentifiers);
5561
};
5662

5763
} // namespace gd

Core/GDCore/IDE/ObjectVariableHelper.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "ObjectVariableHelper.h"
77

88
#include "GDCore/IDE/WholeProjectRefactorer.h"
9+
#include "GDCore/Project/EventsBasedObject.h"
910
#include "GDCore/Project/InitialInstancesContainer.h"
1011
#include "GDCore/Project/Object.h"
1112
#include "GDCore/Project/ObjectGroup.h"
@@ -173,6 +174,7 @@ void ObjectVariableHelper::ApplyChangesToObjects(
173174
groupVariablesContainer.Get(variableName),
174175
variablesContainer.Count());
175176
}
177+
// TODO Check what happens if 2 variables exchange their names.
176178
for (const auto &pair : changeset.oldToNewVariableNames) {
177179
const gd::String &oldVariableName = pair.first;
178180
const gd::String &newVariableName = pair.second;
@@ -215,6 +217,7 @@ void ObjectVariableHelper::ApplyChangesToObjectInstances(
215217
destinationVariablesContainer.Remove(variableName);
216218
}
217219
}
220+
// TODO Check what happens if 2 variables exchange their names.
218221
for (const auto &pair : changeset.oldToNewVariableNames) {
219222
const gd::String &oldVariableName = pair.first;
220223
const gd::String &newVariableName = pair.second;
@@ -236,6 +239,66 @@ void ObjectVariableHelper::ApplyChangesToObjectInstances(
236239
}
237240
}
238241
}
242+
return false;
239243
});
240244
}
245+
246+
void ObjectVariableHelper::ApplyChangesToVariants(
247+
gd::EventsBasedObject &eventsBasedObject, const gd::String &objectName,
248+
const gd::VariablesChangeset &changeset) {
249+
auto &defaultVariablesContainer = eventsBasedObject.GetDefaultVariant()
250+
.GetObjects()
251+
.GetObject(objectName)
252+
.GetVariables();
253+
for (auto &variant : eventsBasedObject.GetVariants().GetInternalVector()) {
254+
if (!variant->GetObjects().HasObjectNamed(objectName)) {
255+
continue;
256+
}
257+
auto &object = variant->GetObjects().GetObject(objectName);
258+
auto &variablesContainer = object.GetVariables();
259+
260+
for (const gd::String &variableName : changeset.removedVariableNames) {
261+
variablesContainer.Remove(variableName);
262+
}
263+
for (const gd::String &variableName : changeset.addedVariableNames) {
264+
if (variablesContainer.Has(variableName)) {
265+
// It can happens if a child-object already had the variable but it was
266+
// missing in other variant child-object.
267+
continue;
268+
}
269+
variablesContainer.Insert(variableName,
270+
defaultVariablesContainer.Get(variableName),
271+
variablesContainer.Count());
272+
}
273+
// TODO Check what happens if 2 variables exchange their names.
274+
for (const auto &pair : changeset.oldToNewVariableNames) {
275+
const gd::String &oldVariableName = pair.first;
276+
const gd::String &newVariableName = pair.second;
277+
if (variablesContainer.Has(newVariableName)) {
278+
// It can happens if a child-object already had the variable but it was
279+
// missing in other variant child-object.
280+
variablesContainer.Remove(oldVariableName);
281+
} else {
282+
variablesContainer.Rename(oldVariableName, newVariableName);
283+
}
284+
}
285+
// Apply type changes
286+
for (const gd::String &variableName : changeset.valueChangedVariableNames) {
287+
size_t index = variablesContainer.GetPosition(variableName);
288+
289+
if (variablesContainer.Has(variableName) &&
290+
variablesContainer.Get(variableName).GetType() !=
291+
defaultVariablesContainer.Get(variableName).GetType()) {
292+
variablesContainer.Remove(variableName);
293+
variablesContainer.Insert(
294+
variableName, defaultVariablesContainer.Get(variableName), index);
295+
}
296+
}
297+
298+
gd::ObjectVariableHelper::ApplyChangesToObjectInstances(
299+
variablesContainer, variant->GetInitialInstances(), objectName,
300+
changeset);
301+
}
302+
}
303+
241304
} // namespace gd

0 commit comments

Comments
 (0)