Cocos3.8版本 实现跟随3d物体的条带拖尾

实现类似汽车2条尾气的效果

1.使用粒子特效Trail Module 会导致在高速情况下,粒子断层,尾部颗粒感严重,而且粒子过多会导致draw call负担

2.使用2D拖尾 MotionStreak,会在轨道扭曲的时候,有显示层级或者重叠在一起的问题

(1)应该显示4条,实际看到了1条,是因为角度问题重叠了

(2)会在模型背侧显示出来,

3.最终实现,选择Mesh渲染,+材质

1.创建材质,渐变图+builtin材质
2.实际作用的脚本,代码如下,新建一个空节点,增加Mesh Renderer组件,无需设置mesh,把刚才的材质绑定,然后把脚本绑定上,调整脚本参数,设置要跟随的target,代码中设置的是两条拖尾,left和right,可以根据需求自己修改。主要就是通过路径绘制网格渲染出来。
复制代码
import {
    _decorator,
    Component,
    Vec3,
    MeshRenderer,
    utils,
    Node,
    find,
    Camera,
    isValid,
} from 'cc';

const { ccclass, property } = _decorator;

@ccclass('Trail3D')
export class Trail3D extends Component {

    @property({ type: Node })
    target: Node = null!; // 车
    @property(Camera)
    camera: Camera = null!;

    @property
    maxPoints = 80;

    @property
    width = 0.3;

    @property
    minDistance = 0.05;

    @property
    maxLength = 5; // 拖尾最大长度(米)

    private _points: Vec3[] = [];
    private _meshRenderer: MeshRenderer = null!;

    private _lastWorldPos: Vec3 = new Vec3();

    private isLeft: boolean = false;

    start() {
        this._meshRenderer = this.getComponent(MeshRenderer)!;

        if (!this.target) {
            console.error('Trail3D: target 未设置');
            return;
        }

        this._lastWorldPos.set(this.target.worldPosition);
    }

    setTarget(target: Node, left = false) {
        this.isLeft = left;
        this.target = target;
        if (left) {
            this._lastWorldPos.set(this.target.worldPosition.x, this.target.worldPosition.y + .4, this.target.worldPosition.z + 0.5);
        }
        else {
            this._lastWorldPos.set(this.target.worldPosition.x, this.target.worldPosition.y + .4, this.target.worldPosition.z - 0.5);
        }
    }

    update() {
        if (!this.target || isValid(this.target) == false) {
            this.node.destroy();
            return;
        }

        let currentWorld = this.target.worldPosition.clone();
        if (this.isLeft) {
            currentWorld.set(this.target.worldPosition.x, this.target.worldPosition.y + .4, this.target.worldPosition.z + 0.5);
        }
        else {
            currentWorld.set(this.target.worldPosition.x, this.target.worldPosition.y + .4, this.target.worldPosition.z - 0.5);
        }

        // ===== 不动就不更新(优化)=====
        if (Vec3.distance(this._lastWorldPos, currentWorld) < 0.0001) {
            return;
        }

        // ===== 距离补点(核心,解决断层)=====
        const dist = Vec3.distance(this._lastWorldPos, currentWorld);

        if (dist > this.minDistance) {
            const count = Math.floor(dist / this.minDistance);

            for (let i = 1; i <= count; i++) {
                const t = i / count;

                const pos = new Vec3();
                Vec3.lerp(pos, this._lastWorldPos, currentWorld, t);

                // ✅ 直接存世界坐标(关键)
                this._points.push(pos);
            }

            this._lastWorldPos.set(currentWorld);
        }

        // ===== 按长度裁剪(核心,防止变圆)=====
        let total = 0;
        for (let i = this._points.length - 1; i > 0; i--) {
            total += Vec3.distance(this._points[i], this._points[i - 1]);

            if (total > this.maxLength) {
                this._points.splice(0, i);
                break;
            }
        }

        // ===== 最大点限制(保险)=====
        if (this._points.length > this.maxPoints) {
            this._points.shift();
        }

        this._updateMesh();
    }

    private _updateMesh() {
        if (this._points.length < 2) return;

        const positions: number[] = [];
        const uvs: number[] = [];
        const indices: number[] = [];

        const right = new Vec3();
        const dir = new Vec3();
        const up = new Vec3(0, 1, 0);

        for (let i = 0; i < this._points.length; i++) {
            const p = this._points[i];

            // ===== 方向 =====
            if (i === 0) {
                Vec3.subtract(dir, this._points[i + 1], p);
            } else {
                Vec3.subtract(dir, p, this._points[i - 1]);
            }
            Vec3.normalize(dir, dir);

            // ===== 侧方向(稳定,不翻转)=====
            const camForward = this.camera.node.forward;

            Vec3.cross(right, dir, camForward);

            if (right.lengthSqr() < 0.0001) {
                right.set(1, 0, 0);
            }

            Vec3.normalize(right, right);

            // ===== 宽度渐变 =====
            const t = i / this._points.length;
            const w = this.width * t;

            const left = new Vec3();
            const rightPos = new Vec3();

            Vec3.scaleAndAdd(left, p, right, -w);
            Vec3.scaleAndAdd(rightPos, p, right, w);

            positions.push(left.x, left.y, left.z);
            positions.push(rightPos.x, rightPos.y, rightPos.z);

            // UV 用于透明渐变
            uvs.push(t, 0);
            uvs.push(t, 1);
        }

        // ===== 索引 =====
        for (let i = 0; i < this._points.length - 1; i++) {
            const i0 = i * 2;
            const i1 = i * 2 + 1;
            const i2 = i * 2 + 2;
            const i3 = i * 2 + 3;

            indices.push(i0, i2, i1);
            indices.push(i1, i2, i3);
        }

        this._rebuildMesh(positions, uvs, indices);
    }

    private _rebuildMesh(positions: number[], uvs: number[], indices: number[]) {

        const geometry = {
            positions,
            uvs,
            indices,
        };

        // ⚠️ Cocos 3.8 推荐
        const mesh = utils.MeshUtils.createMesh(geometry);

        this._meshRenderer.mesh = mesh;
    }
}

最终效果可以在3d场景中,完全显示出所有尾气条带

相关推荐
逸模13 小时前
逸模智造・BIM 效能专栏一|极速土建翻模,2D 一键跨越 3D
笔记·3d·bim·土建·翻模
陈天伟教授1 天前
eDrawings 2026 x64 Edition-免费 3D 模型和 2D 工程图查看器
3d
Tian_Hang2 天前
eclipse ditto 学习笔记
运维·服务器·开发语言·javascript·3d
AI视觉网奇2 天前
BambuStudio 编译实战 2026
3d
AI前沿资讯2 天前
AI3D角色生产如何减少返工?用 V2Fun 前移建模与动画流程
人工智能·3d
蓝速科技2 天前
蓝速科技视觉 3D 全息舱 AI 数字人一体机带灯与无灯款深度评测
人工智能·科技·3d
尘中远2 天前
【Qwt 7.0 系列】3D 数据可视化 —— OpenGL 高性能三维绘图
qt·3d·qcustomplot·qwt·科学绘图·高性能绘图
林恒smileZAZ3 天前
Three.js 3D 地图特效与材质实现指南
javascript·3d·材质
蓝速科技3 天前
蓝速科技 3D 全息舱 AI 数字人博物馆导览效果实录
人工智能·科技·3d
lauo3 天前
【案例】每天省5小时:ibbot手机文章中心-采集到自动发布-工作流(agent)
人工智能·microsoft·3d·智能手机