方块相关的关键概念
- 我是世界是由方块(block)组成的3维格子。世界格子仅256个方块的高度,但是沿水平坐标方向是无限延伸的。
- 16*256*16的方块组成一个区块(chunk)。
- 32*32的区块(chunk)组成区域(region)。每一个区域保存到一个文件中。
- 这样分组的第一个目的是为了减少存储空间,其次是当需要的时候,可以独立的创建和并加载玩家未探索的地方的区块到内存中。
- 在我的世界中使用下面的坐标系:
^
|Y(up)
|
|
/------->X(east)
/
/Z(south)
EnumFacing类有许多常量和与六个方向相关的实用函数(如:getOpposite(), rotateX(), getFrontOffsetX()等)。
最低限度,每个格子位置[x, y, z]包含下面的信息:
- 一个IBlockState,指定了该位置方块的类型(如:BlockDoor)以及方块的当前状态(如:OPEN或CLOSED)。
- 0-15的天空亮度的整数值,指定该方块从天空接收到亮度的总和。
- 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是由两部分组成,方块和它的属性(它的状态)。
- Block类以及它的派生类不保存世界中每个位置的唯一的信息,它们仅保存方块类型相关的信息。因此Block类的方法主要决定方块的行为以及它怎么被绘制到屏幕上。这些方块一般是唯一的,但不总是这样(例如:不同类型的木头共享同一个BlockOldLog实例)。Vanilla方块的实例可以在Blocks中找到。
- 每个方块也可能有一个或多个属性,描述该方块的不同状态。例如: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];在该状态下操作:
- IBlockState.getValue(property)获取特定属性的值,例如:bedFacingDirection = currentBedState.getValue(FACING);返回EAST
- 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);