WebRTC

HTML5,WebSocket,WebRTC

Godot的一大特点是它能够导出到HTML5/WebAssembly平台,当用户访问您的网页时,您的游戏可以直接在浏览器中运行.

这对于演示和完整的游戏来说都是一个很好的机会,但过去有一些限制,在网络领域,浏览器过去只支持HTTPRequests,直到最近,首先是WebSocket,然后是WebRTC被提出为标准.

WebSocket

当WebSocket协议在2011年12月被标准化后,它允许浏览器与WebSocket服务器建立稳定的双向连接.该协议相当简单,但却是一个非常强大的向浏览器发送推送通知的工具,并已被用于实现聊天、回合制游戏等.

不过,WebSockets仍然使用TCP连接,这对可靠性有好处,但对减少延迟没有好处,所以不适合实时应用,比如VoIP和快节奏的游戏.

WebRTC

为此,从2010年开始,谷歌开始研究一项名为WebRTC的新技术,后来在2017年,这项技术成为W3C候选推荐.WebRTC是一套复杂得集合规范,并且在后台依靠许多其他技术(ICE、DTLS、SDP)来提供两个对等体之间快速、实时、安全的通信.

其想法是找到两个对等体之间最快的路线,并尽可能建立直接通信(尽量避开中继服务器).

然而,这是有代价的,那就是在通信开始之前,两个对等体之间必须交换一些媒介信息(以会话描述协议—SDP字符串的形式).这通常采取所谓的WebRTC信号服务器的形式.

../../_images/webrtc_signaling.png

对等体连接到信号服务器(例如 WebSocket 服务器)并发送其媒介信息.然后,服务器将此信息转发到其他对等体,允许它们建立所需的直接通信.这一步完成后,对等体可以断开与信号服务器的连接,并保持直接的点对点(P2P)连接打开状态.

在Godot中使用WebRTC

在Godot中,WebRTC是通过两个主要的类来实现的 WebRTCPeerConnection,加上多人游戏API实现 WebRTCMultiplayer.更多细节请参见 :ref:`high-level multiplayer <doc_high_level_multiplayer>`章节.

注解

这些类在HTML5中自动可用,但 需要在本地(非HTML5)平台上使用外部GDNative插件 .查看 `webrtc-native插件库<https://github.com/godotengine/webrtc-native>`__ ,以获取说明和最新的 `发布<https://github.com/godotengine/webrtc-native/releases>`__ .

最小连接示例

这个例子将向您展示如何在同一应用程序中的两个对等体之间创建WebRTC连接.这在现实场景中并不是很有用,但会让你对如何设置WebRTC连接有一个很好的概览.

  1. extends Node
  2. # Create the two peers
  3. var p1 = WebRTCPeerConnection.new()
  4. var p2 = WebRTCPeerConnection.new()
  5. # And a negotiated channel for each each peer
  6. var ch1 = p1.create_data_channel("chat", {"id": 1, "negotiated": true})
  7. var ch2 = p2.create_data_channel("chat", {"id": 1, "negotiated": true})
  8. func _ready():
  9. # Connect P1 session created to itself to set local description
  10. p1.connect("session_description_created", p1, "set_local_description")
  11. # Connect P1 session and ICE created to p2 set remote description and candidates
  12. p1.connect("session_description_created", p2, "set_remote_description")
  13. p1.connect("ice_candidate_created", p2, "add_ice_candidate")
  14. # Same for P2
  15. p2.connect("session_description_created", p2, "set_local_description")
  16. p2.connect("session_description_created", p1, "set_remote_description")
  17. p2.connect("ice_candidate_created", p1, "add_ice_candidate")
  18. # Let P1 create the offer
  19. p1.create_offer()
  20. # Wait a second and send message from P1
  21. yield(get_tree().create_timer(1), "timeout")
  22. ch1.put_packet("Hi from P1".to_utf8())
  23. # Wait a second and send message from P2
  24. yield(get_tree().create_timer(1), "timeout")
  25. ch2.put_packet("Hi from P2".to_utf8())
  26. func _process(_delta):
  27. # Poll connections
  28. p1.poll()
  29. p2.poll()
  30. # Check for messages
  31. if ch1.get_ready_state() == ch1.STATE_OPEN and ch1.get_available_packet_count() > 0:
  32. print("P1 received: ", ch1.get_packet().get_string_from_utf8())
  33. if ch2.get_ready_state() == ch2.STATE_OPEN and ch2.get_available_packet_count() > 0:
  34. print("P2 received: ", ch2.get_packet().get_string_from_utf8())

