【PCゲーム】No.4 Ultima Underworld 1&2で日本語表示(Windows11)

※ 2025/12/24 v1.1がリリースされました。

 

Ultima Underworld

 

Ultima Underworld

 

お久しぶりです!!五ヵ月ぶりの更新になります。
今回はPCゲームの3回目、

ウルティマアンダーワールド1&2の日本語化になります。
日本語化では第2弾です。

そして、今回はなんとウルティマアンダーワールド1と

ウルティマアンダーワールド2の二本立てです。

何とお得な!!太っ腹っ!!
(1と2がエンジンが同じDLCみたいなもので、

片方が出来るともう片方も出来ちゃうとは言っちゃダメッ!)

 

~2025/12/24追加 ここから~

・V.1.1について

バージョン1.1ではデモアニメ時の翻訳が追加されました。

オープニング時のデモなどにも翻訳が表示されるようになります。

左子ウインドウに「OPデモの翻訳機能」が追加されています。

 ・「OFF」・・OPデモの翻訳機能がOFFになります。

 ・「ON」・・OPデモの翻訳機能がONになります。

 ・「自動でONOFF」・・OPデモの翻訳機能が自動でONOFFされます。

 これら3つの設定があり、oキーを押す度に切り替わります。

 特に問題がなければ「自動でONOFF」で良いでしょう。

 

当初この機能は入れる予定は無かったのですが、

試作したところ、そこそこ扱えるレベルだったのでサポートすることにしました。

~2025/12/24追加 ここまで~

 

では、使い方になります。さらっと行きます。

1、UW12_2025日本語版をダウンロードします。

UW12_2025日本語版(以降UW12と表記します)をここからダウンロードしてください。

 x64_EXE:UW12_2025日本語版_20251224_v1.1(x64).zip 

 ソース:UW12_2025日本語版_20251224_v1.1(ソース).zip

 また過去のバージョンはこちら

 

好きな場所に展開して使用します。

 

2、Google Cloud Translation APIAPI keyを取得します。

MM6の時もそうでしたが、UW12も翻訳にGoogle Cloud Translation APIのライブラリを使っています。
同じく使用する為に、Googleに申請してAPI Keyという文字列をもらう必要があります。
手順はMM6と同じです。詳しい手順は下記のサイトをみてください。

webloco.webolha.com

Google APIキーは「AIzaSy」で始まる39文字のテキストです。
これをUW12_設定.iniに書いて頂く必要があります。

 

3、UW12をインストールします。

今回、UW12のソフトはGOG版を使用しました。
まずUW12をインストールしたら、
ちゃんとゲームが起動出来ることを確認してください。

 

またSteamに登録してから使う方法もあります。
・Steamの「ライブラリ」タブを開く。
・右下の「ゲームを追加」をクリック。
・「非Steamゲームを追加」をクリック。
・GOG Galaxyの中にある「Launch Ultima Underworld」を選択するか
 または適当なゲーム先でクリック
・ライブラリに対象のゲームが登録されているので開き「プロパティ」をクリック。
・ショートカットの各値を変更する。
 名前「Ultima Underworld
 リンク先「"C:\Program Files (x86)\GOG Galaxy\Games\Ultima Underworld\DOSBOX\dosbox.exe"」
 作業ホルダー「"C:\Program Files (x86)\GOG Galaxy\Games\Ultima Underworld\DOSBOX"」
 起動オプション「 -conf "..\dosboxULTIMA1.conf" -conf "..\dosboxULTIMA1_single.conf" -noconsole -c "exit"」
・必要ならゲームパッドを有効にする。
・「管理」の「デスクトップショートカットを追加」をクリックする。
 デスクトップ上に「Ultima Underworld」が出来ているはずです。
 これはUW12_設定.iniの編集に使います。
・UW2も同じく行います。パスの違いなどを除き基本的に同じです。

これでSteamからUW12が起動できます。

 

4、UW12の設定(dosboxULTIMA1.confとdosboxULTIMA2.conf)を変更します。

次にdosboxULTIMA1.confとdosboxULTIMA2.confを開き、
それぞれ以下のように編集します。

 

fullscreen=false          // trueからfalseに変更します。
fulldouble=false          // これは変更は不要です。
fullresolution=1280x720   // 1280x720に変更します。
windowresolution=1280x720 // 1280x720に変更します。

 

UW12_2025日本語版は「ウインドウモードであること」
「1280x720であること」の制限があり、それを記入しています。

 

5、パソコンの設定を変更します。

まず、パソコンのディスプレイのサイズを変更します。
UW12は1280x720の解像度のみサポートしています。
ディスプレイのサイズをその値に変更してください。
(「ディスプレイの設定」で検索し、「ディスプレイの解像度」で変更できます)

 

次にUW12のショートカットアイコンを正しい位置に置きます。

 

GOG版なら、GOG Galaxy内に「Launch Ultima Underworld.lnk」と「Launch Ultima Underworld 2.lnk」という
ショートカットがあるのでそのコピーを任意の場所におきます。

 

Steam版なら、上の行でデスクトップ上に作った「Ultima Underworld
ショートカットを任意の場所におきます。(UW1とUW2どちらも)

 

6、次にUW12_設定.iniを編集します。
UW12_設定.iniをメモ帳で開き、

 

・「APIKey=””」にGoogle APIキーを記入してください。

 

・「UW1_shortcutPath_1」と「UW1_shortcutPath_2」に先ほどおいた
UW1のショートカットの実行パスを書いてください。
アプリを起動させると、「UW1_shortcutPath_1」、「UW1_shortcutPath_2」の順で
UW1の起動を試みます。どちらも起動出来なかった場合、ユーザーにUW1の起動を
行なってもらいます。(10秒間)

 

・UW2も同様に「UW2_shortcutPath_1」と「UW2_shortcutPath_2」に
ショートカットの実行パスを書いてください。

 

