Game
After Sanctuary
18 days ago

Devlog #4

Hurt me plenty...


Hello Sanctuary seekers!

Sticking with the theme of AI, I'd like to share how we maim and kill AI in the After Sanctuary world.

Let's have a look through some code.

First up, we have an interface that all health scripts can implement.
An interface acts like a contract.
Every script which implements it has to implement its methods and variables.

		
			public interface IDamageable
{
    public bool IsDead { get;}
    void TakeDamage(float amount, GameObject attacker, bool canTakeLimbs = false);
    
}
		
	

The TakeDamage method takes in a damage amount, the GameObject which caused the damage and a bool which describes whether this type of damage can remove limbs and heads. It's set to false by default so we don't need to include it in our attack code unless we want to change it to true.

The advantage of inheriting from the IDamageable interface is that we can search the interface rather than searching for each script that inherits from it when we want to actually cause damage.

Different type of AI or destructible objects in the world may use different health scripts that do different things when met with the same conditions.

		
			 var health = hit.collider.GetComponent<IDamageable>();
                if (health != null)
                {
                    health.TakeDamage(25f, this.transform.root.gameObject, false);
                }
		
	

Here we get the IDamageable and cause some damage from one of the weapon scripts in the game.

Over in the ZombieHealth class this calls the take damage method which returns and does nothing if this AI agent is already dead.

If the agent is still alive, it depletes its health by the amount of damage passed into the method, and checks if the health is <0.
If it is, we call the Die method passing in the attacker gameobject.
We use the attacker gameobject to get the position of the attacker and add a little impulse to the ragdoll from that direction.

		
			 public void TakeDamage(float amount, GameObject attacker, bool canTakeLimbs = false)
    {
        if (IsDead) return;

        amount = Mathf.Max(0f, amount);
        currentHealth -= amount;

        if (logDamage) Debug.Log(quot;{name}: Took {amount} dmg from {(attacker ? attacker.name : "unknown")} -> {currentHealth}/{maxHealth}");

        if (currentHealth <= 0f)
        {
            Die(attacker);
        }
        else
        {
            //TODO: Implement knock back and hit reactions.
        }
    }

    void Die(GameObject attacker)
    {
        if (IsDead) return;
        IsDead = true;

        //  Stop AI from moving.
        if (clearPathOnDeath && mover) mover.Stop();
        if (disableMoverOnDeath && mover) mover.enabled = false;

        //  Stop both sensors
        if (disableVisionOnDeath && vision) vision.enabled = false;
        if (disableHearingOnDeath && hearing) hearing.enabled = false;

        //  Trigger death anim (optional) then immediately switch to ragdoll
        if (animator && !string.IsNullOrEmpty(deathTrigger))
            animator.SetTrigger(deathTrigger);

        // Switch to ragdoll (physics takes over; body remains)
        if (ragdoll) ragdoll.SetRagdoll(true, zeroVelocities: true);

        //  Disable CharacterController and any colliders.
        var cc = GetComponent<CharacterController>();
        if (cc) cc.enabled = false;
        if (disableThisCollider)
        {
            var col = GetComponent<Collider>();
            if (col) col.enabled = false;
        }

        //  Add a little impulse from attacker direction.
        if (addDeathImpulse && ragdoll)
        {
            Vector3 hitFrom = attacker ? attacker.transform.position : transform.position - transform.forward;
            Vector3 toZombie = (transform.position - hitFrom);
            Vector3 dir = toZombie.sqrMagnitude > 0.001f ? toZombie.normalized : (-transform.forward);
            dir.y += Mathf.Max(0f, impulseUpBoost);

            Vector3 point = transform.position + Vector3.up * 1.2f; // chest-ish
            ragdoll.AddImpulse(point, dir.normalized * impulseMagnitude, 0.3f);
        }

        // change layer/tag to "Corpse" etc. (commented; add if needed)
        // gameObject.tag = "Corpse";
        // gameObject.layer = LayerMask.NameToLayer("Corpse");

        if (logDamage) Debug.Log(quot;{name}: DIED");
    }
		
	

In the above Die() method, we stop movement, stop the sensors, we can play a death animation if we want to, The script has bools to control this feature but in this case we switch to ragdoll without playing an animation.
Disable the character controller and add the impulse we talked about earlier.
I can also change the physics layer of the zombie so the player can interact with it and loot it for example.
We can also log any events by turning on a bool in the inspector.

There's also a Revive() method that I could use but it's not used....yet...

		
			  public void Revive(float healthFraction = 1f)
    {
        IsDead = false;
        currentHealth = Mathf.Clamp(healthFraction, 0.01f, 1f) * maxHealth;

        var cc = GetComponent<CharacterController>();
        if (cc) cc.enabled = true;

        if (ragdoll) ragdoll.SetRagdoll(false, zeroVelocities: true);
        if (vision) vision.enabled = true;
        if (hearing) hearing.enabled = true;
        if (mover) mover.enabled = true;
    }
		
	

This just turns everything back on.

I think I've covered everything I wanted to.
If you have any questions please leave a comment and remember you can join our Discord server here: https://discord.gg/S8cCdyzB

Until next time sanctuary seekers,

Stay safe.



0 comments

Loading...

Next up

Devlog #2 World streaming.

Short intro of us 💕✌️

We're an eletro duo based in Seoul 🇰🇷 Heavily influenced by the 90s.

Our new album #Xennials is all about the nostalgia of that era 💽 CD listenin 📟 beeper beepin 💾 floppy disks floppin days 😎

Stream now! 🎧

https://open.spotify.com/album/3YwWhnHWVy5cA8XOpbaGRA?si=8E9awqU…

Did you know that today is #InternationalJazzDay? 🎷😎

No? 😯 Well, we couldn't pass up this date!

And what better to celebrate this day than #Pecaminosa #OST? 🎷

Let us know what you think about it and have a great weekend!

Bandana Dee the Dream Friend

Path of Kami: The Evolution of the Lore

I have added rewards for quests so that villagers can give you something in return for your hard work helping them. 🥳

I want to know - what's your favourite quest reward?

Microsoft Windows XP Unprofessional (windows logo prototype)

Why walk when you can jump?

brung back my old oc plus a new one

...they don`t have names yet tho

I mostly build 3D First Person Shooter game mechanics in Unity, and in this I'm just showing C4 mechanics I made for a little side project I am working on! :)