Zara Survival Engine (C#)

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 :wink:

More info here:

Zara wiki:

Zara Rust:

Unity demo screenshot (single person scenario):

.Net 6 demo screenshot (multiple persons scenario):

5 Likes

Engine update: it now fully supports saving and loading of its entire state :wink:

1 Like

Github update: wiki is now complete :slight_smile:

1 Like

Engine update: hypothermia and hyperthermia are now implemented, the engine is now complete.

1 Like

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.

2 Likes

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 :wink:

1 Like

Hi, how can i use these systems?

You can download example Unity project on Zara github page [ https://github.com/vagrod/zara ], and also take a look at Zara wiki for more detailed technical info [ Home · vagrod/zara Wiki · GitHub ]
Or you can import Zara from Unity AssetStore [ Zara Survival Engine | Systems | Unity Asset Store ]

1 Like

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!

1 Like

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.

public class UnityItemDefinition : MonoBehaviour
{
    public InventoryItemBase itemBase;
    private ArianaController _gc;

    void Start()
    {
        _gc = GameObject.Find("Player").GetComponent<ArianaController>();
    }

    void OnTriggerStay(Collider col)
    {
        if (col.transform.tag == "Player")
        {
            if (Input.GetKeyDown(KeyCode.F))
                Pickup();

        }
    }

    public void Pickup()
    {
        var inv = _gc.Inventory as InventoryController;
        inv.AddItem(itemBase);
        //_gc.invPlayer.AddNewItem(myItem);
        foreach (var item in inv.Items)
        {
            Debug.Log("Item: " + item.Name);
        }
        Destroy(transform.gameObject);
    }
}

Hi! Happy Holidays!)
Thank you for your response, I am really happy that you found Zara helpful for your project! :wink:
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 :slight_smile:

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(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

 return new List<IInventoryItem>(new[]
                {
                    (IInventoryItem) new EpinephrineSolution { Count = 5 },
                    (IInventoryItem) new DisinfectingPellets { Count = 25 },
                    (IInventoryItem) new Pin(),
                    (IInventoryItem) new Acetaminophen { Count = 20 },
                    (IInventoryItem) new Antibiotic { Count = 30 },
                    (IInventoryItem) new Aspirin { Count = 50 },

                    (IInventoryItem) new MorphineSolution { Count = 5 },
                    (IInventoryItem) new Loperamide { Count = 15 },
                    (IInventoryItem) new Sedative { Count = 35 },
                    (IInventoryItem) new Oseltamivir { Count = 16 },

                    (IInventoryItem) new AntiVenomSolution { Count = 5 },
                    (IInventoryItem) new Bandage { Count = 10 },
                    (IInventoryItem) new AtropineSolution { Count = 5 },
                    (IInventoryItem) new NeedleAndThread(),

                    (IInventoryItem) new DoripenemSolution { Count = 5 },
                    (IInventoryItem) new AntisepticSponge { Count = 50 },
                    (IInventoryItem) new Plasma { Count = 2 },
                    (IInventoryItem) new SuctionPump { Count = 1 },
                    (IInventoryItem) new EmptySyringe { Count = 15 },
                    (IInventoryItem) new BioactiveHydrogel { Count = 3 }
})

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:

public class ElbaTreeDescription : NatureItemDesctiptionBase
    {

        public ElbaTreeDescription()
        {
            Name = "ElbaTree";
            CanBeUsedWithoutTool = true;
            AvailableToolsToPerformAction = new List<string>(new[]
            {
                InventoryController.CommonTools.Knife,
                InventoryController.CommonTools.SharpDebris,
                InventoryController.CommonTools.SharpenStone
            });
        }

        public override List<IInventoryItem> GetItemsFromObject(string toolName)
        {
            if (toolName == InventoryController.CommonTools.Knife || toolName == InventoryController.CommonTools.SharpDebris || toolName == InventoryController.CommonTools.SharpenStone)
                return new List<IInventoryItem>(new[] { new ElbaTreeSticks() });

            if (toolName == InventoryController.CommonTools.Hand)
                return new List<IInventoryItem>(new[] { new ElbaTreeLeaves { Count = 7 } });

            return null;
        }

    }

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 :wink:

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 :wink: But it is based on 15 years of enterprise coding experience :smile:

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))

RaycastController
RaycastController is used by GameController and checks if player is pointing to some object for further interaction.
This class has Check method that is called on every FixedUpdate, and sets LastResult property to a result of the check.
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.
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.

NatureItemDesctiptionBase implements IObjectDescriptionBase, and ObjectDescriptionScriptBase has ObjectDescription property that is IObjectDescriptionBase too, so IObjectDescriptionBase is the end-result of raycast check.

InteractionController
InteractionController checks RaycastController's LastResult property on every Update, and if there is something, then it is starting to analyze interaction.

IObjectDescriptionBase has this set of properties:

Name of object being interacted
Description of the object
CanBeUsedWithoutTool to determine, whether this item can be "grabbed" bare hands
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.
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.
GetItemsFromObjectFunc function that must return one or more IInventoryItems which are a result of interaction with this object using a given tool.
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.
After the successful interaction, InteractionController will call AddItem method of InventoryController for every acquired item (see Inventory Engine).

Prefab Interaction Scripts
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.

Interaction with Vegetation (trees or rocks)
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:
[Name of a vegetation item]Description
i.e. AkanaTreeDescription

NatureItemDesctiptionBase class contains HideObject method, so you can hide particular tree or stone after interaction:

public override List<IInventoryItem> GetItemsFromObject(string toolName)
{
    if (toolName == InventoryController.CommonTools.Hand)
    {
        HideObject();
        return new List<IInventoryItem>(new[] {new Stone() {Count = 1}});
    }
    return null;
}
Class also should be in Assets.Code.Interaction.Descriptions namespace.

Interaction Actions Localization
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:
[Interaction script Name]-[Tool name]
for example

<String name="SmallRock-Hand" value="Collect" />
<String name="AkanaTree-Knife" value="Collect sticks" />
[Interaction script Name] is a Name that was set to IObjectDescriptionBase. Name, like here:

public SmallRockDescription()
{
    Name = "SmallRock";
    CanBeUsedWithoutTool = true;
}

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 :smile:

Small update. I don’t know how appropriate to write here news like this, but I’ll try anyway :wink:
Very simple Zara CRYENGINE demo is now available on a Zara github (here) :slight_smile:

@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!

2 Likes

@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! :slight_smile:

1 Like

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.

2 Likes

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 :wink:

Could not run it yet but sounds really good

Video tutorial is now completed, you can find it on YouTube: playlist link. :slight_smile:

1 Like