例として、Steam版ならこの設定にします。
[UW12]
UW1_shortcutPath_1="C:\temp\UW12-1Run.url"
UW1_shortcutPath_2=""

UW2_shortcutPath_1="C:\temp\UW12-2Run.url"
UW2_shortcutPath_2=""

 

GOG版ならこの設定にします。
[UW12]
UW1_shortcutPath_1="C:\Program Files (x86)\GOG Galaxy\Games\Ultima Underworld\Launch Ultima Underworld.lnk"
UW1_shortcutPath_2=""

UW2_shortcutPath_1="C:\Program Files (x86)\GOG Galaxy\Games\Ultima Underworld 2\Launch Ultima Underworld 2.lnk"
UW2_shortcutPath_2=""

 

7、インターネットに接続します。
UW12は翻訳にインターネットを使うので、インターネット接続が必須です。

 

8、UW12を起動します。

UW12_2025日本語版.exeを起動させます。
メッセージボックスが表示されますので、
UW1を起動するなら「はい」を、UW2を起動させるなら「いいえ」を選択してください。


UW12は自動で起動し翻訳が始まります。
UW12は必ずウインドウモードにしてください。(Alt+Enterで切替できます。)


終了は通常通りUW12を終了すれば良いです。


9、注意点
カーソルはテキストが表示されている場所におかないでください。
カーソルもOCRの対象になってしまい、誤字脱字の原因になります。
お手数ですがテキストが乗った場所にはカーソルを置かないようにしてください。

 

例えば、このようにテキストの場所にカーソルを置いてしまうと、

 

OCRにはこの画像で計算することになります。
これが誤字脱字の原因になります。

 

また、お金や石、ろうそくなどの複数所持されるアイテムは

何個移動するか選択する操作になります。その間は翻訳はキャンセルされますので

移動する操作を行ってください。

最も下の「Move now many?」がそれです。

 

10、UW12_設定.iniのデバッグ設定

今回UW12_設定.iniにデバッグ設定がいくつか追加されました。

■TranslationFPS

翻訳するタイミングを変更します。
FPSで指定します。
TranslationFPSに指定したFPSを記入します。
FPS~60FPSの範囲で設定できます。
デフォルトは10FPS(100ms)です。

■UseTextColor

翻訳で表示されるテキストの色を変更します。

 まず、bUseTextColor_GlobalEnableに「true」と記入してください。

 テキストは以下の6種類になります。

 1、NPCのテキストの色。パターンその1
 2、NPCのテキストの色。パターンその2
 3、NPCのテキストの色。パターンその3
  NPC(ノンプレイヤーキャラクター)との会話時の
  テキストの色を変更します。テキストの色は3種類あります。
  bUseTextColor_NPC1~3に「true」と記入してください。
  TextColorForNPC1~3に、色を0x00RRGGBBで記入してください。
  NPC2とNPC3はUW2のみ有効です。UW1では無視されます。

 4、MCのテキストの色。
  MC(主人公)のテキストの色を変更します。
  bUseTextColor_MCに「true」と記入してください。
  TextColorForMCに、色を0x00RRGGBBで記入してください。

 5、背景の色。
  背景の色を変更します。
  bUseTextColor_BackgroundRectに「true」と記入してください。
  TextColorForParchmentに、色を0x00RRGGBBで記入してください。

 6、背景の枠線の色。
  背景の枠線の色を変更します。
  bUseTextColor_BackgroundBorderに「true」と記入してください。
  TextColorForParchmentLineに、色を0x00RRGGBBで記入してください。

色のデフォルト値は、ゲームの元々の色になっています。

 

■SaveOCRImageProcess

OCRを行う際に、事前に画面が加工されます。
その画像ファイルを指定したフォルダに保存します。
PNGファイルになります。デバッグ用になります。

 bSaveOCRImageProcessに「true」と記入してください。
 SaveOCRImageProcessPathに出力先のフォルダのパスを記入してください。

 

11、最後に

まず2025年にリリース出来て良かったです。年度が変わると

いろいろ直さなきゃなので大変!

元々もっと早くリリースする予定でした。基礎はMM6で出来ているので

それを移植すれば終わる予定でした。

しかし・・Tesseract OCR・・ッ!貴様精度悪すぎるんじゃ!!

もう今回はサヨナラじゃい!!

でも代わりで見つけたWin11-OneOCR!お前は凄い。よくやった!!

 

※翻訳ものは、これで最後にする予定です。たぶん・・

【PCゲーム】No.3 Might and Magic 6で日本語表示(Windows11)その2

 

MM6の日本語版がv1.0になり、ファイルをアップロードすることが出来ました。

是非ダウンロードして頂き、使用して頂いて欲しいです。

以下、インストールの手順になります。

※ 2025/7/28 v1.1がリリースされました。

※ 2025/8/11 v1.2がリリースされました。+追加文章あります。

※ 2025/12/6 ダウンロードのリンクが切れていたため復旧させました。

 

1、MM6_2025日本語版をダウンロードします。

MM6_2025日本語版(以降MM6と表記します)をここからダウンロードしてください。

現在バージョンはv.1.3まで出ています。が、どうもバグが取り切れていないようで

(本来なら翻訳されるべきなのにされないときがある)

このバージョンはリリースを取り止めている状態です。

v.1.2かv.1.3どちらか安定する方を選択して使用していただければと思います。

大変ご迷惑おかけし申し訳ございません。

好きな場所に展開して使用します。

 

2、Tesseractをインストールします。
MM6は、OCRにTesseract OCRというライブラリを利用しています。

このソフトウェアをダウンロードしインストールして頂く必要があります。

ダウンロード先は以下です。

Home · UB-Mannheim/tesseract Wiki · GitHub

 

x64かx86でインストールするファイルが異なります。

例えば下のような形です。必要な方をダウンロードしてください。

