谷本 心 in せろ部屋 このページをアンテナに追加 RSSフィード

2018-04-23

[]Dell XPS 15とThinkpad X1 Yogaを比較する(対戦格闘ゲーム編) #デルアンバサダー

Dell XPS 15 (9560)とThinkpad X1 Yoga (2017)の比較、第二弾。今回はSteam版のStreet Fighgter V: Arcade Editionをそれぞれで動かしてみます。

XPS 15はdGPUとしてGTX 1050を積んでおり、3Dゲームをやるのに申し分ない性能のはずです。一方でThinkpadIntel 620なので、3Dゲームは苦手なはずです。そんなわけで比較する前から勝負は明らかですが、実際にXPS 15ではどれぐらい安定するのか、Thinkpadでは本当に動かないのか、という辺りを見てみたいと思います。


Street Fighgter V: Arcade Editionを動かしてみた

こういうのは実際に動かした動画を見せるのが手っ取り早いので、動画を置いておきます。なお動画のキャプチャがゲームの動作に影響しないよう、それぞれノートPCからHDMI出力したものを外部機器(GV-HDREC)で録画しました。


Intel 620では標準画質ですらダメ

まずはデフォルト設定の標準画質から。解像度は1920 * 1080、画質は中です。

少し見づらいですが、左上に小さくFPSを表示しているので、そちらが参考になります。


XPS 15 標準画質

D


Thinkpad X1 Yoga 標準画質

D


差は明らかですね。XPS 15は何の問題もなく動作して60fpsで安定しますが、Thinkpad側はひどいスローモーションになってしまい15fpsぐらいしか出ていません。まぁこれぐらいの差は予想通りです。


Thinkpadでも低画質なら動くのか?

Thinkpadでは標準画質だとまともに動きませんでしたが、画質を下げれば動くのでしょうか。解像度と画質を下げてみます。解像度は1366 * 768、画質は低です。


Thinkpad X1 Yoga 低画質

D


見た感じきちんと動いていますが、完全に60fpsで安定しているわけではなく少しだけフレームレートが落ちることもあります。60fpsで安定させるためにはもう少し解像度なり画質なりを下げる必要がありそうです。たださすがにそこまでしてプレイする気にはなれない感じです。


XPS 15なら最高画質でも大丈夫か?

標準画質なら問題なく動いたXPS 15ですが、最高画質ではどうでしょうか。解像度は1920 * 1080、画質は最高です。


XPS 15 最高画質

D


ほぼ問題なく動いているのですが、真空波動拳を出す瞬間に57fps程度まで下がっていることが分かります。この画質で安定して動かしたければ、もう一段上のGTX 1060ぐらいが必要なんでしょうね。画質を「最高」ではなく「高」にすれば60fpsで安定したため、普通に遊ぶ分には全く申し分なさそうです。


描画の遅延はどうか?

対戦格闘ゲームなど、それなりにシビアな反応を要するゲームをプレイする場合、液晶や入力の遅延は無視できません。ディスプレイによって内部遅延(回路遅延)や描画遅延などが異なっていることは、ゲーマーの間でもよく話題に上ります。遅延については絶対的な計測はできないのですが、ディスプレイの遅延の比較であれば、本体のディスプレイHDMI接続したディスプレイに同じものを映することで、どれぐらい差があるか比較することができます。


