【recast-navigation-js】通过websocket获取navmesh数据并初始化

2024-05-13 04:12

本文主要是介绍【recast-navigation-js】通过websocket获取navmesh数据并初始化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 说在前面
  • 目录结构
  • websocket服务器
  • 前端
  • 结果

说在前面

  • 操作系统:windows 11
  • 浏览器:edge版本 124.0.2478.97
  • recast-navigation-js版本:0.29.0
  • golang版本:1.21.5

目录结构

D:.
│  go.mod
│  go.sum
│  main.go // websocket server
└─public│  index.html└─jsmesh.js

websocket服务器

  • 服务器使用golang+gin+gorilla/websocket实现,代码比较简单:
    package mainimport ("fmt""log""net/http""github.com/gin-gonic/gin""github.com/gorilla/websocket"
    )var upgrade = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool {return true},
    }func main() {r := gin.Default()// nav mesh data // 这里只是举例,可以根据需求从文件读取或者生成var mesh []byte// html staticr.Static("/public", "./public")r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.GET("/ws", func(c *gin.Context) {// upgrade to websocketws, err := upgrade.Upgrade(c.Writer, c.Request, nil)if err != nil {log.Fatalln(err)}// release sourcedefer ws.Close()go func() {// connect done<-c.Done()fmt.Println("ws lost connection")}()messageType, p, err := ws.ReadMessage()if err != nil {fmt.Println(err)return}switch messageType {case websocket.TextMessage:// Handle Text Messagews.WriteMessage(websocket.TextMessage, p)// c.Writer.Write(p)case websocket.BinaryMessage:// Handle Binary Datafmt.Println("handle binary data")mesh = p // 这里直接假定客户端传过来的是navmesh数据ws.WriteMessage(websocket.BinaryMessage, mesh)case websocket.CloseMessage:// Websocket closefmt.Println("close websocket connection")returncase websocket.PingMessage:// Websocket pingfmt.Println("ping")ws.WriteMessage(websocket.PongMessage, []byte("ping"))case websocket.PongMessage:// Websocket pongfmt.Println("pong")ws.WriteMessage(websocket.PongMessage, []byte("pong"))default:// Unhandled message typefmt.Printf("unknown message type: %d\n", messageType)return}})r.Run() // listen and serve on 0.0.0.0:8080
    }
    

前端

  • 使用three.js绘制数据
  • index.html
    <html>
    <script type="importmap">{"imports": {"@recast-navigation/core": "https://unpkg.com/@recast-navigation/core@0.29.0/dist/index.mjs","@recast-navigation/wasm": "https://unpkg.com/@recast-navigation/wasm@0.29.0/dist/recast-navigation.wasm-compat.js","@recast-navigation/generators": "https://unpkg.com/@recast-navigation/generators@0.29.0/dist/index.mjs","@recast-navigation/three": "https://unpkg.com/@recast-navigation/three@0.29.0/dist/index.mjs","three": "https://unpkg.com/three@0.164.0/build/three.module.js","three/examples/jsm/controls/OrbitControls": "https://unpkg.com/three@0.164.0/examples/jsm/controls/OrbitControls.js"}}</script>
    <script src="./js/mesh.js" type="module" ></script><style>body {margin: 0;overflow: hidden;}canvas {width: 100%;height: 100vh;}
    </style>
    </html>
    
  • mesh.js
    主要流程
    1. 在websocket建立连接成功后,将烘焙的navmesh数据发送至服务器
    2. 服务器在收到数据后直接返回(模拟通信过程,实际上服务器也可以从文件读取然后返回给前端)
    3. 前端接收到数据后通过importNavMesh接口初始化新的navmesh
    4. 使用threejs重新绘制新的navmesh
    import * as THREE from 'three';
    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
    import {init as initRecastNavigation,NavMeshQuery,
    } from '@recast-navigation/core';
    import { generateSoloNavMesh } from '@recast-navigation/generators';
    import {DebugDrawer,getPositionsAndIndices,
    } from '@recast-navigation/three';
    import { exportNavMesh, importNavMesh } from '@recast-navigation/core';// initialise recast-navigation
    await initRecastNavigation();var ws = new WebSocket("ws://127.0.0.1:8080/ws");
    ws.binaryType = "arraybuffer"; // use arraybuffer
    ws.onopen = function () {console.log("websocket connected.");meshInit();
    };
    ws.onmessage = function (e) {console.log("websockt data:", e);var uint8_msg = new Uint8Array(e.data); // convert to uint8 arraymeshDraw(uint8_msg);
    };
    ws.onerror = function () {console.log("websocket error.");
    };// setup scene
    const renderer = new THREE.WebGLRenderer();
    document.body.appendChild(renderer.domElement);const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera();
    camera.position.set(10, 10, -10);const orbitControls = new OrbitControls(camera, renderer.domElement);// add some meshes
    const ground = new THREE.Mesh(new THREE.BoxGeometry(10, 1, 10),new THREE.MeshBasicMaterial({ color: '#333' })
    );
    ground.position.set(0, -0.5, 0);scene.add(ground);const boxOne = new THREE.Mesh(new THREE.BoxGeometry(8, 2, 1),new THREE.MeshBasicMaterial({ color: '#555' })
    );
    boxOne.rotation.y = Math.PI / 4;
    boxOne.position.set(-2, 1, 0);
    scene.add(boxOne);const boxTwo = new THREE.Mesh(new THREE.BoxGeometry(8, 2, 1),new THREE.MeshBasicMaterial({ color: '#555' })
    );
    boxTwo.rotation.y = Math.PI / 4;
    boxTwo.position.set(2, 1, 0);
    scene.add(boxTwo);// get the positions and indices that we want to generate a navmesh from
    const [positions, indices] = getPositionsAndIndices([ground,boxOne,boxTwo,
    ]);// generate a solo navmesh
    const cs = 0.05;
    const ch = 0.05;
    const walkableRadius = 0.2;
    const { success, navMesh } = generateSoloNavMesh(positions, indices, {cs,ch,walkableRadius: Math.round(walkableRadius / ch),
    });// debug draw the navmesh
    const debugDrawer = new DebugDrawer();
    debugDrawer.drawNavMesh(navMesh);
    scene.add(debugDrawer);// compute a path
    const start = { x: -4, y: 0, z: -4 };
    const end = { x: 4, y: 0, z: 4 };const navMeshQuery = new NavMeshQuery(navMesh);
    const { path } = navMeshQuery.computePath(start, end);// draw the path start
    const startMarker = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.1, 0.1),new THREE.MeshBasicMaterial({ color: 'blue' })
    );
    startMarker.position.set(start.x, start.y + 0.1, start.z);
    scene.add(startMarker);// draw the path end
    const endMarker = new THREE.Mesh(new THREE.BoxGeometry(0.1, 0.1, 0.1),new THREE.MeshBasicMaterial({ color: 'green' })
    );
    endMarker.position.set(end.x, end.y + 0.1, end.z);
    scene.add(endMarker);// draw the path line
    const line = new THREE.Line(new THREE.BufferGeometry().setFromPoints(path.map(({ x, y, z }) => new THREE.Vector3(x, y, z))),new THREE.LineBasicMaterial({ color: 'blue' })
    );
    line.position.y += 0.1;
    scene.add(line);// handle resizing
    const onWindowResize = () => {debugDrawer.resize(window.innerWidth, window.innerHeight);camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);
    };
    onWindowResize();window.addEventListener('resize', onWindowResize);// animate
    const animate = () => {requestAnimationFrame(animate);renderer.render(scene, camera);
    };animate();// send nav mesh data to server
    function meshInit() {const navMeshExport = exportNavMesh(navMesh);ws.send(navMeshExport);
    }// rebuild nav mesh from server and draw
    function meshDraw(bin) {const tmpNavMesh = importNavMesh(bin);const tmpDebugDrawer = new DebugDrawer();tmpDebugDrawer.drawNavMesh(tmpNavMesh.navMesh);tmpDebugDrawer.position.z += 10;scene.add(tmpDebugDrawer);
    }
    

