何をしたいですか?
ゲーム内のプレーヤーアクションとクライアントサーバーアーキテクチャとの同期。ブラウザから再生できるはずです。
たとえば、簡単なチャットルームを実装しましょう。
接続時:
クライアントは一意のIDを受け取ります。
クライアントは、他のすべてのプレーヤーに関する情報(ID +名前)を受け取ります。
他のすべてのプレーヤーは、新しいプレーヤーに関する情報(ID +デフォルト名)を受け取ります。
ログインメッセージがコンソールに表示されます。
接続が失われた場合:
他のすべてのプレーヤーは、サーバー(ID)からのプレーヤーの終了に関する情報を受け取ります。
終了メッセージがコンソールに表示されます。
名前を変更する場合:
名前がすでに使用されている場合、プレーヤーはエラーを受け取ります。
名前の変更はすべてのプレイヤーに通知されます。
コンソールにメッセージが表示されます。
チャットにメッセージを送信する場合:
すべてのプレイヤーはログ/コンソールにメッセージを表示します。
注:より複雑なネットワーク(プレーヤーの移動、その他のアクションなど)の実装を妨げるものは何もありませんが、これはこの記事の範囲を超えており、それ自体はかなり複雑なトピックです。チャットは、データを転送するためのそのようなアプローチが原則として機能することを示す最も簡単な例です。これが私の記事の目的です。
何が起こった?
完成したプロジェクトはここで調べることができます:https://github.com/ktori/godobuf-over-websocket-demo
スクリーンショットは記事の最後にあります。
何を使いますか?
Godot - free and open source ;
Protobuf - / ;
Godobuf - Godot, .gd (GDScript) .proto;
Ktor - Kotlin ( Kotlin - , - - - Protobuf, ).
, , :
;
, ;
VCS, .. ;
- - /.
Protobuf - , , , JSON - ;
Protobuf , .
- :
/ protobuf , , , ;
, protobuf , , .
.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.
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- :
- , .
- . 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 .. - .
. , :
エラー処理(たとえば、別のメッセージ
error
をに渡すClMessageResult
);
接続損失/復元処理;
はるかに。
この記事がGodot、WebSocket、protobufの理解に役立つことを願っています。