Skip to content

Commit

Permalink
Tile Collision Editor: Added two convenience actions (#1960)
Browse files Browse the repository at this point in the history
* Added context menu entries to copy the selected tile collision objects to all selected tiles
* Added action to add auto-detected collision box based on tile image

Related to issue #1322.

Co-authored-by: Thorbjørn Lindeijer <[email protected]>
  • Loading branch information
robinmacharg and bjorn authored Jul 21, 2020
1 parent 10be74b commit e7fd8d7
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 0 deletions.
62 changes: 62 additions & 0 deletions src/tiled/abstractobjecttool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "actionmanager.h"
#include "addremovetileset.h"
#include "changemapobject.h"
#include "changetileobjectgroup.h"
#include "documentmanager.h"
#include "mapdocument.h"
#include "map.h"
Expand Down Expand Up @@ -253,6 +254,56 @@ void AbstractObjectTool::removeObjects()
mapDocument()->removeObjects(mapDocument()->selectedObjects());
}

/**
* Adds the selected collision shapes for the currently selected tile - the
* last selected tile - to all selected tiles.
*/
void AbstractObjectTool::applyCollisionsToSelectedTiles(bool replace)
{
auto document = DocumentManager::instance()->currentDocument();
auto tilesetDocument = qobject_cast<TilesetDocument*>(document);
if (!tilesetDocument)
return;

const auto currentTile = dynamic_cast<Tile *>(tilesetDocument->currentObject());
if (!currentTile)
return;

auto undoStack = tilesetDocument->undoStack();
undoStack->beginMacro(tr("Apply Collision Shapes"));

// The selected collision objects
const auto &selectedObjects = mapDocument()->selectedObjects();

// Add each collision object to each selected tile apart from the current one
for (Tile *tile : tilesetDocument->selectedTiles()) {
if (tile == currentTile)
continue;

std::unique_ptr<ObjectGroup> objectGroup;

// Create a new group for collision objects if none exists or when replacing
if (!tile->objectGroup() || replace)
objectGroup = std::make_unique<ObjectGroup>();
else
objectGroup.reset(tile->objectGroup()->clone());

// Copy across the selected collision shapes
auto highestOjectId = objectGroup->highestObjectId();
for (MapObject *object : selectedObjects) {
MapObject *newObject = object->clone();
newObject->setId(++highestOjectId);
objectGroup->addObject(newObject);
}

undoStack->push(new ChangeTileObjectGroup(tilesetDocument,
tile,
std::move(objectGroup)));
}

undoStack->endMacro();
}