tesseract-ocr-w64-setup-5.5.0.20241111.exe // x64
tesseract-ocr-w32-setup-v5.3.0.20221214.exe // x86

 

インストールの手順は、全て「次へ」で問題ありません。

 

ただし、インストールパスは、標準の「C:\Program Files\Tesseract-OCR」であれば
問題ありませんが、変えた場合はMM6_設定.iniも合わせて変更しなければなりません。

 

3、Google Cloud Translation APIAPI keyを取得します。

MM6はGoogle Cloud Translation APIのライブラリを使って翻訳しています。

これを使う為には、Googleに申請してAPI Keyという文字列をもらう必要があります。

難しく聞こえますが、下のサイト通りに進めればすぐに取得できます。

 

ただし、注意して頂きたい点があり、それは

料金が発生する可能性があるということです。
1ヶ月あたり最初の50万文字までは無料ですが、それを超えて
Google Cloud Translation APIを使うと料金が請求されます。

ただ、50万文字はとても使いきれない量ですし、

月が変わればリセットされるので、実際は殆ど気にする必要は無いと思います。

 

Google APIキーを取得する方法ですが、

手順は下記のサイトをみてください。
私も内容も確認しましたが、問題無く取得出来ました。

2022年5月 Google Cloud Translation API のAPI keyの取得方法 - WEBLOCO

 

Google APIキーは「AIzaSy」で始まる39文字のテキストです。
これをMM6_設定.iniに書いて頂く必要があります。

 

4、MM6のicons.lodを入れ替えます。

マイトアンドマジック6のゲームパスの中にicons.lodというファイルがあります。

このファイルをMM6に附属してある「data\icons.lod」で上書きコピーしてください。
一応バックアップもとっておくと良いでしょう。


マイトアンドマジック6では会話時に専用の背景が使用されていますが

これだとOCRの精度が良くない為(スペルミスが出やすい)

icons.lodの中にある背景を真っ黒のボードに入れ替えて精度を上げています。

 

5、PCのディスプレイのサイズを変更します。
MM6は1920x10801280x720のどちらかをサポートしています。
ディスプレイのサイズをどちらかに変更してください。
(「ディスプレイの設定」で検索し、「ディスプレイの解像度」で変更できます)

 

6、MM6_設定.iniを編集します。

MM6_設定.iniをメモ帳で開き、

 

・「APIKey=””」にGoogle APIキーを書いてください。

 

・「TESSDATA_PATH=””」Tesseractのtessdataのインストールパスが
 「C:\Program Files\Tesseract-OCR\tessdata」であればそのままでOKです。
 違うならそのパスを書いてください。

 

・「MM6_shortcutPath_1」か「MM6_shortcutPath_2」にMM6の実行パスを書いてください。
アプリを起動させると、「MM6_shortcutPath_1」、「MM6_shortcutPath_2」の順で
MM6の起動を試みます。どちらも起動出来なかった場合、ユーザーにMM6の起動を
行なってもらいます。(10秒間)

 

例はこちら

[CloudTranslationAPI]
APIKey=”AIzXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”

[TesseractOCR]
TESSDATA_PATH=”C:/Program Files/Tesseract-OCR/tessdata/” // 標準インストールなら記入しなくても良い

[MM6]
MM6_shortcutPath_1="C:\GOG Games\Might and Magic 6\MM6.exe" // MM6を標準インストールしているなら
MM6_shortcutPath_2="C:\temp\MM6Run.url" // Steam経由で起動させたいなら

 

7、インターネットに接続します。

MM6は翻訳にインターネットを使うので、インターネット接続が必須です。

 

実際には、MM6はテキストのキャッシュ機能をそなえており、

一度翻訳すれば二度目はキャッシュからテキストを使うので

Google Cloud Translation翻訳は行いません。

論理的には、MM6のすべての会話を行なえば、Wifiが無くても使用できるはずです。

キャッシュ自体は単純なテキストファイルであり「MM6_キャッシュ」フォルダに収められています。

 

7、MM6を起動します。

MM6_2025日本語版.exeを起動させれば、マイトアンドマジック6は自動で起動し翻訳が始まります。マイトアンドマジック6は必ずフルスクリーンモードにしてください。(F4キーで切替できます。)

終了は通常通りマイトアンドマジック6を終了すれば良いです。

 

8、左側テキストボックス

翻訳中は左側にテキストボックスが表示されます。

~2025/8/11追加 ここから~

・「翻訳状態表示」

 特に何もせずスタンバイ中の場合は「待機中」と表示され、

 翻訳の処理を実行中の時は、

翻訳中」→「翻訳中・」→「翻訳中・・」→「翻訳中・・・」→「翻訳中

というアニメーションが実施されます。(文字は赤色で表示されます)

翻訳には時間がかかることが多いので、この表示が出たらしばらく待ってください。

~2025/8/11追加 ここまで~

・「翻訳機能 」 

 ONだと日本語表示、OFFだと英語表示になります。

 dキーを押すと切り替わります。

 

・「キャッシュが使われた回数」

 キャッシュにヒットした回数です。

 ヒットした場合Google Cloud Translation翻訳は行いません。

 

・「翻訳が行われた回数」

 Google Cloud Translation翻訳が行われた回数です。

 次回同じ文章だった場合、キャッシュが使われます。

 

・「翻訳で使われた文字数の合計」

 Google Cloud Translation翻訳によって使用された文字列の合計です。

 この数が50万以上超えると料金が発生する可能性があります。

 下の%が100%を超えた場合でも同様です。

 

・「表示ONOFF」

 eキーを押すとこのテキストボックスを非表示にできます。

 再度押下で表示されます。(eキーってMM6で使われているような・・

 別なキーの方が良かったかな・・)

 

9、その他

・動作確認はWIndows11とWindows10で行いました。

WIndows10は「Microsoft Visual C++ 再頒布可能パッケージ」のインストールも必要でした。起動に問題がある場合、このソフトもインストールが必要かもしれません。

 

