Custom exporter

Custom Exporters have saved me dozens of hours. I can't function without PhysicsEditor. Richard Jarell

PhysicsEditor comes with many different data output formats - both generic formats like JSON and XML, as well as platform-specific formats like Cocos2D, Box2D, and others.

However, sometimes these built-in formats aren't sufficient - either because your game framework requires its own specific format, or because the existing formats don't meet your particular needs.

The solution is to create your own custom format. This process is easier than you might think - just follow the instructions below to get started.

How to add custom output formats to PhysicsEditor:

Creating a new exporter

An exporter consists of 2 files:

  • exporter.xml - the description of the exporter itself
  • template.txt - a template file containing the file layout

Modifying an existing exporter

Many of the exporters are available in the PhysicsEditor folder:

  • Windows users look under C:/Program Files/CodeAndWeb/PhysicsEditor/resources/exporters
  • Mac users go to /Applications/PhysicsEditor.app/Contents/Resources/exporters

The plain exporter may be a good starting point. Make sure to duplicate the exporter folder to somewhere outside PhysicsEditor's installation path and rename the folder and the exporter contents as shown below!

Preparations before creating your own exporter

Setting up your custom exporter directory

First, create a directory - it's the Exporter Directory that will contain all your custom exporters - e.g. my-PE-Exporters.

Now create a new subdirectory inside that directory - name it like the exporter you want to create - e.g. myexporter.

Your folder structure should look like this:

my-PE-Exporters/
└── myexporter/
    ├── exporter.xml
    └── template.txt

The exporters shipped with PhysicsEditor are located inside the application's folder in a folder called exporters.

To add your own exporter, point PhysicsEditor's preferences to your custom exporter directory location. After setting the path, you'll need to restart PhysicsEditor for it to recognize the new exporters.

PhysicsEditor Preferences

exporter.xml

Let's first have a look at the exporter.xml. This file describes the basic information about the exporter itself.

PhysicsEditor scans the exporter directories with each start. If you change the directory location in the preferences or make some changes in the exporter.xml you need to restart PhysicsEditor. If you change the contents' template file there is no need for restarting - it is read with each export.

The exporter.xml is - as the name already says - an xml file.

It has 4 main blocks:

  • Global exporter settings - settings for the exporter itself
  • Global parameters - settings for all bodies - e.g. gravity
  • Body parameters - settings for one body - e.g. body name, type, ...
  • Fixture parameters - settings for a single fixture - e.g. bounce rate of the head

Example:

<?xml version="1.0"?>
<exporter>

    <!-- exporter settings -->
    <name>cocos2d-x</name>
    <displayName>Cocos2d-x</displayName>
    <description>Exporter for Cocos2d-x</description>
    <version>1.0</version>
    <yAxisDirection>up</yAxisDirection>
    <physicsEngine>chipmunk</physicsEngine>
    <template>cocos2d-x.plist</template>
    <fileExtension>plist</fileExtension>
    <anchorPoint>
        <enabled>yes</enabled>
        <relX>0.5</relX>
        <relY>0.5</relY>
    </anchorPoint>
    <origin>
        <type>fixed</type>
        <relX>0.5</relX>
        <relY>0.5</relY>
    </origin>

    <global>
        <!-- global parameters -->
    </global>

    <body>
        <!-- body parameters -->
        <parameter>
            <name>is_dynamic</name>
            <displayName>Dynamic body</displayName>
            <description>A non-dynamic body is immovable such as a wall or the ground.</description>
            <type>bool</type>
            <default>true</default>
        </parameter>
        ...
    </body>

    <fixture>
        <!-- fixture parameters -->
        <parameter>
            <name>density</name>
            <displayName>Density</displayName>
            <description>Density of the physics shape.</description>
            <type>float</type>
            <default>1.0</default>
        </parameter>
        ...
    </fixture>
</exporter>

Global exporter parameters

ElementTypeInfo
<name>stringIdentifier for the exporter, must be unique. Otherwise the lastest exporter found for that name will overwrite the others.
<displayName>stringThis is how the exporter's name is displayed in the combo box in the GUI
<description>stringSome information about the exporter
<version>stringExporter version - should be build from digits separated with dots - e.g. 1.0 or 1.0.1
<yAxisDirection>"up" | "down"Direction of the yAxis
<physicsEngine>"box2d" | "chipmunk" | "generic"Name of the physics engine used. This currently set the polygon orientation.
<template>stringName of the template file - relative to the exporter.xml
<fileExtension>stringFile extension used when exporting the data file

Anchor Points

PhysicsEditor editing AnchorPoint/PivotPoint in cocos2d exporter

