Allow to filter external tile map collision per layer (#6998)

This commit is contained in:
D8H
2024-10-02 10:14:52 +02:00
committed by GitHub
parent fe743bbe57
commit f6cb203029
8 changed files with 158 additions and 59 deletions

View File

@@ -1081,6 +1081,14 @@ const defineCollisionMask = function (extension, _, gd) {
objectContent.collisionMaskTag = newValue;
return true;
}
if (propertyName === 'layerIndex') {
objectContent.layerIndex = parseFloat(newValue);
return true;
}
if (propertyName === 'useAllLayers') {
objectContent.useAllLayers = newValue === '1';
return true;
}
if (propertyName === 'debugMode') {
objectContent.debugMode = newValue === '1';
return true;
@@ -1149,6 +1157,28 @@ const defineCollisionMask = function (extension, _, gd) {
)
)
);
objectProperties.set(
'layerIndex',
new gd.PropertyDescriptor((objectContent.layerIndex || 1).toString())
.setType('number')
.setLabel(_('Layer index'))
.setGroup(_('Layers'))
.setAdvanced(true)
);
objectProperties.set(
'useAllLayers',
new gd.PropertyDescriptor(
objectContent.useAllLayers ||
objectContent.useAllLayers === undefined ||
objectContent.useAllLayers === null
? 'true'
: 'false'
)
.setType('boolean')
.setLabel(_('Use all layers'))
.setGroup(_('Layers'))
.setAdvanced(true)
);
objectProperties.set(
'debugMode',
new gd.PropertyDescriptor(objectContent.debugMode ? 'true' : 'false')
@@ -1157,12 +1187,14 @@ const defineCollisionMask = function (extension, _, gd) {
.setDescription(
_('When activated, it displays the hitboxes in the given color.')
)
.setGroup(_('Appearance'))
);
objectProperties.set(
'outlineColor',
new gd.PropertyDescriptor(objectContent.outlineColor)
.setType('color')
.setLabel(_('Outline color'))
.setGroup(_('Appearance'))
);
objectProperties.set(
'outlineOpacity',
@@ -1173,6 +1205,7 @@ const defineCollisionMask = function (extension, _, gd) {
)
.setType('number')
.setLabel(_('Outline opacity (0-255)'))
.setGroup(_('Appearance'))
);
objectProperties.set(
'outlineSize',
@@ -1183,12 +1216,14 @@ const defineCollisionMask = function (extension, _, gd) {
)
.setType('number')
.setLabel(_('Outline size (in pixels)'))
.setGroup(_('Appearance'))
);
objectProperties.set(
'fillColor',
new gd.PropertyDescriptor(objectContent.fillColor)
.setType('color')
.setLabel(_('Fill color'))
.setGroup(_('Appearance'))
);
objectProperties.set(
'fillOpacity',
@@ -1199,6 +1234,7 @@ const defineCollisionMask = function (extension, _, gd) {
)
.setType('number')
.setLabel(_('Fill opacity (0-255)'))
.setGroup(_('Appearance'))
);
return objectProperties;
@@ -1207,6 +1243,8 @@ const defineCollisionMask = function (extension, _, gd) {
tilemapJsonFile: '',
tilesetJsonFile: '',
collisionMaskTag: '',
layerIndex: 1,
useAllLayers: true,
debugMode: false,
fillColor: '255;255;255',
outlineColor: '255;255;255',
@@ -2381,6 +2419,7 @@ module.exports = {
_tilemapJsonFile = '';
_tilesetJsonFile = '';
_collisionMaskTag = '';
_layerIndex = null;
_outlineColor = 0xffffff;
_fillColor = 0xffffff;
_outlineOpacity = 0;
@@ -2481,6 +2520,7 @@ module.exports = {
const tilemapJsonFile = this._tilemapJsonFile;
const tilesetJsonFile = this._tilesetJsonFile;
const collisionMaskTag = this._collisionMaskTag;
const layerIndex = this._layerIndex;
const outlineColor = this._outlineColor;
const fillColor = this._fillColor;
const outlineOpacity = this._outlineOpacity;
@@ -2509,6 +2549,7 @@ module.exports = {
this._pixiObject,
tileMap,
collisionMaskTag,
layerIndex,
outlineSize,
outlineColor,
outlineOpacity,
@@ -2566,6 +2607,8 @@ module.exports = {
const tilemapJsonFile = object.content.tilemapJsonFile;
const tilesetJsonFile = object.content.tilesetJsonFile;
const collisionMaskTag = object.content.collisionMaskTag;
const useAllLayers = object.content.useAllLayers;
const layerIndex = useAllLayers ? null : object.content.layerIndex;
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
object.content.outlineColor
);
@@ -2580,6 +2623,7 @@ module.exports = {
tilemapJsonFile !== this._tilemapJsonFile ||
tilesetJsonFile !== this._tilesetJsonFile ||
collisionMaskTag !== this._collisionMaskTag ||
layerIndex !== this._layerIndex ||
outlineColor !== this._outlineColor ||
fillColor !== this._fillColor ||
outlineOpacity !== this._outlineOpacity ||
@@ -2589,6 +2633,7 @@ module.exports = {
this._tilemapJsonFile = tilemapJsonFile;
this._tilesetJsonFile = tilesetJsonFile;
this._collisionMaskTag = collisionMaskTag;
this._layerIndex = layerIndex;
this._outlineColor = outlineColor;
this._fillColor = fillColor;
this._outlineOpacity = outlineOpacity;

View File

@@ -12,6 +12,7 @@ namespace gdjs {
*/
private _source: TileMapHelper.EditableTileMap;
tag: string;
private _layerIndex: integer | null;
private _layers: Map<integer, TransformedCollisionTileMapLayer>;
// TODO Tiled allows to offset the layers
/**
@@ -37,9 +38,14 @@ namespace gdjs {
/**
* @param source The model that describes the tile map.
*/
constructor(source: TileMapHelper.EditableTileMap, tag: string) {
constructor(
source: TileMapHelper.EditableTileMap,
tag: string,
layerIndex: number | null = null
) {
this._source = source;
this.tag = tag;
this._layerIndex = layerIndex;
this._layers = new Map<integer, TransformedCollisionTileMapLayer>();
this._buildLayersFromTileMap(source, this._layers);
}
@@ -55,17 +61,28 @@ namespace gdjs {
tileMap: TileMapHelper.EditableTileMap,
layers: Map<integer, TransformedCollisionTileMapLayer>
) {
for (const sourceLayer of tileMap.getLayers()) {
// TODO A visitor could be used to avoid a cast.
if (!(sourceLayer instanceof TileMapHelper.EditableTileMapLayer)) {
// TODO Collision mask for object layers is not handled.
continue;
if (this._layerIndex) {
const tileLayer = tileMap.getTileLayer(this._layerIndex);
if (!tileLayer) {
return;
}
const tileLayer = sourceLayer as TileMapHelper.EditableTileMapLayer;
layers.set(
tileLayer.id,
new TransformedCollisionTileMapLayer(this, tileLayer)
);
} else {
for (const sourceLayer of tileMap.getLayers()) {
// TODO A visitor could be used to avoid a cast.
if (!(sourceLayer instanceof TileMapHelper.EditableTileMapLayer)) {
// TODO Collision mask for object layers is not handled.
continue;
}
const tileLayer = sourceLayer as TileMapHelper.EditableTileMapLayer;
layers.set(
tileLayer.id,
new TransformedCollisionTileMapLayer(this, tileLayer)
);
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -60,6 +60,7 @@ export declare namespace PixiTileMapHelper {
pixiGraphics: PIXI.Graphics,
tileMap: EditableTileMap,
typeFilter: string,
layerIndex: integer | null,
outlineSize: integer,
outlineColor: integer,
outlineOpacity: float,

View File

@@ -1 +1 @@
{"version":3,"file":"TileMapPixiHelper.d.ts","sourceRoot":"","sources":["../../src/render/TileMapPixiHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAEL,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,yBAAiB,iBAAiB,CAAC;IACjC;;;;;;;;OAQG;IACH,SAAgB,UAAU,CACxB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EACpD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GACnE,gBAAgB,GAAG,IAAI,CAuBzB;IAED;;;;;;;;OAQG;IACH,SAAgB,uBAAuB,CACrC,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EAC7C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAqBlB;IAED;;;;;;;;;;;;OAYG;IACH,SAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,GAAG,EACvB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,EACxC,UAAU,EAAE,MAAM,GACjB,IAAI,CA0GN;IAED;;OAEG;IACH,SAAgB,uBAAuB,CACrC,YAAY,EAAE,IAAI,CAAC,QAAQ,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,KAAK,EACrB,SAAS,EAAE,OAAO,EAClB,WAAW,EAAE,KAAK,GACjB,IAAI,CAgEN;CACF"}
{"version":3,"file":"TileMapPixiHelper.d.ts","sourceRoot":"","sources":["../../src/render/TileMapPixiHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAEL,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,yBAAiB,iBAAiB,CAAC;IACjC;;;;;;;;OAQG;IACH,SAAgB,UAAU,CACxB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EACpD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GACnE,gBAAgB,GAAG,IAAI,CAuBzB;IAED;;;;;;;;OAQG;IACH,SAAgB,uBAAuB,CACrC,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EAC7C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAqBlB;IAED;;;;;;;;;;;;OAYG;IACH,SAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,GAAG,EACvB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,EACxC,UAAU,EAAE,MAAM,GACjB,IAAI,CA0GN;IAED;;OAEG;IACH,SAAgB,uBAAuB,CACrC,YAAY,EAAE,IAAI,CAAC,QAAQ,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,OAAO,GAAG,IAAI,EAC1B,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,KAAK,EACrB,SAAS,EAAE,OAAO,EAClB,WAAW,EAAE,KAAK,GACjB,IAAI,CA6BN;CA8DF"}

View File

@@ -52,6 +52,7 @@ namespace gdjs {
* For instance, platforms, jumpthru, ladder, spike, water...
*/
private _collisionMaskTag: string;
private _layerIndex: integer | null;
private _tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
/**
@@ -80,6 +81,11 @@ namespace gdjs {
this._tilemapJsonFile = objectData.content.tilemapJsonFile;
this._tilesetJsonFile = objectData.content.tilesetJsonFile;
this._collisionMaskTag = objectData.content.collisionMaskTag;
this._layerIndex = objectData.content.useAllLayers
? null
: Number.isFinite(objectData.content.layerIndex)
? objectData.content.layerIndex
: 1;
this._debugMode = objectData.content.debugMode;
this._fillColor = gdjs.rgbOrHexStringToNumber(
objectData.content.fillColor
@@ -108,7 +114,8 @@ namespace gdjs {
);
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
editableTileMap,
this._collisionMaskTag
this._collisionMaskTag,
this._layerIndex
);
this._renderer = new gdjs.TileMap.TileMapCollisionMaskRenderer(
@@ -178,6 +185,7 @@ namespace gdjs {
if (oldObjectData.outlineSize !== newObjectData.outlineSize) {
this.setOutlineSize(newObjectData.outlineSize);
}
// TODO Handle changes to collisionMaskTag, useAllLayers and layerIndex.
return true;
}
@@ -254,7 +262,8 @@ namespace gdjs {
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
tileMap,
this._collisionMaskTag
this._collisionMaskTag,
this._layerIndex
);
// The tile map polygons always keep the same references.
// It works because the tilemap is never modified.

View File

@@ -221,6 +221,7 @@ export namespace PixiTileMapHelper {
pixiGraphics: PIXI.Graphics,
tileMap: EditableTileMap,
typeFilter: string,
layerIndex: integer | null,
outlineSize: integer,
outlineColor: integer,
outlineOpacity: float,
@@ -233,60 +234,86 @@ export namespace PixiTileMapHelper {
pixiGraphics.lineStyle(outlineSize, outlineColor, outlineOpacity);
pixiGraphics.drawRect(0, 0, tileMap.getWidth(), tileMap.getHeight());
for (const layer of tileMap.getLayers()) {
const tileWidth = tileMap.getTileWidth();
const tileHeight = tileMap.getTileHeight();
if (layerIndex) {
const tileMapLayer = tileMap.getTileLayer(layerIndex);
drawCollisionLayer(
pixiGraphics,
tileMapLayer,
typeFilter,
fillColor,
fillOpacity
);
} else {
for (const layer of tileMap.getLayers()) {
if (layer instanceof EditableTileMapLayer) {
drawCollisionLayer(
pixiGraphics,
layer as EditableTileMapLayer,
typeFilter,
fillColor,
fillOpacity
);
}
}
}
}
if (layer instanceof EditableTileMapLayer) {
const tileLayer = layer as EditableTileMapLayer;
function drawCollisionLayer(
pixiGraphics: PIXI.Graphics,
tileLayer: EditableTileMapLayer,
typeFilter: string,
fillColor: integer,
fillOpacity: float
): void {
const tileMap = tileLayer.tileMap;
const tileWidth = tileMap.getTileWidth();
const tileHeight = tileMap.getTileHeight();
for (let y = 0; y < tileLayer.tileMap.getDimensionY(); y++) {
for (let x = 0; x < tileLayer.tileMap.getDimensionX(); x++) {
const xPos = tileWidth * x;
const yPos = tileHeight * y;
for (let y = 0; y < tileMap.getDimensionY(); y++) {
for (let x = 0; x < tileMap.getDimensionX(); x++) {
const xPos = tileWidth * x;
const yPos = tileHeight * y;
const tileId = tileLayer.getTileId(x, y)!;
const isFlippedHorizontally = tileLayer.isFlippedHorizontally(x, y);
const isFlippedVertically = tileLayer.isFlippedVertically(x, y);
const isFlippedDiagonally = tileLayer.isFlippedDiagonally(x, y);
const tileDefinition = tileLayer.tileMap.getTileDefinition(tileId);
if (!tileDefinition) {
continue;
const tileId = tileLayer.getTileId(x, y)!;
const isFlippedHorizontally = tileLayer.isFlippedHorizontally(x, y);
const isFlippedVertically = tileLayer.isFlippedVertically(x, y);
const isFlippedDiagonally = tileLayer.isFlippedDiagonally(x, y);
const tileDefinition = tileMap.getTileDefinition(tileId);
if (!tileDefinition) {
continue;
}
const hitboxes = tileDefinition.getHitBoxes(typeFilter);
if (!hitboxes) {
continue;
}
for (const vertices of hitboxes) {
if (vertices.length === 0) continue;
pixiGraphics.beginFill(fillColor, fillOpacity);
for (let index = 0; index < vertices.length; index++) {
let vertexX = vertices[index][0];
let vertexY = vertices[index][1];
// It's important to do the diagonal flipping first,
// because the other flipping "move" the origin.
if (isFlippedDiagonally) {
const swap = vertexX;
vertexX = vertexY;
vertexY = swap;
}
const hitboxes = tileDefinition.getHitBoxes(typeFilter);
if (!hitboxes) {
continue;
if (isFlippedHorizontally) {
vertexX = tileWidth - vertexX;
}
for (const vertices of hitboxes) {
if (vertices.length === 0) continue;
pixiGraphics.beginFill(fillColor, fillOpacity);
for (let index = 0; index < vertices.length; index++) {
let vertexX = vertices[index][0];
let vertexY = vertices[index][1];
// It's important to do the diagonal flipping first,
// because the other flipping "move" the origin.
if (isFlippedDiagonally) {
const swap = vertexX;
vertexX = vertexY;
vertexY = swap;
}
if (isFlippedHorizontally) {
vertexX = tileWidth - vertexX;
}
if (isFlippedVertically) {
vertexY = tileHeight - vertexY;
}
if (index === 0) {
pixiGraphics.moveTo(xPos + vertexX, yPos + vertexY);
} else {
pixiGraphics.lineTo(xPos + vertexX, yPos + vertexY);
}
}
pixiGraphics.closePath();
pixiGraphics.endFill();
if (isFlippedVertically) {
vertexY = tileHeight - vertexY;
}
if (index === 0) {
pixiGraphics.moveTo(xPos + vertexX, yPos + vertexY);
} else {
pixiGraphics.lineTo(xPos + vertexX, yPos + vertexY);
}
}
pixiGraphics.closePath();
pixiGraphics.endFill();
}
}
}