Battlefield 1のコンソール対策

最近にBattlefield 1で遊んじゃってます。
友達宅ではBattlefield 2やBattlefield 4を遊んだことはあるけれど、自分で買って遊んだのはこのBattlefield 1が初めて。

で、Battlefieldシリーズは日本語キーボード環境だとコンソールの誤爆問題があるそうです。
Battlefield 1 - コンソール画面
(赤枠がコンソール)

誤爆問題ってなんだろうって事なんだけれど、
英語キーボードだとチルダ(˜)キーでコンソールの表示/非表示ができるのですが、日本語キーボードの場合は半角/全角キーが該当します。
コンソールが表示されるとDirectInputからIMEに入力が移行し、半角/全角を押してもコンソールを閉じることができなくなります。

対策としてはBattlefield起動時前に英語キーボードの設定にするのが一般的なようです。
が、私はATOKを使用しており、MSIMEをアンインストールしているので再インストールが面倒なのと、ATOKで英語キーボードにすると一部のソフトで正常に変換が行えなくなる症状がでるので論外です。

なら、なにで対策するのか?というと、ソフトウエアベースのキーリマップツールを使います。
今回使うのはAutoHotkey L
(ちなみに、多くのオンラインゲームに搭載されているnProtectやHackShieldなどのチート対策ソフトはAutoHotkeyをチートツールと認識するので、オンラインゲームをプレイするときは終了しておかないとゲームが起動しません。)

AutoHotkeyはキーへマクロや別の機能を割り当てを行う常駐型のソフトで、割り当てを行う設定はAHKスクリプトで書いて動作するツールなんです。
使うのはマクロではなく、キーのリマップ機能。

コンソール対策のスクリプト自体は簡単で、「半角/全角」キーが押されたら「1」キーが押された事にするだけ。
SHKスクリプトはこんな感じ。

vkF3sc029::1
vkF4sc029::1

上記コードをメモ帳から適当な名前の.ahkファイルとして保存する。

AutoHotkeyの起動方法はx64環境ならAutoHotkeyU64.exe、x32環境ならAutoHotkeyU32.exeへ保存したahkファイルをD&Dするだけ。
ショートカットやプロンプトから起動する場合は「AutoHotkeyU64.exe 保存したスクリプト.ahk」とすれば問題なし。
終了するときはタスクバーのアイコンから終了するだけ。

これで戦闘中に強制固定砲台になるコンソール誤爆対策ができました。
昔から言われてるんだから、開発側はさっさと対策すればいいのになあ

Division オープンβ開始

Tom Clancy’s The Divisionが昨日の21時からオープンβが開始されましたー
少し前から遊びたいー!と思ってたゲームなのでこの日を指折り待っていましたw

Divisionは致死性のウイルスによって崩壊したニューヨークが舞台のMORPG型のTPSゲーム。
Division: オープニングロゴ
オープンワールドでギャングやテロリストと戦っていく感じみたいです。

起動するとオープンβの簡単な注意と説明が。
Division: β説明

4人で軽ーくプレイした感じは、ubi特有のサーバーのラグもなくスムーズに遊べる感じ。
Division: バグ
オープンβのバグなのかわからないけれど、影の品質を上げるとちらついて品質を下げると稀にプレイヤーの頭や足が伸びるw

戦闘はFPSと同じように移動や射撃でレティクルが開いたりするので、TPSにありがちな移動しながらフルバーストな戦闘はかなりやりにくい感じ。
武器のカスタム部位は若干少ないけれど、MODのレベル等によって能力や性能が違うので同じ武器でも人によってかなり性質の違う武器になったりしそうー
Division: P416

製品版が待ち遠しいなあー

MODメンテナンスの事とか

最近は1日1時間ぐらいMODのメンテナンスをやってます。
と言っても殆どは最適化で、それも体感できるような物は少ない感じのコード。

今日はブロックを叩いたときのパーティクルが出ない事に気付いたからコードを見てみると・・・
パーティクルの座標がずれていましたw

このパーティクルは本体のコードaddBlockHitEffectsから貰っているんだけれど、これものすごく無駄があるんですよ

