【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

相关文章

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过

使用SpringBoot整合Sharding Sphere实现数据脱敏的示例

《使用SpringBoot整合ShardingSphere实现数据脱敏的示例》ApacheShardingSphere数据脱敏模块,通过SQL拦截与改写实现敏感信息加密存储,解决手动处理繁琐及系统改... 目录痛点一:痛点二:脱敏配置Quick Start——Spring 显示配置:1.引入依赖2.创建脱敏

使用Python实现获取屏幕像素颜色值

《使用Python实现获取屏幕像素颜色值》这篇文章主要为大家详细介绍了如何使用Python实现获取屏幕像素颜色值,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、一个小工具,按住F10键,颜色值会跟着显示。完整代码import tkinter as tkimport pyau

详解如何使用Python构建从数据到文档的自动化工作流

《详解如何使用Python构建从数据到文档的自动化工作流》这篇文章将通过真实工作场景拆解,为大家展示如何用Python构建自动化工作流,让工具代替人力完成这些数字苦力活,感兴趣的小伙伴可以跟随小编一起... 目录一、Excel处理:从数据搬运工到智能分析师二、PDF处理:文档工厂的智能生产线三、邮件自动化:

python获取cmd环境变量值的实现代码

《python获取cmd环境变量值的实现代码》:本文主要介绍在Python中获取命令行(cmd)环境变量的值,可以使用标准库中的os模块,需要的朋友可以参考下... 前言全局说明在执行py过程中,总要使用到系统环境变量一、说明1.1 环境:Windows 11 家庭版 24H2 26100.4061

Python数据分析与可视化的全面指南(从数据清洗到图表呈现)

《Python数据分析与可视化的全面指南(从数据清洗到图表呈现)》Python是数据分析与可视化领域中最受欢迎的编程语言之一,凭借其丰富的库和工具,Python能够帮助我们快速处理、分析数据并生成高质... 目录一、数据采集与初步探索二、数据清洗的七种武器1. 缺失值处理策略2. 异常值检测与修正3. 数据

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=