-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Dynamic placeable objects with default properties for custom engine #365
Comments
short: Yes you are allowed to fork and customize it. Different parts of this repository are covered by different licenses. The editor part "tiled" however is covered by a more restrictive license: GPLv2. |
That sounds similar to #70 |
In the menu Edit->Preferences->Object Types you can import and export types to xml. |
So let's start with the Object Types. I know that I can import and export these types, yet I would like to automate this process otherwise I would have to do this everytime I introduce a new placeable object. The default properties you mentioned in #70 are definitely what is still missing and I looking on how to tell the editor where to fetch the templates from. Yet this has to implemented by someone (or me? maybe...). |
@ChristianIvicevic I don't think this has to be very complicated. Sounds like you need the following:
ObjectTypesReader reader;
Preferences::instance()->setObjectTypes(reader.readObjectTypes("my-object-types.xml")); (If you want you can take the file by command line argument, or you even do a input field with 'Browse..' button in the preferences to make this an official way of dealing with object types. I'd accept the latter solution in Tiled proper).
Finally, good luck! If it sounds too much, you can also "fund" me a few hours, btw. |
@bjorn Thank you for your suggestions so far. I was successful implementing half of the requested features yet I think that these are quite specialized for my needs and I don't know of a good generalization yet to pull a commit. However, I am going to explain a bit what I did so far, such that anyone who wants this feature too, can have a look at it. In ObjectTypesReader objectTypesReader;
// Load the object types if available - this will most likely override
// the default one loaded from the registry (windows specific).
QFile objectTypesFile(QLatin1String("ObjectTypes.xml"));
if(objectTypesFile.exists())
Preferences::instance()->setObjectTypes(
objectTypesReader.readObjectTypes(QLatin1String("ObjectTypes.xml"))); This method currently presumes that a possible custom file will exist, such that all values from the registry are ignored. During compilation this file will be automatically created by my compiler based on the source files. To complement this I changed the return statement of the int returnCode = a.exec();
// Clean the object types
Preferences::instance()->setObjectTypes(ObjectTypes());
return returnCode; This is mainly to be only dependent on the local xml file and have everything clean. Let's now have a look at the default properties and those which are exposed by the source code to the editor. First of I changed the The next step was to modify the struct ObjectTypeProperty {
QString name;
enum DataType {
DT_STRING,
DT_BOOL,
DT_INT,
DT_DOUBLE
} type;
QString stringValue;
bool boolValue;
int intValue;
double doubleValue;
}; I wanted to use a <?xml version="1.0" encoding="UTF-8"?>
<objecttypes>
<objecttype name="Entity" color="#000000" />
<objecttype name="TriggerOnce" color="#ffff00">
<property type="bool" name="IsTestVarActive" default="true" />
<property type="int" name="SomeInt" default="0" />
<property type="double" name="Opacity" default="0.5" />
<property type="string" name="StringifiedName" default="A name" />
</objecttype>
<objecttype name="TriggerMultiple" color="#ffaa00" />
<objecttype name="Teleport" color="#00ffff" />
<objecttype name="InfoPlayerStart" color="#005500" />
</objecttypes> The new /* ... */
while (reader.readNextStartElement()) {
if (reader.name() == QLatin1String("objecttype")) {
const QXmlStreamAttributes atts = reader.attributes();
const QString name(atts.value(QLatin1String("name")).toString());
const QColor color(atts.value(QLatin1String("color")).toString());
ObjectType objectType(name, color);
objectType.exposedProperties = this->parseProperties(reader);
objectTypes.append(objectType);
}
}
/* ... */
QVector<ObjectTypeProperty> ObjectTypesReader::parseProperties(QXmlStreamReader& objectTypeNode) {
QVector<ObjectTypeProperty> objectProperties;
while(objectTypeNode.readNextStartElement()) {
if(objectTypeNode.name() == QLatin1String("property")) {
ObjectTypeProperty property;
const QXmlStreamAttributes atts = objectTypeNode.attributes();
const QString type = atts.value(QLatin1String("type")).toString();
if(type == QLatin1String("bool")) {
property.type = ObjectTypeProperty::DT_BOOL;
} else if(type == QLatin1String("int")) {
property.type = ObjectTypeProperty::DT_INT;
} else if(type == QLatin1String("double")) {
property.type = ObjectTypeProperty::DT_DOUBLE;
} else if(type == QLatin1String("string")) {
property.type = ObjectTypeProperty::DT_STRING;
} else {
property.type = ObjectTypeProperty::DT_STRING;
}
property.name = atts.value(QLatin1String("name")).toString();
const QString defaultValue = atts.value(QLatin1String("default")).toString();
switch(property.type) {
case ObjectTypeProperty::DT_BOOL:
if(defaultValue == QLatin1String("true"))
property.boolValue = true;
else
property.boolValue = false;
break;
case ObjectTypeProperty::DT_INT:
property.intValue = defaultValue.toInt();
break;
case ObjectTypeProperty::DT_DOUBLE:
property.doubleValue = defaultValue.toDouble();
break;
case ObjectTypeProperty::DT_STRING:
property.stringValue = defaultValue;
break;
}
objectProperties.append(property);
}
objectTypeNode.skipCurrentElement();
}
return objectProperties;
} So now I have some issues - my intention was include an I would be very glad if you can give me some hints on where to exactly look as I am quite confused by the Qt structure in your files. Ah and if I don't succeed... what about "funding" you a few hours (probably some minutes) - how much coffee do you drink usually? 😄 |
@ChristianIvicevic Good going so far! I can imagine that last part will be a little bit involved, but I don't have time to go into further detail. Contact me by email if you would like to know how much coffee I need. I would estimate that it would still take me about two hours to solve that riddle. |
@bjorn Once again I was successful implementing what I needed. Anyone interested in knowing how I did this should continue reading this comment - I will describe what I have changed. And this feature is really necessary in Tiled so I have decided to optimize the approach and once I'm finished I will contribute to the project with my suggestions on how to include this. So far I have been using my custom struct mModel = new PropertiesModel(this);
// This is actually new.
this->updateDefaultProperties();
mModel->setProperties(mObject->properties()); So far we need some way to check whether we can safely work with void PropertiesDialog::updateDefaultProperties() {
// TODO: This check is horrible! Fix this asap.
if(mKind == QLatin1String("Object")) {
MapObject *mapObject = static_cast<MapObject*>(mObject);
ObjectTypes types = Preferences::instance()->objectTypes();
ObjectType currentType;
foreach(ObjectType type, types) {
if(type.name == mapObject->type()) {
currentType = type;
break;
}
}
foreach(ObjectTypeProperty exposedProperty, currentType.exposedProperties) {
if(!mapObject->properties().contains(exposedProperty.name)) {
if(exposedProperty.type == ObjectTypeProperty::DT_BOOL)
mapObject->setProperty(exposedProperty.name, exposedProperty.boolValue ? QLatin1String("true") : QLatin1String("false"));
else if(exposedProperty.type == ObjectTypeProperty::DT_STRING)
mapObject->setProperty(exposedProperty.name, exposedProperty.stringValue);
else if(exposedProperty.type == ObjectTypeProperty::DT_INT)
mapObject->setProperty(exposedProperty.name, QString::number(exposedProperty.intValue));
else if(exposedProperty.type == ObjectTypeProperty::DT_DOUBLE)
mapObject->setProperty(exposedProperty.name, QString::number(exposedProperty.doubleValue));
}
}
mModel->setProperties(mapObject->properties());
}
} What this code does, it creates the default properties for the object and adds them directly to the object. Soon I will implement a more restrictive version for myself, such that one cannot just add new properties which are unsupported. Therefore the code has to be updated so that if you change the type from A to B
The last step is to handle the event when the void ObjectPropertiesDialog::objectTypeChanged(QString currentItem) {
mMapObject->setType(currentItem);
this->updateDefaultProperties();
} |
@ChristianIvicevic This As soon as you open a pull request I can try it out and review the code in more detail. :-) |
@ChristianIvicevic Did you ever finish your approach? Even if not, would you mind sharing your work so that maybe somebody else could finish it? This feature is still one of the most requested ones for Tiled, so it would be very helpful! |
@bjorn Sorry for not reaching you out. I will be working again with Tiled during the next months so I might share my thoughts on how to properly implement this soonish. |
@ChristianIvicevic Great to hear from you again and looking forward to reading your thoughts. :-) |
Just wanted to thank @ChristianIvicevic . Took me a few hours to get this implemented and working. Most of what he posted above still works fine. Some of it is now unnecessary because of the new properties update and instead of working with propertiesbrowser.cpp the code in the second post needs to be implemented in propertybrowser.cpp. I also played with createobjecttool.cpp to allow me to instantiate MapObjects with a certain type, and property values specific to their tiles (via tile properties) that fill in the default values that have already been implemented. That combined with the aforementioned changes (made to allow default properties) and the new "collection of images" tileset have allowed me to have premade objects that I can drop into the map via an image tile, which is cool. I'd post my complete solution but I'm afraid I butchered the code a bit when implementing this. |
While I cannot help with coding this feature, I like to contribute another real world use case. Stendhal currently uses external manually-edited .xml files to place dynamic content. Based on these files, I created an objecttypes.xml for Stendhal entities. |
@zachprinz @ChristianIvicevic @bjorn I'm looking for this functionality. |
@dazKind Unfortunately @ChristianIvicevic never made it to a pull request and his Tiled fork seems to be gone. I also don't see any of his work in @zachprinz his Tiled fork. I'm not aware of any other patches adding this kind of functionality. |
@bjorn: I thought so. For what it's worth, I started working on this in my fork: dazKind/tiled@bjorn:master...master |
@dazKind Awesome, looks like a good start! |
Ok, I got it to a functional state. Im adding the default properties in updateCustomProperties: dazKind/tiled@bjorn:master...master#diff-fff27e659587ef19ff643fce099f69f7R949 Im not that familiar with Qt. I noticed that the added properties are all kinda greyed out and wont be exported: Is there a flag I have to set somewhere? Edit: I'm testing with @nhnb's objecttypes.xml Edit2: Nvm, I see that the that object itself doesnt have the actual property set, yet. Thus they get grayed out atm. fixed |
Ok, so the proof of concept works. What bugs me is the way the editor keeps the incomplete types between restarts. Now, I read that the objecttype name and color are stored in the registry. Is this still true? It would make more sense to remove this and just store a path to an xml containing the types. Then you could ship with an example that gets loaded by default and have users override it depending on their projects/needs. Maybe a better idea would be to move this information into the actual map-data altogether. Any thoughts? |
@bjorn ping! |
@dazKind I'm reading your updates, but it's "during the week" now which leaves me with only a little time in the evenings. I'll quickly answer some of your questions:
Unfortunately I don't have time for a code review right now, but please open a pull request so that I can better comment on the changes when I do have time. The feature does not need to be finished before opening a pull request. And thanks for taking up this issue! |
@bjorn Sry, didnt mean to be pushy or something. Just wanted to make sure we keep up the discussion so I can proceed in a timely manner.
It depends on the assumptions I guess. If the default values are also set in the game then it makes sense to gray the properties out. But if the game has no knowlegde about the defaults you might want to explicitly export every property per object. Not sure what would be the most reasonable default behavior.
Cool, so i will look into the file reference set in the preferences.
No hurry. I'm implementing this while testing it in my workflow. Once I consider the obvious rough edges gone I'll make it a PR for official consumption and further discussion |
I think a perfectly sane default is to only store those properties which have actually been set (regardless of whether their value is the same as the default). If you always write out all properties you're needlessly bloating up the file for most people. Besides that, it would leave no easy way to change the defaults in a way that would directly affect all objects that did not have a certain property explicitly set up them. |
makes sense. PR is up |
* Code cleanups and simplifications (use QVariant in more places) * Use 'string' and 'float' instead of 'QString' and 'double' type names * Improved the Add Property dialog layout * Remember last used type when adding new properties Closes #365
Hello together,
so far I have been using Tiled for my 2D game engine and everything is neat and works fine. Currently I have much hardcoded the game into the engine and I am working on encapsulating this in other parts. Therefore I have developed a custom scripting language inspired by UnrealScript (with ANTLR 4) which gets executed by the engine during runtime. Now I have a basic
Entity
class which looks like thisand a possible object which could get triggered multiple times (duh.)
I am looking for an easy way to provide these classes as object types to the editor without the need of some registry manipulation of the key
HKEY_CURRENT_USER\Software\mapeditor.org\Tiled\ObjectTypes
. That is: you write the code, metadata in a readable format gets created and the editor will load this automatically (a portable version of the editor to be included with my engine would really decouple it from the registry). So I am looking for a way that the editor can feed off of my provided data.As another request I would like the editor to automatically create the properties
IsVisible
andEvent
in the object properties when provided with the necessary metadata like with the objects themselves. The coolest thing would be to gather them to some grouped view based on the specifiers(EntityProperties)
and(TriggerProperties)
but that is not necessary, yet.I know that this is all a bit too localized and would result in changes of the sourcecode in a custom fork and not the full version, yet I would like to get some hints what the easiest way would be to do so such that I do not have to waste much time looking through the sourcecode to find what to modify.
As a possible side-question: am I allowed to fork the editor and customize it for my engine? What do I have to provide when doing so considering copy(right | left)?
The text was updated successfully, but these errors were encountered: