美文网首页
15 世界地图 SLG视野

15 世界地图 SLG视野

作者: skoll | 来源:发表于2021-12-12 16:22 被阅读0次

    相机类型

    1 .其实什么类型都可以,关键是操作逻辑,要变成拖拽移动视角,并且是滑轮放大和缩小
    2 .先拿node来做一下试试,感觉有别的相机

    第一版,以动态地形为基准,感觉有点粗糙,镜头会变到地下面,这个是平面的,感觉还要操作,应该是只操作x,z坐标

    let startPosition
                    // 开始拖拽的位置
    
                    function getPosition(){
                        let pickInfo=scene.pick(scene.pointerX,scene.pointerY,function(mesh){
                            return true
                        })
                        if(pickInfo.hit){
                            return pickInfo.pickedPoint
                        }
    
                    }
    
                    function pointerDown() {
                        startPosition=getPosition()
                    }
    
                    function pointerUp(){
                        startPosition=null
                    }
    
                    function pointerMove(event){
                       if(!startPosition)return 
    
                       let current=getPosition()
                       if(!current)return 
                       let diff=current.subtract(startPosition)
                       node.position.addInPlace(diff)
                       startPosition=current
                    }
    
    
    
                    scene.onPointerObservable.add((pointerInfo)=>{
                        switch(pointerInfo.type){
                            case BABYLON.PointerEventTypes.POINTERDOWN:
                                pointerDown()
                                break
                            case BABYLON.PointerEventTypes.POINTERUP:
                                pointerUp()
                                break
                            case BABYLON.PointerEventTypes.POINTERMOVE:
                                pointerMove(pointerInfo)
                                break
                        }
                    })
    

    第二版,以屏幕为定位

    <!DOCTYPE html>
    <!-- 添加小人,使用序列图 -->
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
        <title>Babylon - Getting Started</title>
        <!-- Link to the last version of BabylonJS -->
        <script src="https://preview.babylonjs.com/babylon.js"></script>
        <!-- Link to the last version of BabylonJS loaders to enable loading filetypes such as .gltf -->
        <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
        <!-- Link to pep.js to ensure pointer events work consistently in all browsers -->
        <script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
        <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script> -->
        <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script> -->
        <!-- <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script> -->
        <!-- <script src="https://cdn.rawgit.com/BabylonJS/Extensions/master/DynamicTerrain/dist/babylon.dynamicTerrain.min.js"></script> -->
        <script src="./dy.js"></script>
        <script src="./noise.js"></script>
    </head>
    <style>
        html, body {
            overflow: hidden;
            width   : 100%;
            height  : 100%;
            margin  : 0;
            padding : 0;
        }
    
        #renderCanvas {
            width   : 100%;
            height  : 100%;
            touch-action: none;
        }
    </style>
    <body>
        <canvas id="renderCanvas" touch-action="none"></canvas>
        <script>
            const canvas = document.getElementById("renderCanvas");
            var engine = null;
            // 这里还不能用let,不然就爆炸,获取不到engine
            var scene = null;
            var sceneToRender = null;
            const createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true,  disableWebGL2Support: false}); };
            let createScene=async function(){
                // 关键函数都写在这个里面
    
                    var scene = new BABYLON.Scene(engine)
                    var camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3(0, 20, 0), scene);    
                    // 第二个坐标是摄像机在场景中的位置,现在还需要一个就是摄像机的朝向
                    camera.rotation=new BABYLON.Vector3(0.7697057340138546,0.00772265625,0)
                    camera.position.y=60
                    // 高度最高现在定为60,最大值,按照这个最大值来调参数,地图大小也需要调,现在镜头外面还有没有显示的地
                    
    
                    var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.0, 1.0, 0.0), scene);
                    light.intensity = 0.75;
                    light.specular = BABYLON.Color3.Black();
    
                    // 这俩值是不是决定了地图的实际大小
                    let terrainTexture=new BABYLON.Texture("https://www.babylonjs-playground.com/textures/ground.jpg",scene)
    
                    let terrainMat=new BABYLON.StandardMaterial('tm',scene)
                    terrainMat.diffuseTexture=terrainTexture
                    terrainMat.diffuseTexture.UScale=1
                    terrainMat.diffuseTexture.VScale=1
    
                    var spsMaterial = new BABYLON.StandardMaterial("spsm", scene);
                    var spsUrl = "https://jerome.bousquie.fr/BJS/images/uv_texture.jpg";
                    var spsTexture = new BABYLON.Texture(spsUrl, scene);
                    spsMaterial.diffuseTexture = spsTexture;
                    
                    var mapSubX = 1000;            
                    var mapSubZ = 1000;              
                    var seed = 0.3;                 
                    var noiseScale = 0.03;         
                    var elevationScale = 6.0;
                    noise.seed(seed);
                    var mapData = new Float32Array(mapSubX * mapSubZ * 3); 
    
                    let SPmapData=[[],[],[]]
                    // 这俩其实不是很常用,毕竟真的引入mesh这俩应该都有的吧,除非mesh仅仅是颜色不一样代表不同的物体,来做优化的时候
                    var SPcolorData = [[], [],  []];
                    var SPuvData = [[], [], []];
    
                    // x位移系数:在原来的基础上移动的距离,或者说实际屏幕和地图的位移比例吧
                    let xMove=0.033
                    // 第一次0.5的值感觉还是有点大
                    // y位移系数
                    let yMove=0.053
                    // 最后这个值感觉这里就差不多了,但是这个系数还是要和镜头高度有关系的
    
                    for (var l = 0; l < mapSubZ; l++) {
                            for (var w = 0; w < mapSubX; w++) {
                                var x = (w - mapSubX * 0.5) * 2.0;
                                var z = (l - mapSubZ * 0.5) * 2.0;
                                var y = noise.simplex2(x * noiseScale, z * noiseScale);               // altitude
                                y *= (0.5 + y) * y * elevationScale; 
    
                                mapData[3 *(l * mapSubX + w)] = x;
                                mapData[3 * (l * mapSubX + w) + 1] = y;
                                mapData[3 * (l * mapSubX + w) + 2] = z; 
    
                                let index=l*mapSubX+w
                                if(Math.random()>0.85){
                                    // 如果满足这个条件,就添加一个物体
                                    let xp=x
                                    let yp=y
                                    let zp=z
    
                                    let ry=Math.random()*3.6
    
                                    let sx=0.5+Math.random()
                                    let sy=0.5+Math.random()
                                    let sz=0.5+Math.random()
    
                                    let r = Math.abs(xp) / mapSubX * 1.2 + 0.5;
                                    let g = Math.abs(zp) / mapSubZ * 1.2 + 0.5;
                                    let b = Math.abs(yp) / elevationScale + 0.1;
    
                                    // UVs
                                    let u = 0.9 * Math.random();
                                    let v = 0.9 * Math.random();
    
                                    let type = index % 3;
                                    SPmapData[index % 3].push(xp, yp, zp, 0, ry, 0, sx, sy, sz);
                                    SPcolorData[type].push(r, g, b, 1.0); 
                                    SPuvData[type].push(u, v, u + 0.1, v + 0.1);
                                }
                        }
                    }   
    
                        let model1=BABYLON.MeshBuilder.CreateBox('m1',{size:1},scene)
                        let model2=BABYLON.MeshBuilder.CreatePolyhedron('m2',{size:0.5},scene)
                        let model3=BABYLON.MeshBuilder.CreateSphere('m3',{segments:3},scene)
    
                        let sps=new BABYLON.SolidParticleSystem("SPS",scene)
                        let type1=sps.addShape(model1,10000)
                        let type2=sps.addShape(model2,10000)
                        // 为什么数量少的时候,镜头了将要到达的地方物体消失,正好相反了
                        // 这里的数量是当前地形中最多可见的形状的数量,100的意思就是地图中最多可见100个model2模型
                        let type3=sps.addShape(model3,10000)
                        sps.buildMesh()
                        model1.dispose()
                        model2.dispose()
                        model3.dispose()
    
                        sps.mesh.material = spsMaterial;
    
                        var terrainSub = 160;  
                        // 这个才是镜头最大展示的数量
                        var params = {
                            mapData: mapData,               
                            mapSubX: mapSubX,               
                            mapSubZ: mapSubZ,
                            SPmapData: SPmapData,           
                            sps: sps,                       
                            terrainSub: terrainSub,
                            subToleranceX:160,
                            subToleranceZ:160,
                            // 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
                        }
                        var terrain = new BABYLON.DynamicTerrain("t", params, scene);
                        terrain.isAlwaysVisible=true
                        var terrainMaterial = new BABYLON.StandardMaterial("tm", scene);
                        terrainMaterial.diffuseTexture = terrainTexture;
                        terrain.mesh.material = terrainMaterial;
    
                        // terrain.updateCameraLOD = function(terrainCamera) {
                        //     // LOD value increases with camera altitude
                        //     var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
                        //     // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
                        //     return camLOD;
                        // };
                    terrain.update(true);
                   
    
                    let startPosition
                    // 开始拖拽的位置
                    let isPointerDowm=false
    
                    function pointerDown(e) {
                        isPointerDowm=true
                    }
    
                    function pointerUp(){
                        isPointerDowm=false
                    }
    
                    function pointerMove(e){
                       if(!isPointerDowm)return 
                       let mx=e.event.movementX*xMove
                       let my=e.event.movementY*yMove
                       //这是最基础的调整,还需要调整,乘一个系数    
                        camera.position.addInPlace(new BABYLON.Vector3(-mx,0,my))
                    }
    
                    function pointerWhell(e){
                        const delta=e.event.wheelDelta
                        if(delta>0){
                            if(camera.position.y>=60)return
                            camera.position.y+=1
                            
                        }else{
                            if(camera.position.y<=20)return
                            camera.position.y-=1
                        }
    
                        console.log('当前相机高度',camera.position.y)
                        
                    }
    
    
                    scene.onPointerObservable.add((pointerInfo)=>{
                        switch(pointerInfo.type){
                            case BABYLON.PointerEventTypes.POINTERDOWN:
                                pointerDown(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERUP:
                                pointerUp(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERMOVE:
                                pointerMove(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERWHEEL:
                                pointerWhell(pointerInfo)
                                break;
                        }
                    })
                    let count=0
                    scene.registerBeforeRender(()=>{
                        count++
                        if(count>=200){
                            // console.log('speed',camera.speed)
                            // console.log('seppd',camera.position)
                            count=0
                        }
                        
    
                    })
    
                    return scene
            }
    
        window.initFunction=async function(){
            let asyncEngineCreation=async function(){
                try{
                    return createDefaultEngine()
                }catch(e){
                    return createDefaultEngine()
                }
            }
    
            window.engine=await asyncEngineCreation()
            if(!engine){
                throw('engine should not be null')
            }
            window.scene=createScene()
            
        }
    
        initFunction().then(()=>{
            scene.then((returnedScene)=>{
                sceneToRender=returnedScene
            })
    
            let limit=60
            let count=0
            let fps=0
            engine.runRenderLoop(function(){
                count++
                if(count===limit){
                    fps=Math.round(engine.getFps())
                    count=0
                    console.log('当前帧数是'+fps)
                }
    
                if(sceneToRender&&sceneToRender.activeCamera){
                    sceneToRender.render()
                }
            })
    
        })
    
        window.addEventListener('resize',function(){
            engine.resize()
        })
    
    </script>
    </body>
    </html>
    

    问题1

    1 .视角大小,换一种相机之后,展示的大小有问题啊
    2 .默认的freeCamera的操作逻辑如下

    1 .键盘,左右方向键左右移动相机,上下方向键前后移动
    2 .鼠标,以相机为原点绕旋转轴旋转相机
    3 .触摸-左右滑动相机左右移动,上下滑动前后移动
    

    3 .我们想要的效果

    1 .控制缩放,摄像机可以拉近和拉远,滚轮实现
    2 .移动
    3 .限制相机移动范围
    

    4 .后续

    1 .相机的移动需要有缓动,不能很僵硬的移动
    2 .不同相机高度,移动的距离是不一样的。镜头远,滑动移动的距离大,镜头近,滑动移动的距离小
    3 .要把屏幕上的移动距离和实际的体块的大小完全一致,比如屏幕上移动了10cm,地图里面的地块也应该移动了这么多
    4 .按照原来的基本逻辑,按一下键盘,会移动5的距离,但是拖拽的时候就做不了这个
    5 .这里改相机的speed没用,加了一个系数实现了想要的效果
    6 .镜头的朝向,这个要改镜头的旋转
    7 .他的渲染策略是以中心为基础渲染,现在是第三方镜头,会不会存在一些已经渲染的但是不在镜头中的呢
    

    5 .是否需要更改相机类型为followCamera
    6 .裸体一旦沦为艺术,那便是最圣洁的,道德一旦沦为虚伪,那便是最下流的

    SLG地图的更新

    1 .地形,地块的更新,拖动相机才会发生变化。这种是拖动相机自动完成的,这里面存储了物体的信息,这里只有拖动才会触发。
    2 .不拖动相机也会发生的变化,部队,建筑物,本来是想建筑物

    1 .有一些建筑物的信息不拖动也需要触发,比如建筑的升级,这种变化就需要单独触发地形更新,但是为了动态地形里面的一个建筑物,就让全部的update,感觉也有点不合理,这里要看下怎么操作
    2 .部队那些不放在这里处理,需要一个专门更新mesh的函数现在的terrain.update,应该是全部会计算的
    

    LOD不能添加

    terrain.updateCameraLOD = function(terrainCamera) {
                            // LOD value increases with camera altitude
                            var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
                            // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
                            return camLOD;
                        };
    1 .slg里面的lod只是做一些镜头拉小,野怪消失不见,建筑物或者地形的渲染精度是不会变得
    

    性能问题

    1 .首次渲染出来,拖拽的时候会有卡顿


    截屏2021-12-12 下午4.08.52.png

    2 .为什么第一次pointerDown会有问题,第一次不是仅仅是添加了一个鼠标被按下的标志位么


    截屏2021-12-12 下午4.13.50.png

    TODO

    1 .添加阴影
    2 .添加各种地形
    3 .添加各种建筑
    4 .添加各种动画

    最难的,里面的坐标也有点难搞

    1 .

    实践

    1 .这里面好像不能添加复杂的粒子,添加一个引入的mesh很卡,但是单独的固体粒子系统里面是没问题的,而且,动态地形还需要做的是我还要管理mesh的动画和操作,这样越来越复杂的话,感觉还是要分开,那这样动态地形的优势就完全没了.还是需要管理单独的粒子系统

    单独的粒子系统可以加入引入mesh的情况,选中也是可以的,播放动画呢

    const scene = new BABYLON.Scene(engine);
    
        const camera = new BABYLON.ArcRotateCamera("ArcRotateCamera", -Math.PI / 2, Math.PI / 2.2, 50, new BABYLON.Vector3(0, 0, 0), scene);
        camera.attachControl(canvas, true);
        const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
    
        const SPS = new BABYLON.SolidParticleSystem("SPS", scene,{useModelMaterial:true});
    
        BABYLON.SceneLoader.ImportMeshAsync("", "scenes/", "skull.babylon", scene).then((result)=>{
            console.log(result)
            SPS.addShape(result.meshes[0],2)
            const mesh = SPS.buildMesh();
    
            SPS.initParticles = () => {
            for (let p = 0; p < SPS.nbParticles; p++) {
                const particle = SPS.particles[p];
                particle.position.x = BABYLON.Scalar.RandomRange(-50, 50);
                particle.position.y = BABYLON.Scalar.RandomRange(-50, 50);
                particle.position.z = BABYLON.Scalar.RandomRange(-50, 50);
              }
            };
    
            // //Update SPS mesh
            SPS.initParticles();
            SPS.setParticles();
        })
        return scene;
    

    单独的粒子播放各自的动画

    1 .这个就更不要想了
    2 .现在是可以展示粒子了,但是很卡,视野内显示有1000个不行了,下一步就是要看是粒子的局限,还是这个动态系统的问题

    截屏2021-12-15 上午12.18.56.png
    3 .直接拿固体粒子发现是可以的,3000以下是无压力的,所以说地形就应该干地形应该做的事情,或者粒子不是那么多,现在可能许多点都是都是很密的,把密度往下压一压试试
    4 .要看粒子的里面用这种mesh能不能播自己的动画
    5 .粒子是否真的能干这个东西,还是要使用最经典的克隆https://playground.babylonjs.com/#TWQZAU#1或者这个是什么原理 瘦实例来实现的,粒子是完全有用的,所以还是瘦实例是对的.这里应该只是用来摆山,城之类不动的,玩家主城

    解决方法

    1 .减少密度,仅仅只是永远不变的物体,加入粒子,比如一些固定的城,山之类的
    2 .类似土地之类的大面积的,压根就不让他展示渲染,换另一种逻辑算了,操作不了。尤其是带动画的这种,感觉更加不行。粒子,只能用来实现一些简单的,操作,这种算是复杂的是不行的.
    3 .https://doc.babylonjs.com/divingDeeper/particles/particle_system/animation 这里看来是可以实现动画粒子的,但是。只是纹理之类的,也不算是真的mesh动作的那种吧,这里只是可以用来实现复杂的粒子效果而存在。比如火,烟之类的

    减小密度,看是否卡顿

    for (var l = 0; l < mapSubZ; l++) {
                            for (var w = 0; w < mapSubX; w++) {
                                var x = (w - mapSubX * 0.5) * 2.0;
                                var z = (l - mapSubZ * 0.5) * 2.0;
                                var y = noise.simplex2(x * noiseScale, z * noiseScale);               // altitude
                                y *= (0.5 + y) * y * elevationScale; 
    
                                mapData[3 *(l * mapSubX + w)] = x;
                                mapData[3 * (l * mapSubX + w) + 1] = y;
                                mapData[3 * (l * mapSubX + w) + 2] = z; 
    
                                let index=l*mapSubX+w
                                let random=Math.random()
                                if(random>0.99999){
                                    // 如果满足这个条件,就添加一个物体
                                    let xp=x
                                    let yp=y
                                    let zp=z
    
                                    let ry=Math.random()*3.6
    
                                    let sx=0.5+Math.random()
                                    let sy=0.5+Math.random()
                                    let sz=0.5+Math.random()
    
                                    let type = index % 3;
                                    SPmapData[index % 3].push(xp, yp, zp, 0, ry, 0, sx, sy, sz);
                                    console.log(SPmapData,'现在的粒子数',random)
                                }
                                
                        }
                    }  
    //这样看起来完全不卡顿啊
    

    虚拟地图的摄像机

    1 .想做类似于MOBA的地图视角,但是发现地图生成的时候就是按照摄像机在正中间,如果简单的来偏转,就需要重新渲染更多的无用的出来
    [图片上传失败...(image-d10f5-1639585054269)]
    2 .最后结论,虚拟地形上的元素,只适合非常小的,不变的东西,比如草,没了,别的东西都不适合,虽然较少粒子数也支持一些复杂的东西,但是也不支持mesh的动画啥的,投一些宝箱啥的,但是一旦东西多了。粒子是会创建每个实体的,不能用来节省资源。所以还是要用瘦实例的方式来操作
    3 .上面是很多的粒子实体,但是实际上同屏最多展示100块地。试下100块这些会不会卡
    4 .如何查看当前画面有多少粒子呢?
    5 .100188 '现在的粒子数'三倍的这个粒子数,都是可以跑满60帧的
    6 .复杂的这些同屏显示500个就会卡顿,但是正常的slg游戏,一个屏幕最多显示100块地,那就是说这种方法还是可以用的

    <!DOCTYPE html>
    <!-- 添加小人,使用序列图 -->
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
        <title>Babylon - Getting Started</title>
        <!-- Link to the last version of BabylonJS -->
        <script src="https://preview.babylonjs.com/babylon.js"></script>
        <!-- Link to the last version of BabylonJS loaders to enable loading filetypes such as .gltf -->
        <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
        <!-- Link to pep.js to ensure pointer events work consistently in all browsers -->
        <script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
        <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script> -->
        <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script> -->
        <!-- <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script> -->
        <!-- <script src="https://cdn.rawgit.com/BabylonJS/Extensions/master/DynamicTerrain/dist/babylon.dynamicTerrain.min.js"></script> -->
        <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
        <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
        <script src="./dy.js"></script>
        <script src="./noise.js"></script>
        <!-- <script src="./tree.js"></script> -->
    </head>
    <style>
        html, body {
            overflow: hidden;
            width   : 100%;
            height  : 100%;
            margin  : 0;
            padding : 0;
        }
    
        #renderCanvas {
            width   : 100%;
            height  : 100%;
            touch-action: none;
        }
    </style>
    <body>
        <canvas id="renderCanvas" touch-action="none"></canvas>
        <script>
            const canvas = document.getElementById("renderCanvas");
            var engine = null;
            // 这里还不能用let,不然就爆炸,获取不到engine
            var scene = null;
            var sceneToRender = null;
            const createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true,  disableWebGL2Support: false}); };
    
            let createScene=async function(){
                // 关键函数都写在这个里面
    
                    var scene = new BABYLON.Scene(engine)
                    var camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3(0, 20, 0), scene);    
                    // 第二个坐标是摄像机在场景中的位置,现在还需要一个就是摄像机的朝向
                    camera.rotation=new BABYLON.Vector3(0.8697057340138546,0.00772265625,0)
                    camera.position.y=100
                    // 高度最高现在定为60,最大值,按照这个最大值来调参数,地图大小也需要调,现在镜头外面还有没有显示的地
    
                    
                    let tree=await BABYLON.SceneLoader.ImportMeshAsync('','https://playground.babylonjs.com/scenes/Elf/','Elf.gltf',scene)
                    console.log('tree',tree)
                    var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.0, 1.0, 0.0), scene);
                    light.intensity = 0.75;
                    light.specular = BABYLON.Color3.Black();
    
                    // 这俩值是不是决定了地图的实际大小
                    let terrainTexture=new BABYLON.Texture("https://www.babylonjs-playground.com/textures/ground.jpg",scene)
    
                    let terrainMat=new BABYLON.StandardMaterial('tm',scene)
                    terrainMat.diffuseTexture=terrainTexture
                    terrainMat.diffuseTexture.UScale=1
                    terrainMat.diffuseTexture.VScale=1
    
                    var spsMaterial = new BABYLON.StandardMaterial("spsm", scene);
                    var spsUrl = "https://jerome.bousquie.fr/BJS/images/uv_texture.jpg";
                    var spsTexture = new BABYLON.Texture(spsUrl, scene);
                    spsMaterial.diffuseTexture = spsTexture;
                    
                    var mapSubX = 1000;            
                    var mapSubZ = 1000;              
                    var seed = 0.3;                 
                    var noiseScale = 0.03;         
                    var elevationScale = 6.0;
                    noise.seed(seed);
                    var mapData = new Float32Array(mapSubX * mapSubZ * 3); 
    
                    let SPmapData=[[],[],[]]
                    // 这俩其实不是很常用,毕竟真的引入mesh这俩应该都有的吧,除非mesh仅仅是颜色不一样代表不同的物体,来做优化的时候
                    var SPcolorData = [[], [],  []];
                    var SPuvData = [[], [], []];
    
                    // x位移系数:在原来的基础上移动的距离,或者说实际屏幕和地图的位移比例吧
                    let xMove=0.033
                    // 第一次0.5的值感觉还是有点大
                    // y位移系数
                    let yMove=0.053
                    // 最后这个值感觉这里就差不多了,但是这个系数还是要和镜头高度有关系的
    
                    for (var l = 0; l < mapSubZ; l++) {
                            for (var w = 0; w < mapSubX; w++) {
                                var x = (w - mapSubX * 0.5) * 2.0;
                                var z = (l - mapSubZ * 0.5) * 2.0;
                                var y = noise.simplex2(x * noiseScale, z * noiseScale);               // altitude
                                y *= (0.5 + y) * y * elevationScale; 
    
                                mapData[3 *(l * mapSubX + w)] = x;
                                mapData[3 * (l * mapSubX + w) + 1] = y;
                                mapData[3 * (l * mapSubX + w) + 2] = z; 
    
                                let index=l*mapSubX+w
                                let random=Math.random()
                                if(Math.random()>0.9){
                                    // 如果满足这个条件,就添加一个物体
                                    let xp=x
                                    let yp=y
                                    let zp=z
    
                                    let ry=Math.random()*3.6
    
                                    let sx=0.5+Math.random()
                                    let sy=0.5+Math.random()
                                    let sz=0.5+Math.random()
    
                                    let type = index % 3;
                                    SPmapData[type].push(xp, yp, zp, 0, ry, 0, sx, sy, sz);
                                    // console.log(SPmapData,'现在的粒子数',random)
                                }
                                
                        }
                    }   
    
                    console.log(SPmapData[0].length/3,'现在的粒子数')
                    let model2=BABYLON.MeshBuilder.CreatePolyhedron('m2',{size:0.5},scene)
                    let model3=BABYLON.MeshBuilder.CreateSphere('m3',{segments:3},scene)
    
                    let sps=new BABYLON.SolidParticleSystem("SPS",scene)
                    let type1=sps.addShape(tree.meshes[1],100)
                    // let type1=sps.addShape(model2,1000)
                    let type2=sps.addShape(model2,100)
                    // 为什么数量少的时候,镜头了将要到达的地方物体消失,正好相反了
                    // 这里的数量是当前地形中最多可见的形状的数量,100的意思就是地图中最多可见100个model2模型
                    let type3=sps.addShape(model3,100)
    
                    sps.buildMesh()
    
                    tree.meshes[1].dispose()
                    model2.dispose()
                    model3.dispose()
                    // sps.mesh.material = spsMaterial;
    
                    var terrainSub = 160;  
                    // 这个才是镜头最大展示的数量
                    var params = {
                        mapData: mapData,               
                        mapSubX: mapSubX,               
                        mapSubZ: mapSubZ,
                        SPmapData: SPmapData,           
                        sps: sps,                       
                        terrainSub: terrainSub,
                        subToleranceX:160,
                        subToleranceZ:160,
                        // 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
                    }
                    var terrain = new BABYLON.DynamicTerrain("t", params, scene);
                    terrain.isAlwaysVisible=true
                    var terrainMaterial = new BABYLON.StandardMaterial("tm", scene);
                    terrainMaterial.diffuseTexture = terrainTexture;
                    terrain.mesh.material = terrainMaterial;
    
                        // terrain.updateCameraLOD = function(terrainCamera) {
                        //     // LOD value increases with camera altitude
                        //     var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
                        //     // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
                        //     return camLOD;
                        // };
                    terrain.update(true);
                   
    
                    let startPosition
                    // 开始拖拽的位置
                    let isPointerDowm=false
    
                    function pointerDown(e) {
                        isPointerDowm=true
                    }
    
                    function pointerUp(){
                        isPointerDowm=false
                    }
    
                    function pointerMove(e){
                       if(!isPointerDowm)return 
                       let mx=e.event.movementX*xMove
                       let my=e.event.movementY*yMove
                       //这是最基础的调整,还需要调整,乘一个系数    
                        camera.position.addInPlace(new BABYLON.Vector3(-mx,0,my))
                    }
    
                    function pointerWhell(e){
                        const delta=e.event.wheelDelta
                        if(delta>0){
                            // if(camera.position.y>=100)return
                            // camera.position.y+=1
    
                            // 用这里来调试下摄像机吧.现在的摄像机老是感觉很奇怪
                            camera.rotation.y+=0.1
                            // console.log(camera.rotation.x)
                            
                        }else{
                            // if(camera.position.y<=20)return
                            // camera.position.y-=1
    
                            camera.rotation.y-=0.1
                        }
    
                        // console.log('当前相机高度',camera.position.y)
                        console.log(camera.rotation)
                        
                    }
    
                    scene.onPointerObservable.add((pointerInfo)=>{
                        switch(pointerInfo.type){
                            case BABYLON.PointerEventTypes.POINTERDOWN:
                                pointerDown(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERUP:
                                pointerUp(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERMOVE:
                                pointerMove(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERWHEEL:
                                pointerWhell(pointerInfo)
                                break;
                        }
                    })
                    let count=0
                    scene.registerBeforeRender(()=>{
                        count++
                        if(count>=200){
                            // console.log('speed',camera.speed)
                            // console.log('seppd',camera.position)
                            count=0
                        }
                        
    
                    })
    
                    return scene
            }
    
        window.initFunction=async function(){
            let asyncEngineCreation=async function(){
                try{
                    return createDefaultEngine()
                }catch(e){
                    return createDefaultEngine()
                }
            }
    
            window.engine=await asyncEngineCreation()
            if(!engine){
                throw('engine should not be null')
            }
            window.scene=createScene()
            
        }
    
        initFunction().then(()=>{
            scene.then((returnedScene)=>{
                sceneToRender=returnedScene
            })
    
            let limit=60
            let count=0
            let fps=0
            engine.runRenderLoop(function(){
                count++
                if(count===limit){
                    fps=Math.round(engine.getFps())
                    count=0
                    console.log('当前帧数是'+fps)
                }
    
                if(sceneToRender&&sceneToRender.activeCamera){
                    sceneToRender.render()
                }
            })
    
        })
    
        window.addEventListener('resize',function(){
            engine.resize()
        })
    
    </script>
    </body>
    </html>
    

    总结

    1 .也就是说,最小的渲染粒度是100块地,现在可以保证这里是完全正确的
    2 .总的地图数据我们可以一次全加载进来,甚至包括点的数据,但是同时最多显示的数据则是要严格要求的.这样就保证了不会卡顿.在虚拟地形里面,同时操作100块地都会卡顿,无法60帧

    截屏2021-12-19 下午11.12.46.png
    3 .粒子系统和这个分来来展示,单独这个显示是可以4040,就同一个展示来说,那么他们唯一的不同,就还没算,移动相机的时候需要新算的地块,因为就展示来说,5050都不会卡,关键是拖拽之后,还有新的数据需要显示.
    1 .其实这里如果我们要做的就是每次移动完,拿到当前摄像机里面的所有的数据然后渲染出来.这样感觉的话,这种也是要崩的啊,其实有点像这种话操作,每次鼠标抬起的时候,就渲染新的数据,甚至move的时候也渲染
    function pointerUp(e){
                        isPointerDowm=false
    
                        let xCount=20
                        let yCount=20
    
                        console.log(e)
    
                        // 循环添加树
                        for(let x=0;x<xCount;x++){
                            for(let y=0;y<yCount;y++){
                                let newTree=tree.meshes[0].clone(`tree-${x}-${y}`)
                                newTree.position.x=10*x+e.pickInfo.pickedPoint.x
                                newTree.position.z=10*y+e.pickInfo.pickedPoint.z
                            }
                        }
                    }
    //果然不行,40*40着这种情况根本拖动不了.而且还仅仅是up的时候不算
    //进过测试,up这种操作,仅仅支持20*20这种量级,这里还可以优化,一次拖拽,并不需要全部生成新的,仅仅是需要diff,这还有需要操作的东西.也就是说,每次move,都要算出remove,add的数据,然后做删除,增加,先增加,然后删除.这个好像还很麻烦,但是虚拟列表那里已经自带了.还是想下那个行不行,如果move的时候性能最后和虚拟布局的性能一样,那还不如用那个来做,不过,这个还是最简单的clone的操作,可能从实现方面已经是最拉胯的方式了
    
    move的时候
    

    最关键的操作

    1 .最关键的现在感觉是地图的摄像机高度,角度,这些决定了真的应该渲染的元素的数量,比如这样的图,这个高度,这个角度,决定了地图内显示的真正数量的数据,


    截屏2021-12-20 上午12.22.11.png
    var terrainSub = 50;  
                    // 这个才是镜头最大展示的数量,这个才是关键,这个是50的话,一点也不卡
                    var params = {
                        mapData: mapData,               
                        mapSubX: mapSubX,               
                        mapSubZ: mapSubZ,
                        SPmapData: SPmapData,           
                        sps: sps,                       
                        terrainSub: terrainSub,
                        subToleranceX:terrainSub,
                        subToleranceZ:terrainSub,
                        // 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
                    }
    

    3 .现在的镜头高度下,需要70才能完全盖住视野,所以最后渲染的就是70个宽度内的虚拟列表内的东西,一切都和摄像机高度有关系,但是65的时候发现帧数就已经不满60了,所以这个最后还是一个协调的东西,互相妥协,也就是高度,视野内渲染的数量,然后去决定到底使用那种方案
    4 .

    <!DOCTYPE html>
    <!-- 添加小人,使用序列图 -->
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>
        <title>Babylon - Getting Started</title>
        <!-- Link to the last version of BabylonJS -->
        <script src="https://preview.babylonjs.com/babylon.js"></script>
        <!-- Link to the last version of BabylonJS loaders to enable loading filetypes such as .gltf -->
        <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
        <!-- Link to pep.js to ensure pointer events work consistently in all browsers -->
        <script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
        <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.min.js"></script> -->
        <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script> -->
        <!-- <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script> -->
        <!-- <script src="https://cdn.rawgit.com/BabylonJS/Extensions/master/DynamicTerrain/dist/babylon.dynamicTerrain.min.js"></script> -->
        <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
        <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
        <script src="./dy.js"></script>
        <script src="./noise.js"></script>
        <!-- <script src="./tree.js"></script> -->
    </head>
    <style>
        html, body {
            overflow: hidden;
            width   : 100%;
            height  : 100%;
            margin  : 0;
            padding : 0;
        }
    
        #renderCanvas {
            width   : 100%;
            height  : 100%;
            touch-action: none;
        }
    </style>
    <body>
        <canvas id="renderCanvas" touch-action="none"></canvas>
        <script>
            const canvas = document.getElementById("renderCanvas");
            var engine = null;
            // 这里还不能用let,不然就爆炸,获取不到engine
            var scene = null;
            var sceneToRender = null;
            const createDefaultEngine = function() { return new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stencil: true,  disableWebGL2Support: false}); };
    
            let createScene=async function(){
                // 关键函数都写在这个里面
    
                    var scene = new BABYLON.Scene(engine)
                    var camera = new BABYLON.UniversalCamera("camera1", new BABYLON.Vector3(0, 20, 0), scene);    
                    // 第二个坐标是摄像机在场景中的位置,现在还需要一个就是摄像机的朝向
                    camera.rotation=new BABYLON.Vector3( 1.8697057340138552,0.00772265625,0)
                    camera.position.y=70
                    // 高度最高现在定为60,最大值,按照这个最大值来调参数,地图大小也需要调,现在镜头外面还有没有显示的地
    
                    
                    // let tree=await BABYLON.SceneLoader.ImportMeshAsync('','https://playground.babylonjs.com/scenes/Elf/','Elf.gltf',scene)
                    let tree=await BABYLON.SceneLoader.ImportMeshAsync('',"http://192.168.1.102:8080/source/glb/","tree.babylon",scene)
                    console.log('tree',tree.meshes[0].rotation)
                    var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0.0, 1.0, 0.0), scene);
                    light.intensity = 0.75;
                    light.specular = BABYLON.Color3.Black();
    
                    // 这俩值是不是决定了地图的实际大小
                    let terrainTexture=new BABYLON.Texture("https://www.babylonjs-playground.com/textures/ground.jpg",scene)
    
                    let terrainMat=new BABYLON.StandardMaterial('tm',scene)
                    terrainMat.diffuseTexture=terrainTexture
                    terrainMat.diffuseTexture.UScale=1
                    terrainMat.diffuseTexture.VScale=1
    
                    var spsMaterial = new BABYLON.StandardMaterial("spsm", scene);
                    var spsUrl = "https://jerome.bousquie.fr/BJS/images/uv_texture.jpg";
                    var spsTexture = new BABYLON.Texture(spsUrl, scene);
                    spsMaterial.diffuseTexture = spsTexture;
                    
                    var mapSubX = 1000;            
                    var mapSubZ = 1000;              
                    var seed = 0.3;                 
                    var noiseScale = 0.03;         
                    var elevationScale = 6.0;
                    noise.seed(seed);
                    var mapData = new Float32Array(mapSubX * mapSubZ * 3); 
    
                    let SPmapData=[[],[],[]]
                    // 这俩其实不是很常用,毕竟真的引入mesh这俩应该都有的吧,除非mesh仅仅是颜色不一样代表不同的物体,来做优化的时候
                    var SPcolorData = [[], [],  []];
                    var SPuvData = [[], [], []];
    
                    // x位移系数:在原来的基础上移动的距离,或者说实际屏幕和地图的位移比例吧
                    let xMove=0.033
                    // 第一次0.5的值感觉还是有点大
                    // y位移系数
                    let yMove=0.053
                    // 最后这个值感觉这里就差不多了,但是这个系数还是要和镜头高度有关系的
    
                    for (var l = 0; l < mapSubZ; l++) {
                            for (var w = 0; w < mapSubX; w++) {
                                var x = (w - mapSubX * 0.5) * 2.0;
                                var z = (l - mapSubZ * 0.5) * 2.0;
                                var y = noise.simplex2(x * noiseScale, z * noiseScale);               // altitude
                                y *= (0.5 + y) * y * elevationScale; 
    
                                mapData[3 *(l * mapSubX + w)] = x;
                                mapData[3 * (l * mapSubX + w) + 1] = y;
                                mapData[3 * (l * mapSubX + w) + 2] = z; 
    
                                let index=l*mapSubX+w
                                let random=Math.random()
                                if(Math.random()>0.9){
                                    // 如果满足这个条件,就添加一个物体
                                    let xp=x
                                    let yp=y
                                    let zp=z
    
                                    let ry=Math.random()*3.6
    
    
    
                                    let type = index % 3;
                                    SPmapData[type].push(xp, yp, zp, -1.57, 0, 0, 10,10,10);
                                    // console.log(SPmapData,'现在的粒子数',random)
                                } 
                        }
                    }   
    
                    console.log(SPmapData[0].length/3,'现在的粒子数')
                    let model2=BABYLON.MeshBuilder.CreatePolyhedron('m2',{size:0.5},scene)
                    let model3=BABYLON.MeshBuilder.CreateSphere('m3',{segments:3},scene)
    
                    let sps=new BABYLON.SolidParticleSystem("SPS",scene,{useModelMaterial:true})
                    let type1=sps.addShape(tree.meshes[0],1000)
                    // let type1=sps.addShape(model2,1000)
                    let type2=sps.addShape(model2,1)
                    // 为什么数量少的时候,镜头了将要到达的地方物体消失,正好相反了
                    // 这里的数量是当前地形中最多可见的形状的数量,100的意思就是地图中最多可见100个model2模型
                    let type3=sps.addShape(model3,1)
    
                    sps.buildMesh()
    
                    // tree.meshes[0].dispose()
                    model2.dispose()
                    model3.dispose()
                    // sps.mesh.material = spsMaterial;
    
                    var terrainSub = 65;  
                    // 这个才是镜头最大展示的数量
                    var params = {
                        mapData: mapData,               
                        mapSubX: mapSubX,               
                        mapSubZ: mapSubZ,
                        SPmapData: SPmapData,           
                        sps: sps,                       
                        terrainSub: terrainSub,
                        subToleranceX:terrainSub,
                        subToleranceZ:terrainSub,
                        // 这个更新是指所有的静态元素更新,有的时候,可能不移动镜头都需要更新计算某些值,比如地块的变化。所以还需要主动触发地块更新
                    }
                    var terrain = new BABYLON.DynamicTerrain("t", params, scene);
                    terrain.isAlwaysVisible=true
                    var terrainMaterial = new BABYLON.StandardMaterial("tm", scene);
                    terrainMaterial.diffuseTexture = terrainTexture;
                    terrain.mesh.material = terrainMaterial;
    
                        // terrain.updateCameraLOD = function(terrainCamera) {
                        //     // LOD value increases with camera altitude
                        //     var camLOD = Math.abs((terrainCamera.globalPosition.y / 30.0)|0);
                        //     // 这里加了动态调节,就不用别的地方加了,但是这里好像不能使用这个,因为我开大镜头,精度是不能丢失的,现在会丢失地面建筑物的精度,这个只能保证拉大镜头,地面永远都在镜头内
                        //     return camLOD;
                        // };
                    terrain.update(true);
                   
    
                    let startPosition
                    // 开始拖拽的位置
                    let isPointerDowm=false
    
                    function pointerDown(e) {
                        isPointerDowm=true
                    }
    
                    function pointerUp(){
                        isPointerDowm=false
                    }
    
                    function pointerMove(e){
                       if(!isPointerDowm)return 
                       let mx=e.event.movementX*xMove
                       let my=e.event.movementY*yMove
                       //这是最基础的调整,还需要调整,乘一个系数    
                        camera.position.addInPlace(new BABYLON.Vector3(-mx,0,my))
                    }
    
                    function pointerWhell(e){
                        const delta=e.event.wheelDelta
                        if(delta>0){
                            if(camera.position.y>=100)return
                            camera.position.y+=1
    
                            // 用这里来调试下摄像机吧.现在的摄像机老是感觉很奇怪
                            // camera.rotation.x+=0.1
                            // console.log(camera.rotation.x)
                            
                        }else{
                            if(camera.position.y<=20)return
                            camera.position.y-=1
    
                            // camera.rotation.x-=0.1
                        }
    
                        // console.log('当前相机高度',camera.position.y)
                        console.log(camera.rotation)
                        
                    }
    
                    scene.onPointerObservable.add((pointerInfo)=>{
                        switch(pointerInfo.type){
                            case BABYLON.PointerEventTypes.POINTERDOWN:
                                pointerDown(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERUP:
                                pointerUp(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERMOVE:
                                pointerMove(pointerInfo)
                                break
                            case BABYLON.PointerEventTypes.POINTERWHEEL:
                                pointerWhell(pointerInfo)
                                break;
                        }
                    })
                    let count=0
                    scene.registerBeforeRender(()=>{
                        count++
                        if(count>=200){
                            // console.log('speed',camera.speed)
                            // console.log('seppd',camera.position)
                            count=0
                        }
                        
    
                    })
    
                    return scene
            }
    
        window.initFunction=async function(){
            let asyncEngineCreation=async function(){
                try{
                    return createDefaultEngine()
                }catch(e){
                    return createDefaultEngine()
                }
            }
    
            window.engine=await asyncEngineCreation()
            if(!engine){
                throw('engine should not be null')
            }
            window.scene=createScene()
            
        }
    
        initFunction().then(()=>{
            scene.then((returnedScene)=>{
                sceneToRender=returnedScene
            })
    
            let limit=60
            let count=0
            let fps=0
            engine.runRenderLoop(function(){
                count++
                if(count===limit){
                    fps=Math.round(engine.getFps())
                    count=0
                    console.log('当前帧数是'+fps)
                }
    
                if(sceneToRender&&sceneToRender.activeCamera){
                    sceneToRender.render()
                }
            })
    
        })
    
        window.addEventListener('resize',function(){
            engine.resize()
        })
    
    </script>
    </body>
    </html>
    

    自己那个合成的mesh不行的,可以使用gltf导出器,把已经合并好的mesh,连带着材质,合成为一个mesh。然后到处成为一个gltf,不就可以了么,这样也不需要每次都用代码加载了.

    相关文章

      网友评论

          本文标题:15 世界地图 SLG视野

          本文链接:https://www.haomeiwen.com/subject/usuzxrtx.html