So while planning out how to write my swimming script I came across the realization that character controllers don’t rotate. Naturally while swimming the character needs to be in a horizontal position. The only solution i have come up with is having a sphere for my characters head inactive and when I enter the water activating it. The head would be located at the midpoint of the character height and in front of my character. I could do the same thing with legs. Also upon entering the water I would shrink my controllers height so that essentially the character is comprised of 3 separate spheres. Is this a logical way of doing this or does anyone know of a better method?
I wanted to add swimming to my game and I’m also using a character controller (a modified form anyway) so here’s what I came up with after reading a bit more about this. For now, it’s only ‘topside’ swimming, no diving down. This is attached to the player:
using UnityEngine;
using System.Collections;
public class QM_Swimming : MonoBehaviour {
private Transform myTransform;
public float waterDepthInitial; // these are public to play with in Editor
public float waterDepthCurrent; // once they are set, change them to private if you prefer
public float myTreadingDepth;
public bool goDeeper; // these two remain public since they are referenced by the
public bool inWater; // CharacterController or equivalent script
void Start () {
myTransform = transform;
myTreadingDepth = 1.4f; //adjust this base on your models height, game scale, etc
goDeeper = true;
inWater = false;
}
void OnTriggerEnter(Collider collider) {
if(collider.CompareTag("SwimWater")) {
// Grab the Y we entered the water at
waterDepthInitial = collider.gameObject.transform.position.y;
waterDepthCurrent = waterDepthInitial;
inWater=true;
}
}
void OnTriggerExit(Collider collider) {
if(collider.CompareTag("SwimWater")){
waterDepthInitial = 0f;
waterDepthCurrent = 0f;
inWater=false;
}
}
//Update is called once per frame
void Update () {
if(inWater) {
// Check how deep we are
waterDepthCurrent = waterDepthInitial - myTransform.transform.position.y;
// Have we gone to max "wading" depth where we don't want to go deeper/lower on Y
if(waterDepthCurrent > myTreadingDepth && goDeeper) {
goDeeper = false;
}
// Have we come closer to land/gone up
if(waterDepthCurrent <= myTreadingDepth && !goDeeper) {
goDeeper = true;
}
}
}
}
To the game world, I added a Simple Water prefab atop a deep depression to ensure I would check how the player would react (i.e. his feet aren’t touching the ground). I tagged that “SwimWater” (per the collider call) and added a Mesh Collider with IsTrigger checked.
Lastly, in the Character Controller script there’s probably two changes needed:
-
It probably has a whole section that’s protected from a isGrounded check, I had to modify that to
if(Isgrounded || qmswim.inWater)
(where qmswim is a reference back to the QM_Swimming script) -
It probably has a line like
moveDirection.y -= gravity * Time.deltaTime;
which I changed toif(qmswim.goDeeper) {
moveDirection.y -= gravity * Time.deltaTime;
}
Such that once we hit the depth where we don’t want the player to go deeper, we don’t apply gravity and the .y stays flat.
And that did it. I’m going to incorporate some animation state changes and splashy sounds based on some of this and see if I can improve on it. I’ll definitely have to figure out something else for the ‘dive down’ part.