MOD制作メモ – 当たり判定の設定 – 1.12.2

Minecraft1.12.2でMOD制作するにあたっての当たり判定についての備忘録です。

このシリーズは、情報の記録を目的としています。自分がMODを作る時に情報が少なくかなり苦労したので、自分の持つ情報は少しでも共有できればという思いで公開しています。プログラミングに詳しいわけではないので、MOD制作についての質問・相談等を受け付けることはできません。正しい情報を発信するように心がけていますが、情報の審議や疑問点についてはご自身でお調べいただきますようお願いいたします。また、専門用語はわかりにくいので自分の言葉で説明することがあります。ニュアンスが違う場合もあるかと思いますがご容赦ください。

「選択する際の判定」と「触rn る際の判定」

当たり判定には「選択する際の当たり判定」と「触れる際の当たり判定」の2種類があります。

選択する際の当たり判定は、ポインターで壊せる範囲に来たときに表示される枠です。触れる際の当たり判定は、実際に上に乗ったり通り抜けたりする際の、目では見えない当たり判定のことです。

例えば、柵は選択する際の当たり判定は1ブロックの高さですが、飛び越えることができません。これは、触れる際の判定が1.5ブロック分の高さになっているからです。このように、選択する際の当たり判定と触れる際の当たり判定を別々に設定することができます。

選択する際の当たり判定getBoundingBoxで、触れる際の当たり判定getCollisionBoundingBoxで設定することができます。

// 選択する際の当たり判定
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
  ~
}
// 触れる際の当たり判定
@Nullable
public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, IBlockAccess worldIn, BlockPos pos) {
  ~
}

2つとも設定しなくても、getBoundingBoxを設定すれば触れる際の判定もその値になるので選択する際の判定と触れる際の判定が同じで良ければgetBoundingBoxのみ設定すればOKです。選択する際の当たり判定と触れる際の当たり判定を変えたい場合のみ、getCollisionBoundingBoxを設定します。

当たり判定の数値の設定の仕方

当たり判定を設定する際は、以下のように記述します。

public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
  return new AxisAlignedBB(0.0D, 1.0D, 0.0D, 1.0D, 0.0D, 1.0D);
}

この数値の部分で、当たり判定を細かく設定できます。

0.0D, 1.0D, 0.0D, 1.0D, 0.0D, 1.0D

この数値の意味は以下のようになります。

[X削る], [Y足す], [Z削る], [X足す], [Y削る], [Z足す]

XYZとは、北を向いたときに左から右にX軸、下から上にY軸、奥から前にZ軸ということです。これを頭の中で考えようとすると難しいので、私は以下の図と照らし合わせて考えています。

北を見て、[左から削る], [下から足す], [奥から削る], [左から足す], [下から削る], [奥から足す] と覚えると分かりやすいですね!1ブロックの端から端までは0.0D~1.0Dで表します。

この6つの値で足したり削ったりすることで、好きな形にすることができます。

例えば、ハーフブロックの場合は以下です。

//上付きハーフブロック
0.0D, 1.0D, 0.0D, 1.0D, 0.5D, 1.0D
//下付きハーフブロック
0.0D, 0.5D, 0.0D, 1.0D, 0.0D, 1.0D

上付き下付きともに横幅は1ブロックなので、X(左右)とZ(奥前)はともに1.0ずつ与えます。上付きハーフは浮いているので、下から1.0与えて0.5削っています。下付きハーフは0.5与えて削ることはしません。わかりにくい場合は、先程の図と照らし合わせて考えてます。

ブロックをすり抜けられるようにする

当たり判定をなくしたい場合は、getCollisionBoundingBoxにNULL_AABBを返すだけです。これは、AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D)が格納された便利な値です!

逆に、1ブロックまるまるの当たり判定はFULL_BLOCK_AABBに格納されています。

@Nullable
public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, IBlockAccess worldIn, BlockPos pos) {
  return NULL_AABB;
}

以上の記述で、触れる際の当たり判定がなくなり、ブロックをすり抜けられるようになります。簡単シンプル!

