【开发指南】HTML和JS编写多用户VR应用程序的框架

1.概述

Networked-Aframe 的工作原理是将实体及其组件同步到连接的用户。要连接到房间,您需要将networked-scene组件添加到a-scene元素。对于要同步的实体,请向其添加networked组件。默认情况下,positionrotation组件是同步的,但如果您想同步其他组件或子组件,则需要定义架构。有关网络消息的更高级控制,请参阅广播自定义消息和选项部分。

2.场景组件

A-Frame<a-scene>上的组件。

<a-scene networked-scene="serverURL: /;app: <appId>;room: <roomName>;connectOnLoad: true;onConnect: onConnect;adapter: wseasyrtc;audio: false;video: false;debug: false;
">...
</a-scene>

属性

描述

默认值

serverURL

选择 WebSocket/信令服务器所在的位置。

/

app

唯一的应用程序名称。不允许有空格。

default

room

独特的房间名称。每个应用程序可以有多个。不允许有空格。每个应用程序可以有多个房间,客户端只能连接到同一应用程序和房间中的客户端。

default

connectOnLoad

网页加载后立即连接到服务器。

true

onConnect

当客户端成功连接到服务器时调用的函数。

onConnect

adapter

您要使用的网络服务,请参阅适配器。

wseasyrtc

audio

打开/关闭您的应用程序的麦克风音频流。仅当所选适配器支持时才有效。

false

video

打开/关闭您的应用程序的视频流。仅当所选适配器支持时才有效。

false

debug

打开/关闭 Networked-Aframe 调试日志。

false

3.连接

默认情况下,networked-scene将自动连接到您的服务器。为了防止这种情况发生并控制何时连接,请在networked-scene中将connectOnLoad设置为 false。当您准备好连接时,会在a-scene元素上发出connect事件。

AFRAME.scenes[0].emit('connect');

4.断开连接

要断开连接,只需从a-scene元素中删除networked-scene组件即可。

AFRAME.scenes[0].removeAttribute('networked-scene');

从页面中完全删除a-scene也可以彻底断开连接。

5.创建网络实体

<a-assets><template id="my-template"><a-entity><a-sphere color="#f00"></a-sphere></a-entity></template>
</a-assets><!-- Attach local template by default -->
<a-entity networked="template: #my-template">
</a-entity><!-- Do not attach local template -->
<a-entity networked="template:#my-template;attachTemplateToLocal:false">
</a-entity>

创建要在客户端之间同步的模板实例。默认情况下,位置和旋转将同步。buffered-interpolation库用于允许更少的网络更新,同时保持平滑的运动。

模板只能有一个根元素。当attachTemplateToLocal设置为true时,该元素上的属性将被复制到本地实体,并且子元素将被附加到本地实体。远程实例化的实体将是模板根元素的副本,并添加了networked组件。

(1)attachTemplateToLocal=true示例

<a-entity wasd-controls networked="template:#my-template">
</a-entity><!-- Locally instantiated as: -->
<a-entity wasd-controls networked="template:#my-template"><a-sphere color="#f00"></a-sphere>
</a-entity><!-- Remotely instantiated as: -->
<a-entity networked="template:#my-template;networkId:123;"><a-sphere color="#f00"></a-sphere>
</a-entity>

(2)attachTemplateToLocal=false示例

<a-entity wasd-controls networked="template:#my-template;attachTemplateToLocal:false;">
</a-entity><!-- No changes to local entity on instantiation --><!-- Remotely instantiated as: -->
<a-entity networked="template:#my-template;networkId:123;"><a-sphere color="#f00"></a-sphere>
</a-entity>
 

属性

描述

默认值

template

存储在<a-assets>中的模板标签的 css 选择器

''”

attachTemplateToLocal

设置为 false 时,不附加本地用户的模板。当本地和远程存在不同行为时,这非常有用。

true

persistent

在远程创建者(而非所有者)断开连接时,尝试获取持久实体的所有权而不是删除它们

false

6.删除网络实体

目前只有网络实体的创建者可以删除它。要删除,只需使用常规 DOM API 从 HTML 中删除元素,Networked-Aframe 将自动处理同步。

7.同步自定义组件

默认情况下,根实体上的positionrotation组件是同步的。

要同步其他组件和子实体的组件,您需要为每个模板定义一个架构。以下是定义和添加架构的方法:

NAF.schemas.add({template: '#avatar-template',components: ['position','rotation','scale',{selector: '.hairs',component: 'show-child'},{selector: '.head',component: 'material',property: 'color'},]
});

根实体的组件可以用组件的名称来定义。子实体的组件可以使用具有selector字段和component字段的对象来定义,该字段使用document.querySelector使用的标准 CSS 选择器它指定组件的名称。要仅同步多属性组件的一个属性,请添加带有属性名称的property字段。

定义架构后,通过调用NAF.schemas.add(YOUR_SCHEMA)将其添加到架构列表中。

组件数据由 A-Frame 组件data属性检索。在网络更新期间,每个组件的数据都会根据其之前的同步值进行检查;如果数据对象发生了任何变化,它将通过网络同步。

8.同步组件优化

对于每个组件,您可以定义一个requiresNetworkUpdate函数,该函数采用当前值,如果当前值较之前值发生更改,则返回 true。如果当前值和先前值足够接近,您可以返回 false,以免将此更改发送给其他参与者。

默认情况下,当您未定义它时,它始终使用defaultRequiresUpdate函数(在networked.js顶部定义),该函数使用通用deepEqual函数来将当前值与前一个值进行比较,当两个值不同时使用cachedData = AFRAME.utils.clone(newData);以保留前一个值以供下次比较。AFRAME.utils.clone实现正做JSON.parse(JSON.stringify(obj)),可以与任何类型一起使用,但这可能不是 Vector3 类型(如位置、旋转、缩放)的最佳性能实现。

只是为了让你知道这是在做什么:

> const v = new THREE.Vector3(1,2,3);
Vector3 {x: 1, y: 2, z: 3}
> JSON.parse(JSON.stringify(v))
{x: 1, y: 2, z: 3}

因此,如果值发生变化,每次同步过程完成时都会在内存中创建一个新对象,这些对象会在某一时刻被垃圾收集。如果场景很重,垃圾收集通常可能需要 1 毫秒或更长的时间,结果可能是浏览器丢掉一些帧,因此没有一致的 fps。您可以使用 Chrome 分析器来确认这一点。对于移动的头像来说,这一点几乎不会被注意到,但如果用户拥有大量连续移动的对象,这对于您的用例可能很重要。

此外,使用当前的 aframewasd-controls实现以及位置平滑方式,在用户停止按键后 2 秒,玩家位置的变化仍然低于毫米精度,因此通过网络发送大量 NAF 消息视觉上不易察觉的位置变化。 NAF 已经包含位置插值来平滑接收到的位置变化,因此通过网络发送所有这些位置变化甚至是多余的。

您可以使用专用函数来比较给定精度的两个 Vector3,通过避免创建新对象,通过网络和内存发送更少的消息来实现更好的性能:

const vectorRequiresUpdate = epsilon => {return () => {let prev = null;return curr => {if (prev === null) {prev = new THREE.Vector3(curr.x, curr.y, curr.z);return true;} else if (!NAF.utils.almostEqualVec3(prev, curr, epsilon)) {prev.copy(curr);return true;}return false;};};
};

这个函数实际上是在NAF.utils.vectorRequiresUpdate中定义的,供你使用。

要在网络模式中使用它以获得 1 毫米的位置精度和 0.5 度的旋转精度,请按如下方式使用:

{template: '#avatar-template',components: [{component: 'position',requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)},{component: 'rotation',requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)}]
}

从0.11.0版本开始,同步位置和旋转的默认模式使用上述优化。

9.同步嵌套模板 - 例如:手

要同步嵌套模板,请像这样设置 HTML 节点:

<a-entity id="player" networked="template:#player-template;attachTemplateToLocal:false;" wasd-controls><a-entity camera look-controls networked="template:#head-template;attachTemplateToLocal:false;"></a-entity><a-entity hand-controls="hand:left" networked="template:#left-hand-template"></a-entity><a-entity hand-controls="hand:right" networked="template:#right-hand-template"></a-entity>
</a-entity>

在此示例中,头部/摄像头、控制器的左手和右手将生成自己的模板,这些模板将独立于根玩家进行联网。注意:这与当前不支持的手部追踪无关。这种父子关系仅在一个级别之间有效,即。子实体的直接父实体必须具有networked组件。

您需要自己定义左手和右手模板,以便向其他用户显示手部模型。只有位置和旋转会同步给其他用户。要同步手势,请参阅下面的networked-hand-controls组件。

10.带同步手势的跟踪控制器

这是比上述更简单的替代方案。 NAF 允许轻松添加其他人可见的手部模型,这些模型显示与触摸的按钮相匹配的模拟手势(不是手部跟踪),这样您就可以向房间里的其他人指向并竖起大拇指或握拳。

您所要做的就是使用内置的networked-hand-controls组件,将这两个实体添加为相机装备的子级:

<a-entityid="my-tracked-left-hand"networked-hand-controls="hand:left"networked="template:#left-hand-default-template"
></a-entity>
<a-entityid="my-tracked-right-hand"networked-hand-controls="hand:right"networked="template:#right-hand-default-template"
></a-entity

您可以设置的公共架构属性有:

属性

描述

默认值

取值范围

color

将被设置为材质颜色

white

hand

指定实体是用于左手还是右手

left

left, right

handModelStyle

A-Frame中可用的内置模型

highPoly

highPoly, lowPoly, toon, controller

customHandModelURL

可选的自定义手模型网址

请注意“控制器”选项——它将使用控制器本身的模型,根据您的平台自动正确设置——它还将广播模型支持的按钮网格更新。 (不幸的是,Quest 2 模型按钮网格目前存在一个错误,因此不会显示任何更新。)

networked-hand-controls正在完全替换hand-controls,不要同时使用两者。如果您使用如上所述的网络组件,则无需为每只手定义模板和网络架构。默认模板和网络模式已定义如下:

<template id="left-hand-default-template"><a-entity networked-hand-controls="hand:left"></a-entity>
</template>
<template id="right-hand-default-template"><a-entity networked-hand-controls="hand:right"></a-entity>
</template>
NAF.schemas.add({template: '#left-hand-default-template',components: [{component: 'position',requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)},{component: 'rotation',requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)},'networked-hand-controls']
});
NAF.schemas.add({template: '#right-hand-default-template',components: [{component: 'position',requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)},{component: 'rotation',requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)},'networked-hand-controls']
});

11.发送自定义消息

NAF.connection.subscribeToDataChannel(dataType, callback)
NAF.connection.unsubscribeToDataChannel(dataType)NAF.connection.broadcastData(dataType, data)
NAF.connection.broadcastDataGuaranteed(dataType, data)NAF.connection.sendData(clientId, dataType, data)
NAF.connection.sendDataGuaranteed(clientId, dataType, data)

订阅和取消订阅dataType指定的网络消息的回调。使用broadcastData功能向房间内的所有客户端广播数据。要仅发送到特定客户端,请改用sendData函数。

参数

描述

clientId

将此数据发送到的 ClientId

dataType

用于标识网络消息的字符串。u(更新)、um(UpdateMulti)和r(删除)是保留数据类型,请不要使用它们

callback

收到类型dataType的消息时调用的函数。参数:function(senderId, dataType, data, targetObj)使用 easyrtc 适配器,targetObj在广播消息时可以是{targetRoom: 'roomId'},或者在向特定参与者发送消息时可以是{targetEasyrtcid: 'targetId'}。对于 janus 适配器,senderId 始终为 null,并且targetObj更多的是source参数,通常等于“janus-event”。

