Drowning Script Only (NOT SWIMMING)

I asked this question in the Answers tab a week ago and haven’t got any reply so i figure that maybe someone here can help me out here.

Hi guys, I was wondering if anyone can please help me out with a script that triggers a component at a certain depth. Basically I’m trying to do a drowning script. I already have the swimming script that triggers with the collider of the player but I can’t do the same because I can’t add a collider to the camera. Basically I want a breath meter I created to trigger below a “Y” axis height and to reset once you go above the “Y” axis but I can’t figure out how to do this. Can some please help me with how I should declare the value and how to start this script? I can do the rest but I just don’t know how to do the y axis portion.

I will use 2 separate script. 1 for swimming (done) and 1 for holding the breath. once both re complete I’ll try to put them together.

A quick outline:

public float waterLevel;
public float drownTime;

private float drownTimer;

if (transform.position.y < waterLevel)  //Checks the y-position
{
    drownTimer += Time.deltaTime;
}
else
{
    drownTimer = 0;
}

if (drownTimer > drownTime)
{
    //Mercilessly drown the player character here (you monster)
}

It increments the time spent underwater when under the water level, and resets it to 0 when above it. If you spend more than drownTime under the water level, you call the drowning logic.

Is this what you’re looking for?

1 Like

Hmm that sort of works but i just realized that i can’t use y axis as well because i have different water section at different altitudes. I had an idea of using a whole new trigger box and placing it about 6 feet below the swimming trigger. With that i managed to make the player basically made the body (not the head) of the player enter a whole new trigger that triggers the underwater fiction of the hold breath script. Now i’m trying to figure out how i can make the meter regenerate when the player exits the collider. Maybe you can help me with that section? below are the two scripts i;m using to preform this task. If anyone has an easier way of doing this and would like to share it please do. I will appreciate it. Also please excuse my code if it’s kind of messy but I’m a C# noob.


Underwater Trigger:

///////////////////////////////////////////////////////////
///
/// Drowning trigger.
///
///////////////////////////////////////////////////////////

using UnityEngine;
using System.Collections;

public class DrowningTrigger : MonoBehaviour {
public DrowningScript _DrowningScript;

void OnTriggerStay(Collider player){
if (player.tag == “Player”) {
_DrowningScript.UnderWater ();
}
}

void OnTriggerExit(Collider player){
if (player.tag == “Player”) {
_DrowningScript.AboveWater ();
}
}
}


UI Scripts:

/////////////////////////////////////////////////////////
///
/// Drowning Script. (UI Meter)
///
/////////////////////////////////////////////////////////

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

[RequireComponent(typeof(AudioSource))]

public class DrowningScript : MonoBehaviour
{
Image fillImg;
public float timeAmt = 35;
float time;
public AudioClip DrownDeathSfx;
public AudioClip BreathSfx;

void Start()
{
fillImg = this.GetComponent();
time = timeAmt;
}

public void UnderWater(){
if (time > 0) {
time -= Time.deltaTime;
fillImg.fillAmount = time / timeAmt;
if (time <= 0) {
GetComponent ().clip = DrownDeathSfx;
GetComponent ().Play ();
}
}
}

public void AboveWater(){
fillImg.fillAmount = 35f; //sets the meter back to 35 but will start from last update when enter the water :frowning:
}
}

It’s easier if you set your variable ‘fillmg.fillAmount’ to be ‘time / timeAmt’ in the update Method.
Then in the ‘AboveWater’ method, simply do this to slowly regenerate your ‘time’ veriable:

private float regenSpeed = 0.5f;

public void AboveWater()
{
    if (time <= timeAmt)
    {
        time += regenSpeed;
    }
    else
    {
        time = timeAmt;
    }
}

void Update()
{
    fillmg.fillAmount = time / timeAmt;
}

I haven’t tested it, message me if you get any errors or something is not working.

I tried it and It stops when you exit the trigger but won’t regenerate.

Of so i was able to go around the issue that the regeneration stopped about less than 1 sec after stepping away of the trigger. I basically made it regenerate instantly by making the regenSpeed the same amount as the timeAmt.

Sorry for the late fix, but of course the OnTriggerExit will only be ran once.

You simply need to make a flag to tell if you’re in the water or outside.

