import {Container, Graphics} from 'pixi.js';
import {arrayCache} from '../utils/array.js';
import createComponentClass from '../factory.js';
const
collisionColors = {},
createCollisionColor = function (collisionType) {
let r = collisionType.charCodeAt(0) || 0,
g = collisionType.charCodeAt(1) || 0,
b = collisionType.charCodeAt(2) || 0,
min = 0,
max = 0;
min = Math.min(r, g, b);
r -= min;
g -= min;
b -= min;
max = Math.max(r, g, b, 1);
r = (0xCC * r / max) >> 0;
g = (0xCC * g / max) >> 0;
b = (0xCC * b / max) >> 0;
return (r << 8) + (g << 4) + b;
},
createShape = function ({radius, color, left, top, width, height, z, outline, points}) {
const
newShape = new Graphics();
if (radius) {
const
cx = width ? left + width / 2 : left + radius,
cy = height ? top + height / 2 : top + radius;
newShape.circle(cx, cy, radius);
} else if (points) {
newShape.poly(points);
} else if (width && height) {
newShape.rect(left, top, width, height);
}
newShape.z = z;
newShape.fill(color, 0.1);
if (outline) {
newShape.stroke({
width: outline,
color
});
}
return newShape;
},
standardizeColor = function (color) {
if (typeof color === 'string') {
return parseInt(color.replace('#', ''), 16);
} else {
return color;
}
};
export default createComponentClass(/** @lends platypus.components.RenderDebug.prototype */{
id: 'RenderDebug',
properties: {
/**
* The color to use to highlight an entity's AABB. For example, use `"#ffffff"` or `0xffffff` to set as white.
*
* @property aabbColor
* @type Number|String
* @default 0xff88ff
*/
aabbColor: 0xff88ff,
/**
* The color to use to highlight an entity's collision shape. For example, use `"#ffffff"` or `0xffffff` to set as white. Will generate a color based on the collision type if not specified.
*
* @property collisionColor
* @type Number|String
* @default 0
*/
collisionColor: 0,
/**
* The color to use to highlight the AABB for a group of entities attached to this entity. For example, use `"#ffffff"` or `0xffffff` to set as white.
*
* @property groupColor
* @type Number|String
* @default 0x00ff00
*/
groupColor: 0x00ff00,
/**
* The color to use to highlight an entity. This property is only used if there is no `CollisionBasic` component attached to the entity: this component uses the entity's `width` and `height` properties if defined. For example, use `"#ffffff"` or `0xffffff` to set as white.
*
* @property renderColor
* @type Number|String
* @default 0x0000ff
*/
renderColor: 0x0000ff,
/**
* The height of the entity.
*
* @property height
* @type Number
* @default 100
*/
width: 100,
/**
* The width of the entity.
*
* @property width
* @type Number
* @default 100
*/
height: 100,
/**
* The local offset in z-index for the rendered debug area.
*
* @property offsetZ
* @type Number
* @default 10000
*/
offsetZ: 10000
},
/**
* This component is attached to entities that will appear in the game world. It serves two purposes. First, it displays a rectangle that indicates the location of the entity. By default it uses the specified position and dimensions of the object (in grey). If the object has a collision component it will display the AABB of the collision shape (in pink). If the entity has a LogicCarrier component and is/was carrying an object, a green rectangle will be drawn showing the collision group. The RenderDebug component also allows the developer to right-click on an entity and it will print the object in the debug console.
*
* @memberof platypus.components
* @uses platypus.Component
* @constructs
* @listens platypus.Entity#camera-update
* @listens platypus.Entity#collide-off
* @listens platypus.Entity#collide-on
* @listens platypus.Entity#handle-render
* @listens platypus.Entity#load
* @listens platypus.Entity#orientation-updated
*/
initialize: function () {
this.container = new Container();
this.container.cullable = true;
this.parentContainer = this.owner.parent.worldContainer;
this.parentContainer.addChild(this.container);
this.shapes = arrayCache.setUp();
this.isOutdated = true;
this.aabbColor = standardizeColor(this.aabbColor);
this.collisionColor = this.collisionColor ? standardizeColor(this.collisionColor) : 0;
this.groupColor = standardizeColor(this.groupColor);
this.renderColor = standardizeColor(this.renderColor);
},
events: {
"load": function () {
if (!platypus.game.settings.debug) {
this.owner.removeComponent(this);
return;
}
},
"handle-render": function () {
if (this.isOutdated) {
this.updateSprites();
this.isOutdated = false;
}
if (this.owner.getCollisionGroupAABB) {
const
aabb = this.owner.getCollisionGroupAABB(),
offset = -0.5;
if (!this.groupShape) {
this.groupShape = createShape({
color: this.groupColor,
left: offset,
top: offset,
width: 1,
height: 1,
z: this.offsetZ
});
this.container.addChild(this.groupShape);
}
this.groupShape.scaleX = aabb.width;
this.groupShape.scaleY = aabb.height;
this.groupShape.x = aabb.x - this.owner.x;
this.groupShape.y = aabb.y - this.owner.y;
}
this.update();
},
"orientation-updated": function () {
this.isOutdated = true;
},
"collide-on": function () {
this.isOutdated = true;
},
"collide-off": function () {
this.isOutdated = true;
}
},
methods: {
addShape (properties) {
const
shape = createShape({
z: this.offsetZ,
...properties
});
this.shapes.push(shape);
this.container.addChild(shape);
this.offsetZ -= 0.0001;
},
update () {
const
{container, owner} = this;
if (container.zIndex !== owner.z + 0.000001) {
container.zIndex = owner.z + 0.000001;
}
container.updateTransform({
x: owner.x,
y: owner.y
});
},
updateSprites: function () {
const
{owner} = this;
for (let i = 0; i < this.shapes.length; i++) {
this.container.removeChild(this.shapes[i]);
}
this.shapes.length = 0;
if (owner.getAABB) {
for (let j = 0; j < owner.collisionTypes.length; j++) {
const
collisionType = owner.collisionTypes[j],
aabb = owner.getAABB(collisionType),
lineWidth = 2,
shapes = owner.getShapes(collisionType),
width = this.initialWidth = aabb.width,
height = this.initialHeight = aabb.height;
let collisionColor = this.collisionColor || collisionColors[collisionType];
if (!collisionColor) {
collisionColor = collisionColors[collisionType] = createCollisionColor(collisionType);
}
this.addShape({
color: this.aabbColor,
left: aabb.left - owner.x,
top: aabb.top - owner.y,
width,
height
});
for (let i = 0; i < shapes.length; i++) {
const
shape = shapes[i],
shapeAabb = shape.aABB;
this.addShape({
color: collisionColor,
left: shapeAabb.left - owner.x,
top: shapeAabb.top - owner.y,
width: shapeAabb.width,
height: shapeAabb.height,
radius: shape.type === 'circle' ? shape.radius : 0,
outline: lineWidth
});
}
}
} else {
const
{height = 1, width = 1} = this;
this.addShape({
color: this.renderColor,
left: -width / 2,
top: -height / 2,
width,
height
});
}
this.addShape({
color: 0x000000,
left: -1,
outline: 1,
top: -1,
radius: 1
});
},
destroy: function () {
for (let i = 0; i < this.shapes.length; i++) {
this.container.removeChild(this.shapes[i]);
}
arrayCache.recycle(this.shapes);
this.parentContainer.removeChild(this.container);
this.container = null;
}
}
});