・動作確認した結果、MM6の起動に失敗したケースもありました
原因を調査しましたがはっきりはわからず、Tesseract OCRの初期化に使うinit()関数で

落ちています。自分のPCでは無い為これ以上調査は出来ませんでしたが

同じような現象が起こったら、以下の手順を試してみてください。

 ・PCを再起動する。

 ・TESSDATA_PREFIX環境変数に設定し、PCを再起動する。

 ・Visual C++ Redistributable packagesをインストールし、PCを再起動する。

 

・一応動作確認としてMM6のゲームを進めていますが、今はまだアイアンフィストまでしか行っておらず、そこまでしかデバッグは出来ていません。いままでに無い表示の仕方が出たりすると当然表示は出来ないです。今後デバッグは順次進めて、更新していければと思っています。

 

~2025/8/11追加 ここから~

・使用するマイトアンドマジック6について

使用するマイトアンドマジック6について記述がありませんでした。

私の環境で使用したソフトはGOGの「Might and Magic® 6-pack Limited Edition」で

パッチ「GrayFace MM6 Patch v2.5.7」を適用したものになります。

・V.1.2について

バージョン1.2では多くのバグ修正と機能変更が行われています。

出来るだけこのバージョンを使用して頂けると嬉しいです。

機能変更としては、

 ・描画にダブルバッファリングを使うようになりました。

  今までは文字を表示するとチラチラとチラついていましたが

  それが無くなります。

 ・Google Cloud Translationに翻訳をしてもらう際、

  今までは、翻訳してもらいたい文字列ひとつにつきhttp要求をひとつ

  投げていましたが、複数の文字列をひとつにパックして

  ひとつのhttp要求を投げる様に変更しました。翻訳速度の改良になるかと

  思います。

 ・翻訳中は左ウインドウに「翻訳中・・・」とアニメーションするように

  なりました。今までは翻訳しているのかいないのかわかりづらかった為、

  これで多少わかりやすくなったと思います。

~2025/8/11追加 ここまで~

 

【PCゲーム】No.2 Might and Magic 6で日本語表示(Windows11)

※2025/07/23更新あり MM6日本語版のファイルをアップしました!

houz67890.hatenablog.com


お久しぶりです!!五ヵ月ぶりの更新になります。
今回はPCゲームの2回目、マイトアンドマジック6の日本語化になります。

なんとこの試み、自分で作っています。なので時間がかかってしまった。。

というかまだ完成はしていません。一応ゲームをやれる状態にはなっていまして、

バグだったり抜けが無いかチェックしているところです。

今回はそこまでの出来にはなっておらず、リリースは諦めましたが、

なるべくはやくリリースしたいと思っています。(今回はソースも公開する予定です)

 

以下、動画と画像になります。

 

今のところ一番のネックは翻訳に時間がかかるところです。

翻訳は早くするのは難しいんです。対策を考えなければならない点です。

 

 

 

 

 

 

 

 

 

【ゲーム制作】No.26 ImGui + フルスクリーン動作(Direct3D11)

今回はバグ対応になります。

 

私のゲーム更新に扱っているプロジェクトは
ゲームメイン(DLL)とウインドウアプリケーション(Win32)の

構成になっているのですが、

いつからかウインドウアプリケーションの動作に
おかしな点があることに気付きました。

 

・おかしな点

 

①メニューバーが表示されなくなっている。

 

これはNG。メニューバーが表示されていない。

 

これはOK。

 

②メニューバー部分に表示残りがある。

 

これはNG。画面上部に表示残りがある。

 

これはOK。

 

③フルスクリーン時、ホバードボタンの有効表示がズレてしまう。

 

ボタンに届いていないのに、ボタンが有効表示になってしまう。

 

どれも以前は無かったと記憶しているので、
制作の途中で入れてしまったみたいです。

 

原因を調べたところ、
ImGuiの実装箇所に問題がありました。

ImGuiを動かすにあたり、
ImGuiのサンプルは使わず資料を元に作っていた為、
ソースの過不足が生まれバグを入れてしまっていました。

 

ちゃんとImGuiサンプルの「example_win32_directx11」を元に
ソースを作り直した結果、不具合の殆どは直すことが出来ました。

 

ただ、③の「ホバードボタンのズレ」だけは直すことが出来ず
いろいろ調査をしましたがわからず、結局調査中止になりました。

 

調査した結果、結論としては

 

①ホバードボタンのズレはメニューバーの表示と競合している。
そもそもDirect3D11はフルスクリーン時のメニューバー表示は
許容していないのは?(と思う。根拠は薄い・・)

 

②ImGuiサンプルの「example_win32_directx11」では
機能としてのフルスクリーン動作は無い。
そもそもImGuiはフルスクリーン時の動作を許容していない為
「ホバードボタンのズレ」のバグを入れてしまったのかも?
(と思う。根拠は薄い・・)


対処としては以下の2点になりました。

 

①フルスクリーン時はメニューバーの表示を無くす。

 

アプリケーション起動時にメニューバーの表示をOFFにし、
メインループで状態チェックを行って、
ウインドウモード時のみメニューバーの表示をONにするという方法。
フルスクリーン時はメニューバーが操作出来なくなること、
直った様に見えているだけで、後々不具合の原因になるかも、
などがデメリットだと思います。

 

②ImGuiのメニューバーを使う。

 

WinAPIのメニューバーを使うのは止めて、
代わりにImGuiのメニューバーを使う方法。
ちゃんとフルスクリーン時も動作しますし、実装も楽です。
採用はこれが良さそうです。

 

【ゲーム制作】No.25 摩擦ジョイント(物理エンジン)

立方体(Box)

 

今回も物理エンジンの機能追加になります。
物理エンジンの中の拘束オブジェクトの

ひとつである摩擦ジョイントの追加になります。

 

