Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Zara Survival Engine (C#)

Discussion in 'Assets and Asset Store' started by Vagrod, Oct 16, 2020.

  1. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Hi!
    Couple of years ago I tried to gather people to make a survival game on an alien planet (inspired by Robinson's Requiem), but I did not find anyone to help, and for a single person that project was too much -- and I finally decided to extract the survival engine itself (that was in active development by me for a year already) and opensource it.

    It is written in independent C# and has demos for
    - Unity
    - CRYENGINE
    - Godot
    - Flax
    - .Net 6

    Video tutorial: YouTube playlist, 7 episodes

    Zara will be useful for you if you want your game to have weather-aware health control with ton of intertwined parameters, sleeping, fatigue, diseases (flu, food poisoning, venom poisoning, angina and so on), injuries (cuts, fractures), food spoiling, water disinfecting, inventory, crafting, clothes with different water/cold resistance levels and more. On a surface, it is really easy to set up and use.

    Saving/Loading of the engine state is fully supported (including the entire health status, active diseases, injuries, treatment progress, entire inventory, clothes, appliances). The size of a serialized state object is about 14K (I measured non-formatted JSON). Saving engine state is taking about 35ms, loading -- about 30.

    I hope this engine will help somebody in their projects ;)

    More info here:
    https://github.com/vagrod/zara

    Zara wiki:
    https://github.com/vagrod/zara/wiki

    Zara Rust:
    https://github.com/vagrod/zara-rust

    Unity demo screenshot (single person scenario):


    .Net 6 demo screenshot (multiple persons scenario):
     
    Last edited: Nov 22, 2021
  2. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Engine update: it now fully supports saving and loading of its entire state ;)
     
    Roman_Keivan likes this.
  3. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Github update: wiki is now complete :)
     
    Roman_Keivan likes this.
  4. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Engine update: hypothermia and hyperthermia are now implemented, the engine is now complete.
     
    Roman_Keivan likes this.
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,098
    You seem to bump your thread quite often. I think this should be moved to the Work In Progress or the Made With Unity sub forum. Your posts don't seem to revolve around actual scripting topics. I'm not sure if there are community moderators who can move threads around.
     
    Roman_Keivan and SparrowGS like this.
  6. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Actually, the engine is complete now, and I am not planning to update this thread, unless some big change happens, so I think it can stay here with no issues ;)
     
    Roman_Keivan likes this.
  7. guiseppesuantro

    guiseppesuantro

    Joined:
    Oct 31, 2020
    Posts:
    11
    Hi, how can i use these systems?
     
  8. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Roman_Keivan likes this.
  9. Saiga308

    Saiga308

    Joined:
    Jun 14, 2015
    Posts:
    46
    Hello Vagrod,

    I've really enjoyed using this engine for my game. It is very well written and very powerful! I'm using it on my survival character and everything seems to be working well, vitals, modifying movement states, inventory, etc.

    I have a visual inventory UI built and created a MonoBehaviour script to attach to a 3D object in game that can be picked up and later equipped, used, etc. It works, but this is the only area where I could use a little guidance if you don't mind. The way I've done it is creating a script for each unique inventory item that can be picked up which is not very efficient. I want to make a single script that allows me to select the object type from a dropdown (enum) or even be typed in as a string so I don't need a MonoBehaviour script for every unique item. I'll eventually figure it out, but was hoping you could point me in the right direction. Any help would be greatly appreciated.

    Happy Holidays!
     
    Vagrod likes this.
  10. Saiga308

    Saiga308

    Joined:
    Jun 14, 2015
    Posts:
    46
    I just added MonoBehaviour to InventoryItemBase which has got me a lot closer. Now I can attach my MonoBehaviour UnityItemDefinition script, and the item script (i.e. Meat) and it works.

    Code (CSharp):
    1. public class UnityItemDefinition : MonoBehaviour
    2. {
    3.     public InventoryItemBase itemBase;
    4.     private ArianaController _gc;
    5.  
    6.     void Start()
    7.     {
    8.         _gc = GameObject.Find("Player").GetComponent<ArianaController>();
    9.     }
    10.  
    11.     void OnTriggerStay(Collider col)
    12.     {
    13.         if (col.transform.tag == "Player")
    14.         {
    15.             if (Input.GetKeyDown(KeyCode.F))
    16.                 Pickup();
    17.  
    18.         }
    19.     }
    20.  
    21.     public void Pickup()
    22.     {
    23.         var inv = _gc.Inventory as InventoryController;
    24.         inv.AddItem(itemBase);
    25.         //_gc.invPlayer.AddNewItem(myItem);
    26.         foreach (var item in inv.Items)
    27.         {
    28.             Debug.Log("Item: " + item.Name);
    29.         }
    30.         Destroy(transform.gameObject);
    31.     }
    32. }
     
    Last edited: Dec 24, 2020
  11. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Hi! Happy Holidays!)
    Thank you for your response, I am really happy that you found Zara helpful for your project! ;)
    I can describe how I did in-world items in the game that Zara was initially written for, maybe you'll find something useful in the text below :)

    I had a thing called RaycastController. It was a MonoBehaviour class that was checking if player points to an item that has some tag (or layer I don't remember exactly) that tells it that it is an interactable thing. As soon as player pointed to an interactable object, it remembered that object.
    I had also InteractionController that looked if RaycastController recognized any object as interactable, and if so, it asked object what tools can be used, and displayed an onscreen menu thing with only correct set of tools from the player's inventory. After that it waited for user to choose the tool. When player picks the tool, InteractionController passed selected tool name to the interactable object on which the action is taken place, and got list of inventory items in return. All those new gathered items are added to the inventory.
    I had an abstract MonoBehaviour class ObjectDescriptionScriptBase that had a set of properties like list of names of tools that can be used to interact with the item. For example, "Tree" interactable object can produce leafs if interacted with bare hands, and branches if interacted with the knife. It also contained a flag to describe if this item can be interacted without tools (like moss on a rock, it can be scraped only with the knife).
    ObjectDescriptionScriptBase had a method GetItemsFromObject that was triggered when user interacted with an item using some tool (or bare hands). In this method you could describe what player will gain after the interaction. For example: you have a tree, and player chose to use knife to interact with the item. Player clicked (or pressed E or something), and this method is called (by the InteractionController as I described above). This Func receives argument string "Knife", and checks: okay, player used Knife, we must return five sticks [new List<IInventoryItem>(new[] {Stick{Count=5})].
    Or you have an item called Medkit. This function on interaction with this item will return something around twenty items -- the entire medical center
    Code (CSharp):
    1.  return new List<IInventoryItem>(new[]
    2.                 {
    3.                     (IInventoryItem) new EpinephrineSolution { Count = 5 },
    4.                     (IInventoryItem) new DisinfectingPellets { Count = 25 },
    5.                     (IInventoryItem) new Pin(),
    6.                     (IInventoryItem) new Acetaminophen { Count = 20 },
    7.                     (IInventoryItem) new Antibiotic { Count = 30 },
    8.                     (IInventoryItem) new Aspirin { Count = 50 },
    9.  
    10.                     (IInventoryItem) new MorphineSolution { Count = 5 },
    11.                     (IInventoryItem) new Loperamide { Count = 15 },
    12.                     (IInventoryItem) new Sedative { Count = 35 },
    13.                     (IInventoryItem) new Oseltamivir { Count = 16 },
    14.  
    15.                     (IInventoryItem) new AntiVenomSolution { Count = 5 },
    16.                     (IInventoryItem) new Bandage { Count = 10 },
    17.                     (IInventoryItem) new AtropineSolution { Count = 5 },
    18.                     (IInventoryItem) new NeedleAndThread(),
    19.  
    20.                     (IInventoryItem) new DoripenemSolution { Count = 5 },
    21.                     (IInventoryItem) new AntisepticSponge { Count = 50 },
    22.                     (IInventoryItem) new Plasma { Count = 2 },
    23.                     (IInventoryItem) new SuctionPump { Count = 1 },
    24.                     (IInventoryItem) new EmptySyringe { Count = 15 },
    25.                     (IInventoryItem) new BioactiveHydrogel { Count = 3 }
    26. })
    So for every interactable item type I had separate script based on ObjectDescriptionScriptBase that describes how player can interact with the object, and what will happen after the interaction.
    Extinguished campfire can give you ash for example, that can be used to treat wounds.
    Simple interactable object example:
    Code (CSharp):
    1. public class ElbaTreeDescription : NatureItemDesctiptionBase
    2.     {
    3.  
    4.         public ElbaTreeDescription()
    5.         {
    6.             Name = "ElbaTree";
    7.             CanBeUsedWithoutTool = true;
    8.             AvailableToolsToPerformAction = new List<string>(new[]
    9.             {
    10.                 InventoryController.CommonTools.Knife,
    11.                 InventoryController.CommonTools.SharpDebris,
    12.                 InventoryController.CommonTools.SharpenStone
    13.             });
    14.         }
    15.  
    16.         public override List<IInventoryItem> GetItemsFromObject(string toolName)
    17.         {
    18.             if (toolName == InventoryController.CommonTools.Knife || toolName == InventoryController.CommonTools.SharpDebris || toolName == InventoryController.CommonTools.SharpenStone)
    19.                 return new List<IInventoryItem>(new[] { new ElbaTreeSticks() });
    20.  
    21.             if (toolName == InventoryController.CommonTools.Hand)
    22.                 return new List<IInventoryItem>(new[] { new ElbaTreeLeaves { Count = 7 } });
    23.  
    24.             return null;
    25.         }
    26.  
    27.     }
    So all I needed to do to add a new interactable thing -- is to create a prefab, create ObjectDescriptionScriptBase-based class, describe in it behaviour I want, and add this class to a prefab, that simple ;)

    This approach lets you describe other interactions as well, like interaction with a campfire. You can light it, extinguish it, collect ashes -- all inside GetItemsFromObject, just checking for a campfire state and a tool used. Interaction can return null and just do a thing, for example, light it up. Actually, I needed to name this method simply OnInteraction(string toolName) )

    One script for all items -- is handy, but trust me, as your game grows, this "unversal" script will grow with it as well, and at some point it will become a mess. The smaller pieces of code you use the better. It is less convenient to create a separate script for every interactable item type, but in a long run it will save you hours of trying to untangle the mess) It's just my opinion of course ;) But it is based on 15 years of enterprise coding experience :D
     
    Last edited: Dec 25, 2020
  12. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Here I found a wiki page from my private game wiki, maybe you'll find some useful information in it)
    Copy somewhere to read properly))
    Code (CSharp):
    1. RaycastController
    2. RaycastController is used by GameController and checks if player is pointing to some object for further interaction.
    3. This class has Check method that is called on every FixedUpdate, and sets LastResult property to a result of the check.
    4. If object that was determined has VegetationStudio's RuntimeObjectInfo script, it will look at VegetationItemInfo.UserInfo (NatureItemDesctiptionBase) property and will set LastResult to it. If no UserInfo provided, RaycastController will try to create instance of a plant/rock description using plant's name (VegetationItemInfo .Name) as described in Interaction with Vegetation (trees or other) section below. This logic is used to interact with the trees.
    5. If object does not contain RuntimeObjectInfo script, RaycastController will check for ObjectDescriptionScriptBase script and will set LastResult to it. This logic is used to interact with prefabs.
    6.  
    7. NatureItemDesctiptionBase implements IObjectDescriptionBase, and ObjectDescriptionScriptBase has ObjectDescription property that is IObjectDescriptionBase too, so IObjectDescriptionBase is the end-result of raycast check.
    8.  
    9. InteractionController
    10. InteractionController checks RaycastController's LastResult property on every Update, and if there is something, then it is starting to analyze interaction.
    11.  
    12. IObjectDescriptionBase has this set of properties:
    13.  
    14. Name of object being interacted
    15. Description of the object
    16. CanBeUsedWithoutTool to determine, whether this item can be "grabbed" bare hands
    17. RequiredTool that describes a tool required to interact with object. If player has no such tool, InteractionController will not allow user to interact with object.
    18. AvailableToolsToPerformAction describes a list of tools that can be used to interact with object. In this case user should select a particular tool before interaction.
    19. GetItemsFromObjectFunc function that must return one or more IInventoryItems which are a result of interaction with this object using a given tool.
    20. CanToolBeUsedFunc function in which you can determine, can particular tool be used at the moment or not. For example, in water description this function will "hide" all full water vessels from available interactive tools in UI.
    21. After the successful interaction, InteractionController will call AddItem method of InventoryController for every acquired item (see Inventory Engine).
    22.  
    23. Prefab Interaction Scripts
    24. Interaction with prefabs are described via scripts that are derived from ObjectDescriptionScriptBase class. Every prefab that is supposed to be interactive should have such a script on it.
    25.  
    26. Interaction with Vegetation (trees or rocks)
    27. Interaction with VegetationStudio's foliage and rocks should be done through classes derived from NatureItemDesctiptionBase class. This class's name should be constructed the following way:
    28. [Name of a vegetation item]Description
    29. i.e. AkanaTreeDescription
    30.  
    31. NatureItemDesctiptionBase class contains HideObject method, so you can hide particular tree or stone after interaction:
    32.  
    33. public override List<IInventoryItem> GetItemsFromObject(string toolName)
    34. {
    35.    if (toolName == InventoryController.CommonTools.Hand)
    36.    {
    37.        HideObject();
    38.        return new List<IInventoryItem>(new[] {new Stone() {Count = 1}});
    39.    }
    40.    return null;
    41. }
    42. Class also should be in Assets.Code.Interaction.Descriptions namespace.
    43.  
    44. Interaction Actions Localization
    45. Every interaction action can be localized. For example, when you pick at water with a flask, you will see 'refill' hint; if you pick at a tree with a knife, you will see 'collect branches' hint. All such hints are stored in Actions section of every localization file (see Localization) if a format:
    46. [Interaction script Name]-[Tool name]
    47. for example
    48.  
    49. <String name="SmallRock-Hand" value="Collect" />
    50. <String name="AkanaTree-Knife" value="Collect sticks" />
    51. [Interaction script Name] is a Name that was set to IObjectDescriptionBase. Name, like here:
    52.  
    53. public SmallRockDescription()
    54. {
    55.    Name = "SmallRock";
    56.    CanBeUsedWithoutTool = true;
    57. }
     
    Last edited: Dec 25, 2020
  13. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    I just realized -- ObjectDescriptionBase is still a part of Zara, I thought I cleaned it up and removed it when I was extracting the engine from the game :D
     
  14. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Small update. I don't know how appropriate to write here news like this, but I'll try anyway ;)
    Very simple Zara CRYENGINE demo is now available on a Zara github (here) :)
     
    Last edited: Jan 3, 2021
  15. Saiga308

    Saiga308

    Joined:
    Jun 14, 2015
    Posts:
    46
    @Vagrod Awesome! Thank you so much for pointing me in the right direction. With your help I was able to get it all working the way I wanted. I ended up adding a description, max stack size and icon to the InventoryItemBase to suit my projects needs. Now I'm just tweaking my Inventory UI and Vitals UI a little here and there, but they are fully functional with the exception of drag/drop inventory functionality, I'll get to that once I have more of a final UI layout. I used the UI-Extensions to render a UI LineRenderer for the heart rate. Again, thank you for your help and this amazing asset! Happy New Year!
     
    Roman_Keivan and Vagrod like this.
  16. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    @Saiga308, that's awesome! Glad I was able to help a little)
    Just a tip, if you will be adding some properties to the base inventory classes, and if these properties are designed to contain some runtime data (state-related stuff), do not forget to add these properties (or even local fields) to the state management logic for them to save and load correctly (just like in WaterVessel, it saves and loads its custom properties -- boil time, is safe, doses left, etc -- just like here).
    Happy New Year and thank you for your feedback! :)
     
    Roman_Keivan likes this.
  17. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Release 1.05 is out! Unity Asset will be updated soon as well.

    Here is a complete changelog. Core highlights are:
    - Carrying weight now affects fatigue and stamina when walking/running or swimming
    - Wind speed affects drying rate
    - Inventory TryUse improvements
    - New game events
    - Release package includes offline pdf documentation

    If you already made changes to Zara code in your project, I have a complete list of files changed since 1.04, so you can carefully patch your sources if you need to.
     
    Last edited: Jan 10, 2021
    HeadClot88 and drcfrx like this.
  18. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Release 1.06 is out! Unity Asset is already updated.

    Version changes are:
    - WasteItem bug fixed for food items when item count can be set to negative value.
    - TryUse bug fixed when after wasting food item inventory weight and availability cache was not refreshed
    - ClothesGroups can now handle multiple-persons scenario.

    This release introduces no breaking changes.

    As usual, here is a complete list of files changed since 1.05, so you can carefully patch your sources if you need to.

    In addition, full-featured Zara now available in Rust! Visit github or crates.io to learn more ;)
     
    Last edited: Mar 5, 2021
  19. unity_041F47CE810B7C99B82D

    unity_041F47CE810B7C99B82D

    Joined:
    Aug 13, 2021
    Posts:
    1
    Could not run it yet but sounds really good
     
  20. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Video tutorial is now completed, you can find it on YouTube: playlist link. :)
     
    mgear likes this.
  21. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    .NetCore demo updated to .Net 6 :)
     
  22. jh092

    jh092

    Joined:
    Sep 2, 2018
    Posts:
    55
    Hello Vagrod, sincere thanks for making this available to the broader community. I have spent some time understanding how it all works, thank you.

    May I ask please, with regard to the Inventory I'm a bit stuck. From what I can tell, the Inventory system assumes that all inventory items are being carried by the player, therefore the weight of all items becomes the weight the player is carrying. My use-case however requires inventory to be either carried by the player (for example in a backpack) or stored separately (for example in a cupboard in a house). My inventory would contain many things including very heavy items like planks, rocks, large volumes of canned food etc.

    I absolutely want to use the fabulous inventory features that come with the engine such as food perishing, water vessel weights and so on, but I can't see how to achieve this for my scenario. I thought perhaps I could have multiple "Players", each with its own inventory, would that make sense?

    For example, my actual player would not have any inventory. The "backpack player" would contain just inventory items in the backpack, the "cupboard player" would contain just inventory items in the cupboard and so on. Would this work? Could I then perform tasks such as moving inventory from the cupboard to the backpack for example? Given that cupboards don't move I'm guessing the weight wouldn't matter.

    I think too it might end up being a powerful feature if done this way, for example I could have a "fridge player" where the temperature is very low and so any food inside would spoil less quickly.

    So then, thanks for reading :) Does the above make sense or is there a more obvious way to achieve what I need with multiple inventories?

    Many thanks and best wishes from Australia

    John
     
    Vagrod likes this.
  23. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Hi, John! Sorry for the late response -- I already used to lack of activity here, and was not checking this thread often.
    The default implementation for inventory is bound to the player, yes. In case when player can have his items in the external storages, you can simply add one more property to InventoryItemBase, like StorageId, and alter function that calculates weight to ignore all items that have non-empty StorageId. That way, all items will still be saved and loaded with player as per usual, but their weight won't be calculated for the player.
    And in UI same thing -- you show only empty StoradeId-items as your player's items, and for a crate, you show items with its StorageId.

    You can have, for example, a method CalculateWeightForStorage that receives storageId and returns a sum like Items.Where(x => x.StorageId == storageId).Sum(x => x.Weight);

    When altering base classes like InventoryItemBase, do not forget to add new field(s) in state management mechanism, as described, for example, here

    You may also alter the GetByName method, so it receives storageId parameter. Basically, with this approach, item Name no longer is unique, item is described uniquely in this case by a compound "name + storage id" key.

    That was the first idea that came to my mind, maybe (and most likely) there are another, better ways of doing this ;)
     
    Last edited: Feb 12, 2022
    Bleumion likes this.
  24. Atharif

    Atharif

    Joined:
    Jan 22, 2022
    Posts:
    1
    I found this and it seemed fantastic, I have a question, I have my own inventory system and I see that ZARA has theirs, how can I use it with my own inventory system, keeping the properties of its inventory as item life? o Can I transfer those features to another inventory to continue using ZARA?
     
  25. Vagrod

    Vagrod

    Joined:
    Aug 4, 2017
    Posts:
    82
    Hi! Thank you for the feedback!
    The answer is -- it depends. It depends on the exact implementation of your inventory system, but basically, it you implement IInventoryItem interface on your items, they shoud be ok to use with Zara.
    You can refer to the Zara wiki or this youtube video from Zata Tutorial.
    In any way, you wolud need to implement either IInventoryItem interface or InventoryItemBase class on your items in order to use those with Zara. Other details depend on, as I said, the actual implementation and functionality you want to achieve.