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

Next revision
Previous revision
zh_cn:tutorial:inventory [2019/12/19 05:03] – created lightcolourzh_cn:tutorial:inventory [2023/08/28 10:14] (current) – [从物品栏(或任何物品栏)中提取和放入] wjz_p
Line 1: Line 1:
-====== Adding a BlockEntity ======+====== 在方块中存储物品 ====== 
 +阅读本教程之前,请确保已经做好了[[zh_cn:tutorial:blockentity|方块实体]]。
  
-===== Introduction =====+将物品存储在 BlockEntity(方块实体)中的标准方法是使其成为''Inventory''。这使得漏斗(或其他模组)无需任何额外的工作即可从您的 BlockEntity 放入和提取物品。  
 +===== 实现 Inventory 接口 ===== 
 +''Inventory'' 只是一个接口,这意味着实际的 ''ItemStack'' 状态将需要存储在您的''BlockEntity''上。可以使用''DefaultedList <ItemStack>''作为存储这些''ItemStacks''的简便方法,且可以将其默认设置为''ItemStack.Empty'',用来表示物品堆没有任何物品。实现 ''Inventory''非常简单,但乏味且容易出错,因此,我们将使用其默认实现,该实现只需要给它一个''DefaultList <ItemStack>''(将其复制为新文件): 
 +<code java ImplementedInventory.java> 
 +/** 
 + * 一个简单的 {@code Inventory} 实现,仅有默认的方法和物品列表获取器。 
 + * 
 + * Originally by Juuz 
 + */ 
 +public interface ImplementedInventory extends Inventory {
  
-A BlockEntity is primarily used to store data within blocksBefore creating oneyou will need a [[tutorial:blocks|Block]]This tutorial will cover the creation of your BlockEntity class, and it's registration+    /** 
- +     * 从此物品栏中检索物品。 
-===== Creating a BlockEntity ===== +     * 每次被调用时必须返回相同实例。 
- +     */ 
-The simplest Block Entity simply extends ''BlockEntity''and uses the default constructorThis is perfectly validbut will not grant any special functionality to your block+    DefaultedList<ItemStack> getItems(); 
- +     
-<code java> +    /** 
-public class DemoBlockEntity extends BlockEntity { +     * 从物品列表创建物品栏。 
-   public DemoBlockEntity() { +     */ 
-      super(ExampleMod.DEMO_BLOCK_ENTITY); +    static ImplementedInventory of(DefaultedList<ItemStack> items) { 
-   }+        return () -> items; 
 +    } 
 +     
 +    /** 
 +     * 根据指定的尺寸创建新的物品栏。 
 +     */ 
 +    static ImplementedInventory ofSize(int size) { 
 +        return of(DefaultedList.ofSize(sizeItemStack.EMPTY)); 
 +    } 
 +     
 +    /** 
 +     * 返回物品栏的大小。 
 +     */ 
 +    @Override 
 +    default int size() { 
 +        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(slotstack); 
 +        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>
  
-Bellow will show you how to create the ''ExampleMod.DEMO_BLOCK_ENTITY'' field.+现在在您的 ''BlockEntity'' 中实现''ImplementedInventory'',并为其提供存储该物品的 ''DefaultedList <ItemStack> items'' 实例。对于此例,我们将在物品栏中最多存储2件物品: 
 +<code java> 
 +public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { 
 +    private final DefaultedList<ItemStack> items = DefaultedList.ofSize(2, ItemStack.EMPTY);
  
-You can simply add variables to this barebone class or implement interfaces such as ''Tickable'' and ''Inventory'' to add more functionality. ''Tickable'' provides a single ''tick()'' method, which is called once per tick for every loaded instance of your Block in the world., while ''Inventory'' allows your BlockEntity to interact with automation such as hoppers - there will likely be a separate tutorial dedicated entirely to this interface later.+    @Override 
 +    public DefaultedList<ItemStack> getItems() 
 +        return items; 
 +    } 
 +    [...]
  
-===== Registring your BlockEntity ===== +}
- +
-Once you have created the ''BlockEntity'' class, you will need to register it for it to function. The first step of this process is to create a ''BlockEntityType'', which links your ''Block'' and ''BlockEntity'' together. Assuming your ''Block'' has been created and saved to a local variable ''DEMO_BLOCK'', you would create the matching ''BlockEntityType'' with the line below. ''modid:demo'' should be replaced by your Mod ID and the name you want your ''BlockEntity'' to be registered under. +
- +
-The ''BlockEntityType'' should be registered in your ''onInitialize'' method, this is to ensure it gets registered at the correct time.+
  
 +</code>
 +我们还需要将物品栏保存到标签并从那里加载。''Inventories'' 具有帮助方法,可以使得这个非常轻松:
 <code java> <code java>