今回はLCD Delay Checker ( http://bygzam.seesaa.net/article/110314791.html ) というソフトウェアを利用し、ノートPCのディスプレイと、HDMI接続したゲーミングディスプレイに同じタイマーを表示して、その様子をハイスピード撮影することで、遅延の差を比較したいと思います。

HDMI接続するディスプレイは、HORIのポータブルゲーミングモニターです。独自に計測した結果ですが、一般的なゲーミングディスプレイと同等の遅延だということが分かっています。

ハイスピード撮影は、iPhone7のスローモーション撮影機能を利用しました。240fpsで撮影できるため、60fpsの描画1フレーム分を4コマに分けて撮ることができます。


なおYoutubeの動画は停止した後に . (ドット) でコマ送り , (カンマ) でコマ戻しができるため、よく比較して見るときにはその機能を使ってください。


XPS vs HORI Monitor

左がXPS 15、右がHORI Portable Gaming Monitorです

D


XPS側が動画中の3コマ分、つまり3/4フレームほど遅延していることが分かります。ミリ秒で言うと12.5msほどの遅延です。


Thinkpad vs HORI Monitor

左がThinkpad X1 Yoga、右がHORI Portable Gaming Monitorです

D


今度はThinkpadのほうが動画中の4コマ分、ちょうど1フレームほど先行していることが分かります。ミリ秒で言うと16.6msほど早いです。


結果と考察と体感

もしノートPCのディスプレイの表示と、HDMI出力側の表示が完全に同時であると仮定すると、上の結果から次のことが言えます。


(遅延:小) Thinkpad <(1.0F)< HORI Monitor <(0.75F)< XPS (遅延:大)


ただ、これは私の体感とは違っています。

ThinkpadとHORI Monitorを使ったときには、普段ミスしない連続技が「入力が遅いがために」ミスすることが多く、どうも2フレームほど表示が遅れているような体感があります。一方で、XPSとHORI Monitorを使う時にはそこまで大きな遅延を感じません。また、Thinkpad本体のディスプレイXPS本体のディスプレイも、遅延には差がないという体感です。

この体感が正しいとすると、最初の仮定である「ノートPCのディスプレイの表示と、HDMI出力側の表示が完全に同時である」が誤っており、Thinkpadの場合は「HDMI出力に遅延がある」ということになります。

XPSThinkpadの本体ディスプレイの差がないと仮定すると、結果からは次のことが言えます。


(遅延:小) HORI Monitor + XPS <(0.75F)< XPS本体ディスプレイThinkpad本体ディスプレイ <(1.0F)< HORI Monitor + Thinkpad (遅延:大)


HDMI出力同士を比較すると1.75Fとなっており、2Fぐらいの遅延を感じるという体感とも近くなります。厳密っぽく計測しておいて、最後は体感に頼るというのは何ともアレな話なのですけどね。


ファンの音も比較

最後に、気になるファンの音について。

XPS 15のほうはやっぱりうるさいです。高い音ではないですが、扇風機ぐらい(?)の音はします。ゲームを終えれば少し静かになりますが、それでもしばらくは回り続けます。通常電圧のCPUと、性能が高めのGPUを積んでいるので、この辺りは仕方ないのでしょうね。

一方のThinkpadでは、一番うるさい状態がXPS 15の静かな状態とあまり変わらない、という程度の静かさです。そしてゲームを終えれば数秒でファンが止まります。

当たり前ですが、性能と静かさはトレードオフ、ということですね。


まとめ

3Dゲームを動かすという用途では、事前の予想通りXPS 15では高画質でも安定動作しました。一方、全く動かないだろうなと思っていたThinkpadでも低画質ならそれなりに動作することが分かったので、トレーニングモードで少し遊んだり調査するぐらいには使えそうです。

ただThinkpadのほうはHDMI出力が遅延しているのは予想外でした。たとえ本体の液晶がゲーミングディスプレイほどの性能でなかったとしても、HDMI出力すれば補えると考えていただけに、Thinkpadではそこが少し悪い方向に裏切られましたね。もちろん、2D中心で多少の遅延が許されるRPGシミュレーションゲームであればThinkpadでも問題なく遊べるので、ゲーム全般がダメというわけではありません。


ということで1ヵ月の間、XPS 15とThinkpad X1 Yogaの比較を行ってきました。

Thinkpad X1 Yogaキーボードの打鍵感がよく、また話題にはしませんでしたがペン入力ができるというXPS 15にもMacBookにもない特徴を持っています(XPS 15もタッチパネルですが、ペンはありません)。そういう点で、エンジニアの開発マシンとしては良いでしょう。

またXPSはGTX 1050の恩恵もあって3Dゲームでも問題なく動作させることができます。HDMI経由でゲーミングディスプレイを接続すれば、より少ない遅延でプレイすることができます。今回の期間では試せなかったのですが、動画編集や配信などにおいてもXPS 15のほうが強いことが期待できます。そのような性能が必要となる作業向けマシンとして良いでしょうね。


最後になりましたが、XPSのレビュー機会を与えてくれたデルのアンバサダープログラムにはとても感謝しています。もしThinkpadXPSの両方を事前にモニターできていて、いま改めてXPSThinkpadのどちらかを買うか決めるのであれば、かなり悩んだでしょうねw

その場合、恐らくですが「XPSを買って外付けのキーボードを使う」という選択をしたと思います。ThinkpadHDMI出力が遅延するのは残念ですし、キーボード以外はXPSのほうが良い印象でした。


そんな感じでXPS 15とThinkpad X1 Yogaの比較はこれで終わります。このブログが、少しでも皆さんのPC選びに役立てば幸いです!

2018-04-18

[]Dell XPS 15とThinkpad X1 Yogaを比較する(入出力編) #デルアンバサダー

先日、Thinkpad X1 Yoga (2017年モデル)を発注した、という話を書いたばかりですが、その発注をした3日後に、デルアンバサダープログラムから当選メールが届きました! Dell XPS 15 (9560)の体験モニターする機会をもらったのです。

前回のエントリーでは最終的に「GeForceを積んでいること」か「ペンが使えること」の2択になり、ペンを重視してThinkapd X1 Yogaにしたと書いていましたが、もしGeForceを重視していればこのXPS 15を買うつもりでした。そんな製品をモニターできる機会ができたので、せっかくなのでしっかりレビューしたいと思います。


なおXPS 15はモニターで借りたもの、Thinkpadは自分で買ったもの、という比較ですので、「XPS 15のほうは悪いことが言えないでしょ」と思われるかも知れませんが、正味で言うと「借りた感謝の気持ち」と「自分で買ったものを肯定したい気持ち」のせめぎあいみたいな感じなので、総じて五分になると思います。まぁあまり気にしすぎず、ドライに比較したいと思います。

なお元々はMacBook Pro 13 (Late 2013) を使っていたので、そちらとも少し比べたいと思います。


外部インタフェースは、やっぱりHDMIが嬉しい

まずは、そもそもMacからWindowsに乗り換えたきっかけになった外部インタフェースの話から。

XPS 15、Thinkpadともに、USB Type-A、USB Type-C、HDMIがついています。また、XPS 15にはSDカードスロット、ThinkpadにはmicroSDカードスロットがついており、SSDの容量不足をちょっと補うこともできます。

一方のMacBook Proは、Late 2013にはUSB Type-A、HDMI、Thunderbolt、SDカードスロットがついていましたが、現行機種はUSB Type-Cしかありません。これが致命的な問題でした。

「頻繁に客先で画面を見せる」かつ「変換ケーブルを常に持ち歩けるほどの几帳面さがない」という私にとって、HDMIの有無は死活問題なのです。この点で、XPS 15、Thinkpadともに希望を叶えるだけの外部インタフェースを備えているのが良いですね。


キーボード比べ、Thinkpadの打鍵感がいい

続いて、プログラミングする人にとって一番大切なキーボードの比較。これは写真を見てもらうのが手っ取り早いでしょう。

f:id:cero-t:20180417083535p:image:w640

上側がXPS 15のキーボード、下側がThinpkadのキーボードで、同じ距離から撮影しました。


XPS 15のほうが横幅がやや狭くなっており、右のほうにあるEnterやBack space、円マーク、Shiftなどが小さめのキーになっています。XPS 15は筐体サイズに余裕があるのだから、こんないびつな形にしなくても良いんじゃないかと思いますが、XPS 13などと同じサイズにするための工夫なのでしょうか。ちょっと残念です。

一方でThinkpadキーボードにはそのようないびつさはありません。


ただXPS 15のほうが良いのは、Page Up / Page Down / Home / End の配置。Fnキーとカーソルキーの同時押しに割り当てられるという「よくある配置」です。MacBookも同じキーアサインですね。

一方のThinkpadはPage Up / Page Downがカーソルキーの上に配置されているほか、Home / End はファンクションの横というあまり普段押さない位置にあるため、ちょっと使いづらい印象です。しかもそのせいでPring Screenが右下のほうに来ており、スペースキーの右側のキーが多すぎて残念な感じになっています。


打鍵感についてはThinkpadが良いですね、さすがです。ノートPCの割にキーストロークがあり、それでも重くはありません。一方のXPS 15は、キーストロークが少し浅い典型的なノートPC用のキーボードという感じです。MacBookキーボードも打鍵感は普通、現行機種のバタフライキーボードはキーストロークが浅く、打鍵音もうるさいのでちょっと残念ですね。


結論:

サイズ感と打鍵感はThinkpadのほうが良いが、配置に関してはXPS 15のほうが素直。普通に使うならXPSのほうが使いやすいだろうけど、エンジニア的にはThinkpadの勝ち。


画面の解像度は、XPSがやや優勢

XPS 15は15インチで3840 * 2160の、いわゆる4Kディスプレイ

Thinkpadは14インチで2560 * 1440で、こちらもフルHDを超えるWQHD(QHD)ディスプレイ


同じ4Kの壁紙を並べてみたのが、下の写真です(左がXPS 15、右がThinkpad

f:id:cero-t:20180417085827p:image:w600

このまま見ても画像圧縮がイマイチなのでまったく分からないので、オリジナルサイズで見ていただけると良いと思うのですが、比べてみると、XPS 15のほうが明るくて綺麗、Thinkpadのほうが色を濃いめ(色温度が高め)、という感じです。正味の話、どっちも綺麗なので、そんなに解像度の差は感じません。


それよりも変わってくるのが、フォントのほう。これぐらいの解像度になると標準のフォントサイズでは小さすぎて文字が読みづらいため、XPS 15ではフォントサイズが250%、Thinkpadでは200%に拡大された状態になっています。ただこれでは文字が大きすぎてせっかくの高解像度が生かされないため、XPS 15では200%、Thinkpadは150%にして使うことにしました。


この状態Yahoo! Japanを見てみると、表示がはっきり異なってきます。

同じ距離からディスプレイを撮影したのが次の画像です(上がXPS 15、下がThinkpad

f:id:cero-t:20180418082522p:image:w640

f:id:cero-t:20180418082531p:image:w640

どうもYahoo! Japanフォントは、フォントサイズを200%にするとくっきりするのですが、それ未満だと細くなってしまい読みにくくなります。Thinkpadでは150%にしているため、このような細さになってしまうわけです。ほとんどのサイトでは問題ないのですが、たまにこういう表示になってしまうサイトがあるという感じです。ちょっと残念ですね。

なおフォントの見やすさに関して言えば、MacBookの圧勝です。圧倒的に見やすいです。


結論:

私の目では、見た目の綺麗さの区別はそこまで付かない。フォントが細くなってしまうサイトのことを考えると、XPS 15の200%表示が良い。大きいフォントが好きでThinkpadでも200%表示にする人なら、特に差はない。


ポインティングデバイスは五分

私はノートPCを単体で使う時にはマウスを使わないと決めているので、本体のポインティングデバイスは大事です。

まずタッチパッドを比較すると、XPS 15のタッチパッドは「さらさら」した感じ、Thinkpadタッチパッドは「つるつる」した感じです。いわばXPS 15はマット系、Thinpkadは光沢系、という感じです。


操作感を比較すると、XPS 15のタッチパッドのほうが操作しやすく、細かな調整など含めて思ったように動いてくれます。以前に比べればWindowsタッチパッドも割と使いやすくなってきたんだなという印象です。一方のThinkpadは細かな調整をしようとして動かなかったり行き過ぎたり、スクロールしようとしたら拡大縮小することがあるという、かなり残念な感じです。

ただThinkpadにはトラックポイント(赤ポチ)があるので、タッチパッドを無効化してトラックポイントのみを使うことにしました。まだ使い始めて1ヶ月も経ってないですが、「使いやすいですね」とは言えない状態ですけどね。


いずれにせよMacBookタッチパッドに使い慣れていると、XPS 15もThinkpadポインティングデバイスストレスが溜まる感じです。これはもう機体ではなくOSの差なのかも知れませんね。


結論:

Thinkpadタッチパッドは全然ダメ。Thinkpadトラックポイントはまだ慣れていないので評価が難しい。XPSタッチパッドは悪くないけど、MacBookには少し及ばず。


まとめと次回予告

入力デバイスに関してはそこまで大きな差はありませんが、エンジニアであれば、打鍵感のよいThinkpadのほうが良いという結論です。

XPSは、入力デバイスの点で言えば、良くも悪くもあまり特徴のない印象です。ただ4Kディスプレイの強みを生かして、フォントを200%にすればどんなサイトでも綺麗に見られるというのは良いですね。


そもそも入力デバイスとして定評があるThinkpadと、その辺りは平凡なXPS 15の比較だったので、最初からXPS側の苦戦は見えていたかも知れません。

そんなわけで次回は、XPS 15の強みである「GeForce GTX 1050」を活かすため、3Dゲームのプレイ比較をしたいと思います。

2017-12-14

[][]EXレイヤーの無限コンボについて #FEXL

※Fighting EX Layerというゲームの話です。エンジニアの皆さんごめんなさい。


大前提として、僕は無限コンボがとても嫌いで、調整で取り除くべきだという立場です。理由は、最多段や最大ダメージを組み立てる楽しみがなくなるから。

この後の話はそういう立場での意見表明なので、そもそも無限は悪くないじゃんって人とは、また別の場所で殴り合いましょう。


で、発売初日からみんな試してるEXレイヤーの無限コンボ。

正直みんながやりすぎるせいで、このゲームの印象が「ダッシュ小Pから無限コンボ入れるゲームだよね」になって、今後のセールスに影響するんじゃないかって心配するぐらい。

まぁそう言う僕も「いやこれ地上だけじゃなくて、空中でもできるのでは、なんか浮き低いし」っていうSAI-RECチームの会話から、対空での無限コンボをササっと撮ったりしたんですけども。


このエントリーは、そういう無限コンボをなくしてくださいという話です。


EXの無限コンボは3種類

過去のEXシリーズにおける無限コンボの可能性は、大きく分けてこの3つに分類できる。

1. 当たり判定を残したまま落ちてくる必殺技が、空中ヒットしても同等の性質を持つ場合

2. 有利フレームが取れる技を当てた後に密着する場合

3. スパコンのループ中にゲージが溜まる場合


1. 当たり判定が残ったまま落ちてくる必殺技

1は、EXの豪鬼と、2PLUSのロッソ。

https://www.youtube.com/watch?v=UB8ch4ilyqE&t=3m6s (ロッソ)


豪鬼は続編で落下時の当たり判定をなくすことにより対策された。正直、豪鬼は竜巻から拾えるキャラなので、この調整が「豪鬼らしさ」を失った面もあるとは思う。

一方でロッソは明らかな調整ミスか見落としだと思う。ヴェズーヴィオの怒りは空中ヒット時にきりもみにすべきだった。エリアのジャクソンキックはそういう調整がされているのに。

同じような無限の可能性はハヤテの朧月にもあるけど、当たり方や隙の大きさの都合で今のところ無限には至ってない。ただ危険だとは思っていて、無限にならないのはたまたまだと思っている。


EXレイヤーでは、落下時の判定残りは凄く気を使ってるように見える。

スパコンから当たり判定を残しにくくなったのは、過去作のコンボ人としてはちょっと残念だけど、納得する部分もある。今作は空中コンボをチェーンコンボにしてダメージを高くしやすい分、打ち上げスパコンのダメージを抑えるという調整をしているように見える。なので、ファイヤーフォースとか気錬射とか蒼魂弾とかスカロエナジーみたいなダメージあるスパコンから安易に拾われたら困る、と考えれば納得できる。


話が逸れたけど、必殺技から拾える場合は、見落とさずに調整してくれれば大丈夫だと思う。


2. 有利フレームを取ったまま密着

2は、EXの豪鬼と、2PLUSのサガット、春麗、シャドウ

https://www.youtube.com/watch?v=I9JtCuYihiM&t=2m43s (豪鬼)

https://www.youtube.com/watch?v=LZOaASBaaYg&t=2m36s (シャドウ)


「他の技を当てた後に空刃脚を出せる」というパターンは、常に無限コンボの可能性が伴うと思ってる。2PLUSのプルムも無限になりかねないけど、たまたま見つかってない可能性も大いにある。

EXの豪鬼が良い例であるように、たとえヒット時の距離を調整して無限にならないようにしても、コンボ職人は「途中の技を空振り」したり「持続当てになる連続技」を見つけて有利フレームを獲得して、近づいたりする方法を編み出したりする。


EXレイヤーの地上ダッシュループも同じで、正直、最初にダッシュとチェーンコンボがあると聞いた時点で「そんなん無限コンボじゃん」と思ってたぐらい、システム的には当たり前に無限コンボができる。いまは1フレ繋ぎ、2フレ繋ぎでとどまってるけど、そのうち「大Pが持続当たりする」パターンが見つかって、もっとコンボ猶予が増えて誰でもできるようになる可能性がある。そう考えると、ダッシュを1〜2フレ調整したところで意味がないと思う。

あと、もしエリアが出てきて、小Pからレバー入れ大Pがチェーンで繋がったりするとまた小Pに戻れるので、個別の調整が必要になる。やっぱりチェーンのない世界感で作ってきたゲームに、チェーンを入れるというのが無理筋な気がする。


そう考えると、チェーンコンボはダウンを奪わない限り完全五分とか、イージーコンビネーション以外のチェーンはなしとするとか、イージーコンビネーションのPとKを交互に行き来できる程度にするとか、なんかもっと大胆な調整が必要だと思う。

ここで言ってるイージーコンビネーションとは、「開発者がPとKで出せる技を決められるので、有利フレームが取れるものは選ばない」という枠組みでのチェーンコンボのこと。


3. スパコンのループ中にゲージが溜まる

3は、例で言うと2PLUSの七瀬。

https://www.youtube.com/watch?v=8ZIOOguoYRE&t=2m56s


EXレイヤーでも、浮かし技からチェーンやダッシュチェーンを挟んで1ゲージ回復して、また打ち上げコンボにつなげるパターンがいくつも見つかっている。強氣でゲージアップを積めば、さらにやりやすくなるかもしれない。

このあたりは(少なくとも空中コンボの無限がなくなる前提で)ゲージ調整で何とかなるし、コンボ職人がβテスト期間中に頑張るべきやつだと思う。


新しいタイプの無限空中コンボ

浮いてる相手にダッシュ小P。もっと簡単なルートもある。

https://twitter.com/twitter/statuses/940622418745294848


空中相手に対して通常技を刻む無限コンボは、EXシリーズとしては全く新しいタイプの無限と言える。これは浮きが中途半端ゆえに起きてる。

過去のEXシリーズは技を刻んでいると徐々に相手が高く浮いていくので拾えなくなっていたのだけど、今作はそこまで浮かない。恐らく打ち上げ系のスパコンからチェーンコンボを入れたいための調整だと思う。

しかしだからと言って、バーチャシリーズほど落ちていくわけでもないので、結果、コパンや下中Pで浮かし直して無限コンボができてしまう。

もちろん「ダッシュで距離を詰めることができる」のも一因だけど、本質的には「浮きの高さ」の問題。


もっと浮くようにするか、もっと落ちるようにするか、どっちかしかないと思う。浮かし技からいっぱい技を入れて浮かし直すコンボにロマンがあると言うなら、やっぱり「もっと落ちる」方向じゃないかなぁ。なんだったら、多少、バウンドを拾えてもいいと思う。もうEX/レイヤーの世界観ではないけども。


なんて言ってる間にアップデート

そんな事を考えてモヤモヤしてたら、無限コンボの改善をしたアップデートが行われるらしい。

https://twitter.com/arika_co_jp/status/941248365643186176


良かった良かった。

いや、ほんと無限のせいでβ版のコンボを調べる気力をロストしてたので、良かった。

2017-12-07

[][]Goで格闘ゲームのマクロを実装してみた

このところ、夜な夜な格ゲーの動画を撮ってはYoutubeにアップしては、SRK(shoryuken.com)に取り上げられて承認欲求を満たしている @ です。SRKは世界最大の格ゲーコミュニティだよ!

http://shoryuken.com/?s=Shin+Tanimoto


このエントリーは Go2 Advent Calendar 2017 の7日目です。

https://qiita.com/advent-calendar/2017/go2


はじめに

まえがき

学生時代、僕は格闘ゲームの連続技(コンボ)の研究を嗜んでおりまして、学校に行くぐらいならゲーセンを行く、試験勉強するぐらいならコンボの研究をする、卒論を書くぐらいならコンボビデオを撮るという、クズみたいな学生生活を送っておりました。当時、いまは亡き「ゲーメスト」というアーケードゲーム雑誌で体験ライターとして編集部に泊まり込んで記事を書くような体験もしました。

その頃よくプレイしていたストリートファイター系のゲームが、なんと18年の歳月を経て新作を出すというのです。これは万難を排してやらねばならぬと相成ったのですが、当時20歳前後だった僕も先日ついに40歳を迎えたわけで、さすがにもうあの頃のようには手が動きません。

しかし、いまの僕には、あの頃の自分にはなかったプログラミング能力があります。人間が手作業で行っていることを、機械に行わせるのが仕事です。そう、格闘ゲームも機械にやらせてしまえば良いじゃないか、というのが今回のテーマになります。


※なお、ゲームのネット対戦やオンラインランキングでマクロを使うのはマナー違反です。決してやらないでください。


目的

プレステ3やプレステ4、Xboxなどで使えるマクロを作ります。実装にはGoを使いました。


用意するもの

ゲーム機とPCをつなぐ、何かしらのデバイスが必要になります。今回はConsole Tuner社のTitan Oneという機器を利用します。

https://www.consoletuner.com/products/titan-one/

f:id:cero-t:20171207075818j:image:w360


これはUSBゲームコントローラーを様々なゲーム機で使えるようにするための変換器です。しかしただの変換機能だけでなく、PCと繋ぐことでマクロ機能やプログラミングを組んだりできるほか、API自体もDLLとして公開されているので、自作アプリからも利用することができます。今回の用途にはベストです。


Titan OneとDLLを使って、次のような流れで操作を流し込みます。

ゲーム機 <-(USB)- Titan One <-(USB)- Windows(DLL <- 自作アプリケーション)

なんとなくイメージが湧くでしょうか?


なぜアプリを自作するのか?

先ほど「マクロ機能やプログラミング環境が用意されている」と書いた通り、用意された環境を利用すれば、別にアプリを自作するまでもなくマクロを実行することができます。ただ、付属のプログラミング環境ではミリ秒単位の制御しかできず、格闘ゲームの60fps(framer per second)の制御をするには不便でした。格闘ゲーマーは物事をフレーム単位で考えていて、ミリ秒単位で考えるわけではないのです。

なので、フレーム単位で扱いやすくするための、簡単なアプリを自作することにしました。


なぜGoなのか?

今回のアプリは、配布が容易であることと、Mac/Windowsのいずれでも開発できるという点でGoを選びました。

僕はJavaエンジニアなので、最初はJavaでJNAを使って実装しました。Javaでも問題なく動くものができたのですが、ただちょっと配布するのが面倒だなと思い、かと言ってC#にするとMacでの開発環境が微妙なので、Goを選ぶに至りました。もちろんそんな消去法的な選択だけでなく、ちょっとGoを勉強してみたかったという背景もあります。


DLL呼び出しの実装

それではGoで実装していきます。僕はGo初心者で体系的に学んだこともないので、ここがおかしいよとか、ここもっとこうした方が良いよというのがあれば、教えてもらえると凄く嬉しいです。


DLLの読み込み

まずTitan Oneが提供しているDLL(gcdapi.dll)を読み込む部分を作ります。

goでDLLを読み込むにはsyscall.LoadDLLを使います。

dll, _err := syscall.LoadDLL("gcdapi.dll")
if _err != nil {
	log.Fatal("Error loading gcdapi.dll", _err)
}

これでDLLの読み込みができます。


ここで「Failed to load gcdapi.dll: %1 is not a valid Win32 application.」というエラーに悩まされました。

原因は「gcdapi.dllが32bit」だったことです。64bit環境でコンパイル/実行しようとするとエラーになるので、32bit環境向けにコンパイルする必要があります。

set GOARCH=386

これでOK。

ここで1時間ほどハマっていました。


DLL内の関数呼び出し

DLLにある関数を呼び出すには、読み込んだdllのFindProc関数を使います。

gcdapi_Load, _err := dll.FindProc("gcdapi_Load")
if _err != nil {
	log.Fatal("cannot find gcdapi_Load", _err)
}

result, _, _err := gcdapi_Load.Call()
if result == 0 {
	log.Fatal("gcdapi cannot be loaded")
}

これでgcdapi_Loadという関数を実行できます。

DLL内にある他の関数も、同じ要領で先に読み込んでおきます。


また、関数に引数を渡す必要がある場合は、uintptr型のポインタとして渡します。

次の例は、someFunction関数にarg1という値を渡す例です。

result, _, _ := someFunction.Call(uintptr(arg1))

戻り値はresultに入ります。


配列や構造体を引数に渡す

引数に配列や構造体を渡す場合は、先に参照をポインタに変換してから、さらにuintptrに変換します。

正直、参照がどうなってるのかもはや僕には理解できないのですが、こうすれば動くということだけ確認しました。

func write(inputs *[36]int8) bool {
	result, _, _ := procWrite.Call(uintptr(unsafe.Pointer(inputs)))
	// 略
	return true
}

ちなみにここで配列ではなくスライスを渡していたために上手く動かず、2時間ほどハマりました。初学者はとにかくハマって時間をロストしますね。


また構造体を引数にする場合は、DLL側に用意された構造体と同じ名前、同じ型の構造体をGo側に用意します。

次の例ではGCAPI_REPORTという構造体を引数に渡しています。

type GCAPI_REPORT struct {
	console       uint8
	controller    uint8
	led           [4]uint8
	rumble        [2]uint8
	battery_level uint8
	input         [30]GCAPI_INPUT
}

type GCAPI_INPUT struct {
	value      int8
	prev_value int8
	press_tv   uint32
}

func read() bool {
	var report GCAPI_REPORT
	result, _, _ := procRead.Call(uintptr(unsafe.Pointer(&report)))
	log.Println(report)
	// 略
	retun true
}

構造体は恐ろしいぐらいにハマらずに値が入りました。簡単です。最高です。


MacでもWindowsでも実行したい

ちなみにここまで使ってきたsyscall.LoadDLLや、その戻り値であるsyscall.DLL、syscall.ProcなどはWindows環境でしか利用できず、Mac環境でコンパイルしようとするとエラーになってしまいます。

せめてMacではDLL呼び出し部分だけモックにして、アプリ全体としては動くようにしておきたいものです。そういうのもきちんと用意されていました。


同じパッケージ内に、同名の関数を定義した別ソースファイル(モック用関数群)を作り、そのファイルの先頭にこんなコメントを入れます。

// +build !windows

これでWindowsの環境以外でビルドした場合には、モック用関数群のファイルが利用されるようになります。

・・・ちょっと何を言ってるのか分からない感じなので、実物を見て頂いたほうが早いと思います。


Windows用のソースコードがこれ。

https://github.com/cero-t/cero-macro.t1/blob/master/gcapi/gcapi_dll_windows.go


Windows以外用のソースコードはこれです。

https://github.com/cero-t/cero-macro.t1/blob/master/gcapi/gcapi_dll_other.go


いいですね、簡単です。


次に入力側を作る

DLLの呼び出しが上手く行ったので、次は呼び出し側の設計/実装です。


懐かしのコマンド

20世紀に格闘ゲームをやっていた人にとって、「236p」という文字列は波動拳コマンドにしか見えません。これはテンキー配列の「下 右下 右」に相当するもので、最後のpがパンチです。当時のパソコン通信やインターネットで頻繁に用いられた記法です。同様に、昇龍拳は「623p」、竜巻旋風脚は「214k」となります。今回はこの表記をします。

また、パンチの小中大はそれぞれl, m, hで表現し、たとえば小パンチは「lp」、中キックは「mk」、しゃがみ大キックは「2,hk」とします。これは海外の格闘ゲーマーが用いている表記方法です。


この表記法を使い、たとえば次のようなテキストを受け取ることを想定します。

2,mk 8
3 1
6,lp 1

これはレバー下と中キックを8フレーム、右下に1フレーム、右と小パンチを1フレーム、というコマンドです。中足波動拳ですね。

このようなコマンドを受け取って、上で説明した関数の呼び出しを行うようにします。


入力コマンドの変換

格闘ゲームの入力は精度が命ですから、Go側の処理に時間が掛かってコマンドが不安定になってしまっては意味がありません。そのため、上に書いたテキストをあらかじめパースして配列などに変換しておき、その後にまとめてDLL経由でコマンドを流し込む処理を行うようにします。変換とDLL呼び出しを逐次行っていると、想定しない処理遅延が発生する可能性があるためです。


テキストをパースした後、次のような構造体に変換します。

type State struct {
	buttons [36]int8
	frames  uint16
}

buttonsが押すべきボタンの一覧です。配列のそれぞれがボタンやレバーに対応しており、押さない時は0、押した時は100という値が入ります。


変換は地味なコードなので割愛します。ここでやってます。

https://github.com/cero-t/cero-macro.t1/blob/master/processor/converter.go


正確な60fpsのコマンド実行

続いて、コマンド実行の処理です。State構造体の配列をきっちりと指定したフレームで呼び出す必要があります。

ちなみに格闘ゲームではよく「1/60秒」という表現が使われて、「光速は遅すぎる」など話題になることがありますが、少なくとも手元環境のプレステ3で確認したところ、1フレームは「1/60秒」ではなく「1/59.94秒」でした。


そのあたりも踏まえて、次のようなコードを書きました。ボタンを押した後に、そのフレーム数分(たとえば5フレームなら、約 5 * 16.6ms = 83ms)だけ待つというコードです。

ただしこの処理自体の実行時間も考慮する必要があるため、現在時刻など利用して、フレームレートを保つようにしています。

// フレームレート (frame per 1000 seconds)
const frameRate uint64 = 59940

func Process(states *[]State) {
	var totalFrames uint64
	start := time.Now().UnixNano()

	for _, state := range *states {
		// ボタンを押す
		gcapi.Push(&state.buttons)

		// フレーム数分だけsleepする
		totalFrames += uint64(state.frames)
		sleepTime := start + int64(totalFrames*1000*1000*1000*1000/frameRate) - time.Now().UnixNano()
		time.Sleep(time.Duration(sleepTime))
	}
}

正確に59.94fpsを保つためにわざわざアプリを自作したので、この処理が一番のキモと言えますね。


この関数を含む処理全体はここにあります。

https://github.com/cero-t/cero-macro.t1/blob/master/processor/processor.go


UIはどうする?

最後にUIです。

GoのUIをどうすべきかは議論があるようですが、「クロスプラットフォームで使えて、皆が慣れ親しんでいるUI」と言えば、コマンドライン(CUI)とHTMLの2つでしょう。


まずはCUI

テキストファイルを読み込んでその中に書かれた操作コマンドを実行するCUIを作ります。

こんな感じで、実行時の引数にファイル名を指定します。

$ go run macro.go combo.txt

これでcombo.txtに書かれた内容が、ゲーム機に送られるのです。


このmacro.txtという渋い名前のファイルの中身は、こんな感じです。

9 34
hp 20
2,mk 8
6 1
2 1
3,hp 1

ジャンプ大パンチ 中足 昇龍拳、という感じです。


続いてGUI

もう一つのUIが、HTTPサーバとして起動し、画面で入力されたコマンドを実行するGUIです。実行時の引数にファイル名を指定しなければHTTPサーバとして立ち上がります。

$ go run macro.go
Server started at [::]:8080

それでlocalhostの(あるいは別のPCからIPアドレスを指定して)8080ポートにアクセスすれば、こんな画面が出てきます。

f:id:cero-t:20171207075959p:image

テキストボックスにコマンドを入力して「run」を押せば実行できる形です。

マクロで動かす以外にも簡単な操作をリモートでできるよう、他のボタンも置いておきました。「LP」ボタンを押すと、「lp 2」というマクロ(小パンチを2フレーム押す)が送られます。


なおHTMLファイルの使い方ですが、Goでリソースをバンドルするのがちょっと面倒だったので、ソースコード内にヒアドキュメントで書いてしまいました。

func formHTML() string {
	return `
<!DOCTYPE html>
<html>
<head>
(略)
</body>
</html>
`
}

なおViewフレームワークには超軽量で有名な「Vanilla JS」を用いているため、この1ファイルだけでViewのレンダリングやサーバとの通信処理なども実装できています。

http://vanilla-js.com/

はい。


HTTPクライアント、サーバ関連のソース全体はこちらです。

https://github.com/cero-t/cero-macro.t1/blob/master/http/form-html.go

https://github.com/cero-t/cero-macro.t1/blob/master/http/http.go


実際、試してみたところ・・・!!

これで画面からDLLまですべて繋がったので、いざ実機で挑戦!


おぉ、動く動く! キャラが動くよ!


・・・あれ、コンボが途切れるぞ?

なんか、コンボが不安定だぞ?


そうなんです、どうやらTitan Oneは処理遅延が10ms程度あり、その遅延も安定しないため、格闘ゲームの1フレームを安定させて動かすのが難しいようなのです。

ここまで書いてきてこのオチはなかなか酷いのですが、別にこれで終わりというわけではありません。

より遅延が少ないデバイスが出てきて、それをAPI経由で叩けるようになれば、また再挑戦したいと思います。


あれ、この手元にあるデバイスは、何だろう??

f:id:cero-t:20171207080038j:image:w360


来年には、API経由で叩けるようになるのかなぁ。

2017-10-30

PS3のメモリーカードアダプターをPCに繋いで読み書きする

久々の投稿がゲームの、しかもニッチすぎる話題でアレですけど、備忘録として。


やりたいこと

1. PCに保存しているPS1のメモリーカードのデータを、PS1のメモリーカードに書き込む。

2. PS1のメモリーカードのデータを、PS3にコピーする。

3. PS3でPS1のゲームを起動して、メモリーカードのデータを読み込む。


背景

PS3では、PS3内に保存された仮想メモリーカードデータをUSBメモリ経由でPCにコピーできる。ただこのファイルは暗号化されているため、好き勝手できるわけではない。海外のエンジニアが復号ツールを作って公開しており、それを使えばメモリーカードのデータを閲覧することはできるのだけど、逆の暗号化ツールは作っていないため、任意のデータをPS3の仮想メモリーカードデータとして保存することができない。


要するに、USBメモリ経由で、

PS3 → PC はできるけど

PC → PS3 はできない

ということ。


ここで、PS3用のメモリーカードアダプターを使えば、任意のデータを読み書きできるようになるので、今回紹介するのはその方法。


用意するもの

1. PS3用メモリーカードアダプター。プレミアがついて中古でも4000円ぐらいする。

2. Windows、今回は7の64bit版で。別にMac + Parallelsとかでも大丈夫。


手順

1. Windowsにメモリーカードアダプターのデバイスドライバをインストール。

このサイトに書いてあることがすべて。

http://kazzx2.web.fc2.com/64bit_edit.html

ただ「64bit OS版 メモカアダプタドライバ」のURLが変わってるので注意。

http://www.axfc.net/uploader/so/1918620?key=DDR_KAZZ


2. MCRWwinを起動して、メモリーカードアダプターにメモリーカードを挿し、認識されればOK。


3. MCRWwinの「File(VM)」→「Read(File->VM)」を選択して、

 PCに保存しているPS1のメモリーカードのデータを指定する。

 拡張子は .mem が指定されるけど、128KBのファイルならまず読める。


4. メモリーカードへの書き込みが終わったら、メモリーカードアダプターごとPS3に接続


5. 起動後のメニュー画面からメモリーカードの管理を選んで、PS1のメモリーカードからPS3の仮想メモリーカードにコピー。

http://manuals.playstation.net/document/jp/ps3/current/game/mcsavedata.html