结果

  • 左侧为使用服务器数据重绘的navmesh
    在这里插入图片描述

这篇关于【recast-navigation-js】通过websocket获取navmesh数据并初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/984646

相关文章

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬

Python MySQL如何通过Binlog获取变更记录恢复数据

《PythonMySQL如何通过Binlog获取变更记录恢复数据》本文介绍了如何使用Python和pymysqlreplication库通过MySQL的二进制日志(Binlog)获取数据库的变更记录... 目录python mysql通过Binlog获取变更记录恢复数据1.安装pymysqlreplicat

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

C#实现获取电脑中的端口号和硬件信息

《C#实现获取电脑中的端口号和硬件信息》这篇文章主要为大家详细介绍了C#实现获取电脑中的端口号和硬件信息的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 我们经常在使用一个串口软件的时候,发现软件中的端口号并不是普通的COM1,而是带有硬件信息的。那么如果我们使用C#编写软件时候,如

Oracle数据库使用 listagg去重删除重复数据的方法汇总

《Oracle数据库使用listagg去重删除重复数据的方法汇总》文章介绍了在Oracle数据库中使用LISTAGG和XMLAGG函数进行字符串聚合并去重的方法,包括去重聚合、使用XML解析和CLO... 目录案例表第一种:使用wm_concat() + distinct去重聚合第二种:使用listagg,

C#实现WinForm控件焦点的获取与失去

《C#实现WinForm控件焦点的获取与失去》在一个数据输入表单中,当用户从一个文本框切换到另一个文本框时,需要准确地判断焦点的转移,以便进行数据验证、提示信息显示等操作,本文将探讨Winform控件... 目录前言获取焦点改变TabIndex属性值调用Focus方法失去焦点总结最后前言在一个数据输入表单

Python实现将实体类列表数据导出到Excel文件

《Python实现将实体类列表数据导出到Excel文件》在数据处理和报告生成中,将实体类的列表数据导出到Excel文件是一项常见任务,Python提供了多种库来实现这一目标,下面就来跟随小编一起学习一... 目录一、环境准备二、定义实体类三、创建实体类列表四、将实体类列表转换为DataFrame五、导出Da

Python实现数据清洗的18种方法

《Python实现数据清洗的18种方法》本文主要介绍了Python实现数据清洗的18种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录1. 去除字符串两边空格2. 转换数据类型3. 大小写转换4. 移除列表中的重复元素5. 快速统