- Home /
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
velocity.y += bouyancy * Time.deltaTime;
is ur bouyancy ever set to 0?. i mean is the if working
It sets the bouyancy to 0 in the latter two else if blocks in the touch code, where it checks to see if the buttons are being touched during the swim$$anonymous$$g state.
Answer by kmeboe · Oct 01, 2012 at 08:09 PM
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.
$$anonymous$$odifying the function to check for the fingerId like this:
function GetButtonInputs() { // Button inputs var tapCount = Input.touchCount; for ( var i = 0 ; i < tapCount ; i++ ) { var touch = Input.GetTouch(i);
// Check to see if its the same touch as before
if (!touchId)
touchId = touch.fingerId;
// Now handle button touches
if (!swim$$anonymous$$g)
{
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
{
if (touchId != touch.fingerId)
{
bouyancy = 0;
}
// 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
} }
$$anonymous$$erely causes the diving and surfacing actions t become painfully slow, without any changes to the stopping behavior. Will try a new approach now.
You're getting close. Here's what needs to change for your new script:
1) "touchId" is an int, therefore "!touchId" doesn't make sense. It essentially checks for 0, which is a valid touch ID. I suggest you set touchID to -1 to start (and don't forget to set it to -1 again if the touch ends).
2) Do you allow multiple touches? If not, your code should check to see if the touch equals the touchID; if it doesn't, you don't want to do anything. If you do allow multiple touches, then you should also have multiple touchIDs to account for this.
3) You'll also want to see if the touchID that you recorded doesn't show up. You can do this by setting a bool at the beginning of the method (something like "bool foundTouch = false;"). Then, if you find a touch that's the same as your touchID, you can set foundTouch to true. At the end of your method, if foundTouch is still false, you should set buoyancy to 0.
As far as the slow rising/falling, I'm not sure what the difference is. Do you maybe want to modify the buoyancy with every update frame, rather than just during TouchPhase.Began (this only happens once)? Using deltaTime with touchPhase.Began is a little odd, since deltaTime is normally used for things that happen over multiple frames.
I tried this code:
function GetButtonInputs() { // Button inputs var foundTouch : boolean = false; var tapCount = Input.touchCount; for ( var i = 0 ; i < tapCount ; i++ ) { var touch = Input.GetTouch(i);
// Check to see if its the same touch as before
if (touchId == -1)
touchId = touch.fingerId;
// Now handle button touches
if (!swim$$anonymous$$g)
{
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
{
if (touchId != touch.fingerId)
{
bouyancy = 0;
}
// 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;
touchId = -1;
}
else if(touch.phase == TouchPhase.Ended && duckButton.HitTest(touch.position))
{
bouyancy = 0;
touchId = -1;
}
if (touch.fingerId == touchId);
bouyancy = 0;
}
//TODO: Add use buttons
} }
And it won't let me dive at all. That's my attempt to fix my script so far. And yes, I ned multi-touch support because of my on-screen joystick and need for an adjustable camera.
Your "if" near the end has a semi-colon, meaning that the line following (buoyancy = 0) will ALWAYS happen. I'm making some changes to your script for you to try.
Do you want the buoyancy to increase/decrease as they hold down the button (longer holding means faster change, to a point) or all at once?
Answer by lokeshvt · Oct 01, 2012 at 08:15 PM
when u are not pressing any button down, try removing the forces acting on the swimmer that causes it to rise or dive.
Answer by kmeboe · Oct 03, 2012 at 05:05 AM
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;
}
}
You can get a little fancier, if you want, by using $$anonymous$$athf.Clamp ins$$anonymous$$d of checking for <-20 and >20. In this case you would perform the clamp in one spot, probably in the "else" clause of "if (!sinkingOrSwim$$anonymous$$g)".
But try the above script first, and make sure it works properly, before getting fancy with Clamp.
The posted code worked with no compile errors whatsoever. I had to add the following else block to the button hit tests though. The choppiness was resolved with $$anonymous$$athf.Clamp, but the fundamental problem is that the swimmer still doesn't stop moving up or down when the player lets their finger off of the button. The swimmer appears to have a constant inertia that is set when by the bouyancy of the script.
Current code:
function GetButtonInputs() { // Button inputs
var sinkingOrSwim$$anonymous$$g : boolean = false;
var tapCount = Input.touchCount;
for ( var i = 0 ; i < tapCount ; i++ )
{
var touch = Input.GetTouch(i);
// Now handle button touches
if (!swim$$anonymous$$g)
{
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
{
$$anonymous$$athf.Clamp(bouyancy, -20, 20);
// Jump button to rise, crouch button to dive
if(jumpButton.HitTest(touch.position))
{
sinkingOrSwim$$anonymous$$g = true;
bouyancy += diveRate * Time.deltaTime;
}
else if(duckButton.HitTest(touch.position))
{
sinkingOrSwim$$anonymous$$g = true;
bouyancy -= diveRate * Time.deltaTime;
}
else
{
sinkingOrSwim$$anonymous$$g = false;
}
}
//TODO: Add use buttons
}
if (!sinkingOrSwim$$anonymous$$g)
{
// No dive or swim touch was found; reset buoyancy.
bouyancy = 0;
}
}
Sorry it took so long to get back to you; I didn't see your reply for some reason.
Your "else" clause shouldn't be necessary, as "sinkingOrSwim$$anonymous$$g" starts out false.
It seems odd that your posted script doesn't work at this point. What does your "Update" look like now? You need to be calling your GetButtonInputs at the beginning of Update(); are you doing this?