Path

The path component is a scripted component that defines a path in 3D space. The path can be bound to zero or more dynamic path joints. The curve for the path can be specified either by using feature selection or by creating the curve using the scripting API.

You may also unlock axes, allowing the attached body to rotate freely about the tangent, bitangent and/or normal to the curve. The distance along the path can be restricted by specifying limits.

Properties

The path component properties that are accessible through the inspector.

Curve

The curve type for the path is one of the following:

The curve can be selected using feature selection. Click the button to begin selecting a curve from the mesh. Hover over the curve you want to select and then left-click to confirm the selection. You can right-click whilst hovering to negate the direction of the curve. Once you select a curve, it will be automatically converted to one of the curve types listed above. Note that the curve may also be explicitly specified via scripting.

Axis Unlock

The flags specify the axes about which the attached body is allowed to rotate:

  • Tangent: The attached body is permitted to rotate freely about the tangent to the curve
  • Normal: The attached body is permitted to rotate freely about the normal to the curve
  • Bitangent: The attached body is permitted to rotate freely about the bitangent to the curve

Note that multiple axes can be unlocked.

Limits

The limits for the path. You may specify a lower and/or upper limit for the path.

Scripting

An example is provided below that demonstrates how to specify an elliptic curve for the path using the TypeScript API. Note that the curve is defined in the local space of the path component’s entity:

import { Component, type Entity, Handle, PhysicsComponent, Polyline, Vec3, PathComponent, Icon } from "prototwin";

@Icon("mdi:ellipse-outline")
export class Ellipse extends Component {
    public path: Handle<PathComponent>;

    constructor(entity: Entity) {
        super(entity);
        this.path = this.handle(PathComponent);
    }

    public override initialize(): void {
        const physics = this.entity.component(PhysicsComponent);
        const joints = physics.joints;
        const path = this.path.value;
        if (path !== null && joints !== null && joints.length > 0) {
            const joint = joints[0];
            const points: Vec3[] = [];
            
            // The ellipse is approximated as a polyline with 1000 points.
            const count = 1000;
            for (let i = 0; i < count; ++i) {
                const t = (i / (count - 1)) * Math.PI * 2;
                const x = 2 * Math.cos(t); // The x-axis radius is 2m.
                const y = 2; // The curve is 2m above the XZ plane.
                const z = 4 * Math.sin(t); // The z-axis radius is 4m.
                points.push(this.entity.worldToLocalPoint(new Vec3(x, y, z)));
            }

            path.curve = new Polyline(points);
            joint.path = path;
        }
    }
}