AnchorPoints can be used as the center of a sprite - e.g. for rotation or move operations. When setting the origin to anchorPoint it is also possible to center. Additional global parameters are anchor points:

ElementTypeInfo
<enabled>"yes" | "no"Display and use anchor points for this exporter
<relX>floatRelative position of the anchor points x position
<relY>floatRelative position of the anchor points y position

Origin

Origin - the 0/0 location of the sprite. This might be set to a fixed position or to the anchor point.

ElementTypeInfo
<type>"fixed" | "anchorPoint"Fixed sets a fixed relative position to the sprite's size, anchorPoint uses the AnchorPoint as sprite's center
<relX>floatRelative position of the origin x position
<relY>floatRelative position of the origin y position

Parameter blocks

There are 3 parameter blocks which can be used to define properties and values on file base, per body and per fixture.

ElementTypeInfo
<global><parameter>[]The global parameters used for the complete file - e.g. gravity
<body><parameter>[]Parameters used for an individual body
<fixture><parameter>[]Parameters used on an individual fixture. Might be multiple sub-fixtures in case the fixture is decomposed into convex parts.

Parameter definitions

In each of the elements <global>, <body&gt; and <fixture&gt; zero or more parameters can be defined.

Values for all parameters

These elements must be present for all parameters and define their appearance in the GUI.

ElementTypeInfo
<name>stringName of the value (used in the template)
<displayName>stringHuman readable name of the value (used in the user interface)
<description>stringDescription of the element used in the tooltip when hovering over the element for some time
<shortDescription>stringShort description of the element used in the status bar when hovering over the element
<type>

"bool", "string", "float", "int", "uint16", "bitfield"

Type of the parameter - which adds additional fields
<bitfield>"yes", "no"Connects a value to an existing bit field

bool

ElementTypeInfo
<type>boolDefines a bool input field / Checkbox
<default>"false" | "true"The default value
PhysicsEditor with custom bool parameter
A bool parameter rendered in the PhysicsEditor UI.
<parameter>
    <name>isSensor</name>
    <displayName>Is Sensor</displayName>
    <description>The object only detects collisions without colliding</description>
    <shortDescription>Turns the fixture into a sensor</shortDescription>
    <type>bool</type>
    <default>false</default>
</parameter>

string

ElementTypeInfo
<type>stringDefines a string input field
<default>stringThe default value which is used if nothing else is entered
PhysicsEditor with custom string parameter
A string parameter rendered in the PhysicsEditor UI.
<parameter>
    <name>fixture_id</name>
    <displayName>Identifier</displayName>
    <description>Identify a fixture during collision detection</description>
    <shortDescription>Identifier for the fixture</shortDescription>
    <type><code>string</code></type>
    <default>fixture123</default>
</parameter>

float

ElementTypeInfo
<type>floatDefines a float input field
<default>floatThe default value
<min>floatMinimum value
<max>floatMaximum value
PhysicsEditor with custom float parameter
A float parameter rendered in the PhysicsEditor UI.
<parameter>
    <name>density</name>
    <displayName>Density</displayName>
    <description>
        Density of the fixture, results in the mass if multiplied with the fixture's area
    </description>
    <shortDescription>Density of the fixture</shortDescription>
    <type>float</type>
    <min>-1000</min>
    <max>1000</max>
    <default>2.0</default>
</parameter>

int / uint16

ElementTypeInfo
<type>intDefines a int input field
<default>intThe default value
PhysicsEditor with custom int parameter
An int parameter rendered in the PhysicsEditor UI.
<parameter>
    <name>filter_groupIndex</name>
    <displayName>Group</displayName>
    <description>
        Collision group - objects which have collision enabled need to be in the same collision group
    </description>
    <shortDescription>The collision group</shortDescription>
    <type>int</type>
    <default>0</default>
</parameter>

BitField

The bit field is a more complicated structure and might get a better XML representation some day. It works quite simple for now. First you need to define a bitfield parameter which holds the other values. The values themselves get an additional field <bitfield>yes</bitfield> which connects them to the bit field. Make sure to match the size of the values to the <size> value in the bitfield definition.

ElementTypeInfo
<type>"bitfield"Defines a bitfield input
<size>intThe number of bits usually 16 or 32
PhysicsEditor with custom bitfield parameter
An bitfield parameter rendered in the PhysicsEditor UI.

This block creates the basic bit field:

<parameter>
    <name>filter_bitfield</name>
    <type>bitfield</type>
    <size>16</size>
</parameter>

This block adds a variable for the collision category and attaches it to the bit field.

<parameter>
    <name>filter_categoryBits</name>
    <displayName>Cat.</displayName>
    <description>Collision category</description>
    <shortDescription>Collision category</shortDescription>
    <type>uint16</type>
    <default>1</default>
    <bitfield>yes</bitfield>
