This is my first post on the forum since I really can’t come with a clean solution and I didn’t find any hints to work around.
I am currently working on my Objects System. Objects are Equipments with stats on them to upgrade player stats, pretty basic.
Example : You can equip a Ring with : +2 moveSpeed / +50 Max Life / +5 Shot Damage.
But I want to make Unique Items with unique features on them.
Examples :
You can equip a Ring with: “Shop Items/Spells cost 20% less”
You can equip an Artifact with: “Bosses have now 10% less Life”
My problem : How to manage unique features like that without having to especially check for them everywhere. I find it extremely dirty and unmaintainable to have in your shop code or on your boss spawner code something like :
I have a spells system based on adding components to a base spell to make variations but here with objects, features I want to implement are so different from one an other that I can’t find a clean way to implement it.
My “”“best”“” solution : Categorize feature with Type such as
Then in the base code of these entities, check at the begining if player have bonuses of Type X and execute them all.
I think it generalize the process a little bit more but it’s definitly not that good looking and it would reduce creativity because of the mandatory categorisation.
If you have even a small hint I could work my mind around, it would be great !
These things (inventory, shop systems, character customization, dialog tree systems, crafting, etc) are fairly tricky hairy beasts, definitely deep in advanced coding territory.
Inventory code never lives “all by itself.” All inventory code is EXTREMELY tightly bound to prefabs and/or assets used to display and present and control the inventory. Problems and solutions must consider both code and assets as well as scene / prefab setup and connectivity.
Inventories / shop systems / character selectors all contain elements of:
a database of items that you may possibly possess / equip
a database of the items that you actually possess / equip currently
perhaps another database of your “storage” area at home base?
persistence of this information to storage between game runs
presentation of the inventory to the user (may have to scale and grow, overlay parts, clothing, etc)
interaction with items in the inventory or on the character or in the home base storage area
interaction with the world to get items in and out
dependence on asset definition (images, etc.) for presentation
Just the design choices of such a system can have a lot of complicating confounding issues, such as:
can you have multiple items? Is there a limit?
if there is an item limit, what is it? Total count? Weight? Size? Something else?
are those items shown individually or do they stack?
are coins / gems stacked but other stuff isn’t stacked?
do items have detailed data shown (durability, rarity, damage, etc.)?
can users combine items to make new items? How? Limits? Results? Messages of success/failure?
can users substantially modify items with other things like spells, gems, sockets, etc.?
does a worn-out item (shovel) become something else (like a stick) when the item wears out fully?
etc.
Your best bet is probably to write down exactly what you want feature-wise. It may be useful to get very familiar with an existing game so you have an actual example of each feature in action.
Once you have decided a baseline design, fully work through two or three different inventory tutorials on Youtube, perhaps even for the game example you have chosen above.
Breaking down a large problem such as inventory:
If you want to see most of the steps involved, make a “micro inventory” in your game, something whereby the player can have (or not have) a single item, and display that item in the UI, and let the user select that item and do things with it (take, drop, use, wear, eat, sell, buy, etc.).
Everything you learn doing that “micro inventory” of one item will apply when you have any larger more complex inventory, and it will give you a feel for what you are dealing with.
Breaking down large problems in general:
The moment you put an inventory system into place is also a fantastic time to consider your data lifetime and persistence. Create a load/save game and put the inventory data store into that load/save data area and begin loading/saving the game state every time you run / stop the game. Doing this early in the development cycle will make things much easier later on.
Some of these can be expressed as player stats. For example, you could have a stat that’s linked to “purchase prices”, starting at 100%, which the ring adjusts and shops read from. This also means that in a coop game different players would have different prices at the same shop, as it should be in this case. You can also hide this behind a charisma mechanis or something, but the idea remains the same: make it part of the players stats. In a similar fashion, this allows you to adjust prices dynamically for other reasons, such as reputation or relations.
Some effects on enemies can be handled in a similar way. The player could have a list of “effects” (or “debuffs”) of varying normal C# types, which can be apply()ed to enemies/bosses upon combat start. Kurts post about scriptable objects is probably also helpful for this.
The systems you need always depend on the specific effects you can think of. For the ones mentioned, the above would be roughly how i would handle it. This should also work in coop. There is no one-fits-all-sizes type of solution for these kinds of systems, so depending on the type of unique effect you may need to introduce new ways of handling them.
So it’s really how you just calculate the overall general consensus, on how things are at due to variables.
Unless you mean how to implement those variables? Then in that case, there are many ways, all depending on how you have your code structure setup. One way I can think off the top of my head, would be have these variables as static, but if you’re not used to playing around with static variables, it can cause quite the headache.
I would suggest watching several tutorials, not just one, to get an overall idea of how you might want your game to be structured. As I’m sure each tutorial you watch will be a completely different way of doing things. Which all can be narrowed down to user preference, or performance.
So sorry I’m so vague with my response, but it is kind of a huge question you’re asking
Thanks for the readings and videos. Of course I’m already using SOs and will use SOs to manage objects and modifiers. Even with specials features, I understand how to implement it data wise. Your D&D comparaison make me think around it the same as a GM launching an event. How/where to implement this event that will impact a small part of the game.
Having Stats variables would work but for example if I have 100 differents Modifers/Features I will have 100 differents PlayerStats variables and 100 differents places where they are used. It can rapidly become a mess don’t you think ?
I don’t get if it’s sarcasm or not, is there a problem in the wording or something ?
No problem with your answer (it pretty much joins what Yoreki said btw), I know my problem is not small and not that easy to explain. I already watched a lot of tutorials but they always stay at the surface of said system. Implementing Stats system with SOs ect is OK but similar system I want to do seems to be less talked or not talked at all.
!(Visionneuse images - Noelshack - https://image.noelshack.com/fichiers/2023/31/6/1691263535-sans-titre.png]
How to not be force to hardcode the interaction directly in the System ? It bugs my mind so much, so frustrating
Maybe it’s not possible to have a general system managing the majority of unique feature like you said. Maybe I’m kind of forced to have code here and there in a system, implementing features case by case.)
Not every last one would be a player stat, that was just one example. In the end this information needs to be somewhere, the rest is just about how we work with it to keep it maintainable. While i dont want to encourage putting every little unrelated detail into one script, a lot of these effects can be expressed to be a part of the player (charisma example). Keep in mind that you can group related functionality into different scripts, such as a social interaction component which handles npc related effects, and offers functions to, for example, calculate discounts.
You can however also handle positive effects more similar to boss debuffs, by adding them to some sort of list that is part of the player. While there can be hundreds of effects, any given character usually only has a hand full of them, so even if you iterated some kind of list for each npc interaction that wouldnt be a huge deal.
Genres to look into for examples on effect systems would be ARPGs or even just card games, as those both need rather flexible systems to handle all kinds of unique effects. Im not sure how many good sources exist in that area tho.
No, I generally meant I never thought of having such ability. I guess games I’m used to always make it harder on you, so a debuff of “bosses are 20% harder” is more of what I think. Except Skyrim with restoration glitch, the only time I got to splurge without modding, lol…
I personally don’t use SO, I’m more class/singleton/static based. So if I were making something, like what you reference, those stats/abilities would just be within my Player.cs. And just as I mentioned them, “playerShopCostModifier” or “playerBossDamageModifier”, etc…
And then within classes that need those variables, they could easily see and calculate them in, to said shop or boss I would be fighting. Easy-peasy-lemon-squeezy.
Which if I had to guess, you’re probably not having any issues with that. You probably mean to ask “how would you implement them so those variables change?”. I would simply add, or re-calculate any attributed variables within the “equip(Item)” function, and the same for un-equipping.
Hmmm, actually I would have it as a public function within said “Item”. Not only one to “AddPlayerStatics()” but also “RemovePlayerStatistics()”. So equipping and un-equipping the item would just call the relevant function. But, as I say it, I can easily see where things could go wrong, so it would still need a complete re-calc…
In that case, again, my code structure wouldn’t be bothered, as my player would have a List<Item> equippedItemInventory, to which is a parent class, and iterating through each child item to add each different buff/debuff wouldn’t cause any errors. If doing a full statistic reset. And obviously it wouldn’t be a huge number of classes to iterate through, so no performance drain.
Yeah, I’d probably do a full statistic reset each equip/un-equip regardless…
You can treat them just like spell effects, some are temporary, others permanent, and others depend if you are wearing the item or not. For example with the price you get a base price then the price modifier effect which can be positive or negative.
You probably need a Dictionary if you don’t want to manage 100 different modifiers for your character.
So I played around with my idea, and what I came up with looks like this:
public class Player : MonoBehaviour
{
public float buyModifier;
public float sellModifier;
public float damageBossModifier;
public List<Items> equippedBuffItems = new List<Items>();
void CalcBuffs()
{
ResetItemBuffs();
if (equippedBuffItems.Count > 0)
{
for (int i = 0; i < equippedBuffItems.Count; i++)
{
buyModifier += equippedBuffItems[i].costBuyModifier;
sellModifier += equippedBuffItems[i].costSellModifier;
damageBossModifier += equippedBuffItems[i].damageBossModifier;
}
}
}
void ResetItemBuffs()
{
buyModifier = 0;
sellModifier = 0;
damageBossModifier = 0;
}
}
Made just some random float placements:
public class Items : MonoBehaviour
{
public int quality;
public float costSellModifier;
public float costBuyModifier;
public float damageBossModifier;
public void SetQuality()
{
quality = Mathf.RoundToInt(Random.Range(1, 100));
}
public float SetPercentageModifier(int amount)
{
return amount / 100f;
}
}
And two rings(Items):
public class BossRing : Items
{
void Awake()
{
SetQuality();
damageBossModifier = SetPercentageModifier(quality);
name = "Ring of Pucker";
}
}
public class PriceRing : Items
{
void Awake()
{
SetQuality();
costBuyModifier = -SetPercentageModifier(quality);
costSellModifier = SetPercentageModifier(quality / 2);
name = "Ring of Bob Barker";
}
}
The general consensus, or well the bad news, would be you’d have to type the same modifier 3 times in Player.cs, once more in Items.cs, and again for whatever sub-Item you have said variable in. Basically a tedious setup for each new implementation of a ability/buff.
But the good news, is you won’t have to worry about any variables glitching out with equip/unequip, and can safely call shopItem_.cost = basePrice + player.costBuyModifier * basePrice; without ever worrying there’s a way to glitch said price reduction._ And/or easy modifications to each item if chosen to give more or less properties/abilities. But either way, you’re gonna have to type out all the mods no matter which way you go about things.
This feels like it just needs some overarching, perhaps semi-global system that some effects can subscribe themselves to, and be looked for by other objects.
For example, when you equip an artifact that reduces boss health, you subscribe this effect to the global effect system. And whenever a boss spawns, they can check for any effects of this type, and if present, let the effect modify them in some way.
You could probably make it all the more general, but just giving theses effects a CanEffectObject method, and when any object when it comes into scope, can just throw themselves at the system and let themselves be modified without too much manual handling on their own end.
@wideeyenow_unity@dlorre Yes the Equipment part / Aplying Stats modifier to the player is near what I planned to do. It will stay clean as long as the target of these modifiers are THE PLAYER.
@spiney199 I think this is probably the best general solution so far.
Thinking of it I figured out that I will obviously have a finite number of systems in the game (Player, Monsters, Shop, Level Generation, …) and “Sub-system” inside them like Player having an attack system, a health system, ect.
So Having a Publish/Subscribe pattern is maybe the solution. For example on the “Shop Items/Spells cost 20% less” I need to separate my shop system into
UI part (already done)
RandomLootCalculator part (already done)
Loot price calculator (to do)
Other part I don’t think of
All these systems have a subscriber that will be triggered when the ring containing the Loot price calculator modifier is equipped.
Then when I enter the shop system (Here when I open the shop), looping through every effect whitch are registered and applying the effect.
If have want to make an effect “Better quality Loot in shop and Chest” then the item will trigger the RandomLootCalculator System Subscriber to register the effect. Effect that will be called when the RandomLootCalculator is working.
And for ultra specific effect like “You can reroll shop loot” => You have to add a button to the UI to reload the shop loot, for this case I think you don’t have a lot of choices to hardcode the mechanic directly in the UI
if(player.CanResetLoot()){
// Add button to the UI
}