首页 > 其他分享 >three.js 汽车行驶效果

three.js 汽车行驶效果

时间:2023-12-06 17:13:42浏览次数:40  
标签:car await three js 行驶 let positions position model

实现原理是使用TWEEN.Tween实现动画效果

实现

汽车模型加载

使用Promise编写模型的异步加载方法

Car.prototype.loadCar = function (position, rotation) {
    let onProgress = function (xhr) {

    };

    return new Promise((resolve, reject) => {
        if (!this.model) {
            let loader = new THREE.GLTFLoader();
            loader.load(this.url, gltf => {
                const model = gltf.scene || gltf.scenes[0];

                model.position.x = position.x;
                model.position.y = position.y;
                model.position.z = position.z;

                model.scale.set(0.25, 0.25, 0.25);

                model.rotation.set(rotation.x, rotation.y, rotation.z);

                this.model = model;
                this.scene.add(model);

                resolve(model);

            }, onProgress, xhr => {
                console.error(xhr);
                console.info('模型 ' + url + ' 加载失败');
                reject(xhr);
            });
        } else {
            resolve(this.model);
        }
    });
}

调用:
第1个参数是初始位置,第2个参数表示汽车朝向西

await car.loadCar(positions[0], car.WEST);

汽车行驶

参数start是行驶起点位置,参数end是行驶终点位置,参数speed是速度
this.model是汽车模型,onUpdate事件中,不断更新它的position
this.label是汽车车牌号标签,onUpdate事件中,不断更新它的position

Car.prototype.moveCar = function (start, end, speed) {
    let distance = this.distance(start, end);
    let time = distance / speed * 1000;

    return new Promise((resolve, reject) => {
        this.tween = new TWEEN.Tween({
            x: start.x,
            y: start.y,
            z: start.z
        }).to({
            x: end.x,
            y: end.y,
            z: end.z
        }, time).start().onUpdate(e => {
            if (this.model) {
                this.model.position.x = e.x;
                this.model.position.y = e.y;
                this.model.position.z = e.z;
            }
            if (this.label) {
                this.label.position.x = e.x;
                this.label.position.y = e.y + 1.2;
                this.label.position.z = e.z;
            }
        }).onComplete(() => {
            TWEEN.remove(this.tween);
            resolve();
        });
    });
}

汽车转弯

参数start是动画开始时的汽车朝向,end是动画结束时的汽车朝向

Car.prototype.rotateCar = function (start, end) {
    return new Promise((resolve, reject) => {
        this.tween = new TWEEN.Tween({
            x: start.x,
            y: start.y,
            z: start.z
        }).to({
            x: end.x,
            y: end.y,
            z: end.z
        }, 300).start().onUpdate(e => {
            if (this.model) {
                this.model.rotation.set(e.x, e.y, e.z);
            }
        }).onComplete(() => {
            TWEEN.remove(this.tween);
            resolve();
        });
    });
}

汽车行驶一段路线

上述汽车行驶和汽车转弯方法都是异步方法,所以避免了回调地狱,不然下面的多段行驶及转弯就不好写了

Cars.prototype.carLine1 = function () {
    if (!this.run) return;

    let car = new Car(this.scene, this.renderer, './models/车红.glb');
    this.cars.push(car);

    let positions = [
        { x: -121, y: 1.5, z: -16 },
        { x: -130.5, y: 1.5, z: -16 },
        { x: -130.5, y: 1.5, z: 4 },
        { x: -82, y: 1.5, z: 4 },
        { x: -82, y: 1.5, z: 14.7 },
        { x: -18.8, y: 1.5, z: 14.7 },
        { x: -18.8, y: 1.5, z: 70 },
    ];

    let speed = 5;

    setTimeout(async () => {
        await car.loadCar(
            positions[0],
            car.WEST);

        car.showLabel(positions[0], "皖A67893");

        await car.moveCar(
            positions[0],
            positions[1],
            speed);

        await car.rotateCar(
            car.WEST,
            car.SOUTH);

        await car.moveCar(
            positions[1],
            positions[2],
            speed);

        await car.rotateCar(
            car.SOUTH,
            car.EAST);

        await car.moveCar(
            positions[2],
            positions[3],
            speed);

        await car.rotateCar(
            car.EAST,
            car.SOUTH);

        await car.moveCar(
            positions[3],
            positions[4],
            speed);

        await car.rotateCar(
            car.SOUTH,
            car.EAST);

        await car.moveCar(
            positions[4],
            positions[5],
            speed);

        await car.rotateCar(
            car.EAST,
            car.SOUTH);

        await car.moveCar(
            positions[5],
            positions[6],
            speed);

        car.unloadCar();

        this.carLine1(2000);
    }, 5000);
}