public void addBlockHitEffects(int p_78867_1_, int p_78867_2_, int p_78867_3_, int p_78867_4_) {
    Block block = this.worldObj.getBlock(p_78867_1_, p_78867_2_, p_78867_3_);
 
    if (block.getMaterial() != Material.air) {
        float f = 0.1F;
        double d0 = (double)p_78867_1_ + this.rand.nextDouble() * (block.getBlockBoundsMaxX() - block.getBlockBoundsMinX() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinX();
        double d1 = (double)p_78867_2_ + this.rand.nextDouble() * (block.getBlockBoundsMaxY() - block.getBlockBoundsMinY() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinY();
        double d2 = (double)p_78867_3_ + this.rand.nextDouble() * (block.getBlockBoundsMaxZ() - block.getBlockBoundsMinZ() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinZ();
 
        if (p_78867_4_ == 0)
        {
            d1 = (double)p_78867_2_ + block.getBlockBoundsMinY() - (double)f;
        }
 
        if (p_78867_4_ == 1)
        {
            d1 = (double)p_78867_2_ + block.getBlockBoundsMaxY() + (double)f;
        }
 
        if (p_78867_4_ == 2)
        {
            d2 = (double)p_78867_3_ + block.getBlockBoundsMinZ() - (double)f;
        }
 
        if (p_78867_4_ == 3)
        {
            d2 = (double)p_78867_3_ + block.getBlockBoundsMaxZ() + (double)f;
        }
 
        if (p_78867_4_ == 4)
        {
            d0 = (double)p_78867_1_ + block.getBlockBoundsMinX() - (double)f;
        }
 
        if (p_78867_4_ == 5)
        {
            d0 = (double)p_78867_1_ + block.getBlockBoundsMaxX() + (double)f;
        }
 
        this.addEffect((new EntityDiggingFX(this.worldObj, d0, d1, d2, 0.0D, 0.0D, 0.0D, block, this.worldObj.getBlockMetadata(p_78867_1_, p_78867_2_, p_78867_3_))).applyColourMultiplier(p_78867_1_, p_78867_2_, p_78867_3_).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
    }
}

無駄ってのは第4引数p_78867_4_を評価しているところ。
p_78867_4_は叩かれた面の数字が入るのだけれど、途中で値が変動しないのにも関わらず6面全部を評価しているところ。
まあ、これはデコンパイルされたコードだからこうなってるだけで、本当のコードは違うのかもしれないけどw

レンダリングに関わるところなので1秒間に何度も呼ばれる物だからswitchの方が高速。
なのでうちのコードはこんな感じ

public boolean addHitEffects(World world, MovingObjectPosition target, EffectRenderer effectRenderer) {
    float range = 0.1F;
    double pX = (double)target.blockX + world.rand.nextDouble() * (this.getBlockBoundsMaxX() - this.getBlockBoundsMinX() - (double)(range * 2.0F)) + (double)range + this.getBlockBoundsMinX();
    double pY = (double)target.blockY + world.rand.nextDouble() * (this.getBlockBoundsMaxY() - this.getBlockBoundsMinY() - (double)(range * 2.0F)) + (double)range + this.getBlockBoundsMinY();
    double pZ = (double)target.blockZ + world.rand.nextDouble() * (this.getBlockBoundsMaxZ() - this.getBlockBoundsMinZ() - (double)(range * 2.0F)) + (double)range + this.getBlockBoundsMinZ();
 
    switch (target.sideHit) {
        case 0:
            pY = (double)target.blockY + this.getBlockBoundsMinY() - (double)range;
            break;
        case 1:
            pY = (double)target.blockY + this.getBlockBoundsMaxY() + (double)range;
            break;
        case 2:
            pZ = (double)target.blockZ + this.getBlockBoundsMinZ() - (double)range;
            break;
        case 3:
            pZ = (double)target.blockZ + this.getBlockBoundsMaxZ() + (double)range;
            break;
        case 4:
            pX = (double)target.blockX + this.getBlockBoundsMinX() - (double)range;
            break;
        case 5:
            pX = (double)target.blockX + this.getBlockBoundsMaxX() + (double)range;
            break;
    }
 
    int meta = 0;
    try {
        TileEntity_Multibase te = (TileEntity_Multibase)world.getTileEntity(target.blockX, target.blockY, target.blockZ);
        meta = te.getMeta();
    } catch (Exception e) {
    }
 
    effectRenderer.addEffect((new EntityDiggingFX(world, pX, pY, pZ, 0.0D, 0.0D, 0.0D, this, meta, target.sideHit)).applyColourMultiplier(target.blockX, target.blockY, target.blockZ).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
 
    return true;
}

メソッド名や引数が違うのはコードを使ってる場所が違うだけで内容は全く同じ。
本体のコードと違うところは、叩かれた面によって座標を変動させる所。
さっきも書いたけど、1秒間に何回も呼ばれるメソッドだし、変動しない値を6回も評価するのは無意味なのでswitchで目的の値へ飛ぶ方が速い。

でもまあ、こんな感じで本体のコードから持ってきたヤツも信用せずちゃんと書き換えれば少しは体感できるぐらい軽くなるのかも?

Selectable Paintingsが進まないのもこの辺があってすごく面倒だから・・・
テクスチャの解析に時間が掛かるからって言って当時にできる限りの最適化をやってたからなあー。

資源用ディメンションを追加するMODを公開

うちのサーバーで使っていた資源用ディメンションを追加するMODを一般公開しました。

カスタムディメンションを追加するMODは幾つかあったけれど、重かったり不具合が多かったりアップデートチェックがOFFにできず、応答が遅いサーバーのお陰で不安定になったり・・・
ポータルを作らず単純にディメンションを追加するMODってないんですよね。
なので、作ったって感じですw

機能としては、ディメンションを1~2追加しディメンションを行き来できるテレポーターアイテムを追加するだけ。
ディメンションの地形生成タイプをデフォルト、フラット、ラージバイオーム、アンプリファイド、ネザー、エンドと指定できてシードの個別設定も可能。
エンドはおまけ・・で追加した程度の物なので、運が悪いと奈落へドボンw

テレポーターは使用した時点のワールドと座標を記録し、資源ワールド以外なら資源ワールドへ、資源ワールドなら資源ワールド以外で使用された座標へテレポートできます。
なので、通常世界の倉庫でテレポーターを使用して資源ワールドへ移動、そのあと資源ワールドから再度テレポーターを使うと通常世界の倉庫へ戻ってこれるって感じです。
更にテレポーターを使用すると、前回資源ワールドでテレポーターを使った地点に移動できます。

全然移動していないけれど、ちょっとした動画。

一応、向いている方向も記録されるのでどっちの方向に進んでいたか・・もわかると思いますw
結構便利テレポーターなのです。

資源用ワールドなので、サーバーを止めてディメンションのフォルダを削除すればまた再生成可能です。

需要があれば使ってみてくださいー

Minecraft v1.7.10用MOD公開!

前回の更新から1年ぐらい開いちゃいましたけど・・Minecraft v1.7.10に対応しました。

今回対応したMODは
 Halogen Light、Animal Blocks、Marble Blocks、Fullcolor Blocks、More Fences、Fence Slab(新規MOD)

今後1.7.10への対応予定があるMODは
 Selectable Paintings、Country Filter

今回から更新を停止したMODは
 Column、Multislab、More Material Blocks
って感じになります。

もう知ってる方は多いと思いますが、全MODのバージョンを初期化しました。
これは移植の際にコードが変わってる部分が多かったので、位置から作り直しちゃえ!ってことでコードを書き直したからです。
もちろん全く移植無しではないんですけどねw

あとv1.6.4まではMOD毎にTileEntityを登録していたのですけど、今回はTileEntityを見直して前提MODで登録し全てのMODを同じTileEntityで管理するようにしました。
MODを抜いたときにログにでる「TileEntityをスキップした」ってうざいログはでません。
前提MODを抜けばでるかもだけど・・w

あと若干Javaの本を読み直したのでコーディングする時も常に最適に動くようにしてみました。
例えば前回のString+Stringの連結だったり、数字のif-elseが3回以上続くならswitchの方が軽量で高速であるとか、for(int i=0;i<array.length; ++i)よりfor(int i=0, max=array.length;i<max; ++i)の方が高速である等・・・
ちゃんと読めば色々書いてあるんですよねー

今後は気付いた範囲でコードを最適化して少しでも軽く軽快に動くように作ってみようと思います。