0
\$\begingroup\$

I'm making a Mindustry-type prototype. Some block (such as miners) can output resource (with the IOutput interface), some block (such as containers) can input those resource (with IInput) and some can do both.

All of them will have the IStorage interface (for storage) with AddToStorage and RemoveFromStorage methods.

namespace Conveyor
{
    [System.Serializable]
    public class bulkItem
    {
        public int itemCount;
        public int id;

        public bulkItem(int iC, int i)
        {
            itemCount = iC; id = i;
        }
    }

    interface IBlockStorage
    {
        public int maxCapacity {get;}
        public List<bulkItem> items {get; set;}

        public void InitializeStorage();
        public void AddToStorage(bulkItem item);
        public void RemoveFromStorage(bulkItem item);
    }
}

The issue is, where should the script for transportation (for example, transporting building material) be attached?

Should the output blocks actively searches for input blocks, then use RemoveFromStorage on itself, and AddToStorage on the input blocks it found? Or vice-versa?

Or should there be an universal InputOutputManager script somewhere and remotely use AddToStorage and RemoveFromStorage to those input/output blocks?

What would be the most code-efficient method of doing this?

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Unity is a game engine that's designed around the principle of composition over inheritance.

That means the idiomatic way to design this would be something like this:

  • GameObjects that represent items in the game world with:
    • A ResourceItem component which state what kind of resource it is.
    • A FollowConveyorMovement component which controls its movement over a conveyor belt.
  • GameObjects that represent producers, crafters and consumers which have a combination of these components:
    • A MonoBehaviour ResourceStorage which maintains the item counts.
    • A ResourceInput which checks for items in the world, and if so removes them from the world and increments the counter on the ResourceStorage component on the same object.
    • A ResourceConverter behavior which interacts with the ResourceStorage by checking if the resources for its crafting receipt are available, removing the ingredients from the storage and adding its product to the storage.
    • A ResourceOutput behavior which decrements the counts of the ResourceStorage behavior on the same GameObject (if available) and spawns new items into the world.

This pattern is applicable to both classic MonoBehaviours and to the Data-Oriented Tech Stack with its new Entity-Component-System architecture. The ECS architecture is still in an "experimental" state, but really useful for a game like the one you want to make where you deal with a very large number of relatively simple objects. (And besides, the ECS pattern also solves the question of which component of which entity is responsible for an entity-entity interaction. Neither is. It's the responsibity of a system interacting with both components on both entities).

But Unity is a flexible tool. You don't have to follow this pattern. If you feel more comfortable with classic object-oriented patterns like inheritance and interfaces, feel free to use them.

\$\endgroup\$
4
  • \$\begingroup\$ To clarify, you mean that each "blocks" (producers, crafters and consumers,...) shall all have both input and output components, and it's those components' duty to control the resources' flow? \$\endgroup\$
    – silverfox
    Commented Oct 21, 2022 at 16:29
  • 1
    \$\begingroup\$ @silverfox Yes, as long as that block has both input and output. When it doesn't output any resources, then it doesn't need an output component. And when it doesn't consume anything, then it needs no input component either. \$\endgroup\$
    – Philipp
    Commented Oct 21, 2022 at 19:12
  • \$\begingroup\$ So in this case, what would you use for "check items in the world"? For example, what if I put a producer and consumer right next to each other (without any conveyors), and I still want the item to go from the producer to the consumer? How would you implement the "resource" GameObject in cases like that? Should the resource have a literal physical appearance in the game world or could you do something to keep it to just code? \$\endgroup\$
    – silverfox
    Commented Oct 22, 2022 at 10:41
  • \$\begingroup\$ I'm thinking of having a seperate Monobehaviour which only have a list of cell pair with resource pending between them. So, for example, the output resource module at cell [x, y] add a new query inside the list: There's a resource pending in between cell [x,y] and [x,y+1] and remove the resource from its storage. Then, if there actually is a cell with an input module in cell [x, y+1], that input module will remove the query from the list and add the resource to its storage. But that's just seem a lot more complicated than what you're describing. \$\endgroup\$
    – silverfox
    Commented Oct 22, 2022 at 11:16
1
\$\begingroup\$

I'm not a proffesional, so you should take this with a grain of salt.

I think you should create a separate MonoBehaviour script which takes the Item class and executes the logic you need in it. The best example that would fit your situation is this video, the Area Damage part to be exact.

In your case, it will be an animation or a visual effect.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Can you explain why this approach would be best? \$\endgroup\$
    – DMGregory
    Commented Oct 21, 2022 at 10:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .