一款新出的开源框架,主要目的是用来做游戏引擎的,对标端游PUBG
因公司业务需求不同,跟着教程做了一款纯展示的展厅性质的Demo(React)
近几年元宇宙的概念有点热门,这里亦可沾点边
NPM地址
GitHub地址
官方文档地址
展厅Dem地址,PC端,简单做了做,功能很少,除基本的人物移动逻辑外,只做了图片轮播预览和视频展示的功能
官方B站教学视频
相关3D模型问题B站视频
1、纯Web前端
2、上手超简单。支持原JS,React,Vue
3、可通过websocket实现多人联机互动
4、支持PC、移动端摇杆
1、框架不断内测完善中,业务场景功能待完善
2、框架支持的3D模型具有局限性,需要blender进行处理。这也是市面上3D模型不统一导致的。
3、官方文档暂时不完善,文档阅读不便(快一点呀快一点)
一款前端构建工具,跟着官方文档走一下,create一个项目即可,不需要了解很多
当然如果业务需求很多了,可能需要了解这个工具的配置项什么的,可能会有冲突
支持TS,也支持JS,如果觉TS很搞,可以使用JS,但是还是建议使用TS,如果你要构建一个长久维护的项目的话。
用来管理你的键盘事件与动画状态的关系
建议使用blender进行模型处理,blender2.93LTS版本
源码下载地址
import { useState, useRef } from "react"
// lingo3D库
import { World, Editor, Cube, Model, ThirdPersonCamera, Keyboard, OrbitCamera, useLoop, useKeyboard, types, Find, HTML, Reticle, useSpring } from "lingo3d-react"
// 状态机,用来管理动作动画
import { useMachine } from "@xstate/react"
// 状态机文件
import poseMachine from "./stateMachines/poseMachine"
// 一款文字动画库,也是lingo3D作者的
import AnimText from "@lincode/react-anim-text"
import './App.css'
// 图片轮播的组件
import Gallery from './components/gallery'
function App() {
const [mouseOver, setMouseOver] = useState(false) //鼠标移入的开关
const [fixedWindow, setfixedWindow] = useState(false)// fixedWindow的开关
// 角色model的ref
const characterRef = useRef<types.Model>(null)
// 状态机
const [pose, sendPose] = useMachine(poseMachine, {
actions: {
enterJumping: ()=>{
const character = characterRef.current;
if(character === null) return
character.velocity.y = 5
character.onLoop = () => {
if(character.velocity.y === 0){
character.onLoad = undefined
sendPose('LADNED')
}
}
},
enterWaving: () => {
const character = characterRef.current;
if(character === null) return
const animationTimeout = setTimeout(() => {
sendPose('ENDTHISANIMATION')
clearTimeout(animationTimeout)
}, 4000);
}
}
})
// ThirdPersonCameraInnerPosition,第三人称相机的Inner偏移量
const CamInnerX = mouseOver?20:0
const CamInnerZ = mouseOver?40:200
const xSpring = useSpring({to:CamInnerX, bounce: 0})
const zSpring = useSpring({to: CamInnerZ, bounce: 0})
// characterAnimations,主角所有的动画
const characterAnimations = {
idle: 'person/Idle.fbx',
walking: 'person/Walking.fbx',
running:'person/Running.fbx',
jumping: 'person/Falling.fbx',
waklingback:'person/WalkingBackwards.fbx',
waving:'person/Waving.fbx'
}
return (
<>
{/* Word世界,参数分别为:高质量画面,天空盒图片,Find(下文标签的边框,被遮挡是黑色),环境光遮蔽 */}
<World performance="quality" skybox='skylight.hdr' outlineHiddenColor="black" ambientOcclusion>
{/* 场景模型,参数分别为:模型地址(public下的),缩放,物理碰撞检测(我是地图)*/}
<Model
src="scene/Pavilion.glb"
scale={18}
physics='map'
boxVisible={false}
>
{/* Find,可以把他理解成,场景模型中的某一个名字是name的元素,这个name哪里来,blender建模的时候命名的,参数分别为:name,边框,鼠标移入事件、鼠标移出事件、点击事件 */}
<Find
name="a5_CRN.a5_0"
outline
onMouseOver={()=>{setMouseOver(true)}}
onMouseOut={()=>{setMouseOver(false)}}
onClick={()=>{setfixedWindow(true)}}
>
{
mouseOver &&
// Html标签,就是普通的Ht,里面可以放html的标签
<HTML>
<AnimText className='HTML_TITLE'>SDTA-Gallery</AnimText>
</HTML>
}
</Find>
{/* 另一个Find,名字不同,让他播放一个视频 */}
<Find name="b11_CRN.b11_0" texture={"https://media.sdta.cn/themes/sdta/assets/video/sdta-slider3.mp4"}></Find>
</Model>
{/* 第三人称相机,参数分别问:鼠标控制、激活、inner角度(x,y,z) */}
<ThirdPersonCamera
mouseControl
active={!fixedWindow}
innerX={xSpring}
innerY={30}
innerZ={zSpring}
>
{/* 人物模型,参数分别为:模型地址,ref(通过ref可以获取并操作这个model),物理碰撞检测(我是主角),初始位置(x,y,z),这个model都有什么动画,这个model当前执行的动画,模型的盒子线关闭,当设置环境光时需要打开pbr */}
<Model
src="person/T-Pose.fbx"
ref={characterRef}
physics='character'
x={-424.99} y={-825.31} z={-661.13}
animations={characterAnimations}
animation={pose.value as any}
boxVisible={false}
pbr
/>
</ThirdPersonCamera>
{/* 键盘事件,这里就需要配合状态机来进行操作(如果只是简单的上下左右,通过useState即可,当功能复杂时,需要状态机进行管理) */}
<Keyboard
onKeyPress={(key: string) => {
if(key === 'w'){
sendPose('KEY_W_DOWN')
characterRef.current?.moveForward(-3)
}
else if(key === 's'){
sendPose('KEY_S_DOWN')
characterRef.current?.moveForward(1)
}
else if(key === 'd'){
// sendPose('KEY_S_DOWN')
characterRef.current?.moveRight(-3)
}
else if(key === 'a'){
// sendPose('KEY_S_DOWN')
characterRef.current?.moveRight(3)
}
else if(key === 'Space'){
sendPose('KEY_SPACE_DOWN')
}
else if(key === 'Shift'){
sendPose('KEY_SHIFT_DOWN')
characterRef.current?.moveForward(-3)
}
else if(key === 'h'){
sendPose('KEY_H_DOWN')
}
}}
onKeyUp={(key: string) => {
if(key === 'w'){
sendPose('KEY_W_UP')
}
else if(key === 's'){
sendPose('KEY_S_UP')
}
else if(key === 'Shift'){
sendPose('KEY_SHIFT_UP')
}
}}
/>
</World>
{/* Model编辑器,可以查看模型的各种参数进行调试 */}
{/* */}
{
fixedWindow &&
<FixedWindow handleClose={()=>setfixedWindow(false)}></FixedWindow>
}
</>
)
}
function FixedWindow(props: any){
return(
<div className="fixedWindow">
<span onClick={()=>props.handleClose()} className="fixedWindow_close">{"close"}</span>
<span className="fixedWindow_websocket">{"OPEN CHAT"}</span>
{/* 图片轮播组件 */}
<Gallery></Gallery>
</div>
)
}
export default App
1、blender建议使用2.9.3稳定版
2、用vite创建的react是18,后面用到了Swiper不知道什么错,降到了17
3、期待更多的发现