Cars.prototype.carLine2 = function () {
    if (!this.run) return;

    let car = new Car(this.scene, this.renderer, './models/车蓝.glb');
    this.cars.push(car);

    let positions = [
        { x: -5, y: 1.5, z: 70 },
        { x: -5, y: 1.5, z: 14.7 },
        { x: 70, y: 1.5, z: 14.7 }
    ];

    let speed = 5;

    setTimeout(async () => {
        await car.loadCar(
            positions[0],
            car.NORTH);

        car.showLabel(positions[0], "皖AD887U");

        await car.moveCar(
            positions[0],
            positions[1],
            speed);

        await car.rotateCar(
            car.NORTH,
            car.EAST);

        await car.moveCar(
            positions[1],
            positions[2],
            speed);

        car.unloadCar();

        this.carLine2(3000);
    }, 6000);
}

调用

let cars = new Cars(app.scene, app.renderer);
cars.carLine1();
cars.carLine2();

显示车牌号

Car.prototype.showLabel = function (position, text) {
    let canvasDraw = new CanvasDraw();
    let canvasTexture = canvasDraw.drawCarLabel(THREE, this.renderer, text, '#006688'); //标签

    let spriteMaterial = new THREE.SpriteMaterial({
        map: canvasTexture,
        color: 0xffffff,
        depthTest: false,
        side: THREE.DoubleSide,
        sizeAttenuation: false,
        transparent: true,
        opacity: 0.8
    });

    let sprite = new THREE.Sprite(spriteMaterial);
    sprite.scale.set(0.2, 0.1, 0.2)
    sprite.position.x = position.x;
    sprite.position.y = position.y + 1.2;
    sprite.position.z = position.z;

    this.label = sprite;
    this.scene.add(sprite);

    return sprite;
}

完整代码

car.js

// 汽车

let Car = (function () {

    // 汽车朝向
    Car.prototype.EAST = { x: 0, y: 1.5707963, z: 0 };
    Car.prototype.SOUTH = { x: 0, y: 0, z: 0 };
    Car.prototype.WEST = { x: 0, y: -1.5707963, z: 0 };
    Car.prototype.NORTH = { x: 0, y: 3.1415926, z: 0 };

    function Car(scene, renderer, url) {
        this.scene = scene;
        this.renderer = renderer;
        this.url = url;
        this.clock = new THREE.Clock();
    }

    Car.prototype.loadCar = function (position, rotation) {
        let onProgress = function (xhr) {

        };

        return new Promise((resolve, reject) => {
            if (!this.model) {
                let loader = new THREE.GLTFLoader();
                loader.load(this.url, gltf => {
                    const model = gltf.scene || gltf.scenes[0];

                    model.position.x = position.x;
                    model.position.y = position.y;
                    model.position.z = position.z;

                    model.scale.set(0.25, 0.25, 0.25);

                    model.rotation.set(rotation.x, rotation.y, rotation.z);

                    this.model = model;
                    this.scene.add(model);

                    resolve(model);

                }, onProgress, xhr => {
                    console.error(xhr);
                    console.info('模型 ' + url + ' 加载失败');
                    reject(xhr);
                });
            } else {
                resolve(this.model);
            }
        });
    }

    Car.prototype.unloadCar = function () {
        this.stopTween();
        this.removeModel();
        this.removeLabel();
    }

    Car.prototype.stopTween = function () {
        if (this.tween) {
            TWEEN.remove(this.tween);
        } else {
            setTimeout(() => {
                this.stopTween();
            }, 100);
        }
    }

    Car.prototype.removeModel = function () {
        if (this.model) {
            this.scene.remove(this.model);
        } else {
            setTimeout(() => {
                this.removeModel();
            }, 100);
        }
    }

    Car.prototype.removeLabel = function () {
        if (this.label) {
            this.scene.remove(this.label);
        } else {
            setTimeout(() => {
                this.removeLabel();
            }, 100);
        }
    }

    Car.prototype.moveCar = function (start, end, speed) {
        let distance = this.distance(start, end);
        let time = distance / speed * 1000;

        return new Promise((resolve, reject) => {
            this.tween = new TWEEN.Tween({
                x: start.x,
                y: start.y,
                z: start.z
            }).to({
                x: end.x,
                y: end.y,
                z: end.z
            }, time).start().onUpdate(e => {
                if (this.model) {
                    this.model.position.x = e.x;
                    this.model.position.y = e.y;
                    this.model.position.z = e.z;
                }
                if (this.label) {
                    this.label.position.x = e.x;
                    this.label.position.y = e.y + 1.2;
                    this.label.position.z = e.z;
                }
            }).onComplete(() => {
                TWEEN.remove(this.tween);
                resolve();
            });
        });
    }

    Car.prototype.rotateCar = function (start, end) {
        return new Promise((resolve, reject) => {
            this.tween = new TWEEN.Tween({
                x: start.x,
                y: start.y,
                z: start.z
            }).to({
                x: end.x,
                y: end.y,
                z: end.z
            }, 300).start().onUpdate(e => {
                if (this.model) {
                    this.model.rotation.set(e.x, e.y, e.z);
                }
            }).onComplete(() => {
                TWEEN.remove(this.tween);
                resolve();
            });
        });
    }

    Car.prototype.showLabel = function (position, text) {
        let canvasDraw = new CanvasDraw();
        let canvasTexture = canvasDraw.drawCarLabel(THREE, this.renderer, text, '#006688'); //标签

        let spriteMaterial = new THREE.SpriteMaterial({
            map: canvasTexture,
            color: 0xffffff,
            depthTest: false,
            side: THREE.DoubleSide,
            sizeAttenuation: false,
            transparent: true,
            opacity: 0.8
        });

        let sprite = new THREE.Sprite(spriteMaterial);
        sprite.scale.set(0.2, 0.1, 0.2)
        sprite.position.x = position.x;
        sprite.position.y = position.y + 1.2;
        sprite.position.z = position.z;

        this.label = sprite;
        this.scene.add(sprite);

        return sprite;
    }

    Car.prototype.distance = function (p1, p2) {
        return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2) + Math.pow(p1.z - p2.z, 2));
    }

    return Car;

})();

