我们本次预计实现一个方块探测器,让其可以探测我们想要找到的方块。
1.我们希望将方块放下后,可以探测以其坐标为中心的16×16×16的范围内是否具有目标方块:
新建一个方块类BlockBFS
,为了方便枚举区域内的方块状态,我们可以采取一种宽度优先搜索(3维BFS)的算法来对区域内的所有方块进行探测:
BlockBFS.java
package com.joy187.fanfu.common.blocks;
import net.minecraft.block.*;
import net.minecraft.block.material.Material;
import net.minecraft.particles.RedstoneParticleData;
import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.util.math.shapes.VoxelShapes;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.Blockreader;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.ToolType;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import static java.lang.Math.abs;
import static net.minecraft.block.BedBlock.getConnectedDirection;
public class BlockBFS extends RedstoneDiodeBlock {
//3维偏移量
int[]dx = {1,-1,0,0,0,0};
int[]dy = {0,0,1,-1,0,0};
int[]dz = {0,0,0,0,1,-1};
protected static final VoxelShape SHAPE = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D);
//我们的方块有两个属性,一个是朝向,一个是是否充能
public BlockBFS(AbstractBlock.Properties properties) {
super(properties);
this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(POWERED, Boolean.valueOf(false)));
}
public VoxelShape getCollisionShape(BlockState p_220071_1_, IBlockReader p_220071_2_, BlockPos p_220071_3_, ISelectionContext p_220071_4_) {
return SHAPE;
}
public VoxelShape getBlockSupportShape(BlockState p_230335_1_, IBlockReader p_230335_2_, BlockPos p_230335_3_) {
return VoxelShapes.block();
}
public VoxelShape getVisualShape(BlockState p_230322_1_, IBlockReader p_230322_2_, BlockPos p_230322_3_, ISelectionContext p_230322_4_) {
return VoxelShapes.block();
}
//BFS函数
private boolean checkBlock(World level, BlockPos pos, BlockState state) {
boolean[][][] st = new boolean[16][16][16];
for(int i=0;i<16;i++)
for(int j=0;j<16;j++)
for(int k=0;k<16;k++)
st[i][j][k]=false;
boolean flag=false;
//用一个队列,将方块探测器的坐标加入到队列中并将其坐标进行标记(由于mc坐标可能存在负数,我们要对坐标进行取模运算)
Queue<BlockPos> q = new LinkedList();
q.offer(pos);
st[(pos.getX()-pos.getX()+8)%16][(pos.getY()-pos.getY()+8)%16][(pos.getZ()-pos.getZ()+8)%16]=true;
while(q.size()!=0)
{
BlockPos blockP=q.poll();
BlockState s=level.getBlockState(blockP);
//如果当前我们找到了该目标方块,就跳出bfs循环
if(s.getBlock()==Blocks.GRASS_BLOCK){
flag=true;
break;
}
for(int i=0;i<6;i++)
{
for(int j=0;j<6;j++)
{
for(int z=0;z<6;z++)
{
int xx=blockP.getX()+dx[i];
int yy=blockP.getY()+dy[i];
int zz=blockP.getZ()+dz[i];
if(zz>0 && zz<256 && st[(xx-pos.getX()+8)%16][(yy-pos.getY()+8)%16][(zz-pos.getZ()+8)%16]==false && abs(pos.getX()-xx)<9 && abs(pos.getY()-yy)<9 && abs(pos.getZ()-zz)<9)
{
q.offer(new BlockPos(xx,yy,zz));
st[(xx-pos.getX()+8)%16][(yy-pos.getY()+8)%16][(zz-pos.getZ()+8)%16]=true;
}
}
}
}
}
//System.out.println();
return flag;
}
//探测器是否检测到目标方块
@OnlyIn(Dist.CLIENT)
public void animateTick(BlockState p_180655_1_, World p_180655_2_, BlockPos p_180655_3_, Random p_180655_4_) {
//如果方块没有激活就不断对区域内的方块进行检测
if(!p_180655_1_.getValue(POWERED))
{
boolean flag = this.checkBlock(p_180655_2_, p_180655_3_, p_180655_1_);
//如果找到了,就将探测器充能发光
if(flag)
p_180655_2_.setBlock(p_180655_3_, p_180655_1_.setValue(POWERED, Boolean.valueOf(true)), 2);
}
else{
boolean flag = this.checkBlock(p_180655_2_, p_180655_3_, p_180655_1_);
if(!flag)
p_180655_2_.setBlock(p_180655_3_, p_180655_1_.setValue(POWERED, Boolean.valueOf(false)), 2);
}
}
public void neighborChanged(BlockState p_220069_1_, World p_220069_2_, BlockPos p_220069_3_, Block p_220069_4_, BlockPos p_220069_5_, boolean p_220069_6_) {
}
@Override
protected int getDelay(BlockState p_196346_1_) {
return 1;
}
protected void createBlockStateDefinition(StateContainer.Builder<Block, BlockState> p_206840_1_) {
p_206840_1_.add(FACING,POWERED);
}
public int getSignal(BlockState p_180656_1_, IBlockReader p_180656_2_, BlockPos p_180656_3_, Direction p_180656_4_) {
return p_180656_1_.getValue(POWERED) ? 15 : 0;
}
public boolean isSignalSource(BlockState p_149744_1_) {
return true;
}
private void updateNeighbours(BlockState p_196378_1_, World p_196378_2_, BlockPos p_196378_3_) {
p_196378_2_.updateNeighborsAt(p_196378_3_, this);
p_196378_2_.updateNeighborsAt(p_196378_3_.relative(getConnectedDirection(p_196378_1_).getOpposite()), this);
}
public void onRemove(BlockState p_196243_1_, World p_196243_2_, BlockPos p_196243_3_, BlockState p_196243_4_, boolean p_196243_5_) {
if (!p_196243_5_ && !p_196243_1_.is(p_196243_4_.getBlock())) {
if (p_196243_1_.getValue(POWERED)) {
this.updateNeighbours(p_196243_1_, p_196243_2_, p_196243_3_);
}
super.onRemove(p_196243_1_, p_196243_2_, p_196243_3_, p_196243_4_, p_196243_5_);
}
}
}
2.在ModBlocks
类中注册我们的探测器方块:
ModBlocks.java
public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, Utils.MOD_ID);
//添加这个
public static RegistryObject<Block> BFS_BLOCK = BLOCKS.register("bfs_block",()->
new BlockBFS(AbstractBlock.Properties.copy(Blocks.IRON_BLOCK).harvestTool(ToolType.PICKAXE).harvestLevel(1).requiresCorrectToolForDrops()));
3.代码部分结束,来到资源包制作环节
在resources\assets\你的modid\blockstates
下新建bfs_block.json
文件,枚举方块状态:
bfs_block.json
{
"variants": {
"facing=east,powered=false": {
"model": "fanfu:block/bfs_block",
"y": 90
},
"facing=east,powered=true": {
"model": "fanfu:block/bfs_block_on",
"y": 90
},
"facing=north,powered=false": {
"model": "fanfu:block/bfs_block"
},
"facing=north,powered=true": {
"model": "fanfu:block/bfs_block_on"
},
"facing=south,powered=false": {
"model": "fanfu:block/bfs_block",
"y": 180
},
"facing=south,powered=true": {
"model": "fanfu:block/bfs_block_on",
"y": 180
},
"facing=west,powered=false": {
"model": "fanfu:block/bfs_block",
"y": 270
},
"facing=west,powered=true": {
"model": "fanfu:block/bfs_block_on",
"y": 270
}
}
}
在models\block
中新建bfs_block.json
和bfs_block_on.json
,分别表示充能和未充能的方块模型:
未充能时模型
bfs_block.json
{
"parent": "block/orientable",
"textures": {
"top": "fanfu:blocks/virus_generator_side",
"front": "fanfu:blocks/virus_generator",
"side": "fanfu:blocks/virus_generator_side"
}
}
充能时模型
bfs_block_on.json
{
"parent": "block/orientable",
"textures": {
"top": "fanfu:blocks/virus_generator_side",
"front": "fanfu:blocks/virus_generator_on",
"side": "fanfu:blocks/virus_generator_side"
}
}
在models\item
中新建玩家手持探测器的模型文件:
bfs_block.json
{
"parent": "fanfu:block/bfs_block"
}
在textures\blocks
中添加探测器的材质:
在语言包lang\en_us.json
中添加方块英文名称:
"block.fanfu.bfs_block":"Detector",
在zh_cn.json
中添加中文名称:
"block.fanfu.bfs_block":"探测器",
进入数据包,在resources\data\fanfu\loot_tables\blocks
路径下新建bfs_block.json
,编写方块破坏后的掉落物:
bfs_block.json
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"bonus_rolls": 0,
"entries": [
{
"type": "minecraft:item",
"name": "fanfu:bfs_block"
}
],
"conditions": [
{
"condition": "minecraft:match_tool"
}
]
}
]
}
4.保存所有代码 -> 运行游戏测试
我们在距离草方块8格和9格高的地方放置探测器
厉害,可惜我是C++党
更了更了!抢沙发~