|
|
|
2015/7/2(Thu) 01:39:08|NO.69928
ロックマンエグゼ風の対戦ゲームを製作中しています。
pcbnet2を使ってリアルタイムネット対戦を実装したのですが、動作が極端に重いです
クライアント側のコードは書くと以下のような感じです
*battle
repeat
yu = 0 ;1P上移動用
yh = 0 ;1P左移動用
ys = 0 ;1P下移動用
ym = 0 ;1P右移動用
yb = 0 ;1P銃攻撃用
ysw = 0 ;1P剣攻撃用
ycip = 0 ;1Pアイテム1用
ycip2 = 0 ;1Pアイテム2用
ycip3 = 0 ;1Pアイテム3用
eu = 0 ;ここから2P用
eh = 0
es = 0
em = 0
eb = 0
esw = 0
ecip = 0
ecip2 = 0
ecip3 = 0
;yscips = 0
;yscips2 = 0
;yscips3 = 0
;escips = 0
;escips2 = 0
;escips3 = 0
if player == 1{
getkey yu,87
getkey yh,65
getkey ys,83
getkey ym,68
getkey yb,75
getkey ysw,74
getkey ycip,85
getkey ycip2,73
getkey ycip3,79
}
if player == 2{
getkey eu,87
getkey eh,65
getkey es,83
getkey em,68
getkey eb,75
getkey esw,74
getkey ecip,85
getkey ecip2,73
getkey ecip3,79
}
dim recvdata , 24
dim senddata , 9
alloc senddata,1024
alloc recvdata,1024
if playerf == 1{ ;1P
pack senddata,"iiiiiiiii",yu,ym,ys,yh,yb,ysw,ycip,ycip2,ycip3
}
if playerf == 2{ ;2P
pack senddata,"iiiiiiiii",eu,em,es,eh,eb,esw,ecip,ecip2,ecip3
}
repeat
tcpfail socket
if stat != 0 {
tcpshut socket
tcpclose socket
mes "対戦相手との接続が切れました。"
goto *準備画面
}
tcpsend senddata,0,4*9,socket
if stat == 0:break
tcpfail socket
if stat != 0 {
tcpshut socket
tcpclose socket
mes "対戦相手との接続が切れました。"
goto *準備画面
}
await 1
loop
repeat
tcpfail socket
if stat != 0 {
tcpshut socket
tcpclose socket
goto *準備画面
}
tcprecv recvdata,0,4*24,socket
if stat != 0:break
tcpfail socket
if stat != 0 {
tcpshut socket
tcpclose socket
goto *準備画面
}
await 1
loop
yu = 0
yh = 0
ys = 0
ym = 0
yb = 0
ysw = 0
ycip = 0
ycip2 = 0
ycip3 = 0
eu = 0
eh = 0
es = 0
em = 0
eb = 0
esw = 0
ecip = 0
ecip2 = 0
ecip3 = 0
yscips = 0
yscips2 = 0
yscips3 = 0
escips = 0
escips2 = 0
escips3 = 0
unpack recvdata,"iiiiiiiiiiiiiiiiiiiiiiii",yu,ym,ys,yh,yb,ysw,ycip,ycip2,ycip3,eu,em,es,eh,eb,esw,ecip,ecip2,ecip3,yscips,yscips2,yscips3,escips,escips2,escips3
await 16
loop
サーバー側のコードは、1フレーム毎にクライアントからのデータ受信と送信の待機をするだけのものになってます
ローカルネット内の対戦は十分軽く動作したのですが、グローバルネット対戦の場合極端に動作が重くなってしまいます
1フレーム毎の通信では通信頻度が高すぎるのが原因かと思うのですが、解決策がなかなか見出せません
1Pか2Pがキーを押した時のみ通信するようにしようかとも考えましたが、いまいちいい方法がわかりません
雑な質問で申し訳ありません。どなたかこのゲームのオンライン対戦時の動作を軽くする方法がございましたらご教授ください・・・
| |
|
2015/7/2(Thu) 21:53:38|NO.69937
もしサーバーが調停してて同期的に処理する必要がないなら、tcprecvで毎回確実に受け取るのではなくてtcppeek系のでもし受信済みなら処理するみたいなのは難しいんでしょうか?
|
|
2015/7/2(Thu) 22:09:31|NO.69939
格闘ゲームの対戦では通信速度を優先するためTCPではなくてUDPを使ってるみたいです。
|
|
2015/7/3(Fri) 04:12:55|NO.69940
御三方、アドバイスありがとうございます!
踏まえて試行錯誤した結果、UDPを使って書き直しました
明日オンラインテストをして結果次第でまた考えようと思います
|
|
2015/7/10(Fri) 00:50:49|NO.70009
質問からだいぶ時間が経ってしまいましたが、
頂いたアドバイスを元に
TCPで接続⇒対戦中はUDPで通信
という形で組んだのですが、どうしてもUDPの部分でデータの受信が上手くいきません
テストのために簡単な下記のプログラムでグローバル通信を友人としてみたのですが、UDPでのデータ受信が
出来ないようです
自PCで複数起動した場合は、受信含め問題なく動作しました
何が原因なのでしょうか・・・
サーバープログラム
#include "pcbnet2.as"
screen 0,400.400
tcpmake opensocket,50000
userf = 0
username1 = ""
username2 = ""
recvdata1 = 0
recvdata2 = 0
mes "接続を待っています"
goto *main
*main
if userf <= 1 :gosub *check
if userf == 2 :gosub *UDP
if userf == 3 :gosub *UDPr
if userf == 4 :gosub *UDPs
await 16
goto *main
*check
tcpwait opensocket;受付用ソケットにユーザーが来ていないかチェック
if stat == 1{
if userf == 1 {
userf = 2
tcpaccept socket2,opensocket
repeat
tcpget username2,64,socket2
if stat != 0:break
await 1
loop
mes "ユーザー2"+username2+"が接続しました"
sdim pINFO, 256
tcpinfo pINFO, socket2
/*IP*/
getstr eneIP, pINFO, 0, ':'
/*PORT*/
getstr eneP, pINFO, strsize, ':'
}
if userf == 0 {
userf = 1
tcpaccept socket1,opensocket
repeat
tcpget username1,64,socket1
if stat != 0:break
await 1
loop
mes "ユーザー1"+username1+"が接続しました"
sdim pINFO, 256
tcpinfo pINFO, socket1
/*IP*/
getstr youIP, pINFO, 0, ':'
/*PORT*/
getstr youP, pINFO, strsize, '
}
}
return
*UDP
udpsock soc1,0
udpport po1,soc1
udpsock soc2,0
udpport po2,soc2
repeat
tcpsend po1,0,4*5,socket1
if stat == 0:break
await 1
loop
repeat
tcprecv pou1,0,4*5,socket1
if stat != 0:break
await 1
loop
repeat
tcpsend po2,0,4*5,socket2
if stat == 0:break
await 1
loop
repeat
tcprecv pou2,0,4*5,socket2
if stat != 0:break
await 1
loop
udpsendto soc1,youIP,pou1
udpsendto soc2,eneIP,pou2
mes po1
mes po2
mes pou1
mes pou2
mes youIP
mes eneIP
userf = 3
return
stop
*UDPr
repeat
udprecv recvdata1,0,4*10,soc1
if stat !=0 :break
await 1
loop
repeat
udprecv recvdata2,0,4*10,soc2
if stat !=0 :break
await 1
loop
mes "受信完了"
userf = 4
return
*UDPs
senddata1 = 1
senddata2 = 1
repeat
udpsend senddata1,0,4*26,soc1
if stat == 0:break
await 1
loop
repeat
udpsend senddata2,0,4*26,soc2
if stat == 0:break
await 1
loop
mes "送信完了"
userf = 5
return
クライアントプログラム
#include "pcbnet2.as"
screen 0,400.400
username = "TEST"
serverIP = "127.0.0.1";接続相手のIPに随時手動変更
goto *main
*main
tcpopen socket,serverIP,50000
repeat
tcpput username,socket
if stat == 0:break
await 1
loop
*UDP
udpsock soc,0
udpport po1,soc
repeat
tcprecv po2, 0,4*5,socket
if stat != 0:break
await 1
loop
repeat
tcpsend po1, 0,4*5,socket
if stat == 0:break
await 1
loop
udpsendto soc,serverIP,po2
*UDPs
senddata = 1
recvdata = 0
repeat
udpsend senddata,0,4,soc
if stat == 0:break
await 1
loop
mes "送信完了"
*UDPr
repeat
udprecv recvdata,0,4,soc
if stat != 0:break
await 1
loop
if recvdata == 1 :mes "受信完了"
stop
| |
|
2015/7/10(Fri) 07:16:52|NO.70010
ポート開放でググってみて下さい
|
|
2015/7/10(Fri) 12:26:21|NO.70013
アドバイスありがとうございます
udpsock p1,0 で空いてるポートを自動取得するのではなく、
ポートを指定してあらかじめ開放するべきということですか・・・?
すでに開放しているポートを取得してくれるものだと勘違いしていました、試してみますありがとうございます
|
|
2015/7/10(Fri) 13:20:27|NO.70015
>すでに開放しているポートを取得してくれるものだと勘違いしていました
LANならともかくどうやってあいているかを確認するのですか?
WAN側からやったらつかまる危険性がありますよ
|
|
2015/7/10(Fri) 21:29:21|NO.70024
UDPホールパンチングとかで合法的にNAT通す手法ありますがHSPでやれるのかよく知りません。
RakNetやDirectPlayプラグインみたいなのあると楽しそう。
|
|
2015/7/10(Fri) 21:51:24|NO.70026
質問内容とは直接関係ないですが、
IPアドレスをプログラムに直接書くのはセキュリティ上問題があると思います。
まぁテストする段階はそれでもいいと思いますが。
あと、議論している所すいません。
接続できない理由が他にもあるようです。
接続先に指定しているIPアドレスはローカルループバックアドレスという、
自身を指すものらしいです。
自身PCで成功したのもうなずけます。要は自分に向かって通信してるのですから。
|
|
2015/7/11(Sat) 14:58:37|NO.70043
いろいろとありがとうございます!勘違いすみませんでした、理解しました
流れとしては
サーバー側:TCPポート50000、UDPポート50001を開放
クライアント側:UDPポート50001を開放
という感じで大丈夫でしょうか
以上を踏まえて改変してみました
サーバー
#include "pcbnet2.as"
screen 0,400.400
tcpmake opensocket,50000
userf = 0
username1 = ""
username2 = ""
recvdata1 = 0
recvdata2 = 0
mes "接続を待っています"
goto *main
*main
if userf <= 1 :gosub *check
if userf == 2 :gosub *UDP
if userf == 3 :gosub *UDPr
if userf == 4 :gosub *UDPs
await 16
goto *main
*check
tcpwait opensocket;受付用ソケットにユーザーが来ていないかチェック
if stat == 1{
if userf == 1 {
userf = 2
tcpaccept socket2,opensocket
repeat
tcpget username2,64,socket2
if stat != 0:break
await 1
loop
mes "ユーザー2"+username2+"が接続しました"
sdim pINFO, 256
tcpinfo pINFO, socket2
/*IP*/
getstr eneIP, pINFO, 0, ':'
/*PORT*/
getstr eneP, pINFO, strsize, ':'
mes eneP
}
if userf == 0 {
userf = 1
tcpaccept socket1,opensocket
repeat
tcpget username1,64,socket1
if stat != 0:break
await 1
loop
mes "ユーザー1"+username1+"が接続しました"
sdim pINFO, 256
tcpinfo pINFO, socket1
/*IP*/
getstr youIP, pINFO, 0, ':'
/*PORT*/
getstr youP, pINFO, strsize, '
mes youP
}
}
return
*UDP
udpsock soc1,50001
udpsock soc2,50001
udpsendto soc1,youIP,50001
udpsendto soc2,eneIP,50001
mes youIP
mes eneIP
userf = 3
return
stop
*UDPr
repeat
udprecv recvdata1,0,4*10,soc1
if stat !=0 :break
await 1
loop
repeat
udprecv recvdata2,0,4*10,soc2
if stat !=0 :break
await 1
loop
mes "受信完了"
userf = 4
return
*UDPs
senddata1 = 1
senddata2 = 1
repeat
udpsend senddata1,0,4*26,soc1
if stat == 0:break
await 1
loop
repeat
udpsend senddata2,0,4*26,soc2
if stat == 0:break
await 1
loop
mes "送信完了"
userf = 5
return
クライアント
#include "pcbnet2.as"
screen 0,400.400
username = "TEST"
serverIP = "192.168.24.51"
mes "サーバーのグローバルIP"
input serverIP,100,30,20
button "接続",*main
stop
;goto *main
*main
clrobj
tcpopen socket,serverIP,50000
repeat
tcpput username,socket
if stat == 0:break
await 1
loop
*UDP
udpsock soc,50001
udpsendto soc,serverIP,50001
*UDPs
senddata = 1
recvdata = 0
repeat
udpsend senddata,0,4,soc
if stat == 0:break
await 1
loop
mes "送信完了"
*UDPr
repeat
udprecv recvdata,0,4,soc
if stat != 0:break
await 1
loop
if recvdata == 1 :mes "受信完了"
stop
こういった感じで合っていますでしょうか
| |
|
2015/7/13(Mon) 03:18:44|NO.70098
udpsock soc1,50001
udpsock soc2,50001
となってますが2つのソケットに同じポート番号は使えません。
送信先を切り替えながら通信すればソケットは一つでも通信できます。
もしくは別々のソケットで通信したければポート番号を2つ用意する用意する必要があります。
それと通常は固定ポート番号の使用およびルータ(ファイアーウオール)のポート解放はサーバー側だけでクライアントで側では必要ないです。
|
|
2015/7/15(Wed) 18:30:39|NO.70133
横からスミマセン。
>それと通常は固定ポート番号の使用およびルータ(ファイアーウオール)のポート解放はサーバー側だけでクライアントで側では必要ないです。
これはUDPで通信するときも当てはまるのでしょうか?
UDPで通信するゲームを作ろうかと検討してるので気になりました。
|
|
2015/7/16(Thu) 02:06:30|NO.70136
>これはUDPで通信するときも当てはまるのでしょうか?
はい。
TCPでもUDPでもサーバーとクライアントの関係は変わらないです。
>UDPで通信するゲームを作ろうかと検討してるので気になりました。
ネットゲームなどではプレイヤーにルータのポートを開放などをさせずに互いに直接通信させるSTUNなどの方法もあります。
別途サーバーを用意する必要がありますが。
|
|
2015/7/16(Thu) 23:45:12|NO.70137
遅くなりました、hohoさんありがとうございます!頂いた助言通りポートを2つ用意して通信することに
しました。
UDPでもクライアント側はポート開放の必要はないのですね!
サーバーからクライアントへデータを送信する際もポート開放は必要ないのでしょうか、、?
TCPと違いtcpopenやtcpacceptといった命令が無くサーバーであることをどのように定義したらよいでしょうか・・・
度々申し訳ありません;
|
|
2015/7/17(Fri) 05:28:57|NO.70138
>サーバーからクライアントへデータを送信する際もポート開放は必要ないのでしょうか、、?
>TCPと違いtcpopenやtcpacceptといった命令が無くサーバーであることをどのように定義したらよいでしょうか・・・
普通はリクエストをずっと待ち受けているのがサーバー、そこにリクエストを投げるのがクライアントだと思うんですが
最初にクライアントがパケットを送信するとルータは送信元と先のIPアドレスとポートを記憶して相手からの返事と思われるパケットを通過させるようになります。
UDPの場合はコネクションレスですので正確には相手からの返事ではないパケットを通過させる場合もあります。
|
|