HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2015
0702
さーすpcbnet2を使ったリアルタイム対戦16未解決


さーす

リンク

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がキーを押した時のみ通信するようにしようかとも考えましたが、いまいちいい方法がわかりません

雑な質問で申し訳ありません。どなたかこのゲームのオンライン対戦時の動作を軽くする方法がございましたらご教授ください・・・



この記事に返信する


zakki

リンク

2015/7/2(Thu) 21:53:38|NO.69937

もしサーバーが調停してて同期的に処理する必要がないなら、tcprecvで毎回確実に受け取るのではなくてtcppeek系のでもし受信済みなら処理するみたいなのは難しいんでしょうか?



ZAP

リンク

2015/7/2(Thu) 22:06:00|NO.69938

自分もSTGの二人同時プレイをネット経由で作ろうとしたことがありますが、
こちらが参考になると思います。

http://www.4gamer.net/games/105/G010549/20100905002/

ネット経由だと1対1の通信でもこちらの入力が相手に届くのは
早くて3フレーム後くらいということなので、対戦ゲームなどで
完全にこっちと向こうを同期させるには3フレーム程度のラグを
折り込んでゲームを作らないといけないみたいですね。



ZAP

リンク

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



kanamaru

リンク

2015/7/10(Fri) 07:16:52|NO.70010

ポート開放でググってみて下さい



さーす

リンク

2015/7/10(Fri) 12:26:21|NO.70013

アドバイスありがとうございます
udpsock p1,0 で空いてるポートを自動取得するのではなく、
ポートを指定してあらかじめ開放するべきということですか・・・?
すでに開放しているポートを取得してくれるものだと勘違いしていました、試してみますありがとうございます



skyblue

リンク

2015/7/10(Fri) 13:20:27|NO.70015

>すでに開放しているポートを取得してくれるものだと勘違いしていました
LANならともかくどうやってあいているかを確認するのですか?
WAN側からやったらつかまる危険性がありますよ



zakki

リンク

2015/7/10(Fri) 21:29:21|NO.70024

UDPホールパンチングとかで合法的にNAT通す手法ありますがHSPでやれるのかよく知りません。
RakNetやDirectPlayプラグインみたいなのあると楽しそう。



kanamaru

リンク

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

こういった感じで合っていますでしょうか



hoho

リンク

2015/7/13(Mon) 03:18:44|NO.70098

udpsock soc1,50001
udpsock soc2,50001
となってますが2つのソケットに同じポート番号は使えません。
送信先を切り替えながら通信すればソケットは一つでも通信できます。
もしくは別々のソケットで通信したければポート番号を2つ用意する用意する必要があります。

それと通常は固定ポート番号の使用およびルータ(ファイアーウオール)のポート解放はサーバー側だけでクライアントで側では必要ないです。



ZAP

リンク

2015/7/15(Wed) 18:30:39|NO.70133

横からスミマセン。

>それと通常は固定ポート番号の使用およびルータ(ファイアーウオール)のポート解放はサーバー側だけでクライアントで側では必要ないです。

これはUDPで通信するときも当てはまるのでしょうか?
UDPで通信するゲームを作ろうかと検討してるので気になりました。



hoho

リンク

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といった命令が無くサーバーであることをどのように定義したらよいでしょうか・・・
度々申し訳ありません;



hoho

リンク

2015/7/17(Fri) 05:28:57|NO.70138

>サーバーからクライアントへデータを送信する際もポート開放は必要ないのでしょうか、、?
>TCPと違いtcpopenやtcpacceptといった命令が無くサーバーであることをどのように定義したらよいでしょうか・・・
普通はリクエストをずっと待ち受けているのがサーバー、そこにリクエストを投げるのがクライアントだと思うんですが
最初にクライアントがパケットを送信するとルータは送信元と先のIPアドレスとポートを記憶して相手からの返事と思われるパケットを通過させるようになります。
UDPの場合はコネクションレスですので正確には相手からの返事ではないパケットを通過させる場合もあります。



ONION software Copyright 1997-2021(c) All rights reserved.