这将打印:

  1. P1 received: Hi from P1
  2. P2 received: Hi from P2

本地信号示例

这个例子在上一个例子的基础上进行了扩展,将对等体分离在两个不同的场景中,并使用 :ref:`singleton <doc_singletons_autoload>`作为信号服务器.

  1. # An example P2P chat client (chat.gd)
  2. extends Node
  3. var peer = WebRTCPeerConnection.new()
  4. # Create negotiated data channel
  5. var channel = peer.create_data_channel("chat", {"negotiated": true, "id": 1})
  6. func _ready():
  7. # Connect all functions
  8. peer.connect("ice_candidate_created", self, "_on_ice_candidate")
  9. peer.connect("session_description_created", self, "_on_session")
  10. # Register to the local signaling server (see below for the implementation)
  11. Signaling.register(get_path())
  12. func _on_ice_candidate(mid, index, sdp):
  13. # Send the ICE candidate to the other peer via signaling server
  14. Signaling.send_candidate(get_path(), mid, index, sdp)
  15. func _on_session(type, sdp):
  16. # Send the session to other peer via signaling server
  17. Signaling.send_session(get_path(), type, sdp)
  18. # Set generated description as local
  19. peer.set_local_description(type, sdp)
  20. func _process(delta):
  21. # Always poll the connection frequently
  22. peer.poll()
  23. if channel.get_ready_state() == WebRTCDataChannel.STATE_OPEN:
  24. while channel.get_available_packet_count() > 0:
  25. print(get_path(), " received: ", channel.get_packet().get_string_from_utf8())
  26. func send_message(message):
  27. channel.put_packet(message.to_utf8())

现在是本地信号服务器:

注解

这个本地信号服务器应该是作为一个 singleton 来连接同一场景中的两个对等体.

  1. # A local signaling server. Add this to autoloads with name "Signaling" (/root/Signaling)
  2. extends Node
  3. # We will store the two peers here
  4. var peers = []
  5. func register(path):
  6. assert(peers.size() < 2)
  7. peers.append(path)
  8. # If it's the second one, create an offer
  9. if peers.size() == 2:
  10. get_node(peers[0]).peer.create_offer()
  11. func _find_other(path):
  12. # Find the other registered peer.
  13. for p in peers:
  14. if p != path:
  15. return p
  16. return ""
  17. func send_session(path, type, sdp):
  18. var other = _find_other(path)
  19. assert(other != "")
  20. get_node(other).peer.set_remote_description(type, sdp)
  21. func send_candidate(path, mid, index, sdp):
  22. var other = _find_other(path)
  23. assert(other != "")
  24. get_node(other).peer.add_ice_candidate(mid, index, sdp)

然后,您可以这样使用它:

  1. # Main scene (main.gd)
  2. extends Node
  3. const Chat = preload("res://chat.gd")
  4. func _ready():
  5. var p1 = Chat.new()
  6. var p2 = Chat.new()
  7. add_child(p1)
  8. add_child(p2)
  9. yield(get_tree().create_timer(1), "timeout")
  10. p1.send_message("Hi from %s" % p1.get_path())
  11. # Wait a second and send message from P2
  12. yield(get_tree().create_timer(1), "timeout")
  13. p2.send_message("Hi from %s" % p2.get_path())

将打印出类似这样的内容:

  1. /root/main/@@3 received: Hi from /root/main/@@2
  2. /root/main/@@2 received: Hi from /root/main/@@3

使用WebSocket进行远程信号传输

一个更高级的演示,使用WebSocket作为信号对等体和 WebRTCMultiplayer`godot演示项目<https://github.com/godotengine/godot-demo-projects>`_ networking/webrtc_signaling 下提供.