cars.js

// 多个车辆

let Cars = (function () {

    function Cars(scene, renderer) {
        this.scene = scene;
        this.renderer = renderer;
        this.cars = [];
        this.run = true;
    }

    Cars.prototype.carLine1 = function () {
        if (!this.run) return;

        let car = new Car(this.scene, this.renderer, './models/车红.glb');
        this.cars.push(car);

        let positions = [
            { x: -121, y: 1.5, z: -16 },
            { x: -130.5, y: 1.5, z: -16 },
            { x: -130.5, y: 1.5, z: 4 },
            { x: -82, y: 1.5, z: 4 },
            { x: -82, y: 1.5, z: 14.7 },
            { x: -18.8, y: 1.5, z: 14.7 },
            { x: -18.8, y: 1.5, z: 70 },
        ];

        let speed = 5;

        setTimeout(async () => {
            await car.loadCar(
                positions[0],
                car.WEST);

            car.showLabel(positions[0], "皖A67893");

            await car.moveCar(
                positions[0],
                positions[1],
                speed);

            await car.rotateCar(
                car.WEST,
                car.SOUTH);

            await car.moveCar(
                positions[1],
                positions[2],
                speed);

            await car.rotateCar(
                car.SOUTH,
                car.EAST);

            await car.moveCar(
                positions[2],
                positions[3],
                speed);

            await car.rotateCar(
                car.EAST,
                car.SOUTH);

            await car.moveCar(
                positions[3],
                positions[4],
                speed);

            await car.rotateCar(
                car.SOUTH,
                car.EAST);

            await car.moveCar(
                positions[4],
                positions[5],
                speed);

            await car.rotateCar(
                car.EAST,
                car.SOUTH);

            await car.moveCar(
                positions[5],
                positions[6],
                speed);

            car.unloadCar();

            this.carLine1(2000);
        }, 5000);
    }

    Cars.prototype.carLine2 = function () {
        if (!this.run) return;

        let car = new Car(this.scene, this.renderer, './models/车蓝.glb');
        this.cars.push(car);

        let positions = [
            { x: -5, y: 1.5, z: 70 },
            { x: -5, y: 1.5, z: 14.7 },
            { x: 70, y: 1.5, z: 14.7 }
        ];

        let speed = 5;

        setTimeout(async () => {
            await car.loadCar(
                positions[0],
                car.NORTH);

            car.showLabel(positions[0], "皖AD887U");

            await car.moveCar(
                positions[0],
                positions[1],
                speed);

            await car.rotateCar(
                car.NORTH,
                car.EAST);

            await car.moveCar(
                positions[1],
                positions[2],
                speed);

            car.unloadCar();

            this.carLine2(3000);
        }, 6000);
    }

    Cars.prototype.clear = function () {
        this.run = false;
        this.cars.forEach(car => {
            car.unloadCar();
        });
    }

    return Cars;

})();

