mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
2600 lines
82 KiB
JavaScript
2600 lines
82 KiB
JavaScript
//@ts-check
|
||
/// <reference path="../JsExtensionTypes.d.ts" />
|
||
/// <reference path="helper/TileMapHelper.d.ts" />
|
||
|
||
/**
|
||
* This is a declaration of an extension for GDevelop 5.
|
||
*
|
||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change
|
||
* to this extension file or to any other *.js file that you reference inside.
|
||
*
|
||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||
* and search for any errors.
|
||
*
|
||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||
*/
|
||
|
||
/**
|
||
* @param {gd.PlatformExtension} extension
|
||
* @param {(translationSource: string) => string} _
|
||
* @param {GDNamespace} gd
|
||
*/
|
||
const defineTileMap = function (extension, _, gd) {
|
||
var objectTileMap = new gd.ObjectJsImplementation();
|
||
objectTileMap.updateProperty = function (propertyName, newValue) {
|
||
const objectContent = this.content;
|
||
if (propertyName === 'tilemapJsonFile') {
|
||
objectContent.tilemapJsonFile = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'tilesetJsonFile') {
|
||
objectContent.tilesetJsonFile = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'tilemapAtlasImage') {
|
||
objectContent.tilemapAtlasImage = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'displayMode') {
|
||
objectContent.displayMode = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'layerIndex') {
|
||
objectContent.layerIndex = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'levelIndex') {
|
||
objectContent.levelIndex = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'animationSpeedScale') {
|
||
objectContent.animationSpeedScale = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'animationFps') {
|
||
objectContent.animationFps = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
objectTileMap.getProperties = function () {
|
||
var objectProperties = new gd.MapStringPropertyDescriptor();
|
||
const objectContent = this.content;
|
||
|
||
objectProperties.set(
|
||
'tilemapJsonFile',
|
||
new gd.PropertyDescriptor(objectContent.tilemapJsonFile)
|
||
.setType('resource')
|
||
.addExtraInfo('tilemap')
|
||
.addExtraInfo('json')
|
||
.setLabel(_('Tilemap file (Tiled or LDtk)'))
|
||
.setDescription(
|
||
_('This is the file that was saved or exported from Tiled or LDtk.')
|
||
)
|
||
.setGroup(_('LDtk or Tiled'))
|
||
);
|
||
objectProperties.set(
|
||
'tilesetJsonFile',
|
||
new gd.PropertyDescriptor(objectContent.tilesetJsonFile || '')
|
||
.setType('resource')
|
||
.addExtraInfo('tileset')
|
||
.addExtraInfo('json')
|
||
.setLabel(_('Tileset JSON file (optional)'))
|
||
.setDescription(
|
||
_(
|
||
"Optional: specify this if you've saved the tileset in a different file as the Tiled tilemap."
|
||
)
|
||
)
|
||
.setGroup(_('Tiled only'))
|
||
);
|
||
objectProperties.set(
|
||
'tilemapAtlasImage',
|
||
new gd.PropertyDescriptor(objectContent.tilemapAtlasImage)
|
||
.setType('resource')
|
||
.addExtraInfo('image')
|
||
.setLabel(_('Atlas image'))
|
||
.setDescription(_('The Atlas image containing the tileset.'))
|
||
.setGroup(_('Tiled only'))
|
||
);
|
||
objectProperties.set(
|
||
'displayMode',
|
||
new gd.PropertyDescriptor(objectContent.displayMode)
|
||
.setType('choice')
|
||
.addExtraInfo('visible')
|
||
.addExtraInfo('all')
|
||
.addExtraInfo('index')
|
||
.setLabel(_('Display mode'))
|
||
.setGroup(_('Appearance'))
|
||
);
|
||
objectProperties.set(
|
||
'layerIndex',
|
||
new gd.PropertyDescriptor(objectContent.layerIndex.toString())
|
||
.setType('number')
|
||
.setLabel(_('Layer index to display'))
|
||
.setDescription(
|
||
_(
|
||
'If "index" is selected as the display mode, this is the index of the layer to display.'
|
||
)
|
||
)
|
||
.setGroup(_('Appearance'))
|
||
);
|
||
objectProperties.set(
|
||
'levelIndex',
|
||
new gd.PropertyDescriptor((objectContent.levelIndex || 0).toString())
|
||
.setType('number')
|
||
.setLabel(_('Level index to display'))
|
||
.setDescription(_('Select which level to render via its index (LDtk)'))
|
||
.setGroup(_('Appearance'))
|
||
);
|
||
objectProperties.set(
|
||
'animationSpeedScale',
|
||
new gd.PropertyDescriptor(objectContent.animationSpeedScale.toString())
|
||
.setType('number')
|
||
.setLabel(_('Animation speed scale'))
|
||
.setGroup(_('Animation'))
|
||
);
|
||
objectProperties.set(
|
||
'animationFps',
|
||
new gd.PropertyDescriptor(objectContent.animationFps.toString())
|
||
.setType('number')
|
||
.setLabel(_('Animation FPS'))
|
||
.setGroup(_('Animation'))
|
||
);
|
||
|
||
return objectProperties;
|
||
};
|
||
objectTileMap.content = {
|
||
tilemapJsonFile: '',
|
||
tilesetJsonFile: '',
|
||
tilemapAtlasImage: '',
|
||
displayMode: 'visible',
|
||
layerIndex: 0,
|
||
levelIndex: 0,
|
||
animationSpeedScale: 1,
|
||
animationFps: 4,
|
||
};
|
||
|
||
objectTileMap.updateInitialInstanceProperty = function (
|
||
instance,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
return false;
|
||
};
|
||
objectTileMap.getInitialInstanceProperties = function (instance) {
|
||
const instanceProperties = new gd.MapStringPropertyDescriptor();
|
||
return instanceProperties;
|
||
};
|
||
|
||
const object = extension
|
||
.addObject(
|
||
'TileMap',
|
||
_('External Tilemap (Tiled/LDtk)'),
|
||
_(
|
||
'Displays a tiled-based map, made with the Tiled editor (https://www.mapeditor.org/) or the LDtk editor (https://ldtk.io/).'
|
||
),
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
objectTileMap
|
||
)
|
||
.setCategoryFullName(_('Advanced'))
|
||
.addDefaultBehavior('ResizableCapability::ResizableBehavior')
|
||
.addDefaultBehavior('ScalableCapability::ScalableBehavior')
|
||
.addDefaultBehavior('OpacityCapability::OpacityBehavior')
|
||
.setIncludeFile('Extensions/TileMap/tilemapruntimeobject.js')
|
||
.addIncludeFile('Extensions/TileMap/TileMapRuntimeManager.js')
|
||
.addIncludeFile('Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js')
|
||
.addIncludeFile('Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.umd.js')
|
||
.addIncludeFile('Extensions/TileMap/pako/dist/pako.min.js')
|
||
.addIncludeFile('Extensions/TileMap/helper/TileMapHelper.js');
|
||
|
||
object
|
||
.addCondition(
|
||
'TilemapJsonFile',
|
||
_('Tilemap file (Tiled or LDtk)'),
|
||
_('Check the tilemap file (Tiled or LDtk) being used.'),
|
||
_('The tilemap file of _PARAM0_ is _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.addParameter(
|
||
'tilemapResource',
|
||
_('Tilemap file (Tiled or LDtk)'),
|
||
'',
|
||
false
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTilemapJsonFile');
|
||
|
||
object
|
||
.addAction(
|
||
'SetTilemapJsonFile',
|
||
_('Tilemap file (Tiled or LDtk)'),
|
||
_(
|
||
'Set the Tiled or LDtk file containing the Tilemap data to display. This is usually the main file exported from Tiled/LDtk.'
|
||
),
|
||
_('Set the tilemap file of _PARAM0_ to _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.addParameter(
|
||
'tilemapResource',
|
||
_('Tilemap file (Tiled or LDtk)'),
|
||
'',
|
||
false
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setTilemapJsonFile');
|
||
|
||
object
|
||
.addCondition(
|
||
'TilesetJsonFile',
|
||
_('Tileset JSON file'),
|
||
_('Check the tileset JSON file being used.'),
|
||
_('The tileset JSON file of _PARAM0_ is _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.addParameter('tilesetResource', _('Tileset JSON file'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTilesetJsonFile');
|
||
|
||
object
|
||
.addAction(
|
||
'SetTilesetJsonFile',
|
||
_('Tileset JSON file'),
|
||
_(
|
||
'Set the JSON file with the tileset data (sometimes that is embedded in the Tilemap, so not needed)'
|
||
),
|
||
_('Set the tileset JSON file of _PARAM0_ to _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.addParameter('tilesetResource', _('Tileset JSON file'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setTilesetJsonFile');
|
||
|
||
object
|
||
.addCondition(
|
||
'DisplayMode',
|
||
_('Display mode'),
|
||
_('Compare the value of the display mode.'),
|
||
_('The display mode of _PARAM0_ is _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.addParameter(
|
||
'stringWithSelector',
|
||
_('Display mode'),
|
||
'["visible", "all", "index"]',
|
||
false
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isDisplayMode');
|
||
|
||
object
|
||
.addAction(
|
||
'SetDisplayMode',
|
||
_('Display mode'),
|
||
_('Set the display mode'),
|
||
_('Set the display mode of _PARAM0_ to _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.addParameter(
|
||
'stringWithSelector',
|
||
_('Display mode'),
|
||
'["visible", "all", "index"]',
|
||
false
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setDisplayMode');
|
||
|
||
object
|
||
.addCondition(
|
||
'LayerIndex',
|
||
_('Layer index'),
|
||
_('Compare the value of the layer index.'),
|
||
_('the layer index'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardRelationalOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions()
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('getLayerIndex');
|
||
|
||
object
|
||
.addAction(
|
||
'SetLayerIndex',
|
||
_('Layer index'),
|
||
_('Set the layer index of the Tilemap.'),
|
||
_('the layer index'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions()
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setLayerIndex')
|
||
.setGetter('getLayerIndex');
|
||
|
||
object
|
||
.addExpression(
|
||
'LayerIndex',
|
||
_('Layer index'),
|
||
_('Get the layer index being displayed'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('getLayerIndex');
|
||
|
||
object
|
||
.addExpressionAndCondition(
|
||
'number',
|
||
'LevelIndex',
|
||
_('Level index'),
|
||
_('the level index being displayed.'),
|
||
_('the level index'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||
.setFunctionName('getLevelIndex');
|
||
|
||
object
|
||
.addCondition(
|
||
'AnimationSpeedScale',
|
||
_('Animation speed scale'),
|
||
_('Compare the animation speed scale.'),
|
||
_('the animation speed scale'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardRelationalOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Speed scale to compare to (1 by default)')
|
||
)
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('getAnimationSpeedScale');
|
||
|
||
object
|
||
.addAction(
|
||
'SetAnimationSpeedScale',
|
||
_('Animation speed scale'),
|
||
_('Set the animation speed scale of the Tilemap.'),
|
||
_('the animation speed scale'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Speed scale (1 by default)')
|
||
)
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setAnimationSpeedScale')
|
||
.setGetter('getAnimationSpeedScale');
|
||
|
||
object
|
||
.addExpression(
|
||
'AnimationSpeedScale',
|
||
_('Animation speed scale'),
|
||
_('Get the Animation speed scale'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('getAnimationSpeedScale');
|
||
|
||
object
|
||
.addCondition(
|
||
'AnimationFps',
|
||
_('Animation speed (FPS)'),
|
||
_('Compare the animation speed.'),
|
||
_('the animation speed (FPS)'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardRelationalOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Animation speed to compare to (in frames per second)')
|
||
)
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('getAnimationFps');
|
||
|
||
object
|
||
.addAction(
|
||
'SetAnimationFps',
|
||
_('Animation speed (FPS)'),
|
||
_('Set the animation speed of the Tilemap.'),
|
||
_('the animation speed (FPS)'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Animation speed (in frames per second)')
|
||
)
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setAnimationFps')
|
||
.setGetter('getAnimationFps');
|
||
|
||
object
|
||
.addExpression(
|
||
'AnimationFps',
|
||
_('Animation speed (FPS)'),
|
||
_('Get the animation speed (in frames per second)'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('getAnimationFps');
|
||
|
||
// Deprecated
|
||
object
|
||
.addAction(
|
||
'Scale',
|
||
_('Scale'),
|
||
_('Modify the scale of the specified object.'),
|
||
_('the scale'),
|
||
_('Size'),
|
||
'res/actions/scale24_black.png',
|
||
'res/actions/scale_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Scale (1 by default)')
|
||
)
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setScale')
|
||
.setGetter('getScale');
|
||
|
||
// Deprecated
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'ScaleX',
|
||
_('Scale on X axis'),
|
||
_("the width's scale of an object"),
|
||
_("the width's scale"),
|
||
_('Size'),
|
||
'res/actions/scaleWidth24_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Scale (1 by default)')
|
||
)
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.setFunctionName('setScaleX')
|
||
.setGetter('getScaleX');
|
||
|
||
// Deprecated
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'ScaleY',
|
||
_('Scale on Y axis'),
|
||
_("the height's scale of an object"),
|
||
_("the height's scale"),
|
||
_('Size'),
|
||
'res/actions/scaleHeight24_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Scale (1 by default)')
|
||
)
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.setFunctionName('setScaleY')
|
||
.setGetter('getScaleY');
|
||
|
||
// Deprecated
|
||
object
|
||
.addAction(
|
||
'Width',
|
||
_('Width'),
|
||
_('Change the width of an object.'),
|
||
_('the width'),
|
||
_('Size'),
|
||
'res/actions/scaleWidth24_black.png',
|
||
'res/actions/scaleWidth_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions()
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setWidth');
|
||
|
||
// Deprecated
|
||
object
|
||
.addAction(
|
||
'Height',
|
||
_('Height'),
|
||
_('Change the height of an object.'),
|
||
_('the height'),
|
||
_('Size'),
|
||
'res/actions/scaleHeight24_black.png',
|
||
'res/actions/scaleHeight_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'TileMap', false)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions()
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setHeight');
|
||
};
|
||
|
||
/**
|
||
* @param {gd.PlatformExtension} extension
|
||
* @param {(translationSource: string) => string} _
|
||
* @param {GDNamespace} gd
|
||
*/
|
||
const defineSimpleTileMap = function (extension, _, gd) {
|
||
var objectSimpleTileMap = new gd.ObjectJsImplementation();
|
||
objectSimpleTileMap.updateProperty = function (propertyName, newValue) {
|
||
const objectContent = this.content;
|
||
if (propertyName === 'atlasImage') {
|
||
objectContent.atlasImage = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'columnCount') {
|
||
objectContent.columnCount = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'rowCount') {
|
||
objectContent.rowCount = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'tileSize') {
|
||
objectContent.tileSize = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'tilesWithHitBox') {
|
||
objectContent.tilesWithHitBox = newValue;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
objectSimpleTileMap.getProperties = function () {
|
||
var objectProperties = new gd.MapStringPropertyDescriptor();
|
||
const objectContent = this.content;
|
||
|
||
objectProperties.set(
|
||
'columnCount',
|
||
new gd.PropertyDescriptor(
|
||
(typeof objectContent.columnCount === 'undefined'
|
||
? 4
|
||
: objectContent.columnCount
|
||
).toString()
|
||
)
|
||
.setType('number')
|
||
.setLabel(_('Columns'))
|
||
.setDescription(_('Number of columns.'))
|
||
.setHidden(true)
|
||
);
|
||
objectProperties.set(
|
||
'rowCount',
|
||
new gd.PropertyDescriptor(
|
||
(typeof objectContent.rowCount === 'undefined'
|
||
? 4
|
||
: objectContent.rowCount
|
||
).toString()
|
||
)
|
||
.setType('number')
|
||
.setLabel(_('Rows'))
|
||
.setDescription(_('Number of rows.'))
|
||
.setHidden(true)
|
||
);
|
||
objectProperties.set(
|
||
'tileSize',
|
||
new gd.PropertyDescriptor(
|
||
(typeof objectContent.tileSize === 'undefined'
|
||
? 8
|
||
: objectContent.tileSize
|
||
).toString()
|
||
)
|
||
.setType('number')
|
||
.setLabel(_('Tile size'))
|
||
.setDescription(_('Tile size in pixels.'))
|
||
.setHidden(true) // Hidden because a full editor is needed to recompute column/row counts
|
||
);
|
||
objectProperties.set(
|
||
'tilesWithHitBox',
|
||
new gd.PropertyDescriptor(objectContent.tilesWithHitBox || '')
|
||
.setType('string')
|
||
.setLabel(_('Tile ids with hit box'))
|
||
.setDescription(
|
||
_('The list of tile ids with a hit box (separated by commas).')
|
||
)
|
||
.setHidden(true)
|
||
);
|
||
|
||
objectProperties.set(
|
||
'atlasImage',
|
||
new gd.PropertyDescriptor(objectContent.atlasImage)
|
||
.setType('resource')
|
||
.addExtraInfo('image')
|
||
.setLabel(_('Atlas image'))
|
||
.setDescription(_('The Atlas image containing the tileset.'))
|
||
.setHidden(true) // Hidden because a full editor is needed to recompute column/row counts
|
||
);
|
||
|
||
return objectProperties;
|
||
};
|
||
objectSimpleTileMap.content = {
|
||
atlasImage: '',
|
||
rowCount: 1,
|
||
columnCount: 1,
|
||
tileSize: 8,
|
||
tilesWithHitBox: '',
|
||
};
|
||
|
||
objectSimpleTileMap.updateInitialInstanceProperty = function (
|
||
instance,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
if (propertyName === 'tilemap') {
|
||
instance.setRawStringProperty('tilemap', newValue);
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
|
||
objectSimpleTileMap.getInitialInstanceProperties = function (instance) {
|
||
var instanceProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
instanceProperties
|
||
.getOrCreate('tilemap')
|
||
.setValue(instance.getRawStringProperty('tileMap'))
|
||
.setType('string')
|
||
.setLabel('Tilemap')
|
||
.setHidden(true);
|
||
|
||
return instanceProperties;
|
||
};
|
||
|
||
const object = extension
|
||
.addObject(
|
||
'SimpleTileMap',
|
||
_('Tile map'),
|
||
_('Displays a tiled-based map.'),
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
objectSimpleTileMap
|
||
)
|
||
.setCategoryFullName(_('General'))
|
||
.setOpenFullEditorLabel(_('Edit tileset and collisions'))
|
||
.addDefaultBehavior('ResizableCapability::ResizableBehavior')
|
||
.addDefaultBehavior('ScalableCapability::ScalableBehavior')
|
||
.addDefaultBehavior('OpacityCapability::OpacityBehavior')
|
||
.setIncludeFile('Extensions/TileMap/simpletilemapruntimeobject.js')
|
||
.addIncludeFile('Extensions/TileMap/TileMapRuntimeManager.js')
|
||
.addIncludeFile('Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js')
|
||
.addIncludeFile('Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.umd.js')
|
||
.addIncludeFile('Extensions/TileMap/collision/TransformedTileMap.js')
|
||
.addIncludeFile('Extensions/TileMap/pako/dist/pako.min.js')
|
||
.addIncludeFile('Extensions/TileMap/helper/TileMapHelper.js');
|
||
|
||
object
|
||
.addExpression(
|
||
'TilesetColumnCount',
|
||
_('Tileset column count'),
|
||
_('Get the number of columns in the tileset.'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.setFunctionName('getTilesetColumnCount');
|
||
|
||
object
|
||
.addExpression(
|
||
'TilesetRowCount',
|
||
_('Tileset row count'),
|
||
_('Get the number of rows in the tileset.'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.setFunctionName('getTilesetRowCount');
|
||
|
||
object
|
||
.addExpression(
|
||
'TileCenterX',
|
||
_('Scene X coordinate of tile'),
|
||
_('Get the scene X position of the center of the tile.'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.setFunctionName('getSceneXCoordinateOfTileCenter');
|
||
|
||
object
|
||
.addExpression(
|
||
'TileCenterY',
|
||
_('Scene Y coordinate of tile'),
|
||
_('Get the scene Y position of the center of the tile.'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.setFunctionName('getSceneYCoordinateOfTileCenter');
|
||
|
||
object
|
||
.addExpression(
|
||
'GridX',
|
||
_('Tile map grid column coordinate'),
|
||
_(
|
||
'Get the grid column coordinates in the tile map corresponding to the scene coordinates.'
|
||
),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.setFunctionName('getColumnIndexAtPosition');
|
||
|
||
object
|
||
.addExpression(
|
||
'GridY',
|
||
_('Tile map grid row coordinate'),
|
||
_(
|
||
'Get the grid row coordinates in the tile map corresponding to the scene coordinates.'
|
||
),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.setFunctionName('getRowIndexAtPosition');
|
||
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'TileIdAtPosition',
|
||
_('Tile (at position)'),
|
||
_('the id of the tile at the scene coordinates'),
|
||
_('the tile id in _PARAM0_ at scene coordinates _PARAM3_ ; _PARAM4_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.setFunctionName('setTileAtPosition')
|
||
.setGetter('getTileAtPosition');
|
||
|
||
object
|
||
.addAction(
|
||
'FlipTileOnYAtPosition',
|
||
_('Flip tile vertically (at position)'),
|
||
_('Flip tile vertically at scene coordinates.'),
|
||
_(
|
||
'Flip tile vertically in _PARAM0_ at scene coordinates _PARAM1_ ; _PARAM2_: _PARAM3_'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipY24.png',
|
||
'res/actions/flipY.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.addParameter('yesorno', _('Flip vertically'), '', false)
|
||
.setDefaultValue('false')
|
||
.setFunctionName('flipTileOnYAtPosition');
|
||
|
||
object
|
||
.addAction(
|
||
'FlipTileOnXAtPosition',
|
||
_('Flip tile horizontally (at position)'),
|
||
_('Flip tile horizontally at scene coordinates.'),
|
||
_(
|
||
'Flip tile horizontally in _PARAM0_ at scene coordinates _PARAM1_ ; _PARAM2_: _PARAM3_'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipX24.png',
|
||
'res/actions/flipX.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.addParameter('yesorno', _('Flip horizontally'), '', false)
|
||
.setDefaultValue('false')
|
||
.setFunctionName('flipTileOnXAtPosition');
|
||
|
||
object
|
||
.addAction(
|
||
'RemoveTileAtPosition',
|
||
_('Remove tile (at position)'),
|
||
_('Remove the tile at the scene coordinates.'),
|
||
_('Remove tile in _PARAM0_ at scene coordinates _PARAM1_ ; _PARAM2_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('removeTileAtPosition');
|
||
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'TileIdAtGrid',
|
||
_('Tile (on the grid)'),
|
||
_('the id of the tile at the grid coordinates'),
|
||
_('the tile id at grid coordinates _PARAM3_ ; _PARAM4_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.setFunctionName('setTileAtGridCoordinates')
|
||
.setGetter('getTileAtGridCoordinates');
|
||
|
||
object
|
||
.addAction(
|
||
'FlipTileOnYAtGridCoordinates',
|
||
_('Flip tile vertically (on the grid)'),
|
||
_('Flip tile vertically at grid coordinates.'),
|
||
_(
|
||
'Flip tile vertically in _PARAM0_ at grid coordinates _PARAM1_ ; _PARAM2_: _PARAM3_'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipY24.png',
|
||
'res/actions/flipY.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.addParameter('yesorno', _('Flip vertically'), '', false)
|
||
.setDefaultValue('false')
|
||
.setFunctionName('flipTileOnYAtGridCoordinates');
|
||
|
||
object
|
||
.addAction(
|
||
'FlipTileOnXAtGridCoordinates',
|
||
_('Flip tile horizontally (on the grid)'),
|
||
_('Flip tile horizontally at grid coordinates.'),
|
||
_(
|
||
'Flip tile horizontally in _PARAM0_ at grid coordinates _PARAM1_ ; _PARAM2_: _PARAM3_'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipX24.png',
|
||
'res/actions/flipX.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.addParameter('yesorno', _('Flip horizontally'), '', false)
|
||
.setDefaultValue('false')
|
||
.setFunctionName('flipTileOnXAtGridCoordinates');
|
||
|
||
object
|
||
.addAction(
|
||
'RemoveTileAtGridCoordinates',
|
||
_('Remove tile (on the grid)'),
|
||
_('Remove the tile at the grid coordinates.'),
|
||
_('Remove tile in _PARAM0_ at grid coordinates _PARAM1_ ; _PARAM2_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map.svg',
|
||
'JsPlatform/Extensions/tile_map.svg'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('removeTileAtGridCoordinates');
|
||
|
||
object
|
||
.addCondition(
|
||
'IsTileFlippedOnXAtPosition',
|
||
_('Tile flipped horizontally (at position)'),
|
||
_('Check if tile at scene coordinates is flipped horizontally.'),
|
||
_(
|
||
'The tile in _PARAM0_ at scene coordinates _PARAM1_ ; _PARAM2_ is flipped horizontally'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipX24.png',
|
||
'res/actions/flipX.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTileFlippedOnXAtPosition');
|
||
|
||
object
|
||
.addCondition(
|
||
'IsTileFlippedOnYAtPosition',
|
||
_('Tile flipped vertically (at position)'),
|
||
_('Check if tile at scene coordinates is flipped vertically.'),
|
||
_(
|
||
'The tile in _PARAM0_ at scene coordinates _PARAM1_ ; _PARAM2_ is flipped vertically'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipY24.png',
|
||
'res/actions/flipY.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Position X'), '', false)
|
||
.addParameter('number', _('Position Y'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTileFlippedOnYAtPosition');
|
||
|
||
object
|
||
.addCondition(
|
||
'IsTileFlippedOnXAtGridCoordinates',
|
||
_('Tile flipped horizontally (on the grid)'),
|
||
_('Check if tile at grid coordinates is flipped horizontally.'),
|
||
_(
|
||
'The tile in _PARAM0_ at grid coordinates _PARAM1_ ; _PARAM2_ is flipped horizontally'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipX24.png',
|
||
'res/actions/flipX.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTileFlippedOnXAtGridCoordinates');
|
||
|
||
object
|
||
.addCondition(
|
||
'IsTileFlippedOnYAtGridCoordinates',
|
||
_('Tile flipped vertically (on the grid)'),
|
||
_('Check if tile at grid coordinates is flipped vertically.'),
|
||
_(
|
||
'The tile in _PARAM0_ at grid coordinates _PARAM1_ ; _PARAM2_ is flipped vertically'
|
||
),
|
||
_('Effects'),
|
||
'res/actions/flipY24.png',
|
||
'res/actions/flipY.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.addParameter('number', _('Grid X'), '', false)
|
||
.addParameter('number', _('Grid Y'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTileFlippedOnYAtGridCoordinates');
|
||
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'GridRowCount',
|
||
_('Grid row count'),
|
||
_('the grid row count in the tile map'),
|
||
_('the grid row count'),
|
||
_('Size'),
|
||
'res/actions/scaleHeight24_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||
.setFunctionName('setGridRowCount')
|
||
.setGetter('getGridRowCount');
|
||
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'GridColumnCount',
|
||
_('Grid column count'),
|
||
_('the grid column count in the tile map'),
|
||
_('the grid column count'),
|
||
_('Size'),
|
||
'res/actions/scaleWidth24_black.png'
|
||
)
|
||
.addParameter('object', _('Tile map'), 'SimpleTileMap', false)
|
||
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
|
||
.setFunctionName('setGridColumnCount')
|
||
.setGetter('getGridColumnCount');
|
||
};
|
||
|
||
/**
|
||
* @param {gd.PlatformExtension} extension
|
||
* @param {(translationSource: string) => string} _
|
||
* @param {GDNamespace} gd
|
||
*/
|
||
const defineCollisionMask = function (extension, _, gd) {
|
||
var collisionMaskObject = new gd.ObjectJsImplementation();
|
||
collisionMaskObject.updateProperty = function (propertyName, newValue) {
|
||
const objectContent = this.content;
|
||
if (propertyName === 'tilemapJsonFile') {
|
||
objectContent.tilemapJsonFile = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'tilesetJsonFile') {
|
||
objectContent.tilesetJsonFile = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'collisionMaskTag') {
|
||
objectContent.collisionMaskTag = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'debugMode') {
|
||
objectContent.debugMode = newValue === '1';
|
||
return true;
|
||
}
|
||
if (propertyName === 'fillColor') {
|
||
objectContent.fillColor = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'outlineColor') {
|
||
objectContent.outlineColor = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'fillOpacity') {
|
||
objectContent.fillOpacity = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'outlineOpacity') {
|
||
objectContent.outlineOpacity = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'outlineSize') {
|
||
objectContent.outlineSize = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
collisionMaskObject.getProperties = function () {
|
||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||
const objectContent = this.content;
|
||
|
||
objectProperties.set(
|
||
'tilemapJsonFile',
|
||
new gd.PropertyDescriptor(objectContent.tilemapJsonFile)
|
||
.setType('resource')
|
||
.addExtraInfo('tilemap')
|
||
.addExtraInfo('json')
|
||
.setLabel(_('Tilemap JSON file'))
|
||
.setDescription(
|
||
_(
|
||
'This is the JSON file that was saved or exported from Tiled. LDtk is not supported yet for collisions.'
|
||
)
|
||
)
|
||
);
|
||
objectProperties.set(
|
||
'tilesetJsonFile',
|
||
new gd.PropertyDescriptor(objectContent.tilesetJsonFile || '')
|
||
.setType('resource')
|
||
.addExtraInfo('tileset')
|
||
.addExtraInfo('json')
|
||
.setLabel(_('Tileset JSON file (optional)'))
|
||
.setDescription(
|
||
_(
|
||
"Optional, don't specify it if you've not saved the tileset in a different file."
|
||
)
|
||
)
|
||
);
|
||
objectProperties.set(
|
||
'collisionMaskTag',
|
||
new gd.PropertyDescriptor(objectContent.collisionMaskTag)
|
||
.setType('string')
|
||
.setLabel(_('Class filter'))
|
||
.setDescription(
|
||
_(
|
||
'Only the tiles with the given class (set in Tiled 1.9+) will have hitboxes created.'
|
||
)
|
||
)
|
||
);
|
||
objectProperties.set(
|
||
'debugMode',
|
||
new gd.PropertyDescriptor(objectContent.debugMode ? 'true' : 'false')
|
||
.setType('boolean')
|
||
.setLabel(_('Debug mode'))
|
||
.setDescription(
|
||
_('When activated, it displays the hitboxes in the given color.')
|
||
)
|
||
);
|
||
objectProperties.set(
|
||
'outlineColor',
|
||
new gd.PropertyDescriptor(objectContent.outlineColor)
|
||
.setType('color')
|
||
.setLabel(_('Outline color'))
|
||
);
|
||
objectProperties.set(
|
||
'outlineOpacity',
|
||
new gd.PropertyDescriptor(
|
||
objectContent.outlineOpacity === undefined
|
||
? '64'
|
||
: objectContent.outlineOpacity.toString()
|
||
)
|
||
.setType('number')
|
||
.setLabel(_('Outline opacity (0-255)'))
|
||
);
|
||
objectProperties.set(
|
||
'outlineSize',
|
||
new gd.PropertyDescriptor(
|
||
objectContent.outlineSize === undefined
|
||
? '1'
|
||
: objectContent.outlineSize.toString()
|
||
)
|
||
.setType('number')
|
||
.setLabel(_('Outline size (in pixels)'))
|
||
);
|
||
objectProperties.set(
|
||
'fillColor',
|
||
new gd.PropertyDescriptor(objectContent.fillColor)
|
||
.setType('color')
|
||
.setLabel(_('Fill color'))
|
||
);
|
||
objectProperties.set(
|
||
'fillOpacity',
|
||
new gd.PropertyDescriptor(
|
||
objectContent.fillOpacity === undefined
|
||
? '32'
|
||
: objectContent.fillOpacity.toString()
|
||
)
|
||
.setType('number')
|
||
.setLabel(_('Fill opacity (0-255)'))
|
||
);
|
||
|
||
return objectProperties;
|
||
};
|
||
collisionMaskObject.content = {
|
||
tilemapJsonFile: '',
|
||
tilesetJsonFile: '',
|
||
collisionMaskTag: '',
|
||
debugMode: false,
|
||
fillColor: '255;255;255',
|
||
outlineColor: '255;255;255',
|
||
fillOpacity: 64,
|
||
outlineOpacity: 128,
|
||
outlineSize: 1,
|
||
};
|
||
|
||
collisionMaskObject.updateInitialInstanceProperty = function (
|
||
instance,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
return false;
|
||
};
|
||
collisionMaskObject.getInitialInstanceProperties = function (instance) {
|
||
var instanceProperties = new gd.MapStringPropertyDescriptor();
|
||
return instanceProperties;
|
||
};
|
||
|
||
const object = extension
|
||
.addObject(
|
||
'CollisionMask',
|
||
_('External Tilemap (Tiled/LDtk) collision mask'),
|
||
_('Invisible object handling collisions with parts of a tilemap.'),
|
||
'JsPlatform/Extensions/tile_map_collision_mask32.svg',
|
||
collisionMaskObject
|
||
)
|
||
.setCategoryFullName(_('Advanced'))
|
||
.addDefaultBehavior('ResizableCapability::ResizableBehavior')
|
||
.addDefaultBehavior('ScalableCapability::ScalableBehavior')
|
||
.setIncludeFile('Extensions/TileMap/tilemapcollisionmaskruntimeobject.js')
|
||
.addIncludeFile('Extensions/TileMap/TileMapRuntimeManager.js')
|
||
.addIncludeFile('Extensions/TileMap/pako/dist/pako.min.js')
|
||
.addIncludeFile('Extensions/TileMap/helper/TileMapHelper.js')
|
||
.addIncludeFile('Extensions/TileMap/collision/TransformedTileMap.js')
|
||
.addIncludeFile(
|
||
'Extensions/TileMap/collision/TileMapCollisionMaskRenderer.js'
|
||
);
|
||
|
||
object
|
||
.addScopedCondition(
|
||
'TilemapJsonFile',
|
||
_('Tilemap JSON file'),
|
||
_('Check the Tilemap JSON file being used.'),
|
||
_('The Tilemap JSON file of _PARAM0_ is _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
|
||
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.addParameter('jsonResource', _('Tilemap JSON file'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTilemapJsonFile');
|
||
|
||
object
|
||
.addScopedAction(
|
||
'SetTilemapJsonFile',
|
||
_('Tilemap JSON file'),
|
||
_(
|
||
'Set the JSON file containing the Tilemap data to display. This is usually the JSON file exported from Tiled.'
|
||
),
|
||
_('Set the Tilemap JSON file of _PARAM0_ to _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
|
||
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.addParameter('jsonResource', _('Tilemap JSON file'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setTilemapJsonFile');
|
||
|
||
object
|
||
.addScopedCondition(
|
||
'TilesetJsonFile',
|
||
_('Tileset JSON file'),
|
||
_('Check the tileset JSON file being used.'),
|
||
_('The tileset JSON file of _PARAM0_ is _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
|
||
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.addParameter('jsonResource', _('Tileset JSON file'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('isTilesetJsonFile');
|
||
|
||
object
|
||
.addScopedAction(
|
||
'SetTilesetJsonFile',
|
||
_('Tileset JSON file'),
|
||
_(
|
||
'Set the JSON file with the tileset data (sometimes that is embedded in the Tilemap, so not needed)'
|
||
),
|
||
_('Set the tileset JSON file of _PARAM0_ to _PARAM1_'),
|
||
'',
|
||
'JsPlatform/Extensions/tile_map_collision_mask24.svg',
|
||
'JsPlatform/Extensions/tile_map_collision_mask32.svg'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.addParameter('jsonResource', _('Tileset JSON file'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setTilesetJsonFile');
|
||
|
||
// Deprecated
|
||
object
|
||
.addScopedAction(
|
||
'Scale',
|
||
_('Scale'),
|
||
_('Modify the scale of the specified object.'),
|
||
_('the scale'),
|
||
_('Size'),
|
||
'res/actions/scale24_black.png',
|
||
'res/actions/scale_black.png'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Scale (1 by default)')
|
||
)
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setScale')
|
||
.setGetter('getScale');
|
||
|
||
// Deprecated
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'ScaleX',
|
||
_('Scale on X axis'),
|
||
_("the width's scale of an object"),
|
||
_("the width's scale"),
|
||
_('Size'),
|
||
'res/actions/scaleWidth24_black.png'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.useStandardParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Scale (1 by default)')
|
||
)
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.setFunctionName('setScaleX')
|
||
.setGetter('getScaleX');
|
||
|
||
// Deprecated
|
||
object
|
||
.addExpressionAndConditionAndAction(
|
||
'number',
|
||
'ScaleY',
|
||
_('Scale on Y axis'),
|
||
_("the height's scale of an object"),
|
||
_("the height's scale"),
|
||
_('Size'),
|
||
'res/actions/scaleHeight24_black.png'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.useStandardParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions().setDescription(
|
||
_('Scale (1 by default)')
|
||
)
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.setFunctionName('setScaleY')
|
||
.setGetter('getScaleY');
|
||
|
||
// Deprecated
|
||
object
|
||
.addScopedAction(
|
||
'Width',
|
||
_('Width'),
|
||
_('Change the width of an object.'),
|
||
_('the width'),
|
||
_('Size'),
|
||
'res/actions/scaleWidth24_black.png',
|
||
'res/actions/scaleWidth_black.png'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions()
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setWidth');
|
||
|
||
// Deprecated
|
||
object
|
||
.addScopedAction(
|
||
'Height',
|
||
_('Height'),
|
||
_('Change the height of an object.'),
|
||
_('the height'),
|
||
_('Size'),
|
||
'res/actions/scaleHeight24_black.png',
|
||
'res/actions/scaleHeight_black.png'
|
||
)
|
||
.addParameter(
|
||
'object',
|
||
_('Tile map collision mask'),
|
||
'CollisionMask',
|
||
false
|
||
)
|
||
.useStandardOperatorParameters(
|
||
'number',
|
||
gd.ParameterOptions.makeNewOptions()
|
||
)
|
||
.markAsAdvanced()
|
||
.setHidden()
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setHeight');
|
||
};
|
||
|
||
/** @type {ExtensionModule} */
|
||
module.exports = {
|
||
createExtension: function (
|
||
_ /*: (string) => string */,
|
||
gd /*: libGDevelop */
|
||
) {
|
||
const extension = new gd.PlatformExtension();
|
||
|
||
extension
|
||
.setExtensionInformation(
|
||
'TileMap',
|
||
_('Tile map'),
|
||
_(
|
||
"The Tilemap object can be used to display tile-based objects. It's a good way to create maps for RPG, strategy games or create objects by assembling tiles, useful for platformer, retro-looking games, etc..."
|
||
),
|
||
'Todor Imreorov',
|
||
'Open source (MIT License)'
|
||
)
|
||
.setCategory('Advanced')
|
||
.setExtensionHelpPath('/objects/tilemap');
|
||
|
||
extension
|
||
.addInstructionOrExpressionGroupMetadata(_('Tilemap'))
|
||
.setIcon('JsPlatform/Extensions/tile_map.svg');
|
||
|
||
defineTileMap(extension, _, gd);
|
||
defineSimpleTileMap(extension, _, gd);
|
||
defineCollisionMask(extension, _, gd);
|
||
|
||
return extension;
|
||
},
|
||
|
||
registerClearCache: function (
|
||
objectsRenderingService /*: ObjectsRenderingService */
|
||
) {
|
||
const TilemapHelper = objectsRenderingService.requireModule(
|
||
__dirname,
|
||
'helper/TileMapHelper'
|
||
);
|
||
|
||
const clearCaches = (
|
||
project /* InstanceHolder - gdProject in the editor */
|
||
) => {
|
||
/** @type {TileMapHelper.TileMapManager} */
|
||
const manager = TilemapHelper.TileMapManager.getManager(project);
|
||
manager.clearCaches();
|
||
};
|
||
|
||
objectsRenderingService.registerClearCache(clearCaches);
|
||
},
|
||
|
||
/**
|
||
* You can optionally add sanity tests that will check the basic working
|
||
* of your extension behaviors/objects by instantiating behaviors/objects
|
||
* and setting the property to a given value.
|
||
*
|
||
* If you don't have any tests, you can simply return an empty array like this:
|
||
* `runExtensionSanityTests: function(gd, extension) { return []; }`
|
||
*
|
||
* But it is recommended to create tests for the behaviors/objects properties you created
|
||
* to avoid mistakes.
|
||
*/
|
||
runExtensionSanityTests: function (gd, extension) {
|
||
return [];
|
||
},
|
||
/**
|
||
* Register editors for objects.
|
||
*
|
||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||
*/
|
||
registerEditorConfigurations: function (objectsEditorService) {
|
||
objectsEditorService.registerEditorConfiguration(
|
||
'TileMap::TileMap',
|
||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||
helpPagePath: '/objects/tilemap',
|
||
})
|
||
);
|
||
objectsEditorService.registerEditorConfiguration(
|
||
'TileMap::CollisionMask',
|
||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||
helpPagePath: '/objects/tilemap',
|
||
})
|
||
);
|
||
},
|
||
/**
|
||
* Register renderers for instance of objects on the scene editor.
|
||
*
|
||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||
*/
|
||
registerInstanceRenderers: function (objectsRenderingService) {
|
||
const RenderedInstance = objectsRenderingService.RenderedInstance;
|
||
const PIXI = objectsRenderingService.PIXI;
|
||
|
||
const Tilemap = objectsRenderingService.requireModule(
|
||
__dirname,
|
||
'pixi-tilemap/dist/pixi-tilemap.umd'
|
||
);
|
||
const TilemapHelper = objectsRenderingService.requireModule(
|
||
__dirname,
|
||
'helper/TileMapHelper'
|
||
);
|
||
// required for decoding tiled zlib compressed layer data
|
||
const pako = objectsRenderingService.requireModule(
|
||
__dirname,
|
||
'pako/dist/pako.min'
|
||
);
|
||
|
||
// When on the webapp, and using webpack, the extension does not seem to
|
||
// be able to register itself properly. So we do it manually.
|
||
// (This should be done here https://github.com/pixijs/tilemap/blob/master/src/index.ts#L43-L47)
|
||
PIXI.extensions.add({
|
||
name: 'tilemap',
|
||
type: PIXI.ExtensionType.RendererPlugin,
|
||
ref: Tilemap.TileRenderer,
|
||
});
|
||
|
||
/**
|
||
* Renderer for instances of TileMap inside the IDE.
|
||
*/
|
||
class RenderedTileMapInstance extends RenderedInstance {
|
||
constructor(
|
||
project,
|
||
instance,
|
||
associatedObjectConfiguration,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
) {
|
||
super(
|
||
project,
|
||
instance,
|
||
associatedObjectConfiguration,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
);
|
||
|
||
// This setting allows tile maps with more than 16K tiles.
|
||
Tilemap.settings.use32bitIndex = true;
|
||
|
||
this.tileMapPixiObject = new Tilemap.CompositeTilemap();
|
||
this._pixiObject = this.tileMapPixiObject;
|
||
this._editableTileMap = null;
|
||
|
||
// Implement `containsPoint` so that we can set `interactive` to true and
|
||
// the Tilemap will properly emit events when hovered/clicked.
|
||
// By default, this is not implemented in pixi-tilemap.
|
||
this._pixiObject.containsPoint = (position) => {
|
||
if (!this._pixiObject) {
|
||
// Ease debugging by throwing now rather than waiting for an exception later.
|
||
throw new Error(
|
||
'containsPoint called on a destroyed PIXI object - this object was not properly removed from the PIXI container.'
|
||
);
|
||
return;
|
||
}
|
||
|
||
// Turns the world position to the local object coordinates
|
||
const localPosition = new PIXI.Point();
|
||
this._pixiObject.worldTransform.applyInverse(position, localPosition);
|
||
|
||
return (
|
||
localPosition.x >= 0 &&
|
||
localPosition.x < this.width &&
|
||
localPosition.y >= 0 &&
|
||
localPosition.y < this.height
|
||
);
|
||
};
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
this.width = 48;
|
||
this.height = 48;
|
||
this.update();
|
||
this.updateTileMap();
|
||
}
|
||
|
||
onRemovedFromScene() {
|
||
super.onRemovedFromScene();
|
||
// Keep textures because they are shared by all tile maps.
|
||
this._pixiObject.destroy(false);
|
||
|
||
// Not strictly necessary, but helps finding wrong
|
||
// handling of this._pixiObject in its container.
|
||
this._pixiObject = null;
|
||
}
|
||
|
||
_replacePixiObject(newPixiObject) {
|
||
if (this._pixiObject !== null)
|
||
this._pixiContainer.removeChild(this._pixiObject);
|
||
this._pixiObject = newPixiObject;
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
}
|
||
|
||
_onLoadingError() {
|
||
this.errorPixiObject =
|
||
this.errorPixiObject ||
|
||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
|
||
|
||
this._replacePixiObject(this.errorPixiObject);
|
||
}
|
||
|
||
_onLoadingSuccess() {
|
||
if (this.errorPixiObject) {
|
||
this._replacePixiObject(this.tileMapPixiObject);
|
||
|
||
this.errorPixiObject = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return the path to the thumbnail of the specified object.
|
||
*/
|
||
static getThumbnail(project, resourcesLoader, objectConfiguration) {
|
||
return 'JsPlatform/Extensions/tile_map.svg';
|
||
}
|
||
|
||
/**
|
||
* This is used to reload the Tilemap
|
||
*/
|
||
updateTileMap() {
|
||
const object = gd.castObject(
|
||
this._associatedObjectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
|
||
// Get the tileset resource to use
|
||
const tilemapAtlasImage = object.content.tilemapAtlasImage;
|
||
const tilemapJsonFile = object.content.tilemapJsonFile;
|
||
const tilesetJsonFile = object.content.tilesetJsonFile;
|
||
const layerIndex = object.content.layerIndex;
|
||
const levelIndex = object.content.levelIndex;
|
||
const displayMode = object.content.displayMode;
|
||
|
||
const tilemapResource = this._project
|
||
.getResourcesManager()
|
||
.getResource(tilemapJsonFile);
|
||
|
||
let metadata = {};
|
||
try {
|
||
const tilemapMetadataAsString = tilemapResource.getMetadata();
|
||
if (tilemapMetadataAsString)
|
||
metadata = JSON.parse(tilemapMetadataAsString);
|
||
} catch (error) {
|
||
console.warn('Malformed metadata in a tilemap object:', error);
|
||
}
|
||
const mapping = metadata.embeddedResourcesMapping || {};
|
||
|
||
const atlasTexture = this._pixiResourcesLoader.getPIXITexture(
|
||
this._project,
|
||
tilemapAtlasImage
|
||
);
|
||
|
||
const loadTileMap = () => {
|
||
/** @type {TileMapHelper.TileMapManager} */
|
||
const manager = TilemapHelper.TileMapManager.getManager(
|
||
this._project
|
||
);
|
||
manager.getOrLoadTileMap(
|
||
this._loadTileMapWithCallback.bind(this),
|
||
tilemapJsonFile,
|
||
tilesetJsonFile,
|
||
levelIndex,
|
||
pako,
|
||
(tileMap) => {
|
||
if (!tileMap) {
|
||
this._onLoadingError();
|
||
// _loadTileMapWithCallback already log errors
|
||
return;
|
||
}
|
||
|
||
this._editableTileMap = tileMap;
|
||
|
||
/** @type {TileMapHelper.TileTextureCache} */
|
||
manager.getOrLoadTextureCache(
|
||
this._loadTileMapWithCallback.bind(this),
|
||
(textureName) =>
|
||
this._pixiResourcesLoader.getPIXITexture(
|
||
this._project,
|
||
mapping[textureName] || textureName
|
||
),
|
||
tilemapAtlasImage,
|
||
tilemapJsonFile,
|
||
tilesetJsonFile,
|
||
levelIndex,
|
||
(textureCache) => {
|
||
if (!textureCache) {
|
||
this._onLoadingError();
|
||
// getOrLoadTextureCache already log warns and errors.
|
||
return;
|
||
}
|
||
this._onLoadingSuccess();
|
||
if (!this._editableTileMap) return;
|
||
|
||
this.width = this._editableTileMap.getWidth();
|
||
this.height = this._editableTileMap.getHeight();
|
||
TilemapHelper.PixiTileMapHelper.updatePixiTileMap(
|
||
this.tileMapPixiObject,
|
||
this._editableTileMap,
|
||
textureCache,
|
||
displayMode,
|
||
layerIndex
|
||
);
|
||
}
|
||
);
|
||
}
|
||
);
|
||
};
|
||
|
||
if (atlasTexture.valid) {
|
||
loadTileMap();
|
||
} else {
|
||
// Wait for the atlas image to load.
|
||
atlasTexture.once('update', () => {
|
||
loadTileMap();
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor, without reloading the tilemap.
|
||
*/
|
||
updatePixiTileMap() {
|
||
const object = gd.castObject(
|
||
this._associatedObjectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
|
||
// Get the tileset resource to use
|
||
const tilemapAtlasImage = object.content.tilemapAtlasImage;
|
||
const tilemapJsonFile = object.content.tilemapJsonFile;
|
||
const tilesetJsonFile = object.content.tilesetJsonFile;
|
||
const layerIndex = object.content.layerIndex;
|
||
const levelIndex = object.content.levelIndex;
|
||
const displayMode = object.content.displayMode;
|
||
|
||
const tilemapResource = this._project
|
||
.getResourcesManager()
|
||
.getResource(tilemapJsonFile);
|
||
|
||
let metadata = {};
|
||
try {
|
||
const tilemapMetadataAsString = tilemapResource.getMetadata();
|
||
if (tilemapMetadataAsString)
|
||
metadata = JSON.parse(tilemapMetadataAsString);
|
||
} catch (error) {
|
||
console.warn('Malformed metadata in a tilemap object:', error);
|
||
}
|
||
const mapping = metadata.embeddedResourcesMapping || {};
|
||
|
||
/** @type {TileMapHelper.TileMapManager} */
|
||
const manager = TilemapHelper.TileMapManager.getManager(this._project);
|
||
|
||
/** @type {TileMapHelper.TileTextureCache} */
|
||
manager.getOrLoadTextureCache(
|
||
this._loadTileMapWithCallback.bind(this),
|
||
(textureName) =>
|
||
this._pixiResourcesLoader.getPIXITexture(
|
||
this._project,
|
||
mapping[textureName] || textureName
|
||
),
|
||
tilemapAtlasImage,
|
||
tilemapJsonFile,
|
||
tilesetJsonFile,
|
||
levelIndex,
|
||
(textureCache) => {
|
||
if (!textureCache) {
|
||
this._onLoadingError();
|
||
// getOrLoadTextureCache already log warns and errors.
|
||
return;
|
||
}
|
||
this._onLoadingSuccess();
|
||
if (!this._editableTileMap) return;
|
||
|
||
this.width = this._editableTileMap.getWidth();
|
||
this.height = this._editableTileMap.getHeight();
|
||
TilemapHelper.PixiTileMapHelper.updatePixiTileMap(
|
||
this.tileMapPixiObject,
|
||
this._editableTileMap,
|
||
textureCache,
|
||
displayMode,
|
||
layerIndex
|
||
);
|
||
}
|
||
);
|
||
}
|
||
|
||
// GDJS doesn't use Promise to avoid allocation.
|
||
_loadTileMapWithCallback(tilemapJsonFile, tilesetJsonFile, callback) {
|
||
this._loadTileMap(tilemapJsonFile, tilesetJsonFile).then(callback);
|
||
}
|
||
|
||
async _loadTileMap(tilemapJsonFile, tilesetJsonFile) {
|
||
try {
|
||
const tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
|
||
this._project,
|
||
tilemapJsonFile
|
||
);
|
||
|
||
const tileMap = TilemapHelper.TileMapManager.identify(
|
||
tileMapJsonData
|
||
);
|
||
|
||
if (tileMap.kind === 'tiled') {
|
||
const tilesetJsonData = tilesetJsonFile
|
||
? await this._pixiResourcesLoader.getResourceJsonData(
|
||
this._project,
|
||
tilesetJsonFile
|
||
)
|
||
: null;
|
||
|
||
if (tilesetJsonData) {
|
||
tileMapJsonData.tilesets = [tilesetJsonData];
|
||
}
|
||
}
|
||
|
||
return tileMap;
|
||
} catch (err) {
|
||
console.error('Unable to load a Tilemap JSON data: ', err);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor
|
||
*/
|
||
update() {
|
||
if (this._instance.hasCustomSize()) {
|
||
this._pixiObject.scale.x = this.getCustomWidth() / this.width;
|
||
this._pixiObject.scale.y = this.getCustomHeight() / this.height;
|
||
} else {
|
||
this._pixiObject.scale.x = 1;
|
||
this._pixiObject.scale.y = 1;
|
||
}
|
||
|
||
// Place the center of rotation in the center of the object. Because pivot position in Pixi
|
||
// is in the **local coordinates of the object**, we need to find back the original width
|
||
// and height of the object before scaling (then divide by 2 to find the center)
|
||
const originalWidth = this.width;
|
||
const originalHeight = this.height;
|
||
this._pixiObject.pivot.x = originalWidth / 2;
|
||
this._pixiObject.pivot.y = originalHeight / 2;
|
||
|
||
// Modifying the pivot position also has an impact on the transform. The instance (X,Y) position
|
||
// of this object refers to the top-left point, but now in Pixi, as we changed the pivot, the Pixi
|
||
// object (X,Y) position refers to the center. So we add an offset to convert from top-left to center.
|
||
this._pixiObject.x =
|
||
this._instance.getX() +
|
||
this._pixiObject.pivot.x * this._pixiObject.scale.x;
|
||
this._pixiObject.y =
|
||
this._instance.getY() +
|
||
this._pixiObject.pivot.y * this._pixiObject.scale.y;
|
||
|
||
// Rotation works as intended because we put the pivot in the center
|
||
this._pixiObject.rotation = RenderedInstance.toRad(
|
||
this._instance.getAngle()
|
||
);
|
||
|
||
// Update the opacity, if needed.
|
||
// Do not hide completely an object so it can still be manipulated
|
||
const alphaForDisplay = Math.max(
|
||
this._instance.getOpacity() / 255,
|
||
0.5
|
||
);
|
||
|
||
if (
|
||
this._editableTileMap &&
|
||
this._pixiObject.alpha !== alphaForDisplay
|
||
) {
|
||
this._pixiObject.alpha = alphaForDisplay;
|
||
for (const layer of this._editableTileMap.getLayers()) {
|
||
// Only update layers that are of type TileMapHelper.EditableTileMapLayer.
|
||
// @ts-ignore - only this type of layer has setAlpha.
|
||
if (layer.setAlpha) {
|
||
const editableLayer = /** @type {TileMapHelper.EditableTileMapLayer} */ (layer);
|
||
editableLayer.setAlpha(alphaForDisplay);
|
||
}
|
||
}
|
||
// Only update the tilemap if the alpha has changed.
|
||
this.updatePixiTileMap();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return the width of the instance, when it's not resized.
|
||
*/
|
||
getDefaultWidth() {
|
||
return this.width;
|
||
}
|
||
|
||
/**
|
||
* Return the height of the instance, when it's not resized.
|
||
*/
|
||
getDefaultHeight() {
|
||
return this.height;
|
||
}
|
||
}
|
||
|
||
objectsRenderingService.registerInstanceRenderer(
|
||
'TileMap::TileMap',
|
||
RenderedTileMapInstance
|
||
);
|
||
|
||
/**
|
||
* Renderer for instances of SimpleTileMap inside the IDE.
|
||
*/
|
||
class RenderedSimpleTileMapInstance extends RenderedInstance {
|
||
_getStartedText = 'Select this instance\nto start painting';
|
||
_noAtlasText = 'Set up an atlas image\nin the tilemap object.';
|
||
_placeholderTextPixiObject = new PIXI.Text(
|
||
'',
|
||
new PIXI.TextStyle({
|
||
fontFamily: 'Arial',
|
||
fontSize: 16,
|
||
align: 'center',
|
||
padding: 5,
|
||
})
|
||
);
|
||
_placeholderImagePixiObject = new PIXI.Sprite(
|
||
PIXI.Texture.from(
|
||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAADFBMVEUAAAAkMoYsfqH///8FP6xgAAAAAXRSTlMAQObYZgAAAAFiS0dEAxEMTPIAAAAjSURBVBjTpcYxAQAADIMwTGISlTsmoVcCQClzSmvNo2ueGnMajGpBwI5BnwAAAABJRU5ErkJggg=='
|
||
)
|
||
);
|
||
_placeholderPixiObject = new PIXI.Container();
|
||
|
||
constructor(
|
||
project,
|
||
instance,
|
||
associatedObjectConfiguration,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
) {
|
||
super(
|
||
project,
|
||
instance,
|
||
associatedObjectConfiguration,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
);
|
||
|
||
// This setting allows tile maps with more than 16K tiles.
|
||
Tilemap.settings.use32bitIndex = true;
|
||
|
||
this.tileMapPixiObject = new Tilemap.CompositeTilemap();
|
||
this._pixiObject = new PIXI.Container();
|
||
this._pixiObject.addChild(this.tileMapPixiObject);
|
||
this._editableTileMap = null;
|
||
|
||
// Implement `containsPoint` so that we can set `interactive` to true and
|
||
// the Tilemap will properly emit events when hovered/clicked.
|
||
// By default, this is not implemented in pixi-tilemap.
|
||
this._pixiObject.containsPoint = (position) => {
|
||
if (!this._pixiObject) {
|
||
// Ease debugging by throwing now rather than waiting for an exception later.
|
||
throw new Error(
|
||
'containsPoint called on a destroyed PIXI object - this object was not properly removed from the PIXI container.'
|
||
);
|
||
return;
|
||
}
|
||
|
||
// Turns the world position to the local object coordinates
|
||
const localPosition = new PIXI.Point();
|
||
if (this.tileMapPixiObject.visible) {
|
||
this.tileMapPixiObject.worldTransform.applyInverse(
|
||
position,
|
||
localPosition
|
||
);
|
||
} else {
|
||
this._placeholderImagePixiObject.worldTransform.applyInverse(
|
||
position,
|
||
localPosition
|
||
);
|
||
}
|
||
|
||
return (
|
||
localPosition.x >= 0 &&
|
||
localPosition.x < this.width &&
|
||
localPosition.y >= 0 &&
|
||
localPosition.y < this.height
|
||
);
|
||
};
|
||
this._placeholderTextPixiObject.interactive = true;
|
||
this._placeholderImagePixiObject.interactive = true;
|
||
this._placeholderTextPixiObject.anchor.x = 0.5;
|
||
this._placeholderTextPixiObject.anchor.y = 0.5;
|
||
this._placeholderTextPixiObject.y = 30;
|
||
this._placeholderImagePixiObject.y = -30;
|
||
this._placeholderImagePixiObject.x = -16;
|
||
this._placeholderPixiObject.addChild(this._placeholderTextPixiObject);
|
||
this._placeholderPixiObject.addChild(this._placeholderImagePixiObject);
|
||
this._pixiObject.addChild(this._placeholderPixiObject);
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
this.width = 48;
|
||
this.height = 48;
|
||
this._objectName = instance.getObjectName();
|
||
this.update();
|
||
this.updateTileMap();
|
||
}
|
||
|
||
onRemovedFromScene() {
|
||
super.onRemovedFromScene();
|
||
// Keep textures because they are shared by all tile maps.
|
||
this._pixiObject.destroy(false);
|
||
|
||
// Not strictly necessary, but helps finding wrong
|
||
// handling of this._pixiObject in its container.
|
||
this._pixiObject = null;
|
||
}
|
||
|
||
_replacePixiObject(newPixiObject) {
|
||
if (this._pixiObject !== null)
|
||
this._pixiContainer.removeChild(this._pixiObject);
|
||
this._pixiObject = newPixiObject;
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
}
|
||
|
||
_onLoadingError() {
|
||
this.errorPixiObject =
|
||
this.errorPixiObject ||
|
||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
|
||
|
||
this._replacePixiObject(this.errorPixiObject);
|
||
}
|
||
|
||
_onLoadingSuccess() {
|
||
if (this.errorPixiObject) {
|
||
this._replacePixiObject(this.tileMapPixiObject);
|
||
|
||
this.errorPixiObject = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return the path to the thumbnail of the specified object.
|
||
*/
|
||
static getThumbnail(project, resourcesLoader, objectConfiguration) {
|
||
const object = gd.castObject(
|
||
objectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
|
||
const atlasImageResourceName = object.content.atlasImage || '';
|
||
return resourcesLoader.getResourceFullUrl(
|
||
project,
|
||
atlasImageResourceName,
|
||
{}
|
||
);
|
||
}
|
||
|
||
getEditableTileMap() {
|
||
return this._editableTileMap;
|
||
}
|
||
|
||
/**
|
||
* This is used to reload the Tilemap
|
||
*/
|
||
updateTileMap() {
|
||
const object = gd.castObject(
|
||
this._associatedObjectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
const atlasImageResourceName = object.content.atlasImage;
|
||
if (!atlasImageResourceName) return;
|
||
|
||
const tilemapAsJSObject = JSON.parse(
|
||
this._instance.getRawStringProperty('tilemap') || '{}'
|
||
);
|
||
|
||
const tileSize = object.content.tileSize;
|
||
const columnCount = object.content.columnCount;
|
||
const rowCount = object.content.rowCount;
|
||
|
||
const atlasTexture = this._pixiResourcesLoader.getPIXITexture(
|
||
this._project,
|
||
atlasImageResourceName
|
||
);
|
||
|
||
const loadTileMap = () => {
|
||
/** @type {TileMapHelper.TileMapManager} */
|
||
const manager = TilemapHelper.TileMapManager.getManager(
|
||
this._project
|
||
);
|
||
try {
|
||
manager.getOrLoadSimpleTileMap(
|
||
tilemapAsJSObject,
|
||
this._objectName,
|
||
tileSize,
|
||
columnCount,
|
||
rowCount,
|
||
(tileMap) => {
|
||
if (!tileMap) {
|
||
this._onLoadingError();
|
||
console.error('Could not parse tilemap.');
|
||
return;
|
||
}
|
||
|
||
this._editableTileMap = tileMap;
|
||
|
||
manager.getOrLoadSimpleTileMapTextureCache(
|
||
(textureName) =>
|
||
this._pixiResourcesLoader.getPIXITexture(
|
||
this._project,
|
||
textureName
|
||
),
|
||
atlasImageResourceName,
|
||
tileSize,
|
||
columnCount,
|
||
rowCount,
|
||
(
|
||
/** @type {TileMapHelper.TileTextureCache | null} */
|
||
textureCache
|
||
) => {
|
||
this._onLoadingSuccess();
|
||
if (!this._editableTileMap) return;
|
||
|
||
this.width = this._editableTileMap.getWidth();
|
||
this.height = this._editableTileMap.getHeight();
|
||
TilemapHelper.PixiTileMapHelper.updatePixiTileMap(
|
||
this.tileMapPixiObject,
|
||
this._editableTileMap,
|
||
textureCache,
|
||
'all', // No notion of visibility on simple tile maps.
|
||
0 // Only one layer is used on simple tile maps.
|
||
);
|
||
}
|
||
);
|
||
}
|
||
);
|
||
} catch (error) {
|
||
this._onLoadingError();
|
||
console.error('Could not load tilemap:', error);
|
||
}
|
||
};
|
||
|
||
if (atlasTexture.valid) {
|
||
loadTileMap();
|
||
} else {
|
||
// Wait for the atlas image to load.
|
||
atlasTexture.once('update', () => {
|
||
loadTileMap();
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor, without reloading the tilemap.
|
||
*/
|
||
updatePixiTileMap() {
|
||
const object = gd.castObject(
|
||
this._associatedObjectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
|
||
const atlasImageResourceName = object.content.atlasImage;
|
||
|
||
const tileSize = object.content.tileSize;
|
||
const columnCount = object.content.columnCount;
|
||
const rowCount = object.content.rowCount;
|
||
/** @type {TileMapHelper.TileMapManager} */
|
||
const manager = TilemapHelper.TileMapManager.getManager(this._project);
|
||
|
||
manager.getOrLoadSimpleTileMapTextureCache(
|
||
(textureName) =>
|
||
this._pixiResourcesLoader.getPIXITexture(
|
||
this._project,
|
||
textureName
|
||
),
|
||
atlasImageResourceName,
|
||
tileSize,
|
||
columnCount,
|
||
rowCount,
|
||
(
|
||
/** @type {TileMapHelper.TileTextureCache | null} */
|
||
textureCache
|
||
) => {
|
||
this._onLoadingSuccess();
|
||
if (!this._editableTileMap) return;
|
||
|
||
this.width = this._editableTileMap.getWidth();
|
||
this.height = this._editableTileMap.getHeight();
|
||
TilemapHelper.PixiTileMapHelper.updatePixiTileMap(
|
||
this.tileMapPixiObject,
|
||
this._editableTileMap,
|
||
textureCache,
|
||
'all',
|
||
0
|
||
);
|
||
}
|
||
);
|
||
}
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor
|
||
*/
|
||
update() {
|
||
const object = gd.castObject(
|
||
this._associatedObjectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
const atlasImageResourceName = object.content.atlasImage;
|
||
|
||
const isTileMapEmpty = this._editableTileMap
|
||
? this._editableTileMap.isEmpty()
|
||
: false;
|
||
let objectToChange;
|
||
if (this.errorPixiObject) {
|
||
objectToChange = this.errorPixiObject;
|
||
} else if (isTileMapEmpty || !atlasImageResourceName) {
|
||
this.tileMapPixiObject.visible = false;
|
||
this._placeholderPixiObject.visible = true;
|
||
this._placeholderTextPixiObject.text = !atlasImageResourceName
|
||
? this._noAtlasText
|
||
: this._getStartedText;
|
||
objectToChange = this._placeholderPixiObject;
|
||
} else {
|
||
this._placeholderPixiObject.visible = false;
|
||
this.tileMapPixiObject.visible = true;
|
||
objectToChange = this.tileMapPixiObject;
|
||
}
|
||
|
||
if (!isTileMapEmpty) {
|
||
// Don't change size of placeholder object.
|
||
if (this._instance.hasCustomSize()) {
|
||
objectToChange.scale.x = this.getCustomWidth() / this.width;
|
||
objectToChange.scale.y = this.getCustomHeight() / this.height;
|
||
} else {
|
||
objectToChange.scale.x = 1;
|
||
objectToChange.scale.y = 1;
|
||
}
|
||
|
||
// Place the center of rotation in the center of the object. Because pivot position in Pixi
|
||
// is in the **local coordinates of the object**, we need to find back the original width
|
||
// and height of the object before scaling (then divide by 2 to find the center)
|
||
const originalWidth = this.width;
|
||
const originalHeight = this.height;
|
||
objectToChange.pivot.x = originalWidth / 2;
|
||
objectToChange.pivot.y = originalHeight / 2;
|
||
}
|
||
// Modifying the pivot position also has an impact on the transform. The instance (X,Y) position
|
||
// of this object refers to the top-left point, but now in Pixi, as we changed the pivot, the Pixi
|
||
// object (X,Y) position refers to the center. So we add an offset to convert from top-left to center.
|
||
objectToChange.x =
|
||
this._instance.getX() +
|
||
objectToChange.pivot.x * objectToChange.scale.x;
|
||
objectToChange.y =
|
||
this._instance.getY() +
|
||
objectToChange.pivot.y * objectToChange.scale.y;
|
||
|
||
// Rotation works as intended because we put the pivot in the center
|
||
objectToChange.rotation = RenderedInstance.toRad(
|
||
this._instance.getAngle()
|
||
);
|
||
|
||
// Update the opacity, if needed.
|
||
// Do not hide completely an object so it can still be manipulated
|
||
const alphaForDisplay = Math.max(
|
||
this._instance.getOpacity() / 255,
|
||
0.5
|
||
);
|
||
|
||
if (this._editableTileMap && objectToChange.alpha !== alphaForDisplay) {
|
||
objectToChange.alpha = alphaForDisplay;
|
||
for (const layer of this._editableTileMap.getLayers()) {
|
||
// Only update layers that are of type TileMapHelper.EditableTileMapLayer.
|
||
// @ts-ignore - only this type of layer has setAlpha.
|
||
if (layer.setAlpha) {
|
||
const editableLayer = /** @type {TileMapHelper.EditableTileMapLayer} */ (layer);
|
||
editableLayer.setAlpha(alphaForDisplay);
|
||
}
|
||
}
|
||
this.updatePixiTileMap();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return the width of the instance, when it's not resized.
|
||
*/
|
||
getDefaultWidth() {
|
||
return this.width;
|
||
}
|
||
|
||
/**
|
||
* Return the height of the instance, when it's not resized.
|
||
*/
|
||
getDefaultHeight() {
|
||
return this.height;
|
||
}
|
||
}
|
||
|
||
objectsRenderingService.registerInstanceRenderer(
|
||
'TileMap::SimpleTileMap',
|
||
RenderedSimpleTileMapInstance
|
||
);
|
||
|
||
/**
|
||
* Renderer for instances of TileMap collision mask inside the IDE.
|
||
*/
|
||
class RenderedCollisionMaskInstance extends RenderedInstance {
|
||
constructor(
|
||
project,
|
||
instance,
|
||
associatedObjectConfiguration,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
) {
|
||
super(
|
||
project,
|
||
instance,
|
||
associatedObjectConfiguration,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
);
|
||
|
||
this.tileMapPixiObject = new PIXI.Graphics();
|
||
this.tileMapPixiObject._0iAmTheTileMapPixiObject = true;
|
||
this._pixiObject = this.tileMapPixiObject;
|
||
|
||
// Implement `containsPoint` so that we can set `interactive` to true and
|
||
// the Tilemap will properly emit events when hovered/clicked.
|
||
// By default, this is not implemented in pixi-tilemap.
|
||
this._pixiObject.containsPoint = (position) => {
|
||
if (!this._pixiObject) {
|
||
// Ease debugging by throwing now rather than waiting for an exception later.
|
||
throw new Error(
|
||
'containsPoint called on a destroyed PIXI object - this object was not properly removed from the PIXI container.'
|
||
);
|
||
return;
|
||
}
|
||
|
||
// Turns the world position to the local object coordinates
|
||
const localPosition = new PIXI.Point();
|
||
this._pixiObject.worldTransform.applyInverse(position, localPosition);
|
||
|
||
// Check if the point is inside the object bounds
|
||
return (
|
||
localPosition.x >= 0 &&
|
||
localPosition.x < this.width &&
|
||
localPosition.y >= 0 &&
|
||
localPosition.y < this.height
|
||
);
|
||
};
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
this.width = 48;
|
||
this.height = 48;
|
||
this.update();
|
||
this.updateTileMap();
|
||
}
|
||
|
||
onRemovedFromScene() {
|
||
super.onRemovedFromScene();
|
||
this._pixiObject.destroy();
|
||
|
||
// Not strictly necessary, but helps finding wrong
|
||
// handling of this._pixiObject in its container.
|
||
this._pixiObject = null;
|
||
}
|
||
|
||
_replacePixiObject(newPixiObject) {
|
||
if (this._pixiObject !== null)
|
||
this._pixiContainer.removeChild(this._pixiObject);
|
||
this._pixiObject = newPixiObject;
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
}
|
||
|
||
_onLoadingError() {
|
||
this.errorPixiObject =
|
||
this.errorPixiObject ||
|
||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
|
||
|
||
this._replacePixiObject(this.errorPixiObject);
|
||
}
|
||
|
||
_onLoadingSuccess() {
|
||
if (this.errorPixiObject) {
|
||
this._replacePixiObject(this.tileMapPixiObject);
|
||
|
||
this.errorPixiObject = null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Return the path to the thumbnail of the specified object.
|
||
*/
|
||
static getThumbnail(project, resourcesLoader, objectConfiguration) {
|
||
return 'JsPlatform/Extensions/tile_map_collision_mask24.svg';
|
||
}
|
||
|
||
/**
|
||
* This is used to reload the Tilemap
|
||
*/
|
||
updateTileMap() {
|
||
const object = gd.castObject(
|
||
this._associatedObjectConfiguration,
|
||
gd.ObjectJsImplementation
|
||
);
|
||
|
||
const tilemapJsonFile = object.content.tilemapJsonFile;
|
||
const tilesetJsonFile = object.content.tilesetJsonFile;
|
||
const collisionMaskTag = object.content.collisionMaskTag;
|
||
const outlineColor = objectsRenderingService.rgbOrHexToHexNumber(
|
||
object.content.outlineColor
|
||
);
|
||
const fillColor = objectsRenderingService.rgbOrHexToHexNumber(
|
||
object.content.fillColor
|
||
);
|
||
const outlineOpacity = object.content.outlineOpacity / 255;
|
||
const fillOpacity = object.content.fillOpacity / 255;
|
||
const outlineSize = 1;
|
||
|
||
/** @type {TileMapHelper.TileMapManager} */
|
||
const manager = TilemapHelper.TileMapManager.getManager(this._project);
|
||
manager.getOrLoadTileMap(
|
||
this._loadTiledMapWithCallback.bind(this),
|
||
tilemapJsonFile,
|
||
tilesetJsonFile,
|
||
0, // levelIndex
|
||
pako,
|
||
(tileMap) => {
|
||
if (!tileMap) {
|
||
this._onLoadingError();
|
||
// _loadTiledMapWithCallback already log errors
|
||
return;
|
||
}
|
||
this._onLoadingSuccess();
|
||
|
||
this.width = tileMap.getWidth();
|
||
this.height = tileMap.getHeight();
|
||
TilemapHelper.PixiTileMapHelper.updatePixiCollisionMask(
|
||
this._pixiObject,
|
||
tileMap,
|
||
collisionMaskTag,
|
||
outlineSize,
|
||
outlineColor,
|
||
outlineOpacity,
|
||
fillColor,
|
||
fillOpacity
|
||
);
|
||
}
|
||
);
|
||
}
|
||
|
||
// GDJS doesn't use Promise to avoid allocation.
|
||
_loadTiledMapWithCallback(tilemapJsonFile, tilesetJsonFile, callback) {
|
||
this._loadTileMap(tilemapJsonFile, tilesetJsonFile).then(callback);
|
||
}
|
||
|
||
async _loadTileMap(tilemapJsonFile, tilesetJsonFile) {
|
||
try {
|
||
const tileMapJsonData = await this._pixiResourcesLoader.getResourceJsonData(
|
||
this._project,
|
||
tilemapJsonFile
|
||
);
|
||
|
||
const tileMap = TilemapHelper.TileMapManager.identify(
|
||
tileMapJsonData
|
||
);
|
||
|
||
if (tileMap.kind === 'tiled') {
|
||
const tilesetJsonData = tilesetJsonFile
|
||
? await this._pixiResourcesLoader.getResourceJsonData(
|
||
this._project,
|
||
tilesetJsonFile
|
||
)
|
||
: null;
|
||
|
||
if (tilesetJsonData) {
|
||
tileMapJsonData.tilesets = [tilesetJsonData];
|
||
}
|
||
}
|
||
return tileMap;
|
||
} catch (err) {
|
||
console.error('Unable to load a Tilemap JSON data: ', err);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor
|
||
*/
|
||
update() {
|
||
if (this._instance.hasCustomSize()) {
|
||
this._pixiObject.scale.x = this.getCustomWidth() / this.width;
|
||
this._pixiObject.scale.y = this.getCustomHeight() / this.height;
|
||
} else {
|
||
this._pixiObject.scale.x = 1;
|
||
this._pixiObject.scale.y = 1;
|
||
}
|
||
|
||
// Place the center of rotation in the center of the object. Because pivot position in Pixi
|
||
// is in the **local coordinates of the object**, we need to find back the original width
|
||
// and height of the object before scaling (then divide by 2 to find the center)
|
||
const originalWidth = this.width;
|
||
const originalHeight = this.height;
|
||
this._pixiObject.pivot.x = originalWidth / 2;
|
||
this._pixiObject.pivot.y = originalHeight / 2;
|
||
|
||
// Modifying the pivot position also has an impact on the transform. The instance (X,Y) position
|
||
// of this object refers to the top-left point, but now in Pixi, as we changed the pivot, the Pixi
|
||
// object (X,Y) position refers to the center. So we add an offset to convert from top-left to center.
|
||
this._pixiObject.x =
|
||
this._instance.getX() +
|
||
this._pixiObject.pivot.x * this._pixiObject.scale.x;
|
||
this._pixiObject.y =
|
||
this._instance.getY() +
|
||
this._pixiObject.pivot.y * this._pixiObject.scale.y;
|
||
|
||
// Rotation works as intended because we put the pivot in the center
|
||
this._pixiObject.rotation = RenderedInstance.toRad(
|
||
this._instance.getAngle()
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Return the width of the instance, when it's not resized.
|
||
*/
|
||
getDefaultWidth() {
|
||
return this.width;
|
||
}
|
||
|
||
/**
|
||
* Return the height of the instance, when it's not resized.
|
||
*/
|
||
getDefaultHeight() {
|
||
return this.height;
|
||
}
|
||
}
|
||
|
||
objectsRenderingService.registerInstanceRenderer(
|
||
'TileMap::CollisionMask',
|
||
RenderedCollisionMaskInstance
|
||
);
|
||
},
|
||
};
|