data

要发送给所有其他客户端的对象

12.转让实体所有权

实体的所有者负责同步其组件数据。当用户想要修改另一个用户的实体时,他们必须首先获得该实体的所有权。所有权转移示例和切换所有权组件展示了如何获取实体的所有权并更新它。

NAF.utils.takeOwnership(entityEl)

取得实体的所有权。

NAF.utils.isMine(entityEl)

检查您是否拥有指定实体。

13.事件

当 NAF 中发生某些事情时,事件就会被触发。要订阅这些事件,请遵循以下模式:

document.body.addEventListener('clientConnected', function (evt) {console.error('clientConnected event. clientId =', evt.detail.clientId);
});

创建document.body元素后需要订阅事件。这可以通过等待document.bodyonLoad方法或使用 NAF 的onConnect函数来实现。使用 NAF 活动演示作为示例。

事件列表:

事件

描述

取值范围

clientConnected

当另一个客户端连接到您时触发

evt.detail.clientId- 连接客户端的ClientId

clientDisconnected

当另一个客户端与您断开连接时触发

evt.detail.clientId- 断开连接的客户端的ClientId

entityCreated

创建网络实体时触发

evt.detail.el- 新实体

entityRemoved

删除网络实体时触发

evt.detail.networkId- 已删除实体的网络ID

以下事件在networked组件上触发。有关示例,请参阅切换所有权组件。

所有权转让事件列表:

事件

描述

参数范围

ownership-gained

当网络实体的所有权被夺取时触发

evt.detail.el- 获得所有权的实体

evt.detail.oldOwner- 前任所有者的 clientId

ownership-lost

当网络实体的所有权丢失时触发

evt.detail.el- 失去所有权的实体

evt.detail.newOwner- 新所有者的 clientId

ownership-changed

当网络实体的所有权更改时触发

evt.detail.el- 失去所有权的实体

evt.detail.oldOwner- 前任所有者的 clientId

evt.detail.newOwner- 新所有者的 clientId

14.适配器

NAF 可与多个网络库和服务一起使用。适配器是一个向 NAF 添加对库的支持的类。如果您只是在开发一个小项目或概念验证,那么您可能会使用默认配置,并且可以跳过本节。评估不同适配器时应考虑的因素包括:

  • 一间房间需要支持多少个并发用户?

  • 您想托管自己的服务器吗?或者像 Firebase 这样的“无服务器”解决方案可以完成这项工作吗?

  • 您需要音频(麦克风)流吗?

  • 您需要自定义服务器端逻辑吗?

  • 您想要 WebSocket(客户端-服务器)网络架构还是 WebRTC(点对点)网络架构?

默认情况下使用wseasyrtc适配器,它不支持音频并使用 TCP 连接。这对于生产部署来说并不理想,但是由于 WebRTC 固有的连接问题,我们将其设置为默认值。要通过 WebRTC 支持音频,请确保服务器使用 https 并将适配器更改为easyrtc(这使用 UDP)。

支持的适配器列表:

适配器

描述

音频/视频支持情况

基于WebSocket 或 WebRTC

如何启动

wseasyrtc

默认 - 使用 open-easyrtc 库

No

WebSocket

npm run dev

easyrtc

使用 open-easyrtc 库

音频和视频(相机和屏幕共享)

WebRTC

npm run dev

janus

使用 Janus WebRTC 服务器和 janus-plugin-sfu

音频和视频(相机或屏幕共享)

WebRTC

参阅 naf-janus-adapter

socketio

无需外部库的 SocketIO 实现(服务器支持房间实例化)

No

WebSocket

npm run dev-socketio

webrtc

无需外部库的原生 WebRTC 实现(正在进行中,目前没有维护者)

音频

WebRTC

npm run dev-socketio

Firebase

用于 WebRTC 信号传输的 Firebase(目前没有维护者)

No