摩擦はソースをみるとひととおり追加されていて、
ずっと実装済みと思っていたのですが
実際動作確認をしてみると、それっぽい動作をしてくれない・・


「最初はスゥーと滑って次第に止まる」の様な摩擦の動作は無く
「滑るか停止するか」の二択になっている・・

ちょっと修正するか・・から始まり
思いのほか手こずり、完了まで1か月以上かかってしまいました。

 

摩擦は確認する形状に立方体(Box)と球と楕円体の3種類にしたため
その分手続きも増えました。

 

①立方体(Box)
Boxは接触点が2つ以上の複数になることが多く
その為か比較的安定していました。
実際、Box個別に対する修正はありませんでした。

 

②球
球では、「摩擦で永遠に滑る」問題が起きました。
球がずっと滑って永遠に止まらないんです。

球は、Boxとは違い接触点は必ず1つになります。
接触した時に起動される接触ジョイントは
速度のみ拘束して角速度は拘束されずフリーのままです。
だからずっと滑り続けるんです。
(球の1点を接触点として速度をゼロに拘束しても
任意の角度に回転は出来ます)

現実世界で接触点が1点というのはあり得ないので
シミュレーションとみれば正しい動作なんでしょう(多分)

 

修正案としては以下2点を出しました。

 

 ①接触点を増やす
  仮として球の接触点を4つまたは3つに増やすという案です。
  行うとちゃんと停止します。
  ただ、例えばBoxが4接触点だったりすると、接触点数は16点になり
  処理が重くなりそうなので、球限定にする必要はありそうです。

 

 ②角速度の拘束を追加する
  球など接触点1の形状に限り、接触ジョイントに角速度の拘束を追加する案です。
  こちらもいい感じで停止してくれます。
  問題点としては誤差修正の為の角速度を出すのが難しい

  (というか正しい式がわからない)
  なので現状「おおよそ1」な式で胡麻化している。

 

これは見た目が比較的リアルだったので

「②角速度の拘束を追加する」を採用しました。

 

修正前:摩擦では止まらずずっと滑り続ける。※後半もうひとつ球が落ちてきますがこれはバグです(笑)

 

修正後:ちゃんと止まるようになりました。

 

③楕円体

楕円体は摩擦以前に挙動が全然安定せずそれを制御するのに苦労しました。

 

楕円体で挙動がおかしい → 摩擦の処理に問題がある → いろいろ修正する → 余計悪くなる → 無限ループ

 

こんな感じです。

 

あるとき、楕円体は摩擦云々の前に、まず不安定な状態がおかしいのでは?と気付き
楕円体のパラメータを見ていくように変更しました。

その結果、楕円体の直径が細すぎて不安定だったようで、
直径を太めにしたところ安定するようになりました。

こういう、一見当たり前に見えることでも、

ひとりでやっていると気付くのが難しいです。

楕円体

 

資料は以下になります。

 

Gauss-Seidel 法の拡張による剛体関節機構の接触/拘束力計算

 

追加したコードは少なく、資料をそのまま実装するだけでした。

 

{ // T軸で以下を実行します。

// TBN軸のインデックスは安定化の為にランダムにソートされています。
// 以下の処理で正しいインデックスを取得しています。
const uint_t    Ti = i;
const uint_t    Bi = org2cur_index[cur2org_index[i] + 1];
const uint_t    Ni = org2cur_index[cur2org_index[i] + 2];

const cvector2_t    TB = initv(next_f, f(Bi));
const float_t    TB_Len = TB.length;
const float_t    N = f(Ni);
const float_t    N_Len = N;
const float_t    friction_limit = N_Len * parm[Ni].static_friction_factor;
if (TB_Len > friction_limit)
{
const cvector2_t    TB_New = friction_limit / TB_Len * TB;
next_f    = TB_New.x;
insert(f, Bi, TB_New.y);
}
}

 

{ // B軸で以下を実行します。

// TBN軸のインデックスは安定化の為にランダムにソートされています。
// 以下の処理で正しいインデックスを取得しています。
const uint_t    Ti = org2cur_index[cur2org_index[i] - 1];
const uint_t    Bi = i;
const uint_t    Ni = org2cur_index[cur2org_index[i] + 1];

const cvector2_t    TB = initv(f(Ti), next_f);
const float_t    TB_Len = TB.length;
const float_t    N = f(Ni);
const float_t    N_Len = N;

const float_t    friction_limit = N_Len * parm[Ni].static_friction_factor;

if (TB_Len > friction_limit)
{
const cvector2_t    TB_New = friction_limit / TB_Len * TB;

insert(f, Ti, TB_New.x);
next_f    = TB_New.y;
}
}

【ゲーム制作】No.24 ヒンジジョイント (物理エンジン)

ヒンジジョイント

ー90度~+90度の角度制限がついています。(わかりずらいですが)

 

今回は物理エンジンの機能追加になります。
いきなり物理エンジンなんで唐突ですよね?
自分も困惑してます。

 

この11月から12月で、2点機能追加出来たので
今回それを載せることにしました。

 

内容は拘束オブジェクトのひとつであるヒンジジョイントの更新になります。
(もう一つは拘束オブジェクトのひとつである摩擦ジョイントです)

 

ヒンジドアのジョイントは以前からずっと作成していて
ドアを開け閉めは出来るのですが、どこまで開くか、どこまで閉じるか
という角度制限(UnityにおけるLimits_MinとLimits_Max)が実装出来ていませんでした。

 

どうすれば角度制限が実装出来るのか、いろいろ考えたのですが
わからないまま時間だけ過ぎていく感じでした。(ここいらが高卒の限界ですね笑)

 

ですが最近幸運にもネットでやり方を示す資料が見つかったので
今回それを元に実装してみました。

 

資料は以下になります。

ながむしメモ」というブログに載っていた
ConstraintsDerivationRigidBody3D.pdf」という資料です。

