1
\$\begingroup\$

I am trying to code the inventory system in my first real game so I have very little experience in both c# and game engine development. Basically, I need some general guidance and tips with how to structure and organize these sorts of systems. Please tell me if I am on the right track or not before I get too deep into making some badly structured system. It's fine if you don't feel like looking through my code, suggestions about general structure would also be appreciated.

What I am aiming to end up with is some sort of system like Minecraft or Terraria. It must include:

  • main inventory GUI (items can be dragged and placed in whatever slot desired
  • Itembar outside of the main inventory which can be assigned to certain items
  • the ability to use items from either location

So far, I have 4 main classes: Inventory holds the general info and methods, inventoryslot holds info for individual slots, Itembar holds all info and methods for itself, and finally, ItemManager to manage interactions between the two and hold a master list of items.

So far, my itembar works perfectly and interacts well with mousedragging items into and out of it as well as activating the item effect.

Here is the code I have so far: (there is a lot but I will try to keep it relevant)

This is the code for the itembar on the main screen:

class Itembar
    {
        public Texture2D itembarfull, iSelected;
        public static Rectangle itembar = new Rectangle(5, 218, 40, 391); 
        public Rectangle box1 = new Rectangle(itembar.X, 218, 40, 40);
        //up to 10 Rectangles for each slot
        public int Selected = 0;
        private ItemManager manager;

        public Itembar(Texture2D texture, Texture2D texture3, ItemManager mann)
        {
            itembarfull = texture;
            iSelected = texture3;
            manager = mann;
        }

        public void Update(GameTime gametime)
        {

        }

        public void Draw(SpriteBatch spriteBatch)
        {
                spriteBatch.Draw(
                                itembarfull,
                                new Vector2 (itembar.X, itembar.Y),
                                null,
                                Color.White,
                                0.0f,
                                Vector2.Zero,
                                1.0f,
                                SpriteEffects.None,
                                1.0f);
                if (Selected == 1)
                spriteBatch.Draw(iSelected, new Rectangle(box1.X-3, box1.Y-3, box1.Width+6, box1.Height+6), Color.White);
               //goes up to 10 slots

        }

        public int Box1Query()
        {
            foreach (Item item in manager.items)
            {
                if(box1.Contains(item.BoundingBox))
                    return manager.items.IndexOf(item);
            }
            return 999;
        }
//10 different box queries

It is working fine right now. I just put an Item in there and the box will query things like the item's effects, stack number, consumable or not etc...This one is basically almost complete.

Here is the main inventory class:

 class Inventory 
    {
        public bool isActive;
        public List<Rectangle> mainSlots = new List<Rectangle>(24);
        public List<InventorySlot> mainSlotscheck = new List<InventorySlot>(24);
        public static Rectangle inv = new Rectangle(841, 469, 156, 231);
        public Rectangle invfull = new Rectangle(inv.X, inv.Y, inv.Width, inv.Height);
        public Rectangle inv1 = new Rectangle(inv.X + 4, inv.Y +3, 32, 32);
        //goes up to inv24 resulting in a 6x4 grid of Rectangles
public Inventory()
        {
            mainSlots.Add(inv1); mainSlots.Add(inv2); mainSlots.Add(inv3); mainSlots.Add(inv4);
            //goes up to 24
            foreach (Rectangle slot in mainSlots)
                mainSlotscheck.Add(new InventorySlot(slot));
        }
//update and draw methods are empty because im not too sure what to put there
public int LookforfreeSlot()
    {
        int slotnumber = 999;
        for (int x = 0; x < mainSlots.Count; x++)
        {
            if (mainSlotscheck[x].isFree)
            {
                slotnumber = x;
                break;
            }
        }
        return slotnumber;
    }



    }
}

LookforFreeSlot() method is meant to be called when I do AddtoInventory(). I'm kinda stumped about what other things I need to put in this class.

Here is the inventorySlot class: (its main purpose is to check the bool "isFree" to see whether or not something already occupies the slot. But i guess it can also do other stuff like get item info.)

class InventorySlot
    {
        public int X, Y;
        public int Width = 32, Height = 32;
        public Vector2 Position;
        public int slotnumber;
        public bool free = true;
        public int? content = null;

        public bool isFree
        {
            get { return free; }
            set { free = value; }
        }

        public InventorySlot(Rectangle slot)
        {
            slot = new Rectangle(X, Y, Width, Height);
        }
    }
}

Finally, here is the ItemManager (I am omitting the master list because it is too long)

class ItemManager
    {
public List<Item> items = new List<Item>(20);
        public List<Item> inventory1 = new List<Item>(24);
        public List<Item> inventory2 = new List<Item>(24);
        public List<Item> inventory3 = new List<Item>(24);
        public List<Item> inventory4 = new List<Item>(24);
        public Texture2D icon, filta;
        private Rectangle msRect;
        MouseState mouseState;
        public int ISelectedIndex;
        Inventory inventory;
        SpriteFont font;
public void GenerateItems()
        {
            items.Add(new Item(new Rectangle(0, 0, 32, 32), icon, font));
            items[0].name = "Grass Chip";
            items[0].itemID = 0;
            items[0].consumable = true;
            items[0].stackable = true;
            items[0].maxStack = 99;
            items.Add(new Item(new Rectangle(32, 0, 32, 32), icon, font));
        //master list continues. it will generate all items in the game;

        }
public ItemManager(Inventory inv, Texture2D itemsheet, Rectangle mouseRectt, MouseState ms, Texture2D fil, 
                            SpriteFont f)
        {
            icon = itemsheet;
            msRect = mouseRectt;
            filta = fil;
            mouseState = ms;
            inventory = inv;
            font = f;
        }
//once again, no update or draw
 public void mousedrag()
        {

                        items[0].DestinationRect = new Rectangle (msRect.X, msRect.Y, 32, 32);
                        items[0].dragging = true;


        }

        public void AddtoInventory(Item item)
        {
            int index = inventory.LookforfreeSlot();
            if (index == 999)
                return;
            item.DestinationRect = inventory.mainSlots[index];
            inventory.mainSlotscheck[index].content = item.itemID;
            inventory.mainSlotscheck[index].isFree = false;
            item.IsActive = true;
        }

    }
}

