本文主要是介绍【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
主要流程- 在websocket建立连接成功后,将烘焙的
navmesh
数据发送至服务器 - 服务器在收到数据后直接返回(模拟通信过程,实际上服务器也可以从文件读取然后返回给前端)
- 前端接收到数据后通过
importNavMesh
接口初始化新的navmesh
- 使用
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); }
- 在websocket建立连接成功后,将烘焙的
结果
- 左侧为使用服务器数据重绘的
navmesh
这篇关于【recast-navigation-js】通过websocket获取navmesh数据并初始化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!