Schmerzfreier plattformübergreifender Multiplayer auf Godot

Was wollen wir machen?

Synchronisation von Spieleraktionen im Spiel mit einer Client-Server-Architektur. Es sollte möglich sein, über den Browser zu spielen.





Lassen Sie uns zum Beispiel einen einfachen Chatraum implementieren:





  1. Beim Anschließen:





    1. Der Client erhält eine eindeutige ID.





    2. Der Kunde erhält Informationen über alle anderen Spieler (ID + Name);





    3. Alle anderen Spieler erhalten Informationen über den neuen Spieler (ID + Standardname);





    4. Auf der Konsole wird eine Anmeldemeldung angezeigt.





  2. Bei Verbindungsverlust:





    1. Alle anderen Spieler erhalten Informationen über den Ausstieg des Spielers vom Server (ID).





    2. Auf der Konsole wird eine Beendigungsmeldung angezeigt.





  3. Beim Ändern des Namens:





    1. Wenn der Name bereits vergeben ist, erhält der Spieler eine Fehlermeldung.





    2. Alle Spieler werden über die Namensänderung informiert.





    3. Auf der Konsole wird eine Meldung angezeigt.





  4. Beim Senden einer Nachricht an den Chat:





    1. Alle Spieler sehen die Nachricht im Protokoll / in der Konsole.





Hinweis: Nichts hindert Sie daran, komplexere Netzwerke zu implementieren (z. B. die Bewegung von Spielern, einige andere Aktionen). Dies geht jedoch über den Rahmen dieses Artikels hinaus und ist an sich ein recht komplexes Thema. Chat ist das einfachste Beispiel dafür, dass ein solcher Ansatz zur Datenübertragung im Prinzip funktioniert - und das ist der Zweck meines Artikels.





Was ist passiert?

Das fertige Projekt kann hier studiert werden: https://github.com/ktori/godobuf-over-websocket-demo





Screenshots finden Sie am Ende des Artikels.





Was werden wir verwenden?

  • Godot - free and open source ;





  • Protobuf - / ;





  • Godobuf - Godot, .gd (GDScript) .proto;





  • Ktor - Kotlin ( Kotlin - , - - - Protobuf, ).





  • , , :





    • ;





    • , ;





    • VCS, .. ;





    • - - /.





  • Protobuf - , , , JSON - ;





  • Protobuf , .





- :





  • / protobuf , , , ;





  • , protobuf , , .





: game.proto





.proto-, - game.proto. , ( - ).





:





syntax = "proto3";

//  
option java_package = "me.ktori.game.proto";
//        
option java_outer_classname = "GameProto";
      
      



, :





-

, - RPC Cl**Result . gRPC - godobuf gRPC-. :





//
//  -
//

//    
message ClSetName {
  string name = 1;
}

//    
message ClSendChatMessage {
  string text = 1;
}

//   ,  
message ClMessage {
  //        ,   
  //   ,     
  oneof data {
    ClSetName set_name = 1;
    ClSendChatMessage send_chat_message = 2;
  }
}
      
      



-

//
//  -
//

//    ClSetName
message ClSetNameResult {
  //     -      
  bool success = 1;
}

//   -        
message ClMessageResult {
  oneof result {
    ClSetNameResult set_name = 1;
  }
}

//      
//        ID    
message SvConnected {
  int32 id = 1;
  string name = 2;
}

//     
//       ID
message SvClientConnected {
  int32 id = 1;
  string name = 2;
}

//    
//          ID
message SvClientDisconnected {
  int32 id = 1;
}

//    
//       ID  
message SvNameChanged {
  int32 id = 1;
  string name = 2;
}

//   
message SvChatMessage {
  int32 from = 1;
  string text = 2;
}

//       
message SvMessage {
  //          SvMessage
  oneof data {
    ClMessageResult result = 1;
    SvConnected connected = 2;
    SvClientConnected client_connected = 3;
    SvClientDisconnected client_disconnected = 4;
    SvNameChanged name_changed = 5;
    SvChatMessage chat_message = 6;
  }
}
      
      