また、getCollisionBoundingBoxの判定を無くしても、getBoundingBoxはNULL_AABBにはしないように気をつけてください。getBoundingBoxの判定をなくすと、ブロックが選択できなくなり壊せなくなってしまいます。

方角によって当たり判定を変える

置いた方角によって当たり判定を変えたい場合があると思います。これはブロックに方角によって向きが変わるように設定されている場合のみに使えます。なにも設定されていないブロックには使えません。

逆に、状態が設定されていれば、方角以外にも設定された値ごとに当たり判定を設定できます。たとえばハーフブロックなら、上付きの状態と下付きの状態が設定されているので、上付きと下付きで違う当たり判定にできています。

今回は方角で考えてみます!

北のときはコレ、西のときはコレ…とそれぞれの方角に一つずつ指定をしていきます。今回はswitch文を使ってみます!switch文の使い方は検索して調べてください。

以下は、縦ハーフを追加した場合の記述です。縦ハーフには東西南北の値が格納されたFACINGという状態が付与されている設定です。

public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
{
    AxisAlignedBB axisalignedbb;
    switch ((EnumFacing)state.getValue(FACING))
    {
        case NORTH:
        default:
            axisalignedbb = new AxisAlignedBB(0.0D, 1.0D, 0.0D, 1.0D, 0.0D, 0.5D);
            break;
        case SOUTH:
            axisalignedbb = new AxisAlignedBB(0.0D, 1.0D, 0.5D, 1.0D, 0.0D, 1.0D);
            break;
        case WEST:
            axisalignedbb = new AxisAlignedBB(0.0D, 1.0D, 0.0D, 0.5D, 0.0D, 1.0D);
            break;
        case EAST:
            axisalignedbb = new AxisAlignedBB(0.5D, 1.0D, 0.0D, 1.0D, 0.0D, 1.0D);
    }
    return axisalignedbb;
}

先程の図解と合わせてみると以下のような判定になります。

ここでのポイントは、自分がどちらに向いていようとXYZの軸の向きは変わらないということです。必ず北を見て、判定を考えるようにします。

中をくり抜く当たり判定

大釜のように、中をくり抜いた当たり判定はどうやって作られているのでしょうか。

大釜のクラスから、関係のある部分を抜粋してみました。

protected static final AxisAlignedBB AABB_LEGS = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.3125D, 1.0D);
protected static final AxisAlignedBB AABB_WALL_NORTH = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 0.125D);
protected static final AxisAlignedBB AABB_WALL_SOUTH = new AxisAlignedBB(0.0D, 0.0D, 0.875D, 1.0D, 1.0D, 1.0D);
protected static final AxisAlignedBB AABB_WALL_EAST = new AxisAlignedBB(0.875D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D);
protected static final AxisAlignedBB AABB_WALL_WEST = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.125D, 1.0D, 1.0D);
public void addCollisionBoxToList(IBlockState state, World worldIn, BlockPos pos, AxisAlignedBB entityBox, List<AxisAlignedBB> collidingBoxes, @Nullable Entity entityIn, boolean isActualState)
  {
      addCollisionBoxToList(pos, entityBox, collidingBoxes, AABB_LEGS);
      addCollisionBoxToList(pos, entityBox, collidingBoxes, AABB_WALL_WEST);
      addCollisionBoxToList(pos, entityBox, collidingBoxes, AABB_WALL_NORTH);
      addCollisionBoxToList(pos, entityBox, collidingBoxes, AABB_WALL_EAST);
      addCollisionBoxToList(pos, entityBox, collidingBoxes, AABB_WALL_SOUTH);
  }

  public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
  {
      return FULL_BLOCK_AABB;
  }

どうやら、パーツに分けて判定を作って、それを組み合わせているみたいですね。階段ブロックも、このように判定を組み合わせてつくられています。

また、触れる際の判定は細かく設定していますが、getBoundingBoxはFULL_BLOCK_AABBで真四角の判定にしていますね。

複雑な当たり判定は、この形式で組み合わせることで追加できます。