Here is an example script that would enable slow regeneration, other than resetting the breath instantly:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

[RequireComponent(typeof(AudioSource))]

public class DrowningScript : MonoBehaviour
{
    Image fillImg;
    public float timeAmt = 35;
    float time;
    public AudioClip DrownDeathSfx;
    public AudioClip BreathSfx;
  
    float regenSpeed = 0.5f;
    bool inWater;

    void Start()
    {
        fillImg = this.GetComponent<Image>();
        time = timeAmt;
    }

    public void UnderWater(){
        inWater = true;
    }

    public void AboveWater(){
        inWater = false;
    }

    void Update()
    {
        if (inWater)
        {
            if (time > 0) {
                time -= Time.deltaTime;
            }
            else
            {
                GetComponent<AudioSource> ().clip = DrownDeathSfx;
                GetComponent<AudioSource> ().Play ();
            }
        }
        else
        {
            if (time >= timeAmt)
                return;

            time += regenSpeed;
        }
        fillImg.fillAmount = time / timeAmt;
    }
}

Again, please reply if you have any problems.
Also, change the OnTriggerStay to OnTriggerEnter, it’s more performance effecient.

1 Like

WOW that did the trick. Thank you very much for your help. Below is the project i’m working on and I will be adding your name to the credits.

Perish - New Life Redemption

Hmm. the script works flawless with the meter but the audio is now broken. I believe it’s because it set to the Update function. I tried using the playoneshot method but it still loops constantly. I also tried setting the playoneshot to run when (time <=0) but the loops keeps on. below is what i tried.

  • if (inWater)
  • {
  • if (time > 0)
  • {
  • time -= Time.deltaTime;
  • }
  • else
  • {
  • audio.PlayOneShot(DrownDeathSfx, 1F);
  • }
  • }

I also tried:

  • if (inWater)
  • {
  • if (time > 0)
  • {
  • time -= Time.deltaTime;
  • if (time<=0)
  • {
  • audio.PlayOneShot(DrownDeathSfx, 1F);
  • }
  • }
  • }

Have you checked to see if the looping boolean is set to true on the audiosource?

If it isn’t, then you could call a function which then plays the sound.

if (time > 0)
{
    time -= Time.deltaTime;
    if (time<=0)
    {
        playSound();
    }
}

void playSound() //place this outside the update function
{
    audio.PlayOneShot(DrownDeathSfx, 1F);
}

That’s an easy fix just add this check before you play the sound:

if (audio.isPlaying)
    return;
1 Like

Sorry but that wouldn’t work as the function you created is in the Update method, thus being ran every frame.

Hence why I said don’t put it in the Update function

Okay, then it’s really unclear how you meant it, either you don’t understand when you call methods in the Update method they’ll be ran every frame, or you made it really unclear. Your example clearly shows the method inside the update method. Your note makes no sense.

I put ‘place this outside the update function’ what is not to understand

The whole thing, what do you want to be placed ‘outside’ the update function?

Remember, calling functions that aren’t litterally ‘in’ the Update function but are ‘called’ in the Update function will behave the same as if it was ‘in’ the Update function.

Example to clearup what we’re talking about:

void Update()
{
    NotInUpdate(); // Called in the update function
}

void NotInUpdate()
{

    if (value)
        return;

   //This is still inside the update function.
}

This is the exact same as if you would write it like this:

void Update()
{
    if (value)
        return; //The same as above, inside the Update function.
}

I know there could be several ways to make this work. That’s why programming is great. I do have to say that the sound now works fine with the small fix melle suggested. The sound plays 1 time and doesn’t loop anymore. Below is the portion of the script i updates with melles info.

void Update()
{
if (inWater)
{
if (time > 0)
{
time -= Time.deltaTime;
if (audio.isPlaying)
return;
if (time <= 0) {
audio.PlayOneShot (DrownDeathSfx, 1F);
}
}
}
else
{
if (time >= timeAmt)
return;

time += regenSpeed;
}
fillImg.fillAmount = time / timeAmt;
}

Thanks for replying. Have a great day.

1 Like

@melle well seeing as my note is placed directly next to the function, I would assume that you had the initiative to realise that the function needs to be placed outside the Update() function. And what, so your saying that a function that isn’t inside the update function will still behave like it is, makes sense that :')