Fix map editor tile selection
This commit is contained in:
+33
-10
@@ -5,7 +5,7 @@ const state = {
|
||||
selected: null,
|
||||
view: { x: -80, y: -40, scale: 0.35 },
|
||||
gridSize: 10,
|
||||
hitFilter: "collision",
|
||||
hitFilter: "all",
|
||||
imagePreviews: new Map(),
|
||||
dragging: null,
|
||||
};
|
||||
@@ -563,8 +563,8 @@ function line(ctx, x1, y1, x2, y2) {
|
||||
|
||||
function collectObjects() {
|
||||
const objects = [
|
||||
objectForPoint("player", "玩家出生点", state.map.playerSpawn),
|
||||
objectForRect("camera", "摄像机范围", state.map.camera),
|
||||
objectForPoint("player", "玩家出生点", state.map.playerSpawn, false, "player"),
|
||||
objectForRect("camera", "摄像机范围", state.map.camera, false, null, "camera"),
|
||||
];
|
||||
state.map.tilesets.forEach((tileset, index) => objects.push(objectForTileset(`tileset:${index}`, `图块集 ${tileset.id}`, tileset, true)));
|
||||
state.map.collisions.forEach((rect, index) => objects.push(objectForRect(`collision:${index}`, `碰撞块 ${index + 1}`, rect, true, null, "collision")));
|
||||
@@ -650,10 +650,10 @@ function objectForLayerRect(id, title, item, deletable) {
|
||||
};
|
||||
}
|
||||
|
||||
function objectForPoint(id, title, point, deletable = false) {
|
||||
function objectForPoint(id, title, point, deletable = false, type = "spawn") {
|
||||
return {
|
||||
key: id,
|
||||
type: "spawn",
|
||||
type,
|
||||
title,
|
||||
meta: `${fmt(point.x)}, ${fmt(point.y)}`,
|
||||
deletable,
|
||||
@@ -701,18 +701,18 @@ function getSelectedObject() {
|
||||
function onCanvasMouseDown(event) {
|
||||
const world = screenToWorld(event.offsetX, event.offsetY);
|
||||
const selected = getSelectedObject();
|
||||
const object = hitTest(world.x, world.y);
|
||||
if (selected && selected.resize && isInsideResizeHandle(selected, world.x, world.y)) {
|
||||
state.dragging = { resize: true, last: snapPoint(world) };
|
||||
setCanvasCursor("nwse-resize");
|
||||
return;
|
||||
}
|
||||
if (selected && selected.move && isInsideBounds(selected, world.x, world.y)) {
|
||||
if (selected && selected.move && isInsideBounds(selected, world.x, world.y) && canDragSelectedInPlace(selected, object)) {
|
||||
state.dragging = { last: snapPoint(world) };
|
||||
setCanvasCursor("move");
|
||||
return;
|
||||
}
|
||||
|
||||
const object = hitTest(world.x, world.y);
|
||||
if (object) {
|
||||
state.selected = object.key;
|
||||
state.dragging = { last: snapPoint(world) };
|
||||
@@ -768,7 +768,7 @@ function onCanvasWheel(event) {
|
||||
}
|
||||
|
||||
function hitTest(x, y) {
|
||||
const objects = collectObjects().filter(isHitFilterEnabled).reverse();
|
||||
const objects = hitCandidates();
|
||||
for (const object of objects) {
|
||||
const bounds = object.bounds && object.bounds();
|
||||
if (!bounds) continue;
|
||||
@@ -777,12 +777,32 @@ function hitTest(x, y) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function hitCandidates() {
|
||||
const objects = collectObjects().filter(isHitFilterEnabled);
|
||||
if (state.hitFilter !== "all") return objects.reverse();
|
||||
const priority = { tile: 0, rect: 1, spawn: 2, collision: 3, zone: 4, player: 5, camera: 6 };
|
||||
return objects
|
||||
.map((object, index) => ({ object, index }))
|
||||
.sort((a, b) => {
|
||||
const pa = priority[a.object.type] ?? 10;
|
||||
const pb = priority[b.object.type] ?? 10;
|
||||
if (pa !== pb) return pa - pb;
|
||||
return b.index - a.index;
|
||||
})
|
||||
.map((entry) => entry.object);
|
||||
}
|
||||
|
||||
function isInsideBounds(object, x, y) {
|
||||
const bounds = object.bounds && object.bounds();
|
||||
if (!bounds) return false;
|
||||
return x >= bounds.x && x <= bounds.x + bounds.width && y >= bounds.y && y <= bounds.y + bounds.height;
|
||||
}
|
||||
|
||||
function canDragSelectedInPlace(selected, topHit) {
|
||||
if (state.hitFilter !== "all") return true;
|
||||
return topHit && sameSelection(selected.key, topHit.key);
|
||||
}
|
||||
|
||||
function isHitFilterEnabled(object) {
|
||||
if (state.hitFilter === "all") return true;
|
||||
if (state.hitFilter === "zone") return object.type === "zone";
|
||||
@@ -792,15 +812,16 @@ function isHitFilterEnabled(object) {
|
||||
function updateCanvasCursor(event) {
|
||||
const world = screenToWorld(event.offsetX, event.offsetY);
|
||||
const selected = getSelectedObject();
|
||||
const object = hitTest(world.x, world.y);
|
||||
if (selected && selected.resize && isInsideResizeHandle(selected, world.x, world.y)) {
|
||||
setCanvasCursor("nwse-resize");
|
||||
return;
|
||||
}
|
||||
if (selected && selected.move && isInsideBounds(selected, world.x, world.y)) {
|
||||
if (selected && selected.move && isInsideBounds(selected, world.x, world.y) && canDragSelectedInPlace(selected, object)) {
|
||||
setCanvasCursor("move");
|
||||
return;
|
||||
}
|
||||
if (hitTest(world.x, world.y)) {
|
||||
if (object) {
|
||||
setCanvasCursor("move");
|
||||
return;
|
||||
}
|
||||
@@ -815,6 +836,7 @@ function addCollision() {
|
||||
const point = snapPoint(viewCenter());
|
||||
state.map.collisions.push({ x: point.x, y: point.y, width: 160, height: 40 });
|
||||
state.selected = `collision:${state.map.collisions.length - 1}`;
|
||||
state.hitFilter = "collision";
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -878,6 +900,7 @@ function addTileRect() {
|
||||
const point = snapPoint(viewCenter());
|
||||
layer.tileRects.push({ tileset: state.map.tilesets[0].id, x: point.x, y: point.y, columns: 8, rows: 4, tileIndex: 0, color: { r: 0.62, g: 0.68, b: 0.73 } });
|
||||
state.selected = `tile:${layerIndex}:${layer.tileRects.length - 1}`;
|
||||
state.hitFilter = "tile";
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user