</parameter>

This block adds another variable for the collision mask:

<parameter>
    <name>filter_maskBits</name>
    <displayName>Mask</displayName>
    <description>Collision mask</description>
    <shortDescription>Collision mask</shortDescription>
    <type>uint16</type>
    <default>65535</default>
    <bitfield>yes</bitfield>
</parameter>

The Template

PhysicsEditor uses the Grantlee template engine - which is similar to Django templates.

This is a short overview over the most important language elements. The template syntax is described in detail at the end of this document.

Referencing values

You can access 2 types of values

  • Build in values
  • Values that are added through parameters in exporter.xml

Values are surrounded by {{ and }} Sub values are separated by .. You might use | after the value itself to use filters for changing the value itself.

TemplateOutputRemark
g={{global.gravity}};g=9.800000;

This uses the gravity property from the global object and inserts it into the template. (Gravity is a custom property.)

g={{global.gravity|floatformat:3}};g=9.800;

Same as above with shortening the float value to 3 digits.

Control structures

Control-Structure: if

{% if condition %} ... {% else %} ... {% endif %}

Renders the first block if the condition is true, the second if it's false.

Example:

dynamic = {% if body.is_dynamic %}true{% else %}false{% endif %};

Control-Structure: for

{% for object in objectList %} ... {% endfor %}

Loops over a list of elements in the data structure.

The variable forloop.first is true if the loop is in the first cycle.

Example:

{% for body in bodies %}
    {{body.name}}
{% endfor %}

Mixed example

This example prints a list of coordinates, separating each block with a , but not adding a comma before the first element of the list:

{% for point in fixture.hull %}
{% if not forloop.first %}, {% endif %}({{point.x}}, {{point.y}})
{% endfor %}

Data structure

Top level objects

ValueTypeDescription
global

Contains all generic parameters defined in <global>

bodiesBody

Contains all bodies as body objects

Body

A body object holds all the bodies defined in PhysicsEditor. Here's a table of the built-in values. Values defined in the <body> section of the exporter.xml are added to the object.

ValueTypeDescription
anchorPointRelPoint

Anchorpoint's coordinates relative to the sprite's size.

anchorPointAbsPoint

Anchorpoint's coordinate in pixels.

sizeSize

The body's size in pixels. Same as sprite width.

fixtures

Fixture[]

List of body's Fixture objects.

Fixture

A fixture object holds all the fixtures defined in a body. Values defined in the <fixture> section of the exporter.xml are added to the object.

Circle-Fixture

These additional values are available inside circle fixtures.

ValueTypeDescription
type

"CIRCLE"

Type of the fixture

isCircletrue

Convenience function to check if the fixture is a circle

radiusfloat

The radius of the shape in pixels

centerPoint

Circle's center coordinate

Polygon-Fixture

These additional values are available inside polygon fixtures.

ValueDescription
type

"POLYGON"

Type of the fixture

isCirclefalse

Convenience function to check if the fixture is a circle

hullPolygon

All points as a single Polygon object - may be concave. Order of the points depends on the physics engine set in the exporter.xml

polygons

Polygon[]

List of convex Polygon objects after convex decomposition.

Polygon

A polygon is a list of Point objects.

Point

ValueDescription
x

x coordinate of a vertex in pixels

y

y coordinate of a vertex in pixels

Size

ValueDescription
width

width of the object in pixels

height

height of the object in pixels

Enhancing Exporters with JavaScript

While templates are powerful, sometimes you need more advanced functionality - such as complex calculations or data transformations. In these cases, you can enhance your exporter with JavaScript, just like in TexturePacker.

Setting Up JavaScript Support

To add JavaScript capabilities to your exporter:

  1. Create two nested directories inside your exporter folder:
    • First create a grantlee directory
    • Then create a 0.2 directory inside it

Your folder structure should look like this:

my-exporters/
└── myexporter/
    ├── exporter.xml
    ├── template.txt
    └── grantlee/
        └── 0.2/
            └── myfilter.qs
  1. Place your JavaScript file(s) in the grantlee/0.2 directory
  2. Use the .qs extension for your JavaScript files

Creating JavaScript Filters

In your JavaScript file, you can create filters - functions that process template values. A filter:

  1. Receives a value from the template
  2. Processes or transforms that value
  3. Returns the result, which is then included in the output

Here's an example of a filter that converts a value to radians:

// Define the filter function
var ToRadiansFilter = function(input) {
    // Convert the input to a regular JavaScript string
    var input = input.rawString();

    // Process the string (convert degrees to radians)
    return String(parseFloat(input) * 3.14159 / 180);
};

