User Tools

Site Tools


zh_cn:tutorial:inventory

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
zh_cn:tutorial:inventory [2019/12/19 05:03] lightcolourzh_cn:tutorial:inventory [2023/08/28 10:14] (current) – [从物品栏(或任何物品栏)中提取和放入] wjz_p
Line 1: Line 1:
-====== Giving a block state ====== +====== 在方块中存储物品 ====== 
-Every type of block in Minecraft is represented by a singular ''Block'' instance. +阅读本教程之前,请确保已经做好了[[zh_cn:tutorial:blockentity|方块实体]]。 
-This makes it impossible to change a specific block's state by simply changing the ''Block'' instance's state, + 
-as every other block of that type will be affected! +将物品存储在 BlockEntity(方块实体)中的标准方法是使其成为''Inventory''。这使得漏斗(或其他模组)无需任何额外的工作即可从您的 BlockEntity 放入和提取物品。  
-But, what if you //do// want to give a singular block state, so it can change based on some condition? +===== 实现 Inventory 接口 ===== 
-This is what ''BlockState''s are for.  +''Inventory'' 只是一个接口,这意味着实际的 ''ItemStack'' 状态将需要存储在您的''BlockEntity''上。可以使用''DefaultedList <ItemStack>''作为存储这些''ItemStacks''的简便方法,且可以将其默认设置为''ItemStack.Empty'',用来表示物品堆没有任何物品。实现 ''Inventory''非常简单,但乏味且容易出错,因此,我们将使用其默认实现,该实现只需要给它一个''DefaultList <ItemStack>''(将其复制为新文件): 
-Say we wanted a block to have a hardness of ''0.5'' normally, but if we right click it before we try to break it, +<code java ImplementedInventory.java> 
-it would become harder and gain a hardness of ''2''   +/** 
-   + * 一个简单的 {@code Inventory} 实现,仅有默认的方法和物品列表获取器。 
-First we define the boolean property of the block - whether or not it is hard (careful not to import the wrong BooleanProperty!): + * 
-<code java> + * Originally by Juuz 
-public class MyBlock extends Block + */ 
-    public static final BooleanProperty HARDENED = BooleanProperty.of("hardened"); +public interface ImplementedInventory extends Inventory { 
-+ 
-</code> +    /** 
-Then we need to register the property by overriding ''appendProperties'': +     * 从此物品栏中检索物品。 
-<code java> +     * 每次被调用时必须返回相同实例。 
-public class MyBlock extends Block +     */ 
-    [...]+    DefaultedList<ItemStack> getItems(); 
 +     
 +    /** 
 +     * 从物品列表创建物品栏。 
 +     */ 
 +    static ImplementedInventory of(DefaultedList<ItemStack> items
 +        return () -> items
 +    
 +     
 +    /** 
 +     * 根据指定的尺寸创建新的物品栏。 
 +     */ 
 +    static ImplementedInventory ofSize(int size) 
 +        return of(DefaultedList.ofSize(size, ItemStack.EMPTY)); 
 +    } 
 +     
 +    /** 
 +     * 返回物品栏的大小。 
 +     */
     @Override     @Override
-    protected void appendProperties(StateManager.Builder<Block, BlockState> stateManager) { +    default int size() { 
-        stateManager.add(HARDENED);+        return getItems().size();
     }     }
          
 +    /**
 +     * 检查物品栏是否为空。
 +     * @return true,如果物品栏仅有一个空堆,否则为true。
 +     */
 +    @Override
 +    default boolean isEmpty() {
 +        for (int i = 0; i < size(); i++) {
 +            ItemStack stack = getStack(i);
 +            if (!stack.isEmpty()) {
 +                return false;
 +            }
 +        }
 +        return true;
 +    }
 +    
 +    /**
 +     * 检索槽位中的物品。
 +     */
 +    @Override
 +    default ItemStack getStack(int slot) {
 +        return getItems().get(slot);
 +    }
 +    
 +    /**
 +     * 从物品栏槽位移除物品。
 +     * @param slot  从该槽位移除。
 +     * @param count 需要移除的物品个数。如果槽位中的物品少于需要的,则将其全部取出。
 +     */
 +    @Override
 +    default ItemStack removeStack(int slot, int count) {
 +        ItemStack result = Inventories.splitStack(getItems(), slot, count);
 +        if (!result.isEmpty()) {
 +            markDirty();
 +        }
 +        return result;
 +    }
 +    
 +    /**
 +     * 从物品栏槽位移除所有物品。
 +     * @param slot 从该槽位移除。
 +     */
 +    @Override
 +    default ItemStack removeStack(int slot) {
 +        return Inventories.removeStack(getItems(), slot);
 +    }
 +    
 +    /**
 +     * 将物品栏槽位中的当前物品堆替换为提供的物品堆。
 +     * @param slot  替换该槽位的物品堆。
 +     * @param stack 替换后新的物品堆。如果堆对于此物品栏过大({@link Inventory#getMaxCountPerStack()}),则压缩为物品栏的最大数量。
 +     */
 +    @Override
 +    default void setStack(int slot, ItemStack stack) {
 +        getItems().set(slot, stack);
 +        if (stack.getCount() > getMaxCountPerStack()) {
 +            stack.setCount(getMaxCountPerStack());
 +        }
 +    }
 +    
 +    /**
 +     * 清除物品栏。
 +     */
 +    @Override
 +    default void clear() {
 +        getItems().clear();
 +    }
 +    
 +    /**
 +     * 将方块状态标记为脏。
 +     * 更改物品栏之后必须调用,所以游戏正确地储存物品栏内容并提取邻近方块物品栏改变。
 +     */ 
 +    @Override
 +    default void markDirty() {
 +        // 需要行为时,覆盖此方法。
 +    }
 +    
 +    /**
 +     * @return true 如果玩家可以使用物品栏,否则为 false。i
 +     */ 
 +    @Override
 +    default boolean canPlayerUse(PlayerEntity player) {
 +        return true;
 +    }
 } }
 </code> </code>