WebRTC

参阅 naf-firebase-adapter

uWS

uWebSockets 的实现(目前没有维护者)

No

WebSocket

参阅 naf-uws-adapter

表中的 WebRTC 表示组件更新使用 WebRTC 数据通道 (UDP),而不是 WebSocket (TCP)。您仍然有一个用于信令部分的 WebSocket。

更为详细比较,请参阅文档 NAF 适配器比较。

15.音频

audio: true添加到networked-scene组件(并使用支持它的适配器)后,默认情况下您将听不到任何音频。尽管音频将进行流式传输,但在创建具有networked-audio-source的实体之前,它是听不到的。来自该实体所有者的音频将从该实体的位置在 3D 空间中发出。networked-audio-source组件必须与networked组件一起添加到实体(或实体的子实体)。

要使麦克风静音/取消静音,您可以使用以下 API(easyrtc 和 janus 适配器):

NAF.connection.adapter.enableMicrophone(enabled)

其中enabledtruefalse

16.视频

video: true(janus 适配器不需要)添加到networked-scene组件(并使用支持它的适配器)后,默认情况下您将看不到任何视频。尽管视频将进行流式传输,但在创建使用带有networked-video-source的网格(例如<a-plane>)的实体之前,它是不可见的。来自该实体所有者的视频将在 3D 空间中从该实体的位置可见。networked-video-source组件必须添加到具有networked组件的实体的<a-plane>子实体中。

目前,这仅适用于支持getMediaStream(clientId, type="video")API 的 easyrtc 和 janus 适配器。

请参阅,该示例显示了没有音频的用户摄像头。

要禁用/重新启用相机,您可以使用以下 API(仅限 easyrtc 适配器):

NAF.connection.adapter.enableCamera(enabled)

其中enabledtruefalse

使用 easyrtc 适配器,您可以使用addLocalMediaStreamremoveLocalMediaStreamAPI 添加额外的视频轨道,例如屏幕共享:

navigator.mediaDevices.getDisplayMedia().then((stream) => {NAF.connection.adapter.addLocalMediaStream(stream, "screen");
});NAF.connection.adapter.removeLocalMediaStream("screen");

请参阅多流示例,该示例使用带有networked-video-source="streamName: screen"的第二个平面向其他参与者显示屏幕共享。请务必查看此示例 html 文件末尾的注释以了解已知问题。

17.杂项

NAF.connection.isConnected()

如果已与信令服务器建立连接,则返回 true。

NAF.connection.getConnectedClients()

返回当前连接的客户端列表。

18.选项

NAF.options.updateRate

每秒调用网络组件sync函数的频率。对于大多数社交 VR 应用程序来说,10-20 是正常的。默认为15

NAF.options.useLerp

默认情况下,当创建实体时,buffered-interpolation库用于平滑位置、旋转和缩放网络更新。如果您不希望在创建时使用此功能,请将其设置为 false。

19.离线使用

NAF 已经包含 easyrtc,因此运行npm run dev将提供完全有效的解决方案,而无需访问外部服务器。不过,这些示例确实依赖于 AFrame 和其他未与 NAF 打包的依赖项。因此,必须首先使 AFrame 适应离线工作,然后对所有其他组件执行相同的操作。这基本上可以归结为下载所使用的脚本及其内容,例如 3D 模型、字体等资产。建议在网络控制台打开时加载页面并识别哪些请求来自主机外部。

对于 VR,您还需要 https,因为浏览器需要它才能实现沉浸式模式。server/easyrtc-server.js文件中提供了说明。也就是说,您必须生成密钥和证书,将它们添加到本地 CA,然后通过 NAF 提供的 Express 服务器加载它们。确保在server/easyrtc-server.js顶部正确配置,并按照说明通过https.createServer进一步向下启用 https 本身。一旦您连接到 VR 中的 NAF 服务器,浏览器仍然会抱怨证书未知。您可以单击高级并继续。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

未来的社交标杆:如何通过AI让Facebook更加智能化?

