Recently, a feature request was made in Physion's Discord Server: "Add the ability to visualize the trajectory of an object". This functionality was already implemented in the old version of Physion as you can see in the video below:
Unfortunately (and for the time being) the web version of Physion doesn't natively support tracers. The goods news is that we can implement the tracer functionality using scripting.
The Tracer Script
We're going to create a NodeScript (i.e. a script that gets attached to a Node) to implement the tracer functionality.
Let's start with the constructor:
class Tracer {
constructor(node) {
this.node = node;
this.initialized = false;
this.trajectory = [];
this.maxTrajectorySize = 100;
if (typeof physion !== "undefined") {
this.graphics = physion.utils.createGraphics();
this.lineStyle = physion.utils.createLineStyle(0);
this.fillStyle = physion.utils.createFillStyle(0xff0000);
}
this.tracerRadius = 0.1;
}
// ..
}
In the constructor we initialize the data members of the class that we're going to use:
Data member | Description |
---|---|
node | A reference to the node that this script is attached to (passed as argument in the constructor). |
initialized | Boolean switch to help us perform a "one-off" initialization (see below). |
trajectory | The trajectory array which is going to hold the positions (x, y) of our node as the simulation runs. |
maxTrajectorySize | The maximum length of the trajectory array. The bigger this value is the bigger the trace will be. |
graphics | This is the 'canvas' where the trace will be drawn. |
lineStyle | The line style to be used when drawing the tracer (line width of 0 pixels, i.e. don't draw outline) |
fillStyle | The fill style to be used when drawing the tracer (red color) |
tracerRadius | The radius of the circles used when drawing the tracer. |
The update()
method of the script is shown below:
update(delta) {
if (!this.initialized) {
this.initialized = true;
const scene = this.node.findSceneNode();
if (scene) {
scene.layers.foreground.addChild(this.graphics);
}
}
this.updateTrajectory();
this.drawTracer();
}
First, we use our initialized
data member to perform a 'one-off' initialization task:
Add our graphics
to the foreground layer of the scene (that our node
belongs to).
In order to retrieve the scene that our node
belongs to, we use the findscenenode
method.
After initialization is performed, the script keeps repeating the following:
- Updates the
trajectory
array - Draws the tracer
The updateTrajectory()
method is responsible to update our trajectory
array in each update. It does that by pushing the latest position of our node
to the array. It also makes sure that this array doesn't grow infinetely: If the size of the trajectory
array gets bigger than maxTrajectorySize
then old positions will get removed.
updateTrajectory() {
this.trajectory.push(this.node.getPosition());
while (this.trajectory.length > this.maxTrajectorySize) {
this.trajectory.shift();
}
}
Finally we call the drawTrajectory
helper which will use the trajectory
array to draw a series of circles thus visualizing the trajectory of our node
:
drawTracer() {
if (this.graphics && this.fillStyle) {
this.graphics.clear();
this.trajectory.forEach((point, i) => {
this.fillStyle.alpha = (i + 1) / this.trajectory.length;
physion.utils.drawStyledCircle(this.graphics, point.x, point.y, this.tracerRadius, this.lineStyle, this.fillStyle);
});
}
}
Using the Tracer Script
The Tracer script is available in the Assets Browser
. To use it simply drag and drop the script onto the node to create the tracer for.
You can also see the tracer script in action in this sample scene.
Future Work
- Add the ability to trace any point on an object (and not just its center as presented in this solution).
- Add a
TracerNode
(and the corresponding UI) to allow creating tracers natively (i.e. without a custom script) - More customization (e.g. gradient color)