方块相关的关键概念

  • 我是世界是由方块(block)组成的3维格子。世界格子仅256个方块的高度,但是沿水平坐标方向是无限延伸的。
  • 16*256*16的方块组成一个区块(chunk)。
  • 32*32的区块(chunk)组成区域(region)。每一个区域保存到一个文件中。
  • 这样分组的第一个目的是为了减少存储空间,其次是当需要的时候,可以独立的创建和并加载玩家未探索的地方的区块到内存中。
  • 在我的世界中使用下面的坐标系:
       ^
	   |Y(up)
       |
       |
       /------->X(east)
	  /    
	 /Z(south)

EnumFacing类有许多常量和与六个方向相关的实用函数(如:getOpposite(), rotateX(), getFrontOffsetX()等)。

最低限度,每个格子位置[x, y, z]包含下面的信息:

  1. 一个IBlockState,指定了该位置方块的类型(如:BlockDoor)以及方块的当前状态(如:OPEN或CLOSED)。
  2. 0-15的天空亮度的整数值,指定该方块从天空接收到亮度的总和。
  3. 0-15的方块亮度的整数值,指定该方块从附件方块接收到亮度的总和。

这些数据没有保存在Block类中。它们被保存在区块对象内部的ExtendedBlockStorage对象的数组中。可以通过各种World方法访问它们,例如: IBlockState blockAtXYZ = world.getBlockState({x,y,z}); int lightFromSky = world.getLightFor(SKY, {x,y,z}); int lightFromBlocks = world.getLightFor(BLOCK, {x,y,z});

对于模组开发者最重要的方面理解是IBlockState。一个IBlockState是由两部分组成,方块和它的属性(它的状态)。

  1. Block类以及它的派生类不保存世界中每个位置的唯一的信息,它们仅保存方块类型相关的信息。因此Block类的方法主要决定方块的行为以及它怎么被绘制到屏幕上。这些方块一般是唯一的,但不总是这样(例如:不同类型的木头共享同一个BlockOldLog实例)。Vanilla方块的实例可以在Blocks中找到。
  2. 每个方块也可能有一个或多个属性,描述该方块的不同状态。例如:BlockLadder有一个叫FACING的属性,它有四个允许的值(NORTH, SOUTH, EAST, 或WEST),依赖于它朝向的方向。
  • FACING,可能值NORTH, SOUTH, EAST, WEST。
  • PART,可能值HEAD或FOOT,定义床头和床尾。
  • OCCUPIED,可能值TRUE或FALSE。

一个方块和它当前状态的组合由一个IBlockState表示。例如:BlockBed的一个可能状态是 block_bed[facing=east,par=head,occupied=false];在该状态下操作:

  1. IBlockState.getValue(property)获取特定属性的值,例如:bedFacingDirection = currentBedState.getValue(FACING);返回EAST
  2. IBlockState.withProperty(property, new value)返回具有新的属性值的新状态。例如: IBlockState newBedState = oldBedState.withProperty(FACING, NORTH);

如果想为状态设定新的属性值,需要从基本(默认)状态开始,例如:IBlockState bedFacingNorth = Blocks.bed.getDefaultState().withProperty(FACING, NORTH);这有些繁琐但是Vanilla就是这样做的。

许多其他的数据结构用来保存格子位置特殊的信息:

  • TileEntity, 保存自定义的信息,如:文字标识牌,箱子中物品的列表。大多数的格子位置没有对应的TileEntity,每一个chunk包含一个TileEntities的列表。在这个chunk有每一个TileEntity的项和它的位置[x,y,z]。访问特定位置的TileEntity:TileEntity tileEntityAtXYZ = world.getTileEntity({x,y,z});
  • Entity,由于一些特殊的方块,如下降方块(沙子,沙烁),画,物品展示框。

更多详细信息

Vanilla使用的主要IProperties:

  • PropertyEnum,如BlockOldLog的VARIANT(OAK,SPRUCE,BIRCH,JUNGLE)
  • PropertyDirection,如BlockDoor的FACING(N,S,E,W)
  • PropertyInteger, 如BlockCrops(小麦的生长阶段)的AGE(0-7)
  • PropertyBool,如BlockBed的OCCUPIED(true/false)

Vanilla代码包含帮助函数由于创建自定义的属性。查看前面提到的Blocks来获取灵感。

每个给定的方块,有一个BlockState实例用于定义对应方块的有效状态。(这个名字有点误导人,因为BlockState不是IBlockState的实现)。使用Block.getBlockState()来访问。例如,BlockBed的BlockState包含3个属性

  • FACING
  • PART
  • OCCUPIED

自定义方块的BlockState在方块的构造期间通过覆写Block.createBlockState()创建。有需要注意在方块的构造中设置默认的状态。看BlockBed的例子。

游戏的初始化阶段,为每个方块创建唯一的实例,参考Block.registerBlock()和Blocks类。这意味你可以使用==来代替.equals()测试相等,例如:boolean thisBlockIsDirt = (blockAtCursor == Blocks.dirt);换句话说对于每个IBlockState有一个唯一的实例,这个例子也是允许的:用boolean stateHasChanged = (newIBlockState != currentIBlockState);代替boolean stateHasChanged = !newIBlockState.equals(currentIBlockState);