<前文>
とうとう、この教科書のレシピも最後の二つになりました。この教科書で勉強を始めた時はまさかこんなに時間を取られるとは思っていませんでした。1章の勉強に一週間で12章なので3か月が当初の予定でした。途中でこの勉強内容を何処かに記録した方がいいと思いブログを開始しましたがそれが更に時間を取るようになりました。ブログを開始してからですら1年経っています。
この本で勉強を開始した時はUE4C++についての本格的な教科書はこの本以外なかったです。少なくとも私が見つけられた範囲ではこの教科書は最も本格的な物でした。しかしこの教科書の評価も再現出来ないなどの多くの批判が述べられていて実際買った後も勉強しないでいました。いや、こんな大事な話をここで述べるべきではないですね。全部のレシピが終わってから最後にこの教科書についての総括をします。
<本文>
<目的>
HTTPのリクエストをしてサーバーに接続する方法を学びます。勿論UE4C++なのでUE4のHTTP APIを使用します。今のゲームは必ずといっていいほどサーバーに接続しますがUE4におけるサーバーへの接続方法をここで学ぶのでしょうか?勿論、UE4におけるサーバーへの接続方法を学ぶには枚数的に少なすぎますが触りぐらいは学べるようです。
Step.0
今回も新しいプロジェクトを作成します。プロジェクト名は、Chapter12Part13とします。バージョンは4.22です。
Step.1
はい。ですがサンプルコードを見てからやります。
HTTPだけでいいんでしょうか?Networking、Sockets、Messagingなどもひょっとしたら必要になるかもしれないですね。
取りあえず、HTTPだけ追加しました。
Step.2
とのファイルを作成すれば良いのかが分りませんね。こんな時はサンプルコードをみるのが一番です。
Gamemode.hファイルでHttpRetrySystem.hをインクルードしてました。
しかし、Gamemode.hファイルでは他のヘッダーファイルはインクルードしていません。ウーン。他のファイルを探してみた所、Gamemode.cppファイルで他のヘッダもインクルードしていました。
IHtttpResponse.hファイルもインクルードする必要があるのでしょうか?
取りあえずgamemodeクラスの派生クラスを作成します。
こんな感じで作りました。
Cppファイルがまたエラーを吐いています。VSをリフレッシュします。
直りました。
HttpManager.h、HttpModule.h、そしてHttpRetrySystem.hをgamemode.hファイルにインクルードしました。
Step.3
サンプルコードを見ると、
Gamemode.cppファイルのTestHttp()内で作成していますので、
TestHttp()メンバー関数を先に作成します。
更にガワの方を作成します。
ガワだけ動くかテストします。
コンパイルしてplayを実行するとメッセージが表示されたので、期待通りに動いているようです。
それでは、
をTestHttp関数に追加します。
ビルドも成功しました。
はい。
直接関係はないですが、最新のソフトウェアデザインの教科書やチュートリアルではシングルトンは使うべきではないと良く書かれていますが実際は結構使われているみたいですね。
Step.4
サンプルコードを見ると、
となっていました。関数の実装部については教科書はまだ何も述べていないので今は関数の宣言と定義のみコピーします。
Step.5
サンプルコードをみると、以下に示すようにTestHttp()関数内に実装されていました。
以下のように追加しました。
サンプルコードではこの部分のコードは手順の三番目になっています。
教科書の説明では、以下に示す手順2の部分が抜けています。
TestHttp()関数内のサンプルコードの手順をまとめると以下のようになります。
1.FHttpModuleのシングルトンのインターフェイスからIHttpRequestオブジェクトを作成します。
2.Httpのリクエストの進行を見るために、OnRequestProgress()関数を追加します。貴方がこれを見たい見たくないに関わらず、これはHttpのリクエストを熱望するプログレスバーを表示するのに使用されます。
3.何かをするためにOnProcessRequstComplete()関数を追加します。そのHTTPのリクエストが完了した時、我々は7種類のデリゲートを追加する方法を以下に示します。
これを見ると、2番目の手順は今は実装しなくてもいいのかもしれません。TestHttp()関数内のサンプルコードには更に、
がありますが、後で説明があるのでしょう。
やっぱり、TestHttp()関数内の手順2の実装がありました。
まずは、サンプルコードをそのままコピーします。
Requestのエラーを消すために、IHttpResponse.hを追加します。ついでにIHttpRequest.hも足します。
Requestのエラーが消えました。
Info(FS…のエラーは、サンプルコードのLoggingファイルを基にしたLoggingファイルを作成して
それをインクルードする事で、
エラーしなくなりました。勿論ビルドも成功しました。
次の行は来週のレシピ用みたいなので今は消しておきます。
サンプルコードのTestHttp()関数内には、更に以下に示すコードがあります。
この中の、7番がBindUObjectですが、それが教科書の説明の
この部分に当たるのでしょうか?
もしそうならばこの7番だけコピーすればいいのか?それとも1から全部コピーしないといけないのでしょうか?
教科書に答えが書かれていました。ちょっと長いけど全部引用します。
となるとやっぱり全部追加しないといけないみたいですね。でもBindRaw()関数は手順2ですね。手順1は要らないのでしょうか?
良く読んだらコメントに答えが既に書かれていました。
デリゲートを作成した所のコメントです。このコメントを信じれば、7種類のデリゲートを追加する方法を紹介しているので、その中から一種類だけ選べば良い事になります。
あら?
ここまで来て、大変な間違いをしている事に気が付きました。
の部分は、
IHttpRequestクラスのオブジェクトであるhttpのBindLambda()関数と思っていたのですが、教科書で説明されているのはFHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateのBindLamda()関数でした。
本来なら消して書き直すのですが、間違いは間違いとして残すのがこのブログの主旨なので消すわけにはいきません。
色々考えたのですがStep.5からやり直します。そうすれば大丈夫なはずです。
Step.5(やり直し)
何か小学生の時のプラモデル制作で右足を作っていたのに左足のパーツを使ってしまい途中からそれに気が付いて絶望した時の事を思い出しました。プログラミングは消して書き直せばいいだけなので楽チンです。
何故先程間違えてしまったかと言うとサンプルコードのTestHttp()関数にはIHttpRequestクラスのオブジェクトであるhttpの関数を呼び出す工程とFHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateの関数を呼び出す工程の二つがあります。IHttpRequestクラスのオブジェクトであるhttpの関数を呼び出す工程の3でFHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateを作成するのでごちゃごちゃになってしまったのです。以下にそれぞれの工程を整理した図を示します。
この部分に気を付けて今度はやって行きます。
まず現在の私のTestHttp()関数の実装部を見ると、
となっています。IHttpRequestクラスのオブジェクトであるhttpの関数を呼び出す工程の3までが実装された状態です。つまり5の最初の部分が終了した状態です。
しかし前のstep.5ではIHttpRequestクラスのオブジェクトであるhttpの関数を呼び出す工程の2についての記述が教科書に全くないので2を実装する必要があるのか分からなかったのでした。以下にもう一度示しますがhttpオブジェクトの工程の2の部分を読むとプログレスバーについての関数なので今回は実装しなくても良いみたいとの結論を前のstep.5で出しました。
ところがその後で、delegateの方のBindLamda()関数と勘違いしてやっぱり実装しないといけないと間違って解釈してしまいました。と言う事はhttpオブジェクトの工程の2は実装しなくて良いと言う事になります。
のでhttpオブジェクトの工程の工程2は抜きました。
ここからがFHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateについてです。
5の続きではコールバックの関数をデリゲートに追加する必要があると述べています。そのための方法としてこっちのBindLamda()関数を紹介しています。と言う事はdelegateのbindLambda()関数は必ず追加しないといけないはずです。サンプルコードの方も以下に示します。
基本的には全く同じですね。サンプルコードの方をコピーします。
Warningがエラーを吐いています。ので、LoggingファイルにWarning()関数を追加します。
勿論エラーも消えました。
まあ。いい訳になってしまうのですが、このdelegate.BindLambda()関数の実装とhttpのBindLambda()関数の実装部って結構似ていますよね。気が付かないで間違えてしまっても仕方ないですね。
うーん。なるほど。やっと意味が分かって来ました。“コールバックの関数をデリゲートに追加するためのいくつかの方法がある。”と5の続きで述べられていますが、最初のラムダを使うのがその方法の一つ、今回のUObjectを使うのが別な方法の一つと言う事ですね。となるとこのBindUObject()関数は実装しなくてもいいみたいですね。
Step.6
取りあえず、step.5の残りのコードは実装しなくてもいいとの仮定が正しいとして、次のstepを実装します。以下にもう一度httpオブジェクトの工程を示しますがこの手順の4を実装します。
サンプルコードでは以下に示すように実装されていました。
教科書と全く同じですね。
私のコードにも実装しました。
Step.7
サンプルコードの方も同じです。
私のコードにも実装します。
<結果>
ビルドします。
成功しました。
Playを実行します。
途中省略
途中省略
最後に、Request successの文字も確認出来ました。
成功しているみたいですね。
確認のために、
ここから
ここまでをテキストにコピーしてhtmlファイルにして開いて見ると、
UE4のホームページの最初に現れるイラストの一部が表示されました。
取りあえずは成功したみたいです。
<考察>
Delegateオブジェクトは以下に示す図の1にあるBindLambda()関数を使用して5の続きで述べられている「コールバックの関数をデリゲートに追加する。」を行いました。
しかし教科書には、以下に示すように
工程表(FHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateの関数を呼び出す工程)の7に示すUObjectを使用して「コールバックの関数をデリゲートに追加する。」方法についての解説があります。この部分はかなり詳しく説明されているので、ここで勉強しておきます。
まず、確認ですが、step.4 で作成した関数HttpRequestComplete()が「コールバックの関数をデリゲートに追加する。」におけるコールバックの関数と考えられます。上記の5の続きでもはっきりとHttpRequestCompleteと書かれています。以下にstep.4をもう一度示しておきます。
早速BindUObject()関数を試してみましょう。
私のHttpRequestComplete()関数は実装していないのでサンプルコードのHttpRequestComplete()関数の実装部をコピーします。
更に、1のBindLambda()関数をコメントして7のBindUObject()関数を追加します。
ここまで書いて思い出したのですが、はるか昔、確か5章のHaving Events and DelegatesでUFunctionでない生の関数はコールバック出来ないみたい事を勉強した記憶があります。が取り敢えず試してみます。
普通にビルド出来ましたね。Playを実行すると
普通に出来ました。HttpRequestComplete()関数を持つgamemodeクラスがUObjectだからHttpRequestComplete()関数そのものがUFunctionが無くても良いみたいですね。5章のHaving Events and Delegatesを読み直せばはっきり分るのですが今はそこまでの気力がないのでこの解釈で行きます。
となると、FHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateの関数を呼び出す工程の6であるBindUFunction()はHttpRequestComplete()関数には使えないのかもしれませんね。
サンプルコードの6のBindUFunction()にしっかりとその事が書かれていました。この文章は今の今まで何回も読んでいたのですが意味が分かっていませんでした。今やっとこの6のコメントの意味が分かりました。となると残りの教科書の解説もdelegateについてのようでそれなら5章のHaving Events and Delegatesを読めばいいのでここで敢えてやる必要はないみたいですね。
<IHttpRequestクラスとFHttpRequestCompleteDelegateクラスについて>
この二つは多分クラスではないと思いながら敢えて調べないでクラスとして扱っていました。その方が理解しやすくかつ扱いやすかったからです。一応ここで調べておきます。
UE4のAPIによるとIHttpRequestは、
Httpリクエストのためのインターフェイス(FHttpFactoryを使用して作成される)
とありますがシンタックスを見ると、
普通にクラスですよね。
FHttpModuleのUE4APIによるとCreateRequestメンバー関数を持ちそれが
TSharedRef<IHttpRequest>を返すようです。IHttpRequestはクラスと呼んでも問題はなさそうですね。
FHttpRequestCompleteDelegateについて調べます。
UE4のAPIによるとFHttpRequestCompleteDelegateは、
Httpリクエストが終了した時に呼ばれるデリゲート
とあり
タイプと紹介されていました。ので、「FHttpRequestCompleteDelegateクラスのオブジェクトであるdelegateは…」と言う言い方は「FHttpRequestCompleteDelegateタイプの変数であるdelegateは…」と言った方が良いみたいですね。ただC++の変数とタイプとオブジェクトとクラスの関係は抽象的な概念も入ってくるので人それぞれかもしれません。「FHttpRequestCompleteDelegateタイプの変数であるdelegateは…」と言った方が良いと考える人の方が多そうみたいです。ぐらいにしておいた方が良いかもしれません。
<まとめ>
UE4のHTTP APIを使用してHTTPのリクエストをしてサーバーに接続する方法を学びました。以下に示す工程でIHttpRequestクラスのオブジェクトの関数を呼び出します。今回はgamemodeクラスから呼び出しました。
上の工程の3でFHttpRequestCompleteDelegateクラスからのオブジェクトを作成しますがいかに示す1から7のいずれかの方法でコールバックの関数をデリゲートに追加します。
今回は、1と7で試してみました。
<おまけ>
ラムダについての復習です。これは来週まとめます。