【宝可梦专场】 如何通过鼠标控制3D角色的移动

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金

前言

相信玩过宝可梦剑盾的小伙伴看到泪眼蜥一定会非常亲切,本期我们通过一个鼠标控制宝可梦中泪眼蜥移动的场景为例,仅讲述如何通过鼠标捕获3D坐标值的,并且让角色执行奔跑使其移动到坐标点的,其他的场景搭建,资源引入及使用等等并不是本期的主题内容,故不作讲述。

先来让我们康康效果吧:

VID_20211014_165903.gif

通过上面的效果可以了解到,当点击鼠标的任何一点时我们的小泪眼蜥就都麻溜的跑过去非常的听话。接下来,我们就会讲述这个点击事件如何实现的,他又是如果跑过去,我们这就出发~

正文

开发流程

首先大致讲解一下,这个案例的开发流程:

  1. 引入three.js、GLTFLoader.js、gsap.js、OrbitControls.js。
  2. 在主逻辑初始化,创建场景,灯光,摄像机,加载资源,绑定鼠标事件。
  3. 资源导入后,创建地面和泪眼蜥,跳转大小和坐标及角色动画。
  4. 鼠标点击实现泪眼蜥的移动和奔跑动画切换。

坐标的捕获与转化

bindEvent() {
    // 其他要绑定的事件...
    window.addEventListener("mouseup",this.onMouseUp.bind(this));
}
复制代码

我们将在先绑定鼠标抬起事件,因为我们有场景控制器实现拖拽视角等所以我们不能直接用鼠标按下,而是绑定抬起,这样就可以判断摄像机是否移动(即:让摄像机动还是角色动的判断)。

onMouseUp(e){
    // 一些逻辑的判断,如果通过则让他执行捕获坐标让角色移动...
    let vector = new THREE.Vector3();
    vector.set(
        (e.clientX / window.innerWidth) * 2 - 1,
        -(e.clientY / window.innerHeight) * 2 + 1,
        0.5);
    vector.unproject(this.camera);
    let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children);
    if (intersects.length > 0) {
        // 如果有坐标点,就存储下来
        this.point = intersects[0].point;
        // 宝可梦奔跑方法
        this.pokemonRun();
    }
}
复制代码

这就是鼠标坐标值的捕获与转化万能写法。实际上是用鼠标坐标值转化出一个3D坐标点,来利用光线投射根据摄像机做了一次计算,就是将你点击的那个鼠标坐标转化成一道射线,射线贯穿的3D物体可以拿到,贯穿的点也可以拿到。因为我们仅仅涉及到地面,所以Y轴可以不变,那么后面小泪眼蜥就可以只改变x和z轴的坐标就可以了。

微信截图_20211015095624.png

角色奔跑与移动

首先我们在角色加载完成的时候,已经把其中的动画与其动画混合器给存储起来,并让其播放等待动画。

this.pokemon = gltf.scene;
this.animations = gltf.animations;
// ...
this.mixer = new THREE.AnimationMixer(this.pokemon);
this.mixer.clipAction(this.animations[0]).play()
// ...
复制代码

接下来,就是要让他奔跑并且移动到指定的坐标点。

pokemonRun() {
    if (this.timer) this.timer.pause();
    this.mixer.stopAllAction();
    this.mixer.clipAction(this.animations[2]).play();
    this.timer = new gsap.timeline({
        defaults: {
            duration: 0
        },
    });
    let dx = this.pokemon.position.x - this.point.x;
    let dz = this.pokemon.position.z - this.point.z;
    let d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dz, 2));
    let angle = Math.atan2(dx, dz) + Math.PI;
    this.pokemon.rotation.y = angle;
    this.timer.to(
        this.pokemon.position,
        {
            duration: d / this.speed,
            x: this.point.x,
            z: this.point.z,
            onComplete: () => {
                this.mixer.stopAllAction();
                this.mixer.clipAction(this.animations[0]).play();
            },
        }
    );
}
复制代码

我们要先停止他之前的动画然后执行下标为2这个动画即为奔跑动画。然后就要通过三角函数计算他的方向使其转向。后面移动推荐大家使用gsap,通过改变x与z坐标,产生位移和过渡,特别方便。另外,值得注意的是,我们每只宝可梦移动速度和要指定移动的路程也各不相同,所以我要计算他的耗时。又因为只是涉及到平面所以我们通过两点间距离公式计算出距离来,再给角色写一个初始化速度。后的时间就好算了(即:时间=路程/速度)。别忘了执行完,还要结束奔跑动画,再让他原地待命。

微信截图_20211015100405.png

结语

不知你学会了么,本身是通过2D鼠标坐标值与摄像机坐标值进行换算的到的结果,可以封装起来以后直接去使用。而角色移动在通过动画库gsap去实现的,还是非常的简单实用的。有志向做3DRPG游戏的小伙伴可以通过这个案例来联手,初步熟悉一下,加油鸭~


若点赞过百,则证明大家还是非常喜爱的,我会上传本期源码地址(github地址,而非codepen),供大家进一步学习整个完成流程,所以别控制尽情的点赞吧~

猜你喜欢

转载自juejin.im/post/7019111048225488910