【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开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Java发送SNMP至交换机获取交换机状态实现方式

《Java发送SNMP至交换机获取交换机状态实现方式》文章介绍使用SNMP4J库(2.7.0)通过RCF1213-MIB协议获取交换机单/多路状态,需开启SNMP支持,重点对比SNMPv1、v2c、v... 目录交换机协议SNMP库获取交换机单路状态获取交换机多路状态总结交换机协议这里使用的交换机协议为常

JS纯前端实现浏览器语音播报、朗读功能的完整代码

《JS纯前端实现浏览器语音播报、朗读功能的完整代码》在现代互联网的发展中,语音技术正逐渐成为改变用户体验的重要一环,下面:本文主要介绍JS纯前端实现浏览器语音播报、朗读功能的相关资料,文中通过代码... 目录一、朗读单条文本:① 语音自选参数,按钮控制语音:② 效果图:二、朗读多条文本:① 语音有默认值:②

在Node.js中使用.env文件管理环境变量的全过程

《在Node.js中使用.env文件管理环境变量的全过程》Node.js应用程序通常依赖于环境变量来管理敏感信息或配置设置,.env文件已经成为一种流行的本地管理这些变量的方法,本文将探讨.env文件... 目录引言为什么使php用 .env 文件 ?如何在 Node.js 中使用 .env 文件最佳实践引

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

C#使用iText获取PDF的trailer数据的代码示例

《C#使用iText获取PDF的trailer数据的代码示例》开发程序debug的时候,看到了PDF有个trailer数据,挺有意思,于是考虑用代码把它读出来,那么就用到我们常用的iText框架了,所... 目录引言iText 核心概念C# 代码示例步骤 1: 确保已安装 iText步骤 2: C# 代码程

Pandas处理缺失数据的方式汇总

《Pandas处理缺失数据的方式汇总》许多教程中的数据与现实世界中的数据有很大不同,现实世界中的数据很少是干净且同质的,本文我们将讨论处理缺失数据的一些常规注意事项,了解Pandas如何表示缺失数据,... 目录缺失数据约定的权衡Pandas 中的缺失数据None 作为哨兵值NaN:缺失的数值数据Panda

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

使用Node.js和PostgreSQL构建数据库应用

《使用Node.js和PostgreSQL构建数据库应用》PostgreSQL是一个功能强大的开源关系型数据库,而Node.js是构建高效网络应用的理想平台,结合这两个技术,我们可以创建出色的数据驱动... 目录初始化项目与安装依赖建立数据库连接执行CRUD操作查询数据插入数据更新数据删除数据完整示例与最佳