// Set the filter name (this is how it will be referenced in templates)
ToRadiansFilter.filterName = "toradians";

// Register the filter with the template system
Library.addFilter("ToRadiansFilter");

Important Notes:

  • Converting Input: Always convert the input parameter to a regular JavaScript string using .rawString() before using JavaScript string methods.
  • Return Type: Filters must return a string value.
  • Conditional Filters: To create a filter that evaluates a condition, return an empty string "" for false or any non-empty string for true.

Using JavaScript Filters in Templates

To use a JavaScript filter in your template, you first need to load it:

{% load toradians %}

Important: The value in the load tag must match the JavaScript file name (without the .qs extension).

Once loaded, you can apply the filter to any value in your template:

{{ fixture.rotation|toradians }}

Syntax of the Templates

The syntax of templates contains four types of tokens:

  • Plain text
  • Comments
  • Variables
  • Control tags

A simple template might look like this:

{# A simple template example #}
Hello {{ person.name }},

{% if person.hasBirthday %}
Happy Birthday!
{% else %}
Have a nice day!
{% endif %}

Bye,
{{ myname }}
  • Content inside {# #} are comments and are not part of the output.
  • Content inside {{ }} are variables which are substituted when the template is rendered.
  • Content inside {% %} are control tags which can affect the rendering of the template.

PhysicsEditor supports commonly used tags such as if, for, cycle and a host of others (see https://docs.djangoproject.com/en/dev/ref/templates/builtins/). It is also possible to define your own custom tags.

Loops

The type of a variable determines how it can be used in templates. For example, treatment of items in an iterable context works as follows:

Lists work as expected:

{% for item in mylist %}
- {{ item }}
{% endfor %}

Truthiness

Truthiness of a variable is evaluated in a similar way to Python. That is:

  • An empty string is false
  • 0 is false
  • An empty list is false
  • Boolean false is false

Everything else is true.

{% if mylist %}
List items:
{% for item in mylist %}
- {{ item }}
{% endfor %}
{% else %}
The list is empty.
{% endif %}

Variable Lookups

So far we've mostly used simple variable lookups in variable tags, but the template system is capable of a lot more. Complex structures can be evaluated using the dot (.) character.

The dot can be used for list index lookup (note that list lookup is 0-indexed):

The first item is {{ mylist.0 }}, and the fifth item is {{ mylist.4 }}.

It can also be used for key lookups:

The hash value is {{ myhash.mykey }}.

And it can retrieve properties from object instances:

The object property is {{ myobj.myprop }}.

Filters

Filters can further affect the result of including a variable in a template. Filters are separated by the pipe (|) character.

Name is {{ name }}, and name in uppercase is {{ name|upper }}.

Which is rendered as:

Name is Alice, and name in uppercase is ALICE.

Note that filters may be combined with dot-lookup. For example, if people is a list of Person objects, and Person objects have name properties:

First person's name: {{ people.0.name|upper }}.

Rendered as:

First person's name: BOB.

Filters may also be chained. Here, 'Claire' is first uppercased, then lowercased:

Name is {{ name|upper|lower }}

Rendered as:

Name is claire.

Filters may take one string argument, which is delimited by a colon (:).

If peoplestring contains "Dave and Ellen and Frank", we can cut the string and :

The people are {{ peoplestring|cut:"and " }}

Rendered as:

The people are Dave Ellen Frank.

Internal filters include:

  • upper - Converts text to uppercase
  • lower - Converts text to lowercase
  • slice - Returns a slice of the list
  • truncate - Truncates a string
  • join - Joins a list with a string
  • split - Splits a string into a list

For more filters, see the Django template built-in filters documentation.

Auto-Escaping in Templates

When creating HTML output, it's necessary to consider escaping data inserted into the template. HTML escaping involves replacing characters like < with &lt; and & with &amp; to prevent them from being interpreted as HTML markup. The template engine automatically escapes string input before adding it to the output.

This is relevant when writing a variable from the context into the template.

If the context object companies is a list containing (Burger King, Ben & Jerries, Ford), and it is used in a template such as:

{% for company in companies %}
- {{ company }}
{% endfor %}

The output would be:

  • Burger King
  • Ben &amp; Jerries
  • Ford

Notice that the & has been replaced with &amp;, as is appropriate for HTML output.

Use the |safe filter to get the unescaped content of the string:

{% for company in companies %}
- {{ company|safe }}
{% endfor %}

It is also possible to turn this auto-escaping feature off for a block in a template:

{% autoescape off %}
...
{% endautoescape %}

This would not escape the content between the autoescape and endautoescape tags. This should only be used for content which is actually already safe.

For more information, see the Django template auto-escaping documentation.