-Then we need to set the default state of our property in the block constructor:+ 
 +现在在您的 ''BlockEntity'' 中实现''ImplementedInventory'',并为其提供存储该物品的 ''DefaultedList <ItemStack> items'' 实例。对于此例,我们将在物品栏中最多存储2件物品:
 <code java> <code java>
-public class MyBlock extends Block +public class DemoBlockEntity extends BlockEntity implements ImplementedInventory 
-    [...] +    private final DefaultedList<ItemStack> items = DefaultedList.ofSize(2, ItemStack.EMPTY); 
-    public MyBlock(Settings settings) { + 
-        super(settings); +    @Override 
-        setDefaultState(getStateManager().getDefaultState().with(HARDENED, false));+    public DefaultedList<ItemStack> getItems() { 
 +        return items;
     }     }
-    +    [...] 
 } }
-</code> 
-(To set multiple properties, chain ''with()'' calls) 
  
-Now, to set the property we need to call  ''world.setBlockState()'':   +</code> 
-      +我们还需要将物品栏保存到标签并从那里加载。''Inventories'' 具有帮助方法,可以使得这个非常轻松:
-(Replace ''MyBlocks.MY_BLOCK_INSTANCE'' with your block's instance)+
 <code java> <code java>
-public class MyBlock extends Block {+public class DemoBlockEntity extends BlockEntity implements ImplementedInventory {
     [...]     [...]
     @Override     @Override
-    public boolean activate(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) { +    public void readNbt(NbtCompound nbt) { 
-        world.setBlockState(pos, MyBlocks.MY_BLOCK_INSTANCE.getDefaultState().with(HARDENEDtrue)); +        super.readNbt(nbt); 
-        return true;+        Inventories.readNbt(nbt, items)
 +    } 
 + 
 +    @Override 
 +    public NbtCompound writeNbt(NbtCompound nbt) { 
 +        Inventories.writeNbt(nbtitems); 
 +        return super.writeNbt(nbt);
     }     }
 } }
 </code> </code>
-And to use the property we call ''blockState.get(<our-property-name>)'':+===== 从物品栏(或任何物品栏)中提取和放入 ===== 
 +我们覆盖方块类中的 `onUse` 行为以从我们的物品栏中加入和提取物品。注意这也可以对任何 ''Inventory'' 实例完成,不仅是我们自己的(例如,也因此可以对箱子方块做同样的事)。首先我们处理第一个槽位,如果是空的。玩家如果拿着物品,则会将拿着的物品放入。物品进入第一个槽位,如果是空的,或者进入第二个槽位,如果第一个是空的,或者如果第二个是空的,我们则会输出与物品栏有关的信息。注意我们将 ''ItemStack'' 插入物品栏时调用 ''copy()'',这样不会随着玩家的 ''ItemStack'' 而被破坏。
 <code java> <code java>
-public class MyBlock extends Block {+public class ExampleBlock extends Block implements BlockEntityProvider {
     [...]     [...]
     @Override     @Override
-    public float getHardness(BlockState blockState, BlockView blockView, BlockPos pos) { +    public ActionResult onUse(BlockState blockState, World world, BlockPos blockPos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) { 
-        boolean hardened blockState.get(HARDENED); +        if (world.isClient) return ActionResult.SUCCESS; 
-        if(hardenedreturn 2.0f+        Inventory blockEntity (Inventory) world.getBlockEntity(blockPos); 
-        else return 0.5f;+  
 +  
 +        if (!player.getStackInHand(hand).isEmpty()) { 
 +            // Check what is the first open slot and put an item from the player's hand there 
 +            if (blockEntity.getStack(0).isEmpty()) { 
 +                // Put the stack the player is holding into the inventory 
 +                blockEntity.setStack(0, player.getStackInHand(hand).copy())
 +                // Remove the stack from the player's hand 
 +                player.getStackInHand(hand).setCount(0); 
 +            } else if (blockEntity.getStack(1).isEmpty()) { 
 +                blockEntity.setStack(1, player.getStackInHand(hand).copy()); 
 +                player.getStackInHand(hand).setCount(0); 
 +            } else { 
 +                // If the inventory is full we'll print it's contents 
 +                System.out.println("The first slot holds " 
 +                        + blockEntity.getStack(0) + " and the second slot holds " + blockEntity.getStack(1)); 
 +            } 
 +        }  
 +        return ActionResult.SUCCESS;
     }     }
 } }
 </code> </code>
  
-==== Adding models for your blockstates ====+当玩家不持有物品时,我们将采取相反的行为。我们将从第二个槽位中取出项目,然后第二个中的第一个为空。如果第一个也是空的,我们将不做任何事情。
  
-You can also make the texture and model of your block change based on the stateThis is done through a JSON file called a Blockstate JSONAll blocks need a blockstate JSONwhether they have multiple states or notbut the contents of the JSON can be as simple or complex as you like. If you want to change the textures of your block based on the state, you //will// need multiple models. +<code java> 
- +public class ExampleBlock extends Block implements BlockEntityProvider { 
-Let's say you register an instance of ''MyBlock'' to the ID ''mymod:my_block''Minecraft would look for a file at the location ''src/main/resources/assets/mymod/blockstates/my_block.json'' to load the state from. If you don't want your block to change models between states, the blockstate JSON can be very simpleIt would look something like this: +    [...] 
- +    @Override 
-<code JavaScript resources/assets/mymod/blockstates/my_block.json> +    public ActionResult onUse(BlockState blockStateWorld worldBlockPos blockPos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) { 
-{ +        ... 
-    "variants": { +        if (!player.getStackInHand(hand).isEmpty()) { 
-        "": { "model": "mymod:block/my_block" }+            ... 
 +        } else { 
 +            // If the player is not holding anything we'll get give him the items in the block entity one by one 
 +  
 +             // Find the first slot that has an item and give it to the player 
 +            if (!blockEntity.getStack(1).isEmpty()) { 
 +                // Give the player the stack in the inventory 
 +                player.getInventory().offerOrDrop(blockEntity.getStack(1)); 
 +                // Remove the stack from the inventory 
 +                blockEntity.removeStack(1); 
 +            } else if (!blockEntity.getStack(0).isEmpty()) { 
 +                player.getInventory().offerOrDrop(blockEntity.getStack(0)); 
 +                blockEntity.removeStack(0); 
 +            } 
 +        } 
 +  
 +        return ActionResult.SUCCESS;
     }     }
 } }
 </code> </code>
  
-Let's break this simple example downThere are a couple important parts to this JSON:+===== 实现 SidedInventory 接口 ===== 
 +如果你希望有基于与方块不同的面(漏斗或者其他模组)进行交互的不同逻辑,你可以实现 SidedInventory 接口。如果说你想使得方块不能从上侧插入,可以这样做: 
 +<code java> 
 +public class DemoBlockEntity extends BlockEntity implements ImplementedInventory, SidedInventory { 
 +    [...] 
 +    @Override 
 +    public int[] getInvAvailableSlots(Direction var1) { 
 +        // Just return an array of all slots 
 +        int[] result = new int[getItems().size()]; 
 +        for (int i = 0; i < result.length; i++) { 
 +            result[i] = i; 
 +        }
  
-- The ''"variants"'' block will be where all possible variations for your blockstate go. We'll explore variants more in a little. +        return result; 
-- A variant named ''""'' will apply to //every// permutation of a blockstate. If you have a ''""'' variant, you shouldn't have any other variants in the JSON, or Minecraft will get upset. +    }
-- The object assigned to the ''""'' variant can have various properties added to it like rotation or texture manipulation. Check out the linked Model page below for more documentation on what properties can be added. All variants //must// contain a ''"model"'' property. +
-- The ''"model"'' property is always passed an ID of a model. In this case, the game will look at the location ''src/main/resources/assets/mymod/models/block/my_block.json''. The ID here can be anything. It doesn't //need// to be the same as your block's ID, but if you only have one variant, it probably should. Block models have their own setup, which is documented very well on the Minecraft wiki page linked below. You can either write the JSON by hand or use a program like [[https://blockbench.net|Blockbench]] to generate it more easily.+
  
-If you //do// want to have different models for each blockstateyou'd want to add multiple variantsFor the same ''src/main/resources/assets/mymod/blockstates/my_block.json'' location we used above, your could would probably look like such:+    @Override 
 +    public boolean canInsert(int slotItemStack stack, Direction direction) { 
 +        return direction != Direction.UP; 
 +    }
  
-<code JavaScript resources/assets/mymod/blockstates/my_block.json> +    @Override 
-{ +    public boolean canExtract(int slot, ItemStack stack, Direction direction) 
-    "variants": +        return true;
-        "hardened=false": { "model": "mymod:block/my_block" }, +
-        "hardened=true": { "model": "mymod:block/my_block_hardened" }+
     }     }
 } }
 +
 </code> </code>
- 
-In this JSON, there are two variants, one for each possibility of the ''HARDENED'' property we defined above. Since we gave the property the string name of ''hardened'' in the Java, that's what we use here. Booleans only have two states, but if you use properties based on integers or enums, you'll have more variants.  
- 
-Variants are based on possible permutations of the properties added to your block. A property can be totally ignored in the blockstate JSON if you want, like in the first blockstate JSON where we ignored the ''hardened'' property, but if you want to include a property in one variant, it must be included in //all// variants. If ''mymod:my_block'' also had a boolean property called ''glowing'', and you wanted to change the model based on whether it was glowing and based on whether it was hardened, you would need four variants: hardened off and glowing off, hardened on and glowing off, hardened off and glowing on, and hardened on and glowing on. The same model can be assigned to multiple variants if you need it to be. 
- 
-This is only a simple introduction to blockstate JSONs. All of the tricks you can do with blockstate and model JSONs are documented on the [[https://minecraft.gamepedia.com/Model|Minecraft wiki]], along with examples of how the features are used in vanilla. Best of luck! 
-==== A note about performance ==== 
-Every possible state of a block is registered at the start of the game. This means that if you have 14 boolean properties, the block has 2^14 = 16384 different states and 2^14 states are registered. For this reason blocks should not contain too many blockstate properties. Rather, blockstates should be mostly reserved for visuals, and [[tutorial:blockentity|Block Entities]] should be used for more advanced state. 
zh_cn/tutorial/inventory.1576731825.txt.gz · Last modified: 2019/12/19 05:03 by lightcolour