void AbstractObjectTool::resetTileSize()
{
QList<QUndoCommand*> commands;
Expand Down Expand Up @@ -466,6 +517,17 @@ void AbstractObjectTool::showContextMenu(MapObject *clickedObject,
duplicateAction->setIcon(QIcon(QLatin1String(":/images/16/stock-duplicate-16.png")));
removeAction->setIcon(QIcon(QLatin1String(":/images/16/edit-delete.png")));

// Allow the currently selected collision shapes to be applied to all selected tiles in the tileset editor
if (auto document = DocumentManager::instance()->currentDocument()) {
if (auto tilesetDocument = qobject_cast<TilesetDocument*>(document)) {
menu.addSeparator();
auto collisionMenu = menu.addMenu(tr("Apply Collision(s) to Selected Tiles"));
collisionMenu->setEnabled(tilesetDocument->selectedTiles().count() > 1);
collisionMenu->addAction(tr("Replace Existing Objects"), this, [this] { applyCollisionsToSelectedTiles(true); });
collisionMenu->addAction(tr("Add Objects"), this, [this] { applyCollisionsToSelectedTiles(false); });
}
}

bool anyTileObjectSelected = std::any_of(selectedObjects.begin(),
selectedObjects.end(),
isTileObject);
Expand Down
1 change: 1 addition & 0 deletions src/tiled/abstractobjecttool.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class AbstractObjectTool : public AbstractTool
private:
void duplicateObjects();
void removeObjects();
void applyCollisionsToSelectedTiles(bool replace);
void resetTileSize();
void saveSelectedObject();
void detachSelectedObjects();
Expand Down
Binary file added src/tiled/images/24/detect-bounding-box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/tiled/images/48/detect-bounding-box.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions src/tiled/tilecollisiondock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
#include "zoomable.h"

#include <QActionGroup>
#include <QBitmap>
#include <QCloseEvent>
#include <QComboBox>
#include <QCoreApplication>
Expand Down Expand Up @@ -95,6 +96,14 @@ TileCollisionDock::TileCollisionDock(QWidget *parent)
CreateObjectTool *polygonObjectsTool = new CreatePolygonObjectTool(this);
CreateObjectTool *templatesTool = new CreateTemplateTool(this);

// Autodetection of tile extents
QIcon autoDetectMaskIcon(QLatin1String("://images/24/detect-bounding-box.png"));
autoDetectMaskIcon.addFile(QLatin1String("://images/48/detect-bounding-box.png"));
mActionAutoDetectMask = new QAction(this);
mActionAutoDetectMask->setEnabled(false);
mActionAutoDetectMask->setIcon(autoDetectMaskIcon);
connect(mActionAutoDetectMask, &QAction::triggered, this, &TileCollisionDock::autoDetectMask);

QToolBar *toolsToolBar = new QToolBar(this);
toolsToolBar->setObjectName(QLatin1String("TileCollisionDockToolBar"));
toolsToolBar->setMovable(false);
Expand All @@ -109,6 +118,8 @@ TileCollisionDock::TileCollisionDock(QWidget *parent)
toolsToolBar->addAction(mToolManager->registerTool(ellipseObjectsTool));
toolsToolBar->addAction(mToolManager->registerTool(polygonObjectsTool));
toolsToolBar->addAction(mToolManager->registerTool(templatesTool));
toolsToolBar->addSeparator();
toolsToolBar->addAction(mActionAutoDetectMask);

mActionDuplicateObjects = new QAction(this);
mActionDuplicateObjects->setIcon(QIcon(QLatin1String(":/images/16/stock-duplicate-16.png")));
Expand Down Expand Up @@ -232,6 +243,29 @@ TileCollisionDock::~TileCollisionDock()
setTile(nullptr);
}

/**
* Automatically detect the extents of the tile and append a simple
* rectangular collision mask.
*/
void TileCollisionDock::autoDetectMask()
{
if (!mDummyMapDocument)
return;

const QPixmap &pixmap = mTile->image();
const QRect content = pixmap.hasAlphaChannel() ? QRegion(pixmap.mask()).boundingRect()
: pixmap.rect();

// Create the rectangular collision shape
MapObject *newObject = new MapObject(QString(), QString(),
content.topLeft(),
content.size());

ObjectGroup *objectGroup = static_cast<ObjectGroup*>(mDummyMapDocument->map()->layerAt(1));
mDummyMapDocument->undoStack()->push(new AddMapObjects(mDummyMapDocument.data(), objectGroup, newObject));
mDummyMapDocument->setSelectedObjects({ newObject });
}

void TileCollisionDock::saveState()
{
preferences::objectsViewVisibility = QVariant::fromValue(mObjectsViewVisibility).toString();
Expand Down Expand Up @@ -351,6 +385,7 @@ void TileCollisionDock::setTile(Tile *tile)
auto previousDocument = mDummyMapDocument;

mMapView->setEnabled(tile);
mActionAutoDetectMask->setEnabled(tile);

if (tile) {
Map::Orientation orientation = Map::Orthogonal;
Expand Down Expand Up @@ -672,6 +707,8 @@ void TileCollisionDock::retranslateUi()
{
setWindowTitle(QCoreApplication::translate("Tiled::MainWindow", "Tile Collision Editor"));

mActionAutoDetectMask->setText(tr("Detect Bouding Box"));

mActionDuplicateObjects->setText(tr("Duplicate Objects"));
mActionRemoveObjects->setText(tr("Remove Objects"));
mActionMoveUp->setText(tr("Move Objects Up"));
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/tilecollisiondock.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public slots:
void pasteInPlace();
void paste(ClipboardManager::PasteFlags flags);
void delete_(Operation operation = Delete);
void autoDetectMask();

protected:
void changeEvent(QEvent *e) override;
Expand Down Expand Up @@ -134,6 +135,7 @@ public slots:
QAction *mObjectsViewShowRightAction;
QAction *mObjectsViewShowBottomAction;
ToolManager *mToolManager;
QAction *mActionAutoDetectMask;
QAction *mActionDuplicateObjects;
QAction *mActionRemoveObjects;
QAction *mActionMoveUp;
Expand Down
2 changes: 2 additions & 0 deletions src/tiled/tiled.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,7 @@
<file>images/scalable/software-update-available-symbolic.svg</file>
<file>images/scalable/wrap.svg</file>
<file>images/scalable/replace.svg</file>
<file>images/24/detect-bounding-box.png</file>
<file>images/48/detect-bounding-box.png</file>
</qresource>
</RCC>

0 comments on commit e7fd8d7

Please sign in to comment.