在当今信息爆炸的时代&#xff0c;社交媒体平台的智能化已成为提高用户体验和互动质量的关键因素。Facebook&#xff0c;作为全球最大的社交平台之一&#xff0c;通过人工智能&#xff08;AI&#xff09;的广泛应用&#xff0c;正不断推进其智能化进程。本文将探讨Facebook如何…

昇思25天学习打卡营第16天 | Vision Transformer图像分类

昇思25天学习打卡营第16天 | Vision Transformer图像分类 文章目录 昇思25天学习打卡营第16天 | Vision Transformer图像分类Vision Transform&#xff08;ViT&#xff09;模型TransformerAttention模块Encoder模块 ViT模型输入 模型构建Multi-Head Attention模块Encoder模块Pa…

工业三防平板助力工厂生产数据实时管理

在当今高度数字化和智能化的工业生产环境中&#xff0c;工业三防平板正逐渐成为工厂实现生产数据实时管理的得力助手。这种创新的技术设备不仅能够在恶劣的工业环境中稳定运行&#xff0c;还为工厂的生产流程优化、效率提升和质量控制带来了前所未有的机遇。 工业生产场景通常充…

机器学习——数据预处理和特征工程(sklearn)

目录 一、数据挖掘流程 1. 获取数据 2. 数据预处理 3. 特征工程 4. 建模&#xff0c;测试模型并预测出结果 5. 验证模型效果 二、sklearn中的相关包 1.sklearn.preprocessing 2.sklearn.Impute 3.sklearn.feature_selection 4.sklearn.decomposition 三、数据预处理…

【网络安全】PostMessage:分析JS实现XSS

未经许可&#xff0c;不得转载。 文章目录 前言示例正文 前言 PostMessage是一个用于在网页间安全地发送消息的浏览器 API。它允许不同的窗口&#xff08;例如&#xff0c;来自同一域名下的不同页面或者不同域名下的跨域页面&#xff09;进行通信&#xff0c;而无需通过服务器…

【Arduino IDE】安装及开发环境、ESP32库

一、Arduino IDE下载 二、Arduino IDE安装 三、ESP32库 四、Arduino-ESP32库配置 五、新建ESP32-S3N15R8工程文件 乐鑫官网 Arduino官方下载地址 Arduino官方社区 Arduino中文社区 一、Arduino IDE下载 ESP-IDF、MicroPython和Arduino是三种不同的开发框架&#xff0c;各自适…

定制开发AI智能名片商城微信小程序在私域流量池构建中的应用与策略

摘要 在数字经济蓬勃发展的今天&#xff0c;私域流量已成为企业竞争的新战场。定制开发AI智能名片商城微信小程序&#xff0c;作为私域流量池构建的创新工具&#xff0c;正以其独特的优势助力企业实现用户资源的深度挖掘与高效转化。本文深入探讨了定制开发AI智能名片商城微信…

AIoTedge智能物联网边缘计算平台:引领未来智能边缘技术

引言 随着物联网技术的飞速发展&#xff0c;我们正步入一个万物互联的时代。AIoTedge智能物联网边缘计算平台&#xff0c;以其创新的边云协同架构&#xff0c;为智能设备和系统提供了强大的数据处理和智能决策能力&#xff0c;开启了智能物联网的新篇章。 智能边缘计算平台的核…

LLaMA-Factory

文章目录 一、关于 LLaMA-Factory项目特色性能指标 二、如何使用1、安装 LLaMA Factory2、数据准备3、快速开始4、LLaMA Board 可视化微调5、构建 DockerCUDA 用户&#xff1a;昇腾 NPU 用户&#xff1a;不使用 Docker Compose 构建CUDA 用户&#xff1a;昇腾 NPU 用户&#xf…

【Java项目笔记】01项目介绍