:





  • ClMessage



    ;





  • SvMessage



    ;





    • result - ClMessageResult



      .





naming convention:





  • ClFooBar



    , ;





  • SvFooBar



    , , :





  • ClFooBarResult



    ClFooBar



    .





Godot

( 2D ).





Godobuf

: https://github.com/oniksan/godobuf, README - addons.





Projekt nach der Installation von Godobuf Addon
godobuf

WebSocketClient



( WebSocketClient). : , URL .





, - :





extends Node2D

var ws: WebSocketClient

#    
func _ready():
    #  WebSocketClient    
    ws = WebSocketClient.new()
    ws.connect("connection_established", self, "_on_ws_connection_established")
    ws.connect("data_received", self, "_on_ws_data_received")
    #      8080
    ws.connect_to_url("ws://127.0.0.1:8080")

#     
func _on_ws_connection_established(_protocol):
    pass

#       
func _on_ws_data_received():
    pass
      
      



protobuf:GDScript

! Godobuf proto- :





Godobuf Fenster
Godobuf

- , .





 Szene

- . pressed



Send Rename . show_message



, Label VBoxContainer, .





- .





:





const GameProto = preload("res://game_proto.gd") 
      
      



ClMessage Send/Rename:





#      $Name
func _on_SetName_pressed():
    var msg = GameProto.ClMessage.new()
    var sn = msg.new_set_name()
    sn.set_name(name_input.text)
    send_msg(msg)

#    $Message   
func _on_SendMessage_pressed():
    var msg = GameProto.ClMessage.new()
    var scm = msg.new_send_chat_message()
    scm.set_text(message_input.text)
    message_input.clear()
    send_msg(msg)
      
      



- send_msg. :





#  ClMessage  
func send_msg(msg: GameProto.ClMessage):
    #  ClMessage  PoolByteArray      ws
    ws.get_peer(1).put_packet(msg.to_bytes())
      
      



to_bytes



( ClMessage



) godobuf - !





- . , - , .





#    
func _process(_delta):
    #    ,   
    ws.poll()

#     
func _on_ws_connection_established(_protocol):
    show_message("Connection established!")

#       
func _on_ws_data_received():
    #     
    for i in range(ws.get_peer(1).get_available_packet_count()):
        #    
        var bytes = ws.get_peer(1).get_packet()
        var sv_msg = GameProto.SvMessage.new()
        #      
        sv_msg.from_bytes(bytes)
        #    
        _on_proto_msg_received(sv_msg)

#         
func _on_proto_msg_received(msg: GameProto.SvMessage):
    # ..       oneof -    
    #   
    if msg.has_connected():
        pass
    elif msg.has_client_connected():
        pass
    elif msg.has_client_disconnected():
        pass
    elif msg.has_chat_message():
        pass
    elif msg.has_name_changed():
        pass
    elif msg.has_result():
        pass
    else:
        push_warning("Received unknown message: %s" % msg.to_string())

      
      







poll



WebSocketClient



, . _process







- ID :





#  ID  
var own_id: int
#   ID <> 
var names = Dictionary()
      
      



:





#  _on_proto_msg_received
  if msg.has_connected():
		var c = msg.get_connected()
		own_id = c.get_id()
		name_input.text = c.get_name()
		show_message("Welcome! Your ID is %d and your assigned name is '%s'." % [c.get_id(), c.get_name()])
      
      



if/elif . GitHub: Main.gd





. - Kotlin Ktor. , GitHub - .





:





gradle- :





  • server - ;





  • proto - - :





    • com.google.protobuf, com.google.protobuf:protobuf-java ;





    • , / -.





- , broadcast- , .





Godot- , Linux/Windows/Android .. - .





Native Client
WebSocket-Client
WebSocket-

. , :





  • Fehlerbehandlung (z. B. Weitergabe einer separaten Nachricht error



    an ClMessageResult



    );





  • Verbindungsverlust / Handhabung der Wiederherstellung;





  • Vieles andere.





Ich hoffe, dieser Artikel war hilfreich und hat geholfen, Godot, Websockets und Protobuf zu verstehen.








All Articles