Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply collision to multiple tiles, autodetect tile extents #1960

Merged
merged 8 commits into from
Jul 21, 2020
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
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>