Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* Rewritten Properties view to enable direct widget interaction (#4045)
* Added support for lists in custom properties (#1493)
* Added capsule object shape (by Jocelyn, #2153)
* Allow filtering tilesets by name in the tileset dock (with dogboydog, #4239)
* Allow changing the values of number inputs using expressions (with dogboydog, #4234)
* Added support for SVG 1.2 / CSS blending modes to layers (#3932)
Expand Down
44 changes: 36 additions & 8 deletions docs/manual/objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ to switch to the :ref:`select-objects-tool` tool.
Insert Rectangle
~~~~~~~~~~~~~~~~

Shortcut: ``R``
Shortcut: ``R`` |insert-rectangle|

The rectangle was the first type of object supported by Tiled, which is why
objects are rectangles by default in the :doc:`/reference/tmx-map-format`. They
Expand All @@ -61,7 +61,7 @@ around its position. This is mainly to keep it visible and selectable.
Insert Point
~~~~~~~~~~~~~~

Shortcut: ``I``
Shortcut: ``I`` |insert-point|

Points are the simplest objects you can place on a map. They only represent a
location, and cannot be resized or rotated. Simply click on the map to position
Expand All @@ -72,18 +72,35 @@ a point object.
Insert Ellipse
~~~~~~~~~~~~~~

Shortcut: ``C``
Shortcut: ``C`` |insert-ellipse|

Ellipses work the same way as `rectangles <#insert-rectangle>`__, except
that they are rendered as an ellipse. Useful for when your area or
collision shape needs to represent a circle or ellipse.


.. raw:: html

<div class="new">Since Tiled 1.12</div>


.. _insert-capsule-tool:

Insert Capsule
~~~~~~~~~~~~~~

Shortcut: ``Shift+C`` |insert-capsule|

Capsules work the same way as `rectangles <#insert-rectangle>`__, except
that they are rendered as a capsule. Useful for when your area or
collision shape needs to represent a circle or capsule.

.. _insert-polygon-tool:

Insert Polygon
~~~~~~~~~~~~~~

Shortcut: ``P``
Shortcut: ``P`` |insert-polygon|

Polygons are the most flexible way of defining the shape of an area.
They are most commonly used for defining collision shapes.
Expand Down Expand Up @@ -125,7 +142,7 @@ The :ref:`edit-polygons-tool` tool is used to edit polylines as well.
Insert Tile
~~~~~~~~~~~

Shortcut: ``T``
Shortcut: ``T`` |insert-tile|

Tiles can be inserted as objects to have full flexibility in placing,
scaling and rotating the tile image on your map. Like all objects, tile
Expand Down Expand Up @@ -168,7 +185,7 @@ objects consistent with that of :ref:`rectangle objects
Insert Template
~~~~~~~~~~~~~~~

Shortcut: ``V``
Shortcut: ``V`` |insert-template|

Can be used to quickly insert multiple instances of the template
selected in the Templates view. See :ref:`creating-template-instances`.
Expand All @@ -178,7 +195,7 @@ selected in the Templates view. See :ref:`creating-template-instances`.
Insert Text
~~~~~~~~~~~

Shortcut: ``X``
Shortcut: ``X`` |insert-text|

Text objects can be used to add arbitrary multi-line text to your maps.
You can configure various font properties and the wrapping / clipping
Expand All @@ -190,7 +207,7 @@ the game.
Select Objects
--------------

Shortcut: ``S``
Shortcut: ``S`` |tool-select-objects|

When you're not inserting new objects, you're generally using the Select
Objects tool. It packs a lot of functionality, which is outlined below.
Expand Down Expand Up @@ -384,3 +401,14 @@ jump there by right-clicking the property and selecting *Go to Object*.
faster by `sponsoring Tiled development <https://www.mapeditor.org/donate>`__. The
more support I receive the more time I can afford to spend improving
Tiled!


.. |tool-select-objects| image:: ../../src/tiled/resources/images/22/tool-select-objects.png
.. |insert-rectangle| image:: ../../src/tiled/resources/images/24/insert-rectangle.png
.. |insert-point| image:: ../../src/tiled/resources/images/24/insert-point.png
.. |insert-ellipse| image:: ../../src/tiled/resources/images/24/insert-ellipse.png
.. |insert-capsule| image:: ../../src/tiled/resources/images/24/insert-capsule.png
.. |insert-polygon| image:: ../../src/tiled/resources/images/24/insert-polygon.png
.. |insert-tile| image:: ../../src/tiled/resources/images/24/insert-image.png
.. |insert-template| image:: ../../src/tiled/resources/images/24/insert-template.png
.. |insert-text| image:: ../../src/tiled/resources/images/24/insert-text.png
11 changes: 7 additions & 4 deletions docs/reference/json-map-format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ Object
:header: Field, Type, Description
:widths: 1, 1, 4

capsule, bool, "Used to mark an object as a capsule"
ellipse, bool, "Used to mark an object as an ellipse"
gid, int, "Global tile ID, only if object represents a tile"
height, double, "Height in pixels."
Expand Down Expand Up @@ -492,10 +493,10 @@ Tileset
wangsets, array, "Array of :ref:`Wang sets <json-wangset>` (since 1.1.5)"

Each tileset has a ``firstgid`` (first global ID) property which
tells you the global ID of its first tile (the one with local
tile ID 0). This allows you to map the global IDs back to the
right tileset, and then calculate the local tile ID by
subtracting the ``firstgid`` from the global tile ID. The first
tells you the global ID of its first tile (the one with local
tile ID 0). This allows you to map the global IDs back to the
right tileset, and then calculate the local tile ID by
subtracting the ``firstgid`` from the global tile ID. The first
tileset always has a ``firstgid`` value of 1.

.. _json-tileset-grid:
Expand Down Expand Up @@ -769,6 +770,8 @@ Tiled 1.12
* Added ``mode`` property to :ref:`json-layer` to specify the blend mode to use
when rendering the layer.

* Added ``capsule`` property to :ref:`json-object`.

Tiled 1.11.1
~~~~~~~~~~~~

Expand Down
10 changes: 10 additions & 0 deletions docs/reference/tmx-changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ Tiled 1.12

- Added ``mode`` attribute on :ref:`tmx-layer` to specific its blend mode.

- Added capsule object shape. Same parameters as rectangular objects,
but marked as capsule with a child element:

.. code:: xml

<object name="..." x="..." y="...">
<capsule/>
</object>


Tiled 1.10
----------

Expand Down
15 changes: 12 additions & 3 deletions docs/reference/tmx-map-format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -578,9 +578,9 @@ properties from the specified template, properties saved with the object
will have higher priority, i.e. they will override the template
properties.

Can contain at most one: :ref:`tmx-properties`, :ref:`tmx-ellipse` (since
0.9), :ref:`tmx-point` (since 1.1), :ref:`tmx-polygon`, :ref:`tmx-polyline`,
:ref:`tmx-text` (since 1.0)
Can contain at most one: :ref:`tmx-properties`, :ref:`tmx-ellipse` (since 0.9),
:ref:`tmx-capsule` (since 1.12), :ref:`tmx-point` (since 1.1),
:ref:`tmx-polygon`, :ref:`tmx-polyline`, :ref:`tmx-text` (since 1.0)

.. _tmx-ellipse:

Expand All @@ -591,6 +591,15 @@ Used to mark an object as an ellipse. The existing ``x``, ``y``,
``width`` and ``height`` attributes are used to determine the size of
the ellipse.

.. _tmx-capsule:

<capsule>
~~~~~~~~~

Used to mark an object as a capsule. The existing ``x``, ``y``,
``width`` and ``height`` attributes are used to determine the size of
the capsule.

.. _tmx-point:

<point>
Expand Down
21 changes: 16 additions & 5 deletions src/libtiled/isometricrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ QPainterPath IsometricRenderer::shape(const MapObject *object) const
QPainterPath path;

switch (object->shape()) {
case MapObject::Capsule: {
QRectF bounds = object->bounds();
bounds.translate(-alignmentOffset(bounds, object->alignment(map())));
const qreal rad = std::min(std::abs(bounds.height()),
std::abs(bounds.width())) / 2;
path.addRoundedRect(bounds, rad, rad, Qt::SizeMode::AbsoluteSize);
path = transform().map(path);
break;
}
case MapObject::Ellipse: {
QRectF bounds = object->bounds();
bounds.translate(-alignmentOffset(bounds, object->alignment(map())));
Expand Down Expand Up @@ -166,7 +175,8 @@ QPainterPath IsometricRenderer::interactionShape(const MapObject *object) const
} else {
switch (object->shape()) {
case MapObject::Rectangle:
case MapObject::Ellipse: {
case MapObject::Ellipse:
case MapObject::Capsule: {
QRectF bounds = object->bounds();
bounds.translate(-alignmentOffset(bounds, object->alignment(map())));

Expand Down Expand Up @@ -404,18 +414,19 @@ void IsometricRenderer::drawMapObject(QPainter *painter,
// TODO: Do something sensible to make null-sized objects usable

switch (object->shape()) {
case MapObject::Ellipse: {
case MapObject::Ellipse:
case MapObject::Capsule: {
const QPolygonF rect = pixelRectToScreenPolygon(bounds);
const QPainterPath ellipse = shape(object);
const QPainterPath path = shape(object);

painter->drawPath(ellipse.translated(shadowOffset));
painter->drawPath(path.translated(shadowOffset));
painter->drawPolygon(rect.translated(shadowOffset));

painter->setPen(colorPen);
painter->drawPolygon(rect);

painter->setBrush(brush);
painter->drawPath(ellipse);
painter->drawPath(path);
break;
}
case MapObject::Point:
Expand Down
5 changes: 3 additions & 2 deletions src/libtiled/mapobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ QRectF MapObject::screenBounds(const MapRenderer &renderer) const
} else {
switch (mShape) {
case MapObject::Ellipse:
case MapObject::Capsule:
case MapObject::Rectangle: {
QRectF bounds(this->bounds());
align(bounds, alignment(renderer.map()));
Expand Down Expand Up @@ -491,8 +492,8 @@ void MapObject::flipInScreenCoordinates(FlipDirection direction, const QPointF &
}

/**
* Rectangles, ellipses and polygons are flipped in "pixel space", the
* coordinate space before isometric projection.
* Rectangles, ellipses, capsules and polygons are flipped in "pixel space",
* the coordinate space before isometric projection.
*
* NOTE: No attempt to handle rotated shapes on isometric maps is made. The
* expectation is that due to their weird handling, nobody really does that
Expand Down
1 change: 1 addition & 0 deletions src/libtiled/mapobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class TILEDSHARED_EXPORT MapObject : public Object
Polygon,
Polyline,
Ellipse,
Capsule,
Text,
Point,
};
Expand Down
4 changes: 4 additions & 0 deletions src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,10 @@ std::unique_ptr<MapObject> MapReaderPrivate::readObject()
xml.skipCurrentElement();
object->setShape(MapObject::Ellipse);
object->setPropertyChanged(MapObject::ShapeProperty);
} else if (xml.name() == QLatin1String("capsule")) {
xml.skipCurrentElement();
object->setShape(MapObject::Capsule);
object->setPropertyChanged(MapObject::ShapeProperty);
} else if (xml.name() == QLatin1String("text")) {
object->setTextData(readObjectText());
object->setShape(MapObject::Text);
Expand Down
4 changes: 4 additions & 0 deletions src/libtiled/maptovariantconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,10 @@ QVariant MapToVariantConverter::toVariant(const MapObject &object) const
if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty))
objectVariant[QStringLiteral("ellipse")] = true;
break;
case MapObject::Capsule:
if (notTemplateInstance || object.propertyChanged(MapObject::ShapeProperty))
objectVariant[QStringLiteral("capsule")] = true;
break;
case MapObject::Text:
if (notTemplateInstance || (object.propertyChanged(MapObject::TextProperty) ||
object.propertyChanged(MapObject::TextFontProperty) ||
Expand Down
4 changes: 4 additions & 0 deletions src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,10 @@ void MapWriterPrivate::writeObject(QXmlStreamWriter &w,
if (shouldWrite(true, isTemplateInstance, mapObject.propertyChanged(MapObject::ShapeProperty)))
w.writeEmptyElement(QLatin1String("ellipse"));
break;
case MapObject::Capsule:
if (shouldWrite(true, isTemplateInstance, mapObject.propertyChanged(MapObject::ShapeProperty)))
w.writeEmptyElement(QLatin1String("capsule"));
break;
case MapObject::Text: {
if (shouldWrite(true, isTemplateInstance,
mapObject.propertyChanged(MapObject::TextProperty) ||
Expand Down
30 changes: 29 additions & 1 deletion src/libtiled/orthogonalrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ QRectF OrthogonalRenderer::boundingRect(const MapObject *object) const

switch (object->shape()) {
case MapObject::Ellipse:
case MapObject::Capsule:
case MapObject::Rectangle:
if (bounds.isNull()) {
boundingRect = bounds.adjusted(-10 - extraSpace,
Expand Down Expand Up @@ -172,6 +173,15 @@ QPainterPath OrthogonalRenderer::shape(const MapObject *object) const
path.addEllipse(bounds);
break;
}
case MapObject::Capsule: {
const qreal rad = std::min(std::abs(bounds.height()),
std::abs(bounds.width())) / 2;
if (rad == 0.)
path.addEllipse(bounds.topLeft(), 10, 10);
else
path.addRoundedRect(bounds, rad, rad, Qt::SizeMode::AbsoluteSize);
break;
}
case MapObject::Point:
path = pointShape(object->position());
break;
Expand Down Expand Up @@ -203,6 +213,7 @@ QPainterPath OrthogonalRenderer::interactionShape(const MapObject *object) const
case MapObject::Rectangle:
case MapObject::Polygon:
case MapObject::Ellipse:
case MapObject::Capsule:
case MapObject::Text:
path = shape(object);
break;
Expand Down Expand Up @@ -394,7 +405,7 @@ void OrthogonalRenderer::drawMapObject(QPainter *painter,
// CoreGraphics when drawing the path requested by the
// QCoreGraphicsPaintEngine. Draw them as rectangle instead.
MapObject::Shape shape = object->shape();
if (shape == MapObject::Ellipse &&
if ((shape == MapObject::Ellipse || shape == MapObject::Capsule) &&
((bounds.width() == qreal(0)) ^ (bounds.height() == qreal(0)))) {
shape = MapObject::Rectangle;
}
Expand Down Expand Up @@ -458,6 +469,23 @@ void OrthogonalRenderer::drawMapObject(QPainter *painter,
break;
}

case MapObject::Capsule: {
if (bounds.isNull())
bounds = QRectF(QPointF(-10, -10), QSizeF(20, 20));

const qreal rad = std::min(std::abs(bounds.height()),
std::abs(bounds.width())) / 2;

// Draw the shadow
painter->setPen(shadowPen);
painter->drawRoundedRect(bounds.translated(shadowOffset), rad, rad);

painter->setPen(linePen);
painter->setBrush(fillBrush);
painter->drawRoundedRect(bounds, rad, rad);
break;
}

case MapObject::Text: {
const auto& textData = object->textData();
painter->setFont(textData.font);
Expand Down
5 changes: 5 additions & 0 deletions src/libtiled/varianttomapconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ std::unique_ptr<MapObject> VariantToMapConverter::toMapObject(const QVariantMap
const QVariant polylineVariant = variantMap[QStringLiteral("polyline")];
const QVariant polygonVariant = variantMap[QStringLiteral("polygon")];
const QVariant ellipseVariant = variantMap[QStringLiteral("ellipse")];
const QVariant capsuleVariant = variantMap[QStringLiteral("capsule")];
const QVariant pointVariant = variantMap[QStringLiteral("point")];
const QVariant textVariant = variantMap[QStringLiteral("text")];

Expand All @@ -823,6 +824,10 @@ std::unique_ptr<MapObject> VariantToMapConverter::toMapObject(const QVariantMap
object->setShape(MapObject::Ellipse);
object->setPropertyChanged(MapObject::ShapeProperty);
}
if (capsuleVariant.toBool()) {
object->setShape(MapObject::Capsule);
object->setPropertyChanged(MapObject::ShapeProperty);
}
if (pointVariant.toBool()) {
object->setShape(MapObject::Point);
object->setPropertyChanged(MapObject::ShapeProperty);
Expand Down
Loading
Loading