Skip to main content

Creating a Tracer Script

· 4 min read
Physion

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 memberDescription
nodeA reference to the node that this script is attached to (passed as argument in the constructor).
initializedBoolean switch to help us perform a "one-off" initialization (see below).
trajectoryThe trajectory array which is going to hold the positions (x, y) of our node as the simulation runs.
maxTrajectorySizeThe maximum length of the trajectory array. The bigger this value is the bigger the trace will be.
graphicsThis is the 'canvas' where the trace will be drawn.
lineStyleThe line style to be used when drawing the tracer (line width of 0 pixels, i.e. don't draw outline)
fillStyleThe fill style to be used when drawing the tracer (red color)
tracerRadiusThe 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)