(ブログで教えてくれた方ありがとうございます!!)

 

この資料は、実装者メインというか、余計な説明は最小限にとどめ、
コード製作者に必要なことだけを羅列している形で
わかりやすく作られており、コストがかからない良い資料でした。

 

実際の制作の方は、変更は1点のみ。
それ以外は資料の内容そのままを適用することで実装出来ました。

 

変更点は、資料の20ページ
「2.4.1 Position constraint」で「Ctrans(s) = x2 + r2 + r2 − x1 − r1」とありますが
正しくは「Ctrans(s) = x2 + r2 − x1 − r1」です。(ボールジョイントで使われている)
関連する式はヒンジジョイントでは無く、ボールジョイントで表記されている方を使う必要がありました。

 

次回も物理エンジンの追加点にする予定です。

【ゲーム制作】No.23 両眼立体視②(Direct3D11)

 

今回のテーマは「両眼立体視」で前回の続きとなります。
Direct3D立体視を行う為のソース作成がメインとなります。

立体視を行う為には、大きく分けて2つのフェーズがあります。

 

(1).マルチビューポート機能により右目用・左目用の複数ウインドウを作成する。
(2).右目用・左目用のView行列と射影行列を作る。

 

以下、それぞれの作成について述べていきます。


(1).マルチビューポート機能により右目用・左目用の複数ウインドウを作成する。

 

これは以前のブログで紹介したサイト「MaverickProjectの147.ステレオグラム」
参照頂けれは良いかと思います。
私もフルコピペで作成出来ました。

 

発生した問題としては、「SV_ViewportArrayIndexを使うとポリゴン欠けが起こる」です。
普通、SV_ViewportArrayIndex=0なら左目用、SV_ViewportArrayIndex=1なら右目用
と表示するウインドウを変えることが出来るのですが、
SV_ViewportArrayIndex=0の場合のみポリゴンの欠けが起こったり
モデル一式がまるまる表示されなくなるといった症状が起きました。
調査しましたが修正方法を見つけることが出来ず、仕方なく複数パスに分けて
描画することで回避することにしました。


(2).右目用・左目用のView行列と射影行列を作る。

これはサンプルサイトが複数見つかりましたので
これから情報を得て作り上げてみました。

 

①MaverickProjectの147.ステレオグラム

おなじみのこちらのサイトです。
このサイトにもサンプルコードがありましたので
動作してみました。

その結果ですが・・
残念ながらサンプルコードのビルドが出来ず
動作の確認は出来ませんでした。
"HalfLambert2_HalfLambert2_VS_Main.h"
"HalfLambert2_HalfLambert2_GS_Main.h"
"HalfLambert2_HalfLambert2_PS_Main.h"
の3本のファイルがアップされていない為です。
ファイルが無いのではコンパイルしようがない・・

まあこのサンプルである立体視部分のコードは、
View行列に適当な値を加えるという形で
汎用性は無く、私が求める情報は無いなという結論です。

 

②月刊誌、CQ出版社<インターフェース>2011年1月号

こちらも以前紹介した雑誌。
この雑誌の「OpenGLを使って立体視の絵を作ろう!」という記事があり
こちらにもサンプルコードがあるので動かしてみました。
このサンプルコードはOpenGLなのでGLUTを使ってコンパイルします。
(ちなみに記事はネット上にはありませんがサンプルコードなら
こちらにアップしてあります。)

そして、その結果ですが・・
サンプルはoff-axis法でView行列は両目平行です。
ソースはシンプルで扱いやすいです。
OpenGLなので私は今回はキャンセルしましたが
使える方ならばこれをメインにしても良いかもしれません。
ただ、表示された立体視のサンプルは、立体度が弱く
効いているのかわかりにくいなぁという印象でした。
また、ソースには誤りがあります。
誤:glTranslated(eye * distance, 0, 0);
正:glTranslated(eye, 0, -distance);

これはサンプルコードを動かしたものです。立体的に見えるでしょうか・・?

 

③GitHubのd3d-stereo-sample
Direct3Dのサンプルということで、こちらも動かしてみました。
ビルドするには、Visual StudioC++/WinRTVisualStudio拡張機能(VSIX)をインストール
必要がありました。あと細かい修正がいくつかありました。

そして、その結果ですが・・
どうやらこのサンプルは3Dステレオ機能を持つディスプレイが必要とのことで
私の環境では有効にはならず立体視を確認することは出来ませんでした。
一応その後、私の立体視サンプルに移植して立体視を確認しましたが
立体になってるかなぁ程度で特に得られる情報はありませんでした。

 

④off-axis法を用いたステレオグラフィックスの奥行き感に関する研究
PDFです。立体視に対する情報をいろいろと調べたんですが、
立体視を目的としたView行列と射影行列の作り方という点では、
この資料が一番だと思いました。

そして、その結果ですが・・
サンプルコードを移植して立体視を確認してみましたが
まずまずの結果に見えました。サンプルはOpenGLを使用しており
Diret3Dに変更する必要があり苦労しました。
(というかDirect3Dへの移植はまだ完成では無くバグが残ってそうな感じです)

 

以下ソースコードです。

// 「off-axis 法を用いたステレオグラフィックスの奥行き感に関する研究」の移植(off-axis法)