调用

// 显示汽车
function showCars() {
	cars = new Cars(app.scene, app.renderer);
	cars.carLine1();
	cars.carLine2();
}

// 清除汽车
function clearCars() {
	cars.clear();
}

// 显示汽车
showCars();

总结

  1. 解耦:依赖的scene, renderer参数是通过构造函数传到Car和Cars对象中的
  2. 汽车行驶和转向等方法都是异步方法,可以避免回调地狱,这样汽车多段行驶的代码会写的比较清晰

改进

运行效果

标签:car,await,three,js,行驶,let,positions,position,model
From: https://www.cnblogs.com/s0611163/p/17879849.html

相关文章

  • Go--gjson
    GJSON是一个用于处理JSON数据的Go语言库。它提供了一些方便的功能,例如解析JSON字符串、查询JSON对象、生成JSON对象等下载gjson: go get -ugithub.com/tidwall/gjson 1、使用传入JSON串和要读取的键路径,路径使用点号语法,如"name.last"或"age"。一旦找到值,......
  • appsettings.json和appsettings.Development.json
    在ASP.NETCore中,当应用程序处于开发环境时,默认情况下会加载appsettings.json和appsettings.Development.json文件中的配置,并且appsettings.Development.json中的配置会覆盖appsettings.json中的相同配置。这是ASP.NETCore提供的一种便捷的配置管理机制。如果你希......
  • js上传文件夹的功能如何实现
    在JavaScript中无法直接上传整个文件夹,但可以通过以下步骤实现上传文件夹的功能:1.使用``标签来选择文件夹。该标签支持同时选择多个文件和文件夹。2.监听文件夹选择变化的事件,并获取所选择的文件和文件夹。constfileInput=document.querySelector('input[type="file"]')......
  • 用html和css和js实现一个班级点名
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>班级点名系统</title>......
  • three.js 使用 sortObjects 和 renderOrder 处理网格修改后覆盖模型的问题
    问题效果:目标效果处理此问题首先需要了解three的渲染机制:渲染机制threejs的渲染器是基于webGL的。它的渲染机制是根据物体离照相机的距离来控制和进行渲染的。也就是说,它根据物体的空间位置进行排序,然后根据这个顺序来渲染物体。对于透明的物体,是按照从最远到最近的顺序进行......
  • evalFn 字符串转执行函数 附带JSONParse函数
    constevalFn=(fn)=>{varFun=Function//一个变量指向Function,防止前端编译工具报错returnnewFun('return'+fn)()}/****JSON反序列化,支持函数和undefined*@paramdata*/constJSONParse=(data)=>{returnJSON.parse(data,(k......
  • jsdoc基础
    /***2、函数*@param{string}a-参数1*@param{number}b-参数2*@returns{boolean}-返回值*/constsum=(a,b)=>{returnBoolean(a+b)}/***3、变量*@property{string}a-属性1*@property{111|222}b-属性2*/constobj={......
  • JS(JavaScript)-if-switch选择结构-for-while循环
     前言:回到JS基础,用console输出; console.log(); 输入内容:window.prompt();向页面做出输入接收(类似于后端Scanner) 整数转换:parseInt();1.选择结构:①if结构if(){};  if(){}else{};  if(){}elseif{};......
  • antd 5.0 定制主题如此酷炫,我决定开启 @ant-design/cssinjs 阅读之旅
    前言antd5.0正式发布已经有一段时间了,发布当天,一心二用的看完直播。很喜欢整个设计,有种简约快乐工作的感觉,某些功能设计初衷也给了我一些启发。antd5.0提供的主题定制挺酷炫的,加之我最近对「CSS-in-JS」很感兴趣。于是迫不及待的打开了它的源码,准备研究一番。我大部分情况下都......
  • 将json数据导入到ES集群——解决方案对比&填坑日记
    需求将写好的json数据。导入到es集群数据说明文件JSON数据,一行一个JSON。{"id":"d2716ae8fba4e026c4bd9445c3f49e2c","lang":"zh","title":"吉美旅馆","content":"吉美..."}{"id":"d2716ae8fba4e026c4bd9445......