【Minecraft】 独自のMOBの リード の高さを変更 【modding】

【Minecraft】 独自のMOBの リード の高さを変更 【modding】

マイクラ MOD 製作で、独自の MOB を追加したとき、特に何もしなくても RenderLiving を継承してレンダラを作成し、EntityLiving を継承して Entity を作成していればとりあえずリード (Leash) が使えるようになるのですが、その MOB の大きさや形状によってはリードの位置がとても変なことになってしまいます (^^;
ところが、ググってもリードに関する資料が全然見つからず、現バージョンの MCP によるデコンパイル後のコードでも、リードの処理の部分が全然難読化解除されてなかったので、コードを読むのに苦労してしまいました (^^;
もしかしたら、今後の MCP のバージョンアップでその部分も難読化解除してくれるかもしれませんが、とりあえず今は自分が人力で難読化解除したコードを載せようと思います (^^

リードを処理している場所

リードが繋がれた時の処理などは EntityLiving に記述されていますが、今回は表示のバグだけが直したいので、こちらは弄らなくて良さそうです (^^
リードの描画処理は、リードというアイテムとしてではなく、RenderLiving のほうに記述されています
はじめ、他のアイテムと同様にアイテムの処理として書かれているのかと思ってあちこち探してみましたが、そっちは本当に手に持っているほうの描画だけで、プレイヤー・フェンス等と MOB をつなぐ部分は MOB の描画処理に含まれていました (^^;
と言うわけで、独自の MOB のレンダラでその部分を書き換えれば良いです (^^
それをしているのは renderLeash というメソッドで、これをオーバーライドします。

コード (Java)

Minecraft 1.8.8 を Forge でデコンパイル (正確には、中に入ってる MCP ですが) したものにさらに人力の難読化解除したコードです。
このバージョンでは RenderLiving<T extends EntityLiving> クラスでジェネリクス型 T をメソッドの引数に使用していますが、バージョンによっては EntityLiving かもしれません。(うろ覚えですが、前のバージョンではそうだった気がしたので (^^;
また、MCP のバージョンの違いによってメソッド名等が置き換わるかもしれません。
一部、変数を再利用していたり、コードの順番的に分かりづらかった部分があったので、変数名を置き換えるだけでなく、一部順番や別の変数で置き換えたりしています。
また、コードの途中に、独自の高さを設定するためのコードをコメントアウトして入れてあります。

    //
    // pct の割合に対する start と end の間の値を取得
    //
    private double interpolateValue(double start, double end, double pct) {
        return start + (end - start) * pct;
    }

    //
    // リードの高さを調整
    //
    // ■メモ
    //     Yaw   角 : 上下方向の軸周りの回転角度
    //     Pitch 角 : 左右方向の軸周りの回転角度
    //
    @Override
    protected void renderLeash(T entityLivingIn, double x, double y, double z, float entityYaw, float partialTicks) {

        // リードが結ばれているフェンス・持っているプレイヤー等 取得
        Entity entity = entityLivingIn.getLeashedToEntity();

        if (entity != null) {

            // テッセレータ・レンダラを取得
            Tessellator tessellator = Tessellator.getInstance();
            WorldRenderer worldrenderer = tessellator.getWorldRenderer();

            // リードが結ばれているフェンス・持っているプレイヤー等の側の位置 算出
            double rotationYaw = this.interpolateValue((double)entity.prevRotationYaw, (double)entity.rotationYaw, (double)(partialTicks * 0.5F)) * Math.PI / 180D;
            double rotationPitch = this.interpolateValue((double)entity.prevRotationPitch, (double)entity.rotationPitch, (double)(partialTicks * 0.5F)) * Math.PI / 180D;

            double cosYaw = Math.cos(rotationYaw);
            double sinYaw = Math.sin(rotationYaw);
            double cosPitch = Math.cos(rotationPitch);
            double sinPitch = Math.sin(rotationPitch);

            if (entity instanceof EntityHanging) {
                cosYaw = 0.0D;
                sinYaw = 0.0D;
                sinPitch = -1.0D;
            }

            double posX = this.interpolateValue(entity.prevPosX, entity.posX, (double)partialTicks);
            double posY = this.interpolateValue(entity.prevPosY, entity.posY, (double)partialTicks) + (double)entity.getEyeHeight() * 0.7D;
            double posZ = this.interpolateValue(entity.prevPosZ, entity.posZ, (double)partialTicks);

            posX += - cosYaw * 0.7D - sinYaw * 0.5D * cosPitch;
            posY += - sinPitch * 0.5D - 0.25D;
            posZ += - sinYaw * 0.7D + cosYaw * 0.5D * cosPitch;

            // リードに繋がれている MOB 側の位置 算出
            double renderYawOffset = this.interpolateValue((double)entityLivingIn.prevRenderYawOffset, (double)entityLivingIn.renderYawOffset, (double)partialTicks) * Math.PI / 180;
            renderYawOffset += Math.PI / 2D;

            double renderXOffset = Math.cos(renderYawOffset) * (double)entityLivingIn.width * 0.4D;
            double renderZOffset = Math.sin(renderYawOffset) * (double)entityLivingIn.width * 0.4D;

            double renderX = this.interpolateValue(entityLivingIn.prevPosX, entityLivingIn.posX, (double)partialTicks);
            double renderY = this.interpolateValue(entityLivingIn.prevPosY, entityLivingIn.posY, (double)partialTicks);
            double renderZ = this.interpolateValue(entityLivingIn.prevPosZ, entityLivingIn.posZ, (double)partialTicks);

            renderX += renderXOffset;
            renderZ += renderZOffset;

            x += renderXOffset;
            y += - (1.6D - (double)entityLivingIn.height) / 2D;
            z += renderZOffset;

            /*
            // 独自の MOB のリード位置を設定
            double leashHeight = 0.625FD;
            renderY += (1.6D - (double)entityLivingIn.height) / 2D;
            y       += (1.6D - (double)entityLivingIn.height) / 2D;
            renderY += leashHeight - 1.5D;
            y       += leashHeight - 1.5D;
            */

            // 描画
            /* 差分 */
            double diffX = posX - renderX;
            double diffY = posY - renderY;
            double diffZ = posZ - renderZ;

            /* 色々無効に */
            GlStateManager.disableTexture2D();
            GlStateManager.disableLighting();
            GlStateManager.disableCull();

            /* 描画 1 */
            worldrenderer.func_181668_a(5, DefaultVertexFormats.field_181706_f);

            for (int i = 0; i <= 24; ++i) {

                float f = 0.5F;
                float f1 = 0.4F;
                float f2 = 0.3F;

                if (i % 2 == 0) {
                    f *= 0.7F;
                    f1 *= 0.7F;
                    f2 *= 0.7F;
                }

                float f3 = (float)i / 24.0F;
                worldrenderer.func_181662_b(x + diffX * (double)f3 + 0.0D, y + diffY * (double)(f3 * f3 + f3) * 0.5D + (double)((24.0F - (float)i) / 18.0F + 0.125F), z + diffZ * (double)f3).func_181666_a(f, f1, f2, 1.0F).func_181675_d();
                worldrenderer.func_181662_b(x + diffX * (double)f3 + 0.025D, y + diffY * (double)(f3 * f3 + f3) * 0.5D + (double)((24.0F - (float)i) / 18.0F + 0.125F) + 0.025D, z + diffZ * (double)f3).func_181666_a(f, f1, f2, 1.0F).func_181675_d();

            }

            tessellator.draw();

            /* 描画 2 */
            worldrenderer.func_181668_a(5, DefaultVertexFormats.field_181706_f);

            for (int i = 0; i <= 24; ++i) {

                float f = 0.5F;
                float f1 = 0.4F;
                float f2 = 0.3F;

                if (i % 2 == 0) {
                    f *= 0.7F;
                    f1 *= 0.7F;
                    f2 *= 0.7F;
                }

                float f3 = (float)i / 24.0F;
                worldrenderer.func_181662_b(x + diffX * (double)f3 + 0.0D, y + diffY * (double)(f3 * f3 + f3) * 0.5D + (double)((24.0F - (float)i) / 18.0F + 0.125F) + 0.025D, z + diffZ * (double)f3).func_181666_a(f, f1, f2, 1.0F).func_181675_d();
                worldrenderer.func_181662_b(x + diffX * (double)f3 + 0.025D, y + diffY * (double)(f3 * f3 + f3) * 0.5D + (double)((24.0F - (float)i) / 18.0F + 0.125F), z + diffZ * (double)f3 + 0.025D).func_181666_a(f, f1, f2, 1.0F).func_181675_d();

            }

            tessellator.draw();

            /* 色々有効に */
            GlStateManager.enableLighting();
            GlStateManager.enableTexture2D();
            GlStateManager.enableCull();

        }

    }

主な変数の説明

posX, posY, posZ : 繋がれている Entity のリード座標
renderX, renderY, renderZ : MOB のリード座標
x, y, z : MOB の描画座標

※単位はいずれも 1 ブロック = 1D とする double 値です。

コードの説明

実際に画面に描画するところらへんは難読化解除の手を抜いていますが、実際、位置の算出のところだけ修正すれば良いので今回は無視します (^^;
ようは MOB と、リードで繋がれている Entity のそれぞれのリード座標を求めて、それを結ぶように描画しているだけなのですが、実際に描画するときに、MOB の位置と、MOB と Entity の相対的な位置 のみを使用して描画するため、上手く座標を変えないと MOB 側も Entity 側も座標が変わってしまう可能性があります
と言うわけで、コード中の 「// 独自の MOB のリード位置を設定」 にあるように、変数 y と renderY を両方変えます。
MOB の高さはなぜかすべて地面から 1.5 ブロック ( = 1.5D) の位置を基準としていますので、上記のような算出の仕方になっています。
ただ、これだといちいち MOB ごとにリードの高さを指定するためだけにこれだけのコードを書かなければいけないことになり、コピペで良いにしても、コードが見づらくなります (^^;
そこで、自分が作った独自の Entity に getLeashHeight() などのメソッドを作り、設定したい高さを返すようにして、これを取得してリードを描画するレンダラを作り、自分の独自の MOB のレンダラでこれを継承する、とするとだいぶ楽になると思います (^^
EntityHoge に対して RenderHoge<T extends EntityHoge> とすればキャストしなくてもメソッドを呼べて便利ですし (^^
(ただ、可読性の観点から、自分はあえてキャストして取得したりします。実行速度には影響ないので。
状況によるでしょうが、getEyeHeight() と getLeashHeight() の宣言が並んでいると分かりやすそうです (^^


雑談

※画像は開発途中のもので、デザインが変更になる可能性があります

このコードを実際に使って、現在「ジュエルペット MOD (非公式)」というのを作成しています (^^
と言いますか、実は数か月前に本当に基本的な部分はできてたのですが、このリードの処理が面倒くさくて放置してしまいました (^^;
でもやっとリードの問題が解決できたので、これでまた前に進めそうです (^^

それでも、一人で製作するとなるとどうしても時間もかかり、すごい大変なので、「興味がある!」「時間があるときは手伝えるかも!」という方がいらっしゃったら製作を手伝ってもらえると嬉しいです (^^;

ソースコードは GitHub にて公開しています。
ではまたいつか~ (^^ ノシ
↓ブログランキング投票お願いします~