- 投稿日:2020-01-24T23:00:10+09:00
Go言語とginをWindows上で動かしてみる
はじめに
始めてqiitaで記事を書くので緊張しています...
『良い記事を書くには』を参考にしていますが、至らぬ点などございましたら申し訳ありません。環境
- OS: windows 10 Home
- Go: 1.13.6
- Git: 2.25.0
- Text Editor: Atom
インストール
Go言語、Gitのインストールは、Windows用インストーラから行います。
ginのインストールはコマンドプロンプトより行います。
Go言語をダウンロード&インストール -> https://golang.org/dl/
Gitをダウンロード&インストール -> https://git-scm.com/download/win
ginをgithubよりダウンロードするのですが、その際にGitが必要になります(当たり前か...)
僕はこれに気が付かず、時間を浪費しました。ginをダウンロード&インストール -> https://gin-gonic.com/ja/docs/quickstart/
上記ページを参考にginのダウンロード&インストールを行います。
go get -u github.com/gin-gonic/gin
動作確認
『クイックスタート | Gin Web Framework』を参考にさせていただきました。
以下のプログラムを、コマンドプロンプトよりgo run test.go
で実行します。test.gopackage main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // 0.0.0.0:8080 でサーバーを立てます。 }参考ページには『ブラウザで
0.0.0.0:8080/ping
にアクセスする』と書いてありますが、なぜかつながりませんでした。
かわりに、localhost:8080/ping
から接続したところ無事アクセスすることができました。Hello, world from Gin ! をしてみる
『Go / Gin で超簡単なWebアプリを作る』を参考に、先ほどの
test.go
を少し書き換えます。test2.gopackage main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.LoadHTMLGlob("*.html") r.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", gin.H{}) }) r.Run() // 0.0.0.0:8080 でサーバーを立てます。 }
index.html
を用意します。index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>test index</title> </head> <body> <h1>Hello, world from Gin !</h1> </body> </html>コマンドプロンプトより
go run test2.go
で実行し、ブラウザよりlocalhost:8080
へ接続します。
以下が表示されて、無事動作していることがわかりました。
さいごに
Goとginを使ったWebアプリの開発がWindows上で行えることがわかり一安心です。
また、最近はWebアプリもどきをHeroku上で動かしたりしています。
データのやり取りなんかはJavaScript+GASなんかで行っていたのですが、限界が見えてしまったためサーバ側でも処理をさせようと思ったのが、今回 Goとginを触ったきっかけです。今回書いたプログラムをベースに使えるアプリを作っていきたいと思う所存です。
- 投稿日:2020-01-24T23:00:10+09:00
GO言語とginをWindows上で動かしてみる
はじめに
始めてqiitaで記事を書くので緊張しています...
『良い記事を書くには』を参考にしていますが、至らぬ点などございましたら申し訳ありません。環境
- OS: windows 10 Home
- Go: 1.13.6
- Git: 2.25.0
- Text Editor: Atom
インストール
Go言語、Gitのインストールは、Windows用インストーラから行います。
ginのインストールはコマンドプロンプトより行います。
Go言語をダウンロード&インストール -> https://golang.org/dl/
Gitをダウンロード&インストール -> https://git-scm.com/download/win
ginをgithubよりダウンロードするのですが、その際にGitが必要になります(当たり前か...)
僕はこれに気が付かず、時間を浪費しました。ginをダウンロード&インストール -> https://gin-gonic.com/ja/docs/quickstart/
上記ページを参考にginのダウンロード&インストールを行います。
go get -u github.com/gin-gonic/gin
動作確認
『クイックスタート | Gin Web Framework』を参考にさせていただきました。
以下のプログラムを、コマンドプロンプトよりgo run test.go
で実行します。test.gopackage main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // 0.0.0.0:8080 でサーバーを立てます。 }参考ページには『ブラウザで
0.0.0.0:8080/ping
にアクセスする』と書いてありますが、なぜかつながりませんでした。
かわりに、localhost:8080/ping
から接続したところ無事アクセスすることができました。Hello, world from Gin ! をしてみる
『Go / Gin で超簡単なWebアプリを作る』を参考に、先ほどの
test.go
を少し書き換えます。test2.gopackage main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.LoadHTMLGlob("*.html") r.GET("/", func(c *gin.Context) { c.HTML(200, "index.html", gin.H{}) }) r.Run() // 0.0.0.0:8080 でサーバーを立てます。 }
index.html
を用意します。index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>test index</title> </head> <body> <h1>Hello, world from Gin !</h1> </body> </html>コマンドプロンプトより
go run test2.go
で実行し、ブラウザよりlocalhost:8080
へ接続します。
以下が表示されて、無事動作していることがわかりました。
さいごに
Goとginを使ったWebアプリの開発がWindows上で行えることがわかり一安心です。
また、最近はWebアプリもどきをHeroku上で動かしたりしています。
データのやり取りなんかはJavaScript+GASなんかで行っていたのですが、限界が見えてしまったためサーバ側でも処理をさせようと思ったのが、今回 Goとginを触ったきっかけです。今回書いたプログラムをベースに使えるアプリを作っていきたいと思う所存です。
- 投稿日:2020-01-24T15:58:31+09:00
Swift4 の String.count を Go でやる
やりたいこと
家族????
を3文字として数えたい。Swift4 の
String.count
はすごい内部で
Grapheme Cluster
なるアルゴリズムを使っていて、それによってUnicode文字列の文字数を正確に計算できているらしい(参考)。print("家族????".count) // -> 3Go の
utf8.RuneCountInString
では対応できない
????
が7文字として数えられてしまう。import "unicode/utf8" func main() { print(utf8.RuneCountInString("家族????")) // -> 9 }Go でも Swift4 の
String.count
がやりたいgithub.com/rivo/unisegを使うと
Grapheme Cluster
を利用して文字列を処理できる。
Playgroundでやるとこんな感じになる。import "github.com/rivo/uniseg" func main() { print(uniseg.GraphemeClusterCount("家族????")) // -> 3 }
- 投稿日:2020-01-24T09:57:48+09:00
スタンドアローンな自作ゲームに通信機能を追加する
この記事では
通信の事を勉強するためにスタンドアローンな自作ゲームに通信機能を追加して勉強しています。操作キャラのアクションと座標の同期が完成したのでその方法を投稿したいと思います。
ゲームは3Dアクションゲームです。複数人で協力しながら戦える機能を追加していきます。具体的にはモンスターハンターの様に通信して遊べるようにします。サーバー
並列処理を簡単にできそうという理由からGo言語を使用しています。
参考URL(https://qiita.com/ak-ymst/items/259960bfffa42d51d246)
package main import ( "fmt" "io" "net" "strconv" ) var connList []net.Conn func main() { service := ":10000" listener, error := net.Listen("tcp", service) if error != nil { panic(error) } fmt.Printf("Server running at %s)\n", service) //fmt.Println("Server running at localhost:10000") fmt.Println("サーバ受付待機中…") waitClient(listener) } func waitClient(listener net.Listener) { connection, error := listener.Accept() connList = append(connList, connection) fmt.Println("通信スタート!") fmt.Printf("接続数 : %v \n", len(connList)) fmt.Println("ローカルアドレス :", connList[len(connList)-1].LocalAddr()) fmt.Println("リモートアドレス :", connList[len(connList)-1].RemoteAddr()) fmt.Println() // 接続先に、サーバーに何番目に接続したかを伝える(クライアントを親と子で分ける) var buf = make([]byte, 1024) var connCnt = len(connList) - 1 // 接続数を送信 0スタートにしたいから-1 var str string str = strconv.Itoa(connCnt) buf = []byte(str) _, error = connection.Write(buf[:1]) if error != nil { panic(error) } go goEcho(connection) waitClient(listener) } func goEcho(connection net.Conn) { defer connection.Close() echo(connection) } func echo(connection net.Conn) { var buf = make([]byte, 1024) // 受信 n, error := connection.Read(buf) if error != nil { if error == io.EOF { return } else { panic(error) } } fmt.Printf("受信(%s) : %s \n", connection.RemoteAddr(), buf[:n]) // リモートアドレスと受け取った内容を表示 //接続先全てに送信 for i := range connList { fmt.Printf("***送信元と送信先の宛先が同じ場合は送信しない***\n") fmt.Printf("受信先(%s)\n", connList[i].RemoteAddr()) fmt.Printf("送信元(%s)\n", connection.RemoteAddr()) // 送信元には返さないようにする if(connList[i] == connection){ fmt.Printf("送信元と送信先の宛先が同じなのでcontinue") continue } n, error = connList[i].Write(buf[:n]) if error != nil { panic(error) } fmt.Printf("送信(%s) : %s \n", connList[i].RemoteAddr(), buf[:n]) } fmt.Println() echo(connection) }解説
サーバーは受信した文字列を受信元以外の通信相手全員に送信するようにしています。
補足:(A,B,C,D が通信しているとしたらAから受信したデータをB,C,Dに送信します。Bから受信したデータはA,C,Dに送信...というイメージです。)座標の同期(クライアント)
json形式で入力情報を送受信して同期する様にしました。
Game.cppvoid Game::Update() { m_Ms.UpdatePointLight(); /*状態管理*/ Input(); m_GameState->Update(this); m_EffectMgr->Update(); m_FragmentMgr->Update(); m_BltMgr->Update(); /*キャラクリア*/ std::vector<SPTR<Character>>::iterator it = m_CharaList.begin(); while (it != m_CharaList.end()) { if (!(*it)->GetVisibleFlg() && (*it) != m_Player) { it = m_CharaList.erase(it); } else { it++; } } //end while /*=====送受信=====*/ // jsonデータにして座標を送る string jsonStr; // jsonファイルの文字列 json11::Json json; // jsonデータ // 送信 m_Player->UpdateJson(); json = m_Player->GetJson(); jsonStr = json.dump(); m_TcpClient->Send<string>(jsonStr); // 受信 jsonStr = m_TcpClient->RecvAndGetString(); json = Helper::JsonStrToPureJson(jsonStr); m_OnlinePlayer->SetJson(json); /*================*/ // カリングと描画順を決める m_RenderMgr->Update(); }解説
ゲームの更新を大方終わらした後に、自分の入力情報を送信する事と通信相手の入力情報を受信しています。
サーバーから受け取った情報を元にキャラクターを動作させる
OnlineCharacterInputComponent.cppvoid My::OnlineCharacterInputComponent::Update() { // 入力情報をjsonで更新 auto&& json = m_Owner->GetJson(); if (json.dump() != "null") { // 何も入っていないときの文字列は"null" auto&& inputData = json["inputData"].array_items()[0]; m_MouseLButton = inputData["mouseLButton"].bool_value(); m_MouseRButton = inputData["mouseRButton"].bool_value(); m_Key_W = inputData["key_W"].bool_value(); m_Key_A = inputData["key_A"].bool_value(); m_Key_S = inputData["key_S"].bool_value(); m_Key_D = inputData["key_D"].bool_value(); m_HistoryMouseLButton = inputData["HoldMouseLButton"].bool_value(); } }PlayerState.cppSPTR<PlayerState> IdleState::HandleInput(const Player& player){ if (player.GetDamageCnt() > 0.0f) { return MAKE_S<DamageState>(); } auto&& input = player.GetComponent<InputComponent>(); if (input->GetKey_W() || input->GetKey_A() || input->GetKey_S() || input->GetKey_D()) { return MAKE_S<DushState>(); } if (input->GetMouseLButton() && !input->IsHoldMouseLButton()) { return MAKE_S<AttackState>(); } if (input->GetMouseRButton()) { return MAKE_S<ChantState>(); } return nullptr; }解説
サーバーから受け取った入力情報をinputDataに格納します。inputData通りに動作するキャラクターを用意してアクションと座標の同期を実装しました。
最後に
初めて通信機能を作成したので改善点は多いと思います。自分のようにゲーム通信に初めて手を出す方の参考になれば幸いです。
- 投稿日:2020-01-24T09:57:48+09:00
初めての同期通信(ゲーム)
この記事では
スタンドアローンな自作ゲームに通信機能を追加して勉強しています。操作キャラのアクションと座標の同期が完成したのでその方法を投稿したいと思います。
ゲームは3Dアクションゲームです。複数人で協力しながら戦える機能を追加していきます。具体的にはモンスターハンターの様に通信して遊べるようにします。サーバー
並列処理を簡単にできそうという理由からGo言語を使用しています。
参考URL(https://qiita.com/ak-ymst/items/259960bfffa42d51d246)
package main import ( "fmt" "io" "net" "strconv" ) var connList []net.Conn func main() { service := ":10000" listener, error := net.Listen("tcp", service) if error != nil { panic(error) } fmt.Printf("Server running at %s)\n", service) //fmt.Println("Server running at localhost:10000") fmt.Println("サーバ受付待機中…") waitClient(listener) } func waitClient(listener net.Listener) { connection, error := listener.Accept() connList = append(connList, connection) fmt.Println("通信スタート!") fmt.Printf("接続数 : %v \n", len(connList)) fmt.Println("ローカルアドレス :", connList[len(connList)-1].LocalAddr()) fmt.Println("リモートアドレス :", connList[len(connList)-1].RemoteAddr()) fmt.Println() // 接続先に、サーバーに何番目に接続したかを伝える(クライアントを親と子で分ける) var buf = make([]byte, 1024) var connCnt = len(connList) - 1 // 接続数を送信 0スタートにしたいから-1 var str string str = strconv.Itoa(connCnt) buf = []byte(str) _, error = connection.Write(buf[:1]) if error != nil { panic(error) } go goEcho(connection) waitClient(listener) } func goEcho(connection net.Conn) { defer connection.Close() echo(connection) } func echo(connection net.Conn) { var buf = make([]byte, 1024) // 受信 n, error := connection.Read(buf) if error != nil { if error == io.EOF { return } else { panic(error) } } fmt.Printf("受信(%s) : %s \n", connection.RemoteAddr(), buf[:n]) // リモートアドレスと受け取った内容を表示 //接続先全てに送信 for i := range connList { fmt.Printf("***送信元と送信先の宛先が同じ場合は送信しない***\n") fmt.Printf("受信先(%s)\n", connList[i].RemoteAddr()) fmt.Printf("送信元(%s)\n", connection.RemoteAddr()) // 送信元には返さないようにする if(connList[i] == connection){ fmt.Printf("送信元と送信先の宛先が同じなのでcontinue") continue } n, error = connList[i].Write(buf[:n]) if error != nil { panic(error) } fmt.Printf("送信(%s) : %s \n", connList[i].RemoteAddr(), buf[:n]) } fmt.Println() echo(connection) }解説
サーバーは受信した文字列を受信元以外の通信相手全員に送信するようにしています。
補足:(A,B,C,D が通信しているとしたらAから受信したデータをB,C,Dに送信します。Bから受信したデータはA,C,Dに送信...というイメージです。)座標の同期(クライアント)
json形式で入力情報を送受信して同期する様にしました。
Game.cppvoid Game::Update() { m_Ms.UpdatePointLight(); /*状態管理*/ Input(); m_GameState->Update(this); m_EffectMgr->Update(); m_FragmentMgr->Update(); m_BltMgr->Update(); /*キャラクリア*/ std::vector<SPTR<Character>>::iterator it = m_CharaList.begin(); while (it != m_CharaList.end()) { if (!(*it)->GetVisibleFlg() && (*it) != m_Player) { it = m_CharaList.erase(it); } else { it++; } } //end while /*=====送受信=====*/ // jsonデータにして座標を送る string jsonStr; // jsonファイルの文字列 json11::Json json; // jsonデータ // 送信 m_Player->UpdateJson(); json = m_Player->GetJson(); jsonStr = json.dump(); m_TcpClient->Send<string>(jsonStr); // 受信 jsonStr = m_TcpClient->RecvAndGetString(); json = Helper::JsonStrToPureJson(jsonStr); m_OnlinePlayer->SetJson(json); /*================*/ // カリングと描画順を決める m_RenderMgr->Update(); }解説
ゲームの更新を大方終わらした後に、自分の入力情報を送信する事と通信相手の入力情報を受信しています。
サーバーから受け取った情報を元にキャラクターを動作させる
OnlineCharacterInputComponent.cppvoid My::OnlineCharacterInputComponent::Update() { // 入力情報をjsonで更新 auto&& json = m_Owner->GetJson(); if (json.dump() != "null") { // 何も入っていないときの文字列は"null" auto&& inputData = json["inputData"].array_items()[0]; m_MouseLButton = inputData["mouseLButton"].bool_value(); m_MouseRButton = inputData["mouseRButton"].bool_value(); m_Key_W = inputData["key_W"].bool_value(); m_Key_A = inputData["key_A"].bool_value(); m_Key_S = inputData["key_S"].bool_value(); m_Key_D = inputData["key_D"].bool_value(); m_HistoryMouseLButton = inputData["HoldMouseLButton"].bool_value(); } }PlayerState.cppSPTR<PlayerState> IdleState::HandleInput(const Player& player){ if (player.GetDamageCnt() > 0.0f) { return MAKE_S<DamageState>(); } auto&& input = player.GetComponent<InputComponent>(); if (input->GetKey_W() || input->GetKey_A() || input->GetKey_S() || input->GetKey_D()) { return MAKE_S<DushState>(); } if (input->GetMouseLButton() && !input->IsHoldMouseLButton()) { return MAKE_S<AttackState>(); } if (input->GetMouseRButton()) { return MAKE_S<ChantState>(); } return nullptr; }解説
サーバーから受け取った入力情報をinputDataに格納します。inputData通りに動作するキャラクターを用意してアクションと座標の同期を実装しました。
最後に
初めて通信機能を作成したので改善点は多いと思います。自分のようにゲーム通信に初めて手を出す方の参考になれば幸いです。