The mousedrag works pretty well. AddtoInventory doesn't work because LookforfreeSlot doesn't work.

Relevant code from the main program: When I want to add something to the main inventory, I do something like this:

 foreach (Particle ether in ether1.ethers)
                    {
                        if (ether.isCollected)
                            itemmanager.AddtoInventory(itemmanager.items[14]);
                    }

This turned out to be much longer than I had expected :(

But I hope someone is interested enough to comment.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Can you be a little more specific on what troubles you most? From what I see, I think it's rather bad design since it violates DRY priniciple with your Box*Query methods at least. But for a beginner that can be ok for some time. The question is to vague, it's hard to answer. \$\endgroup\$ Apr 28, 2012 at 4:31
  • \$\begingroup\$ What does LookforfreeSlot doesn't work mean? Have you stepped the code? Are you getting an error message? Does it always return 999? \$\endgroup\$ Apr 30, 2012 at 15:12

1 Answer 1

2
\$\begingroup\$

I agree with Petr Abdulin that the question is too vague. Still, here are a couple of pointers that might help you to improve your design:

  • ItemManager is not needed at all. Use the Active Record pattern: Store item data in a database or XML file and use statements like Item::findById(5) to create item objects.
  • Separate your classes' business logic from presentation logic. For example, in InventorySlot, x, y, width, and height do not belong in the same class as free and content; checking whether a slot in Itembar is free should not be done via a comparison of some rectangle positions; etc.
  • Get rid of unnecessary dependencies and redundancies. For example: InventorySlot->free can be calculated from its content; InventorySlot does not need to know its position; nothing needs to be coupled with ItemManager; etc.
  • Use encapsulation to your advantage. For example: Inventory and ItemBar only need one class! (Or, if they need to be distinguished from one another for reasons you did not mention, they can be derived from the same base class.)

Here is what your classes might look like:

class Inventory {
    property slots (List of InventorySlots)
    property numberOfSlots (int)
    property numberOfColumns (int)
    NO OTHER PROPERTIES

    function Inventory(slots, numberOfSlots, numberOfColumns) {
        if(slots == null) {
            // create numberOfSlots empty InventorySlots
        }
        if(numberOfColumns == null) {
            this->numberOfColumns = numberOfSlots;
        }
    }
    function addItem(Item) {
        slot = this->getFreeSlot();
        if(!slot) { return false; }
        slot->setContent(Item);
        return true;
    }
    function getFreeSlot() {
        while(slot = slots->next()) {
            if(slot->isFree()) {
                return slot;
            }
        }
        return false;
    }
    // Let draw except a position for the inventory on the screen
    function draw(position, sprite) {
        // draw sprite
        for(row = 0; row < this->numberOfSlots / this->numberOfColumns; row++) {
            for(column = 0; column < this->numberOfColumns; column++) {
                slot = slots->getElement(row*6+column);
                // InventorySlot->draw also expects a position, so let's calculate that
                positionToSlotPosition = <column*InventorySlot::width, row*InentorySlot::height>
                slot.draw(position + positionToSlotPosition);
            }
        }
    }
}

class InventorySlot extends SomeUIClass which implements Draggable {
    property content (Item)
    const width=32
    const height=32
    NO OTHER PROPERTIES

    function InventorySlot() {}
    function setContent(Item) {...}
    function getContent() {
        if(this->isFree()) {
            // return something that does not throw errors when you call Item's interfaces without actually doing anything
            return new NullItem();
        }
        return this->content;
    }
    function isFree() {...}
    function draw(position) {
        // draw rectangle with self::width and self::height at position
        // Draw the item in the item slot; draws nothing if getContent() returns a NullItem
        this->getContent->draw(position);
    }
}

class SomeUIControlStuff {
    property Inventories (List of Inventories)
    property ItemBars (List of ItemBars)
    property activeInventory (Inventory)
    property activeItemBar (Inventory)
    property selectedSlot (InventorySlot)
    function setSelectedSlot(InventorySlot)
    function getSelectedSlot()

    function initialize() {
        // If you have some game state, use that data to fill Inventories and ItemBars accordingly. 
        // If not, create empty Inventories and ItemBars:
        this->activeItemBar = new Inventory(null, 10);
        this->activeInventory = new Inventory(null, 24, 6);
    }
}

If you want to add an Item to the Inventory:

Inventory->addItem(Item);

If you want to draw an Inventory (i.e. an inventory in your sense or an itembar):

Inventory->draw(position, sprite);
\$\endgroup\$

You must log in to answer this question.

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