-public static BlockEntityType<DemoBlockEntity> DEMO_BLOCK_ENTITY;+public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { 
 +    [...] 
 +    @Override 
 +    public void readNbt(NbtCompound nbt) { 
 +        super.readNbt(nbt); 
 +        Inventories.readNbt(nbt, items); 
 +    }
  
-@Override +    @Override 
-public void onInitialize() { +    public NbtCompound writeNbt(NbtCompound nbt) { 
-   DEMO_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY, "modid:demo", BlockEntityType.Builder.create(DemoBlockEntity::newDEMO_BLOCK).build(null));+        Inventories.writeNbt(nbtitems)
 +        return super.writeNbt(nbt); 
 +    }
 } }
 </code> </code>
- +===== 从物品栏(或任何物品栏)中提取和放入 ===== 
-Once your ''BlockEntityType'' has been created and registered as seen above, you can simply implement ''BlockEntityProvider'' in your ''Block'' class: +我们覆盖方块类中的 `onUse` 行为以从我们的物品栏中加入和提取物品。注意这也可以对任何 ''Inventory'' 实例完成,不仅是我们自己的(例如,也因此可以对箱子方块做同样的事)。首先我们处理第一个槽位,如果是空的。玩家如果拿着物品,则会将拿着的物品放入。物品进入第一个槽位,如果是空的,或者进入第二个槽位,如果第一个是空的,或者如果第二个是空的,我们则会输出与物品栏有关的信息。注意我们将 ''ItemStack'' 插入物品栏时调用 ''copy()'',这样不会随着玩家的 ''ItemStack'' 而被破坏。
 <code java> <code java>
-@Override +public class ExampleBlock extends Block implements BlockEntityProvider { 
-public BlockEntity createBlockEntity(BlockView blockView) { +    [...] 
-   return new DemoBlockEntity();+    @Override 
 +    public ActionResult onUse(BlockState blockState, World world, BlockPos blockPos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) { 
 +        if (world.isClient) return ActionResult.SUCCESS; 
 +        Inventory blockEntity = (Inventory) world.getBlockEntity(blockPos); 
 +  
 +  
 +        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>
  
-===== Serializing Data ===== +当玩家不持有物品时,我们将采取相反的行为。我们将从第二个槽位中取出项目,然后第二个中的第一个为空。如果第一个也是空的,我们将不做任何事情。
- +
-If you want to store any data in your ''BlockEntity'', you will need to save and load it, or it will only be held while the ''BlockEntity'' is loaded, and the data will reset whenever you come back to it. Luckily, saving and loading is quite simple - you only need to override ''toTag()'' and ''fromTag()''.  +
- +
-''toTag()'' returns a ''CompoundTag'', which should contain all of the data in your ''BlockEntity''. This data is saved to the disk and also send through packets if you need to sync your ''BlockEntity'' data with clients. It is very important to call the default implementation of ''toTag'', as it saves "Identifying Data" (position and ID) to the tag. Without this, any further data you try and save will be lost as it is not associated with a position and ''BlockEntityType''. Knowing this, the example below demonstrates saving an integer from your ''BlockEntity'' to the tag. In the example, the integer is saved under the key ''"number"'' - you can replace this with any string, but you can only have one entry for each key in your tag, and you will need to remember the key in order to retrieve the data later.+
  
 <code java> <code java>
-public class DemoBlockEntity extends BlockEntity +public class ExampleBlock extends Block implements BlockEntityProvider 
- +    [...] 
-   // Store the current value of the number +    @Override 
-   private int number = 7; +    public ActionResult onUse(BlockState blockState, World world, BlockPos blockPos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) { 
-    +        ... 
-   public DemoBlockEntity() { +        if (!player.getStackInHand(hand).isEmpty()) { 
-      super(ExampleMod.DEMO_BLOCK_ENTITY); +            ... 
-   +        else { 
-    +            // If the player is not holding anything we'll get give him the items in the block entity one by one 
-   // Serialize the BlockEntity +  
-   public CompoundTag toTag(CompoundTag tag) { +             // Find the first slot that has an item and give it to the player 
-      super.toTag(tag); +            if (!blockEntity.getStack(1).isEmpty()) { 
-       +                // Give the player the stack in the inventory 
-      // Save the current value of the number to the tag +                player.getInventory().offerOrDrop(blockEntity.getStack(1)); 
-      tag.putInt("number", number); +                // Remove the stack from the inventory 
-       +                blockEntity.removeStack(1); 
-      return tag+            } else if (!blockEntity.getStack(0).isEmpty()) { 
-   }+                player.getInventory().offerOrDrop(blockEntity.getStack(0)); 
 +                blockEntity.removeStack(0); 
 +            } 
 +        } 
 +  
 +        return ActionResult.SUCCESS
 +    }
 } }
 </code> </code>
  
-In order to retrieve the data later, you will also need to override ''fromTag''. This method is the opposite of ''toTag'' - instead of saving your data to a ''CompoundTag'', you are given the tag which you saved earlier, enabling you to retrieve any data that you need. As with ''toTag'', it is essential that you call ''super.fromTag'', and you will need to use the same keys to retrieve data that you saved. To retrieve, the number we saved earlier, see the example below. +===== 实现 SidedInventory 接口 ===== 
 +如果你希望有基于与方块不同的面(漏斗或者其他模组)进行交互的不同逻辑,你可以实现 SidedInventory 接口。如果说你想使得方块不能从上侧插入,可以这样做:
 <code java> <code java>
-// Deserialize the BlockEntity +public class DemoBlockEntity extends BlockEntity implements ImplementedInventory, SidedInventory { 
-public void fromTag(CompoundTag tag) { +    [...] 
-   super.fromTag(tag); +    @Override 
-   number tag.getInt("number"); +    public int[] getInvAvailableSlots(Direction var1) { 
-} +        // Just return an array of all slots 
-</code>+        int[] result = new int[getItems().size()]
 +        for (int i 0; i < result.length; i++
 +            result[i] = i
 +        }
  
-Once you have implemented the ''toTag'' and ''fromTag'' methods, you simply need to ensure that they are called at the right time. Whenever your ''BlockEntity'' data changes and needs to be saved, call ''markDirty()''. This will force the ''toTag'' method to be called when the world is next saved by marking the chunk which your block is in as dirty. As a general rule of thumb, simply call ''markDirty()'' whenever you change any custom variable in your ''BlockEntity'' class.+        return result; 
 +    }
  
-If you need to sync some of your ''BlockEntity'' data to the clientfor purposes such as renderingyou should implement ''BlockEntityClientSerializable'' from the Fabric API. This class provides the ''fromClientTag'' and ''toClientTag'' methods, which work much the same as the previously discussed ''fromTag'' and ''toTag'' methods, except that they are used specifically for sending to and receiving data on the client+    @Override 
 +    public boolean canInsert(int slotItemStack stackDirection direction) { 
 +        return direction != Direction.UP; 
 +    }
  
-===== Overview =====+    @Override 
 +    public boolean canExtract(int slot, ItemStack stack, Direction direction) { 
 +        return true; 
 +    } 
 +}
  
-You should now have your very own ''BlockEntity'', which you can expand in various ways to suit your needs. You registered a ''BlockEntityType'', and used it to connect your ''Block'' and ''BlockEntity'' classes together. Then, you implemented ''BlockEntityProvider'' in your ''Block'' class, and used the interface to provide an instance of your new ''BlockEntity''. Finally, you learned how to save data to your ''BlockEntity'', and how to retrieve for use later.+</code>
zh_cn/tutorial/inventory.1576731787.txt.gz · Last modified: 2019/12/19 05:03 by lightcolour