一、技术框架 1.后端服务 Spring Boot为主体框架 Spring MVC为Web框架 MyBatis、MyBatis Plus为持久层框架&#xff0c;负责数据库的读写 阿里云短信服务 2.存储服务 MySql redis缓存数据 MinIO为对象存储&#xff0c;存储非结构化数据&#xff08;图片、视频、音频&a…

推荐一款处理TCP数据的架构--EasyTcp4Net

EasyTcp4Net是一个基于c# Pipe,ReadonlySequence的高性能Tcp通信库,旨在提供稳定,高效,可靠的tcp通讯服务。 基础的消息通讯 重试机制 超时机制 SSL加密通信支持 KeepAlive 流量背压控制 粘包和断包处理 (支持固定头处理,固定长度处理,固定字符处理) 日志支持Pipe &…

Spring MVC 的常用注解

RequestMapping 和 RestController注解 上面两个注解&#xff0c;是Spring MCV最常用的注解。 RequestMapping &#xff0c; 他是用来注册接口的路由映射。 路由映射&#xff1a;当一个用户访问url时&#xff0c;将用户的请求对应到某个方法或类的过程叫做路由映射。 Reques…

定制QCustomPlot 带有ListView的QCustomPlot 全网唯一份

定制QCustomPlot 带有ListView的QCustomPlot 文章目录 定制QCustomPlot 带有ListView的QCustomPlot摘要需求描述实现关键字: Qt、 QCustomPlot、 魔改、 定制、 控件 摘要 先上效果,是你想要的,再看下面的分解,顺便点赞搜藏一下;不是直接右上角。 QCustomPlot是一款…

基于springboot+vue+uniapp的驾校预约平台小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

35.UART(通用异步收发传输器)-RS232(2)

&#xff08;1&#xff09;RS232接收模块visio框图&#xff1a; &#xff08;2&#xff09;接收模块Verilog代码编写: /* 常见波特率&#xff1a; 4800、9600、14400、115200 在系统时钟为50MHz时&#xff0c;对应计数为&#xff1a; (1/4800) * 10^9 /20 -1 10416 …

【作业】 贪心算法1

Tips:三题尚未完成。 #include <iostream> #include <algorithm> using namespace std; int a[110]; int main(){int n,r,sum0;cin>>n>>r;for(int i0;i<n;i){cin>>a[i];}sort(a0,an);for(int i0;i<n;i){if(i>r){a[i]a[i-r]a[i];}suma[…

大气热力学(8)——热力学图的应用之一(气象要素求解)

本篇文章源自我在 2021 年暑假自学大气物理相关知识时手写的笔记&#xff0c;现转化为电子版本以作存档。相较于手写笔记&#xff0c;电子版的部分内容有补充和修改。笔记内容大部分为公式的推导过程。 文章目录 8.1 复习斜 T-lnP 图上的几种线8.1.1 等温线和等压线8.1.2 干绝热…

连锁零售门店分析思路-人货场 数据分析

连锁零售门店分析思路 以下是一个连锁零售门店的分析思路&#xff1a; 一、市场与竞争分析 二、门店运营分析&#xff08;销售分析&#xff09; 三、销售与财务分析 四、客户分析 五、数字化与营销分析 最近帮一个大学生培训&#xff0c;就门店销售分析 &#xff0c;说到门店…

使用windows批量解压和布局ImageNet ISLVRC2012数据集

使用的系统是windows&#xff0c;找到的解压命令很多都linux系统中的&#xff0c;为了能在windows系统下使用&#xff0c;因此下载Git这个软件&#xff0c;在其中的Git Bash中使用以下命令&#xff0c;因为Git Bash集成了很多linux的命令&#xff0c;方便我们的使用。 ImageNe…

[iOS]类和对象的底层原探索

[iOS]类和对象的底层探索 文章目录 [iOS]类和对象的底层探索继承链&#xff08;类&#xff0c;父类&#xff0c;元类&#xff09;instance 实例对象class 类对象meta-class 元类对象 对对象、类、元类和分类的探索instance 实例对象class 类对象meta-class 元类对象分类(catego…