我正在使用多点连接在通过Wifi连接的几个设备之间同步3D SceneKit环境中节点的移动。
节点移动在主设备上计算并发送到从设备,然后从设备将节点设置到接收到的位置。移动通过主设备的SceneView的渲染器循环以及每个从设备打开并使用MultipeerConnectivity框架管理的数据流发送。
下面的视频显示了结果,以及从机(右)上的球抖动问题,这是由于通过流接收数据时每0.6-0.7秒有规律地暂停一次。左上角的计数器显示没有丢失数据包。接收到的数据的完整性也没有问题。
模拟器中不存在这种非常有规律的从机暂停,但只有在真实的设备上运行时才存在,无论是什么设备(iPhone或iPad,旧的或新的)。
- 是否有办法找出导致从设备定期暂停的原因?*
- 让从线程上的输入流在专用线程/runloop上执行,而不是在主线程的runloop上执行,这有意义吗?*
低于执行水平
主多点连接初始化
PeerID = MCPeerID(displayName: "Master (" + UIDevice.current.name + ")")
MPCSession = MCSession(peer: PeerID, securityIdentity: nil, encryptionPreference: .none)
MPCSession.delegate = self
ServiceAdvertiser = MCNearbyServiceAdvertiser(peer: PeerID, discoveryInfo: nil, serviceType: "ARMvt")
ServiceAdvertiser.delegate = self
ServiceAdvertiser.startAdvertisingPeer()
从属多对等连接初始化
PeerID = MCPeerID(displayName: "Slave (" + UIDevice.current.name + ")")
MPCSession = MCSession(peer: PeerID, securityIdentity: nil, encryptionPreference: .none)
MPCSession.delegate = self
ServiceBrowser = MCNearbyServiceBrowser(peer: PeerID, serviceType: "ARMvt")
ServiceBrowser.delegate = self
ServiceBrowser.startBrowsingForPeers()
发送数据的函数,在呈现器函数中调用
func MPCSendData(VCRef: GameViewController, DataToSend: Dictionary<String, Any>, ViaStream: Bool = false)
{
var DataFilledToSend = DataToSend
var DataConverted = try! NSKeyedArchiver.archivedData(withRootObject: DataFilledToSend, requiringSecureCoding: true)
var TailleData: Int = 0
var NewTailleData: Int = 0
if ViaStream // Through the stream
{
// Filling data to have a constant size packet
// kSizeDataPack is set to 2048. The bigger it is the worst is the jittering.
VCRef.Compteur = VCRef.Compteur + 1
VCRef.Message.SetText(Text: String(VCRef.Compteur))
DataFilledToSend[eTypeData.Compteur.rawValue] = VCRef.Compteur
DataFilledToSend[eTypeData.FillingData.rawValue] = "A"
TailleData = DataConverted.count
DataFilledToSend[eTypeData.FillingData.rawValue] = String(repeating: "A", count: kSizeDataPack - TailleData)
DataConverted = try! NSKeyedArchiver.archivedData(withRootObject: DataFilledToSend, requiringSecureCoding: false)
NewTailleData = DataConverted.count
DataFilledToSend[eTypeData.FillingData.rawValue] = String(repeating: "A", count: kSizeDataPack - TailleData - (NewTailleData - kSizeDataPack))
DataConverted = try! NSKeyedArchiver.archivedData(withRootObject: DataFilledToSend, requiringSecureCoding: false)
if VCRef.OutStream!.hasSpaceAvailable
{
let bytesWritten = DataConverted.withUnsafeBytes { VCRef.OutStream!.write($0, maxLength: DataConverted.count) }
if bytesWritten == -1 { print("Erreur send stream") }
} else { print("No space in stream") }
}
else // Not through the stream
{
let Peer = VCRef.MPCSession.connectedPeers.first!
try! VCRef.MPCSession.send(DataConverted, toPeers: [Peer], with: .reliable)
}
}
通过从机上的流接收数据时调用的函数
func stream(_ aStream: Stream, handle eventCode: Stream.Event)
{
DispatchQueue.main.async
{
switch(eventCode)
{
case Stream.Event.hasBytesAvailable:
let InputStream = aStream as! InputStream
var Buffer = [UInt8](repeating: 0, count: kSizeDataPack)
let NumberBytes = InputStream.read(&Buffer, maxLength: kSizeDataPack)
let DataString = NSData(bytes: &Buffer, length: NumberBytes)
if let _ = NSKeyedUnarchiver.unarchiveObject(with: DataString as Data) as? [String:Any] //deserializing the NSData
{
ProcessMPCDataReceived(VCRef: self, RawData: DataString as Data)
}
case Stream.Event.hasSpaceAvailable:
break
case Stream.Event.errorOccurred:
print("ErrorOccurred: \(String(describing: aStream.streamError?.localizedDescription))")
default:
break
}
}
}
处理接收到的数据的函数
func ProcessMPCDataReceived(VCRef: GameViewController, RawData: Data)
{
let DataReceived: Dictionary = (try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(RawData) as! [String : Any])
switch DataReceived[eTypeData.EventType.rawValue] as! String
{
case eTypeEvent.SetMovement.rawValue:
VCRef.CurrentMovement = eTypeMovement(rawValue: DataReceived[eTypeData.Movement.rawValue] as! String)!
case eTypeEvent.SetPosition.rawValue:
VCRef.Ball.position = DataReceived[eTypeData.Position.rawValue] as! SCNVector3
default:
break
}
}
2条答案
按热度按时间3lxsmp7m1#
看起来您正在主线程上分派工作。虽然我不认为解包数据真的会导致这些定期暂停,但您可能遇到了数据处理瓶颈。换句话说,接收、解包并重新定位节点(这也会导致隐式SceneKit事务)可能刚好足以使设备变慢。看起来你的代码足够紧凑,可以让你把所有的东西都分派到一个队列中。我建议你试试看是否有新的行为。试试
DispatchQueue.global()
,或者更好的。用DispatchQueue(label:"StreamReceiver", qos: .userInteractive)
制作自己的。我认为异步调度在这里非常好。编辑:实际上,看得更多,我认为这可能与SceneKit事务有关。看起来你不是真的在“暂停”,而是在减速。我提到了隐式事务--当你定位节点时,显式地开始,设置动画时间,并结束一个
SCNTransaction
。我一直在使用的一个代码片段是:尝试使用重新定位代码调用它,或者只是将事务start/animationTime/end粘贴在您的块周围。祝您好运!
编辑2:好的,还有一件事。如果这对你的用例有意义,确保你已经***停止浏览和广告。这是一个昂贵的网络,而且可能会拖累整个子系统。
hkmswyz62#
我也遇到过这个问题,这似乎取决于设备所处的环境。
以下是我尝试的结果:在通过多点连接连接的两个设备上,发送方每秒发送数据60次,接收方打印日志。2有时每秒会有0.1-0.2秒的轻微停顿,但有时工作正常。
最后,我用CoreBuetooth框架替换了多点连接框架,它似乎没有这个问题。