vue3 轻松创建 物体交互 -Sandi-UI 可交互的3D组件库

688 阅读2分钟

自我介绍

大家好,网名:迷离,本名:宋帅,一枚喜欢折腾的前端工程师,设计师转职到前端工程师的世界,爱好写代码和听着音乐绘画,偶尔弹弹吉他(初学者),电子手工爱好者。

简介

光线投射 - Raycaster

Raycaster 用于检测 是否于3D物体相交,可以创造出很多好玩场景。

简单介绍 SDRaycaster 组件

我们想实现物体与物体之间的交互,首先要检测到物体,物体是否接触到其他物体,这个接触在three中检测光线投射是否于物体相交。一个射线 穿过一个物体,而这个射线并不是可见的。

简单的例子

创建一个带有射线的箱子

image.png

<template>
    <SDWebglRenderer :width="720" :height="360" :backgroundColor="0x1f63d1">
        <SDPerspectiveCamera :positionY="0" :positionZ="3" />
        <SDScene>
            <SDMesh :rotation="[0, y, 0]" :scaleXYZ="1">
                <SDRaycaster :lockDirection="true" :direction="new Vector3(1, 0, 0)" :far="2"
                    :offset="new Vector3(0, 0, 0)" />
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
        </SDScene>
    </SDWebglRenderer>
</template>
<script setup >
import { ref } from 'vue'
import { Vector3 } from "three"
const y = ref(0)
</script>

此时我们创建了一个 带有Raycaster 的箱子, 你会发现,默认你是看不到他的,他并不是一个物体,我们让他显示出来,我们通过direction 属性来设置 方向向量

image.png

<template>
    <SDWebglRenderer :width="720" :height="360" :backgroundColor="0x1f63d1">
        <SDPerspectiveCamera :positionY="0" :positionZ="3" />
        <SDScene>
            <SDMesh :rotation="[0, y, 0]" :scaleXYZ="1">
                <SDRaycaster  :helper="{ color: 'red' }" :direction="new Vector3(1, 0, 0)" :far="2"
                    :offset="new Vector3(0, 0, 0)" />
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
        </SDScene>
    </SDWebglRenderer>
</template>
<script setup >
import { ref } from 'vue'
import { Vector3 } from "three"
const y = ref(0)
</script>

我们看到 我们使用helper 属性,让它显示了出来,接下来 让箱子转起来 看看

boxrotat2.gif

默认 射线方向和物体旋转是绑定的,我也可以通过设置lockDirection 为false,让其始终朝一个方向

boxrotat5.gif

两个箱子 互动起来

boxrotat6.gif

右箱子逐渐变小,当左箱子的射线碰到右箱子,右箱子变大,射线离开后,箱子继续变小。

代码

<template>
    <SDWebglRenderer :width="720" :height="360" :backgroundColor="0x1f63d1" :renderCallback="render">
        <SDPerspectiveCamera :positionY="0" :positionZ="3" />
        <SDScene>
            <SDMesh :position="[-1, 0, 0]" :rotation="[0, y, 0]" :scaleXYZ="1">
                <SDRaycaster :lockDirection="true" :helper="{ color: 'red' }" :direction="new Vector3(1, 0, 0)" :far="2"
                    :offset="new Vector3(0, 0, 0)" :raycasterCallback="testObject" />
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
            <SDMesh :scale="[scale, scale, scale]" :position="[1, 0, 0]" :scaleXYZ="1">
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
        </SDScene>
    </SDWebglRenderer>
</template>
<script setup >
import { ref } from 'vue'
import { Vector3 } from "three"
const y = ref(0)
const touch = ref(false)
const scale = ref(1)
let touchObject;
const render = () => {
    y.value += 0.025
    touch.value = false
    scale.value -= 0.0005
}

const testObject = (object) => {
    touchObject = object;
    scale.value += 0.009
}
</script>

三个箱子联动

上面的例子展示了两个箱子联动,与物体之间的交互,现实情况 我们的物体是多个的,我们来展示3个箱子的互动.

boxrotat7.gif

我们使用name属性,对物体命名,来区分射线检测哪个物体,去做不同的动作

<template>
    <SDWebglRenderer :width="720" :height="360" :backgroundColor="0x1f63d1" :renderCallback="render">
        <SDPerspectiveCamera :positionY="0" :positionZ="3" />
        <SDScene>
            <SDMesh name="left" :rotation="[0, y1, 0]" :position="[-2, 0, 0]" :scaleXYZ="1">
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
            <SDMesh :position="[0, 0, 0]" :rotation="[0, y, 0]" :scaleXYZ="1">
                <SDRaycaster :lockDirection="true" :helper="{ color: 'red' }" :direction="new Vector3(1, 0, 0)" :far="2"
                    :offset="new Vector3(0, 0, 0)" :raycasterCallback="testObject" />
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
            <SDMesh name="right" :scale="[scale, scale, scale]" :position="[2, 0, 0]" :scaleXYZ="1">
                <SDBoxGeometry :width="1" />
                <SDMeshBasicMaterial>
                    <SDTextureLoader url="/sandi-ui/img/crate.gif" type="map" />
                </SDMeshBasicMaterial>
            </SDMesh>
        </SDScene>
    </SDWebglRenderer>
</template>
<script setup >
import { ref } from 'vue'
import { Vector3 } from "three"
const y = ref(0)
const y1 = ref(0)
const touch = ref(false)
const scale = ref(1)
const render = () => {
    y.value += 0.025
    touch.value = false
    scale.value -= 0.0005
}

const testObject = (object) => {
    if (object.name === "left") {
        y1.value += 0.09
    }
    if (object.name === "right") {
        scale.value += 0.012

    }
}
</script>

交个朋友&& 找工作

Slice 1.png

下篇 我们 会做更炫酷的例子来 展示动态模型的交互