My unfortunate swimmer endlessly sinks. Help!

“Dive controls for swimming character controller cause character to continue sinking or rising after button is released” …

Hi guys,

I’ve implemented the following code to make a Character Controller-based character dive and rise in water. The code is intended to work wih BugZergArcade’s method of making a character swim:

function GetButtonInputs()
{
	// Button inputs
	
	var tapCount = Input.touchCount;
	
	for ( var i = 0 ; i < tapCount ; i++ ) {
	
    	var touch = Input.GetTouch(i);
    	
    	if (!swimming)
    	{
    		if(touch.phase == TouchPhase.Began && jumpButton.HitTest(touch.position))
    		{
    			if ( character.isGrounded )
       				Jump();
       		}
       		if(touch.phase == TouchPhase.Began && jumpButton.HitTest(touch.position))
    		{
    			if ( character.isGrounded )
       				Duck();
       		}
       	}
       	else
       	{
       		// Jump button to rise, crouch button to dive
       		
       		if(touch.phase == TouchPhase.Began && jumpButton.HitTest(touch.position))
    		{
    			bouyancy += diveRate * Time.deltaTime;
    			if (bouyancy > 20)
    				bouyancy = 20;
       		}
       		else if(touch.phase == TouchPhase.Began && duckButton.HitTest(touch.position))
    		{
    			bouyancy -= diveRate * Time.deltaTime;
    			if (bouyancy < -20)
    				bouyancy = -20;
       		}
       		else if(touch.phase == TouchPhase.Ended && jumpButton.HitTest(touch.position))
    		{
    			bouyancy = 0;
       		}
       		else if(touch.phase == TouchPhase.Ended && duckButton.HitTest(touch.position))
    		{
    			bouyancy = 0;
       		}
       	}
       	//TODO: Add use buttons
	}
}

function Update()
{
	...
	
	// Tap rotation stick to orient towards movement
	
	if (rotateJoystick.tapCount == 2 )
	{
		FaceMovementDirection();
	}
	
	// Check for jump using right stick
	if ( character.isGrounded )
	{
		if ( !rotateJoystick.IsFingerDown() )
			canJump = true;
	}
	else
	{			
		// Apply gravity to our velocity to diminish it over time, only do so when not in water.
		if (!swimming)
			velocity.y += Physics.gravity.y * Time.deltaTime;
		else
			velocity.y += bouyancy * Time.deltaTime;
		
		// Adjust additional movement while in-air
		movement.x *= inAirMultiplier;
		movement.z *= inAirMultiplier;
	}
	
	GetButtonInputs();
	
	// Move player
	
	movement += velocity;
	
	if (!swimming)
		movement += Physics.gravity;
		
	movement *= Time.deltaTime;
	
	...
}

The jump button maks you rise in water, while the duck button makes you dive. Unfortunately, if you take your finger off of either button while you’re swimming, the player character continues to rise or fall instead of staying at whatever depth you’ve gone to. What is the mistake?

MachCUBED

I’ve heard of problems with TouchPhase.Ended not firing in some cases. Here’s what I would suggest: when you record that a jump or duck touch has started (via TouchPhase.Began), store the unique index of this touch (touch.fingerId). Then, on subsequent passes through GetButtonInputs, if you ever DO NOT see a touch with this fingerId (after looping through all touches), you can assume the touch has ended.

This will have the additional benefit that, if the user accidentally moves their finger off of the buttons while touching, you’ll still handle this case correctly. The code you have here would never remove the buoyancy, because when they released their finger it wouldn’t be over the button anymore.

Good luck, and let me know if you need more help in figuring out how to implement this.

when u are not pressing any button down, try removing the forces acting on the swimmer that causes it to rise or dive.

I’m going to assume you want the sinking/swimming to gradually increase as the user holds down the button. In that case, we want to process EVERY touch (and not just touch.began). We also take note if the user is not holding either touch, and in that case we reset the buoyancy.

As I don’t have your external variables defined, I wasn’t able to ensure complete correctness; please excuse compilation errors.

function GetButtonInputs()
{
	// Button inputs
	
	var sinkingOrSwimming : boolean = false;
	
	var tapCount = Input.touchCount;
	
	for ( var i = 0 ; i < tapCount ; i++ ) {
	
    	var touch = Input.GetTouch(i);
        	
    	// Now handle button touches
    	
    	if (!swimming)
    	{
    		if(touch.phase == TouchPhase.Began && jumpButton.HitTest(touch.position))
    		{
    			if ( character.isGrounded )
       				Jump();
       		}
       		if(touch.phase == TouchPhase.Began && jumpButton.HitTest(touch.position))
    		{
    			if ( character.isGrounded )
       				Duck();
       		}
       	}
       	else
       	{
       		// Jump button to rise, crouch button to dive
       		
       		if(jumpButton.HitTest(touch.position))
    		{
    			sinkingOrSwimming = true;
    			bouyancy += diveRate * Time.deltaTime;
    			if (bouyancy > 20)
    				bouyancy = 20;
       		}
       		else if(duckButton.HitTest(touch.position))
    		{
    			sinkingOrSwimming = true;
    			bouyancy -= diveRate * Time.deltaTime;
    			if (bouyancy < -20)
    				bouyancy = -20;
       		}
       	}
       	//TODO: Add use buttons
	}
	
	if (!sinkingOrSwimming)
	{
		// No dive or swim touch was found; reset buoyancy.
		bouyancy = 0;
	}
}