HOUZMETHOD_VD    perspective_view_t::get_stereogram_matrix(    rmatrix4x4_t&    m_左目_ビュー行列,
                                                            rmatrix4x4_t&    m_右目_ビュー行列,
                                                            rmatrix4x4_t&    m_左目_射影行列,
                                                            rmatrix4x4_t&    m_右目_射影行列) const
{
    //p_camera->set_position(initv(0,0,-50));        // 参考:カメラの位置
    //const cvector3_t    pos        = initv(0, 0, 0);    // 参考:モデルの位置

    extern int    gui_i_pupillary_distance;    // 視差量(左目と右目の距離) 調節用のパラメータ変数
    const float_t    gui_f_pupillary_distance    = gui_i_pupillary_distance * 0.01f;


    // View Matrix

    const cvector3_t    side    = transpose(get_orthonormal_basis()(0));    // カメラの向き。横方向。X軸。
    const cvector3_t    up        = transpose(get_orthonormal_basis()(1));    // カメラの向き。縦方向。Y軸。
    const cvector3_t    front    = transpose(get_orthonormal_basis()(2));    // カメラの向き。奥方向。Z軸。

    cvector3_t    v_両目_カメラの位置        = get_center_position();
    cvector3_t    v_両目_視点                = front;
    cvector3_t    v_両目_上視点            = initv(0,+1,0,0);    // 変数upを使用しても良い。

     // to_view()はD3DXMatrixLookAtLH()相当
    m_左目_ビュー行列    = to_view( v_両目_カメラの位置, to_orthonormal_basis( front, v_両目_上視点));    // 左目用のビュー行列。カメラの向きに平行。

    m_右目_ビュー行列    = to_view( v_両目_カメラの位置, to_orthonormal_basis( front, v_両目_上視点));    // 右目用のビュー行列。カメラの向きに平行。

    // ビュー行列使用時はtranspose()で転置すること。

    // Projection Matrix

    float_t    f_距離N        = 1.0;        // N:(視点から前方クリッピング面までの距離) 値はPDFの実験の項から借用した。
    float_t    f_距離L        = 50.0;        // L:(視点からスクリーン面(ディスプレイ面)までの距離) 値はPDFの実験の項から借用した。
    float_t    f_距離F        = 150.0;    // F:(視点から後方クリッピング面までの距離) 値はPDFの実験の項から借用した。
    float_t    f_左右眼の区別    = 0.0;    // E:(左右眼の区別(右眼= 1.0, 左眼=-1.0)
    float_t    f_視点間距離    = 6.5 * 0.5 + gui_f_pupillary_distance;    // D:(視点間距離の1/2の値) 値はPDFの実験の項から借用した。
    float_t    f_スクリーンの幅    = 30.4;    // W:(スクリーン(ディスプレイ)の横の大きさ) 値はPDFの実験の項から借用した。
    float_t    f_スクリーンの高さ    = 27.2;    // H:(スクリーン(ディスプレイ)の縦の大きさ) 値はPDFの実験の項から借用した。

    {    // 左目
        f_左右眼の区別    = -1.0;    //(右眼= 1.0, 左眼=-1.0)
        float_t    射影行列P_パラメータ_A    = 2.0 * f_距離L / f_スクリーンの幅;
        float_t    射影行列P_パラメータ_B    = 0;
        float_t    射影行列P_パラメータ_C    = 2.0 * f_距離L / f_スクリーンの高さ;
        float_t    射影行列P_パラメータ_D    = 0;
        float_t    射影行列P_パラメータ_E    = -( f_距離F + f_距離N) / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_F    = -2.0 * f_距離F * f_距離N / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_EDx    = f_距離F / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_FDx    = -f_距離N * f_距離F / ( f_距離F - f_距離N);
        float_t    回転行列R_パラメータ_sinθ    = f_左右眼の区別 * f_視点間距離 / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);
        float_t    回転行列R_パラメータ_cosθ    = f_距離L / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);

        // 元のコード
        //rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
        //                                        0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
        //                                        0,                        0,                        射影行列P_パラメータ_E,    射影行列P_パラメータ_F,
        //                                        0,                        0,                        -1,                        0);
        
        // Direct3D用に変更
        rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
                                                0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
                                                0,                        0,                        射影行列P_パラメータ_EDx,    射影行列P_パラメータ_FDx,
                                                0,                        0,                        1,                        0);

        rmatrix4x4_t    回転行列R    = initm(    回転行列R_パラメータ_cosθ,    0,    -回転行列R_パラメータ_sinθ,    0,
                                                0,                            1,    0,                                0,
                                                回転行列R_パラメータ_sinθ,    0,    回転行列R_パラメータ_cosθ,        0,
                                                0,                            0,    0,                                1);

        rmatrix4x4_t    移動行列T    = initm(    1,    0,    0,    f_左右眼の区別 * f_視点間距離,    // 元のコードでは「-f_左右眼の区別」。変更した。
                                                0,    1,    0,    0,
                                                0,    0,    1,    -f_距離L,
                                                0,    0,    0,    1);

        m_左目_射影行列    = mul( 射影行列P, mul( 回転行列R, 移動行列T));

        // 射影行列使用時はtranspose()で転置すること。
    }

    {    // 右目
        f_左右眼の区別    = 1.0;    //(右眼= 1.0, 左眼=-1.0)
        float_t    射影行列P_パラメータ_A    = 2.0 * f_距離L / f_スクリーンの幅;
        float_t    射影行列P_パラメータ_B    = 0;
        float_t    射影行列P_パラメータ_C    = 2.0 * f_距離L / f_スクリーンの高さ;
        float_t    射影行列P_パラメータ_D    = 0;
        float_t    射影行列P_パラメータ_E    = -( f_距離F + f_距離N) / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_F    = -2.0 * f_距離F * f_距離N / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_EDx    = f_距離F / ( f_距離F - f_距離N);
        float_t    射影行列P_パラメータ_FDx    = -f_距離N * f_距離F / ( f_距離F - f_距離N);
        float_t    回転行列R_パラメータ_sinθ    = f_左右眼の区別 * f_視点間距離 / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);
        float_t    回転行列R_パラメータ_cosθ    = f_距離L / sqrt( pow( f_左右眼の区別 * f_視点間距離, 2.0) + f_距離L * f_距離L);

        // 元のコード
        //rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
        //                                        0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
        //                                        0,                        0,                        射影行列P_パラメータ_E,    射影行列P_パラメータ_F,
        //                                        0,                        0,                        -1,                        0);
        
        // Direct3D用に変更
        rmatrix4x4_t    射影行列P    = initm(    射影行列P_パラメータ_A,    0,                        射影行列P_パラメータ_B,    0,
                                                0,                        射影行列P_パラメータ_C,    射影行列P_パラメータ_D,    0,
                                                0,                        0,                        射影行列P_パラメータ_EDx,    射影行列P_パラメータ_FDx,
                                                0,                        0,                        1,                        0);

        rmatrix4x4_t    回転行列R    = initm(    回転行列R_パラメータ_cosθ,    0,    -回転行列R_パラメータ_sinθ,    0,
                                                0,                            1,    0,                                0,
                                                回転行列R_パラメータ_sinθ,    0,    回転行列R_パラメータ_cosθ,        0,
                                                0,                            0,    0,                                1);

        rmatrix4x4_t    移動行列T    = initm(    1,    0,    0,    f_左右眼の区別 * f_視点間距離,    // 元のコードでは「-f_左右眼の区別」。変更した。
                                                0,    1,    0,    0,
                                                0,    0,    1,    -f_距離L,
                                                0,    0,    0,    1);

        m_右目_射影行列    = mul( 射影行列P, mul( 回転行列R, 移動行列T));

        // 射影行列使用時はtranspose()で転置すること。
    }
}

 

