Skip to content

Commit

Permalink
Move sprite islands around in generated tilesheetss.json
Browse files Browse the repository at this point in the history
  • Loading branch information
Jbudone committed Jun 3, 2018
1 parent 2f8e5da commit 0eb7976
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 93 deletions.
84 changes: 75 additions & 9 deletions resourceBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,15 @@ const packageRoutines = {
const generatedSheet = generatedSheets[sheet.id];
generatedSheet.exists = true;
generatedSheet.list = sheet.dependencies;
generatedSheet.sprites = sheet.sprites;
generatedSheet.dirty = sheet.dirty;
}
});

// Mark generated sheets as dirty if lists differ
_.forEach(generatedSheets, (sheet, sheetId) => {

if (sheet.currentList.length === sheet.list.length) {
if (sheet.list && sheet.currentList && (sheet.currentList.length === sheet.list.length)) {
for (let i = 0; i < sheet.currentList.length; ++i) {
const newAsset = sheet.currentList[i],
oldAsset = sheet.list.find((a) => a.assetId === newAsset.assetId);
Expand All @@ -273,6 +275,7 @@ const packageRoutines = {
const tilesheet = data.tilesheets.list.find((tilesheet) => tilesheet.id === sheetId);
tilesheet.dirty = true;
tilesheet.newDependencies = sheet.currentList;
tilesheet.sprites = sheet.sprites;
} else {
data.tilesheets.list.push({
id: sheetId,
Expand Down Expand Up @@ -374,6 +377,7 @@ const packageRoutines = {
sheet.sprites = asset.sprites;
sheet.columns = asset.columns;
sheet.rows = asset.rows;
sheet.spriteGroups = asset.spriteGroups;

delete sheet.dirty;
delete sheet.newDependencies;
Expand Down Expand Up @@ -994,13 +998,15 @@ const processGeneratedTilesheet = (package) => {
let spritesToExtract = [],
yOffset = 0,
genMaxX = 0,
genMaxY = 0;
genMaxY = 0,
spriteGroups = [];
newDependencies.forEach((dependency) => {

const source = dependency.asset.image,
columns = parseInt(dependency.asset.columns, 10),
rows = parseInt(dependency.asset.rows, 10),
tilesize = parseInt(dependency.asset.tilesize, 10);
tilesize = parseInt(dependency.asset.tilesize, 10),
spriteIslands = [];

let minY = Number.MAX_SAFE_INTEGER,
minX = Number.MAX_SAFE_INTEGER,
Expand All @@ -1017,27 +1023,80 @@ const processGeneratedTilesheet = (package) => {
maxX = Math.max(minX, x);
});

debugger;

dependency.sprites.forEach((sprite) => {
let x = sprite % columns,
y = Math.floor(sprite / columns),
dstX = (x - minX) * package.tilesize,
dstY = (y - minY + yOffset) * package.tilesize;
dstX = (x - minX),
dstY = (y - minY + yOffset);

const existingSprite = oldSprites.find((s) => s.source === source && s.sprite === sprite);

if (existingSprite) {
dstX = existingSprite.dstX / package.tilesize;
dstY = existingSprite.dstY / package.tilesize;
}

spritesToExtract.push({
source,
srcX: x * tilesize,
srcY: y * tilesize,
srcW: tilesize,
srcH: tilesize,
dstX, dstY
dstX: dstX * package.tilesize,
dstY: dstY * package.tilesize
});

package.sprites.push({
source, sprite,
dstX, dstY
dstX: dstX * package.tilesize,
dstY: dstY * package.tilesize
});

// Check sprite islands to see if this sprite is touching any other sprite islands. This way we can keep
// track of groups of sprites that should stick together
let touchingSpriteIslands = [];
spriteIslands.forEach((island) => {
// Sprite touching any other sprites in this island?
for (let i = 0; i < island.length; ++i) {
const islandSprite = island[i];
if
(
_.inRange(x, islandSprite.x - 1, islandSprite.x + 2) &&
_.inRange(y, islandSprite.y - 1, islandSprite.y + 2)
)
{
touchingSpriteIslands.push(island);
break;
}
}
});

if (touchingSpriteIslands.length === 0) {
// Starting a new island w/ this sprite
spriteIslands.push([{
sprite, x, y, dstX, dstY
}]);
} else {
// Add sprite to first island, and merge the islands since they're each connected via this sprite
touchingSpriteIslands[0].push({
sprite, x, y, dstX, dstY
});

// Merge other islands to the first one, and remove the other islands
for (let i = 1; i < touchingSpriteIslands.length; ++i) {
touchingSpriteIslands[i].forEach((island) => {
touchingSpriteIslands[0] = touchingSpriteIslands[0].concat(island);
});

// Remove island from list
for (let j = 0; j < spriteIslands.length; ++j) {
if (touchingSpriteIslands[i] === spriteIslands[j]) {
spriteIslands.splice(j, 1);
break;
}
}
}
}
});

yOffset += (maxY - minY + 1);
Expand All @@ -1048,6 +1107,12 @@ const processGeneratedTilesheet = (package) => {
assetId: dependency.asset.id,
sprites: dependency.sprites
});

spriteIslands.forEach((spriteIsland) => {
spriteGroups.push({
spriteIsland
});
});
});

console.log("Generated tilesheet:");
Expand All @@ -1058,6 +1123,7 @@ const processGeneratedTilesheet = (package) => {

package.columns = genMaxX;
package.rows = genMaxY;
package.spriteGroups = spriteGroups;

// convert \( resources/sprites/tilesheet.png -crop 16x16+0+64 -repage +0+0 \) \( resources/sprites/tilesheet.png -crop 16x16+72+64 -repage +32+16 \) -background none -layers merge autogen.png
let convertCmd = "convert ",
Expand Down
3 changes: 3 additions & 0 deletions tools/toolbelt/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
<canvas id='tilesheetCanvas'>
</canvas>

<canvas id='tilesheetVirtualCanvas'>
</canvas>

<div id="tilesheetControls" class="controls">

<div id="tilesheetActionList">
Expand Down
84 changes: 75 additions & 9 deletions tools/toolbelt/js/interactionmgr.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ const InteractionMgr = (new function(){

this.canvasEl = null;

let interactables = [];
let interactables = [],
dragging = {
interactions: [],
mouseDownPos: null
};

const hoveringInteractables = [];
const hittingInteractable = (worldPt, interactable) => {
Expand Down Expand Up @@ -69,6 +73,17 @@ const InteractionMgr = (new function(){
}
}

// Draging interaction
if (dragging.interactions.length > 0) {
const draggedDist = {
x: worldPt.x - dragging.mouseDownPos.x,
y: worldPt.y - dragging.mouseDownPos.y
};
dragging.interactions.forEach((interaction) => {
interaction.onDrag(draggedDist);
});
}

// Hover In new interactions
newHitInteractions.forEach((hitInteraction) => {
hitInteraction.onHoverIn();
Expand All @@ -94,9 +109,44 @@ const InteractionMgr = (new function(){
}
});

hitInteractions.forEach((hitInteraction) => {
hitInteraction.onClick();
if (dragging.interactions.length > 0) {

// Stop dragging interactions
dragging.interactions.forEach((interaction) => {
interaction.onEndDrag();
});
dragging.interactions = [];
} else {

// Otherwise handle as a click event
hitInteractions.forEach((hitInteraction) => {
hitInteraction.onClick();
});
}
};

const onMouseDown = (evt) => {

const worldPt = mouseToCanvasCoords(evt);

// What interactables have we hit?
const hitInteractions = [];
hoveringInteractables.forEach((interactable) => {
if (hittingInteractable(worldPt, interactable)) {
hitInteractions.push(interactable);
}
});

// Dragging interactions
if (evt.ctrlKey) {
hitInteractions.forEach((hitInteraction) => {
if (hitInteraction.canDrag) {
hitInteraction.onBeginDrag();
dragging.interactions.push(hitInteraction);
}
dragging.mouseDownPos = worldPt;
});
}
};


Expand All @@ -105,6 +155,7 @@ const InteractionMgr = (new function(){

canvasEl.addEventListener('mousemove', onMouseMove);
canvasEl.addEventListener('mouseup', onMouseUp);
canvasEl.addEventListener('mousedown', onMouseDown);
};

this.unload = () => {
Expand All @@ -113,6 +164,7 @@ const InteractionMgr = (new function(){

this.canvasEl.removeEventListener('mousemove', onMouseMove);
this.canvasEl.removeEventListener('mouseup', onMouseUp);
this.canvasEl.removeEventListener('mousedown', onMouseDown);
};

let entityId = 0;
Expand All @@ -122,18 +174,32 @@ const InteractionMgr = (new function(){
x, y, w, h,
id: (++entityId),

canDrag: false,

onHoverIn: () => {},
onHoverOut: () => {},
onClick: () => {}
onClick: () => {},
onBeginDrag: () => {},
onEndDrag: () => {},
onDrag: () => {}
};

const setCallbacks = {
onHoverIn: (cb) => { interaction.onHoverIn = cb; return setCallbacks; },
onHoverOut: (cb) => { interaction.onHoverOut = cb; return setCallbacks; },
onClick: (cb) => { interaction.onClick = cb; return setCallbacks; }
const interactionFunctions = {

// Callbacks
onHoverIn: (cb) => { interaction.onHoverIn = cb; return interactionFunctions; },
onHoverOut: (cb) => { interaction.onHoverOut = cb; return interactionFunctions; },
onClick: (cb) => { interaction.onClick = cb; return interactionFunctions; },
onDrag: (cb) => { interaction.onDrag = cb; return interactionFunctions; },
onBeginDrag: (cb) => { interaction.onBeginDrag = cb; return interactionFunctions; },
onEndDrag: (cb) => { interaction.onEndDrag = cb; return interactionFunctions; },

// Functions
setCanDrag: (canDrag) => { interaction.canDrag = true; return interactionFunctions; },
move: (x, y) => { interaction.x = x; interaction.y = y; return interactionFunctions; }
};

interactables.push(interaction);
return setCallbacks;
return interactionFunctions;
};
});
79 changes: 8 additions & 71 deletions tools/toolbelt/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,82 +31,19 @@
// - Add/Remove collision/shoot-through/floating/extract
// - Save changes
// - Extract sprites
// - Autogenerated type is listed in sheets.json; has a list of dependencies, no source image, and an
// output image
// - Resourcebuilder:
// 1) If an item changes which has extractions, add extraction to to-generate list (w/ the dependency included)
// - Prepare resource: go through all assets to build dependency list for autogenerated shit;
// keep track of diff of old generated assets vs. current generated json?
// - Processing asset: if autogenerated, compare diff (or crc) of json to see if anything has
// changed; if so then re-generate asset from scratch. Need to do these last! Should build a
// dependency graph, and have something solve it to order assets accordingly
//
// a) packageRoutines[sheets].prepare: Go through assets, if any has extractions then create/add to
// extraction list. If generated asset is found, then swap around so that accumulated data in
// the extraction is placed in a "current" object. Afterwards, check generated list to see if
// anything is dirty?
//
// b) packageRoutines[sheets].read: Add generated assets last to the list
//
// c) Processing asset: if generated: check if dirty. If so, build from scratch. Delete transient
// shit (properties added to autogenerated to help w/ determining dependencies)
//
//
// 2) After building; go through to-generate list and build those next
// 3) If to-generate item doesn't exist, create it
//
//
// 4) Build image from generate list. NOTE: Either need to use processed image of dependency, OR need
// to have a preprocess on dependency asset before using the image (important for scaling/filter
// passing assets to fit correctly in the same generated image)
// - NOTE: Because of promises, we could have a queued process for our dependency image by the
// time we start on our generated sheet. So we either need to build a dependency graph to
// finish dependent assets first, OR make assets completely independent and process images
// which will be extracted.
// - Independent assets: would be nice for less complexity, slowness isn't a big deal
// since this only changes on full rebuild or dependency extraction changes. Also we may
// need different postprocessing (eg. scaling), and can easily copy postprocessing from
// dependency asset for temporary postprocessing before copy
//
// a) Gather list of dependencies
// b) For each dependency: for each sprite: copy sprite - do post processing - paste into
// temporary image. Write image into output
// c) Fix: new generated asset data: Columns/tilesize/rows/sheet_offset; data, gid? output
// d) Save changes
//
//
// 5) Compare autogenerated item (before/after) and store a translation from old sprite -> new sprite
// 6) Go through map sources and translate necessary sprites
// 7) Copy over sprite data for extractions (collisions, objects, etc.) -- OR would it be better to
// allow editing on the generated sheet, and simply update collisions/objects/etc. after
// updating image?
// 8) If auto-generated asset now has an empty list, remove the output image, throw error if maps
// - If auto-generated asset now has an empty list, remove the output image, throw error if maps
// depend on image, and remove from sheets
// 9) How to handle updating postprocessing for dependency? (eg. postprocessing to brighten image)
// - How to handle updating postprocessing for dependency? (eg. postprocessing to brighten image)
// Probably best to have post-processing per-extraction group for dependency. Just copies sheet's
// postprocessing initially, and can be edited
// 10) Easily/Automatically handle scaling? We know the tilesize for each dependency, just scale to the
// lowest or highest or something
//
//
// 11) Allow selecting/moving groups of sprites on generated tilesheet in editor; save these positions
// so that when we update the sheet again, we retain the same positions for sprites, and simply
// toss new sprites underneath everything (ready for manual intervention/moving). Need to go
// through each extraction group, find all sprites that are touching each other, and group those
// together. Store groups of sprites. Saving changes should store sprite translations and update
// maps & packages/etc.
//
// a) Go through sprites and find those that are touching each other; separate these into groups
// b) Keep track of groups of sprites (merge new groups w/ old groups on save)
// c) Generated tilesheets: extra button for moving sprites
// d) Moving sprites: Hovering sprite group show edges, highlight sprites; move sprites
// e) Moved sprites: keep track of translation of sprites (old sprites -> new sprites)
// f) On save: translate necessary things from old sprite pos -> new sprites
// Data: collisions, objects, floating, etc.
// Update maps
// Update other things??? (eg. icons, items, etc.) -- may be better to disallow this for now,
// outside of maps/objects
// - Update maps
// - Update other things??? (eg. icons, items, etc.) -- may be better to disallow this for now,
// - Fix removing sprites from dependency (remove collision/etc. too?)
// - Update objects on translating sprite groups
// - Disable extraction from generated sheet
//
// - Save changes: reload everything?
// - Add new tilesheet: Add New, show blank canvas, drag/drop to add tilesheet
// - Sheets gid
// - Data editor (buffs, npcs, etc.)
Expand Down
Loading

0 comments on commit 0eb7976

Please sign in to comment.