⑤オリジナルコード
いろいろな資料を得て私が作ったオリジナルのコードです。
立体視を目的として、右目用と左目用のView行列と射影行列を作ります。
資料としたサイトは以下の2つです。
3D ベーシック講座 第2回 AG-3DA1での立体映像の記録
液晶シャッタメガネ(時分割方式)を用いた立体視の実現

 

この画像は上のリンク先に貼っているものです。
小さい画像ですが、ちゃんと立体に見えると思います。
この画像を参考にして、球の位置から
球の傾きを求めれば良い訳です。
それで立体視した画像が表示出来ます。

 

以下ソースコードです。

HOUZMETHOD_VD    perspective_view_t::get_stereogram_matrix(    rmatrix4x4_t&    m_左目_ビュー行列,
                                                            rmatrix4x4_t&    m_右目_ビュー行列,
                                                            rmatrix4x4_t&    m_左目_射影行列,
                                                            rmatrix4x4_t&    m_右目_射影行列) const
{
    // 各種パラメータ変数
    extern int    gui_i_pupillary_distance;    // 視差量(左目と右目の距離)
    extern int    gui_i_pupillary_distance2;    // 基準面距離(カメラから基準点までの距離)
    const float_t    gui_f_pupillary_distance    = gui_i_pupillary_distance * 0.01f;
    const float_t    gui_f_pupillary_distance2    = gui_i_pupillary_distance2 * 0.01f;


    // View Matrix

    const cvector3_t    side    = transpose(get_orthonormal_basis()(0));    // カメラの向き。横方向。X軸。
    const cvector3_t    up        = transpose(get_orthonormal_basis()(1));    // カメラの向き。縦方向。Y軸。
    const cvector3_t    front    = transpose(get_orthonormal_basis()(2));    // カメラの向き。奥方向。Z軸。

    cvector3_t    v_両目_カメラの位置        = get_center_position();
    cvector3_t    v_左目_位置        = get_center_position() + side * gui_f_pupillary_distance;
    cvector3_t    v_右目_位置        = get_center_position() + -side * gui_f_pupillary_distance;
    cvector3_t    v_両目_遠視点    = get_center_position() + front * gui_f_pupillary_distance2;
    cvector3_t    v_左目_視点        = normalize( v_両目_遠視点 - v_左目_位置);
    cvector3_t    v_右目_視点        = normalize( v_両目_遠視点 - v_右目_位置);
    cvector3_t    v_両目_上視点    = initv(0,+1,0,0);    // 変数upを使用しても良い。

    // to_view()はD3DXMatrixLookAtLH()相当
    const rmatrix4x4_t    m_左目_新ビュー行列    = to_view( v_左目_位置, to_orthonormal_basis( v_左目_視点, v_両目_上視点));    // 左目用のビュー行列。遠視点へ視点。
    const rmatrix4x4_t    m_右目_新ビュー行列    = to_view( v_右目_位置, to_orthonormal_basis( v_右目_視点, v_両目_上視点));    // 右目用のビュー行列。遠視点へ視点。

    m_左目_ビュー行列    = m_左目_新ビュー行列;
    m_右目_ビュー行列    = m_右目_新ビュー行列;

    // ビュー行列使用時はtranspose()で転置すること。


    // Projection Matrix

    // pers_proj_LH()の実装
    //const element_type0 cot            = rcp(tan( fov_y * 0.5f));
    //const element_type0 z_factor    = far_z / ( far_z - near_z);
    //return initm(    cot / aspect,    0,        0,            0,
    //                0,                cot,    0,            0,
    //                0,                0,        z_factor,    -near_z * z_factor,
    //                0,                0,        +1,            0);

    m_左目_射影行列    = pers_proj_LH(
                    get_fov_y(),                    
                    2.0 / 3.0,    //get_aspect(),ビューポートが横幅の半分のサイズになるのでアスペクト比を変更する
                    get_near_range(),
                    get_far_range());

    m_右目_射影行列    =     m_左目_射影行列;

    // 射影行列使用時はtranspose()で転置すること。
}

 

今回の目標について

前回目標として以下の3点を挙げていました。

 

①「3DS並の立体感を実現したい」
⓶「ビュー射影行列を得る関数を作りたい」
③「飛び出す立体視を実現したい」

 

今回で①と②は実現出来たと思いますが
③はちょっと実現出来ませんでした。
「飛び出す立体視」という技術が
殆ど見当たらないんですよね。。
何か情報があったら教えて欲しいです。
今回は以上です。