Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411505 Posts in 69374 Topics- by 58429 Members - Latest Member: Alternalo

April 25, 2024, 07:30:38 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Interpolating Gamepads
Pages: [1]
Print
Author Topic: Interpolating Gamepads  (Read 1169 times)
quantumpotato
Quantum Potato
Level 10
*****



View Profile WWW
« on: July 13, 2017, 08:18:48 PM »

Hello, I am having trouble interpolating gamepads.

2 main problems: 1, Xbox360 controller keeps jiggling, it's off center. I try to use a deadzone to avoid this.

2: I want the movement vector to be applied proportionally based on your angle. Moving diagonally down and right should not make you move +1 x and +1 y, but +0.5x and +0.5y.

To account for deadzones + scaling, I try using a "zm" (margin back to zero). This seems to help some, but the controls still don't feel right moving diagonals. Please help!  Shocked

Code:
	loopGamepadInput() {
var pads = navigator.getGamepads();

if (pads) {
for (var i = 0; i < pads.length; i++) {
var gp = pads[i];
if (!gp) {
break;
}
var x = gp.axes[0];
    var y = gp.axes[1];
    if (!this.deadzone) {
    this.deadzone = {'x' : x, 'y': y};
    }
   
    var ratio = 0.8;
    x = x - this.deadzone.x;
    y = y - this.deadzone.y;

    var zm = 1 - ratio;

    if (x > 0) {
    x -= zm;
    if (x < 0) {
    x = 0;
    }
    } else if (x < 0) {
    x += zm;
    if (x > 0) {
    x = 0;
    }
    }

    if (y > 0) {
    y -= zm;
    if (y < 0) {
    y = 0;
    }
    } else if (y < 0) {
    y += zm;
    if (y > 0) {
    y = 0;
    }
    }

    x = x / ratio;
    y = y / ratio;
   
var input = {'x' : x, 'y': y, 'buttons' : gp.buttons};
input = interpolateGamepad(input);

    this.game.gamepadCount = i;
    this.game.parseGamepadInput(i, input);
}
}

I think I was doing my ratios wrong in this interpolate function, but both ways feel wrong

Code:
interpolateGamepad = function(input) {
  var x = input.x;
  var y = input.y;

  var sum = Math.abs(x) + Math.abs(y);
  if (sum <= 0) {
    return input;
  }

  if (sum < 1.732050808) {
    return {'x' : x, 'y': y, 'buttons' : input.buttons };   
  }

  var c = (x * x) + (y * y);
  var csq = Math.sqrt(c);

  var xRatio = Math.abs(x) / csq;
  var yRatio = Math.abs(y) / csq;
 
  // var xRatio = Math.abs(x) / sum;
  // var yRatio = Math.abs(y) / sum;

  x = xRatio * x;
  y = yRatio * y;

  return {'x' : x, 'y': y, 'buttons' : input.buttons };
}
Logged

oahda
Level 10
*****



View Profile
« Reply #1 on: July 14, 2017, 01:46:01 AM »

IIRC just did something like clamp(normalise(vec(x, y)) - deadzone, 0, 1) last time I had to deal with this. Clamping fixes issue #1 and normalisation fixes issue #2.
Logged

quantumpotato
Quantum Potato
Level 10
*****



View Profile WWW
« Reply #2 on: July 14, 2017, 07:16:42 AM »

IIRC just did something like clamp(normalise(vec(x, y)) - deadzone, 0, 1) last time I had to deal with this. Clamping fixes issue #1 and normalisation fixes issue #2.

Clamping = https://msdn.microsoft.com/en-us/library/hh308289(v=vs.120).aspx ?

And normalizing being

The general one-line formula to linearly rescale data values having observed min and max into a new arbitrary range min' to max' is

  newvalue= (max'-min')/(max-min)*(value-max)+max'
  or
?
Logged

oahda
Level 10
*****



View Profile
« Reply #3 on: July 14, 2017, 08:35:43 AM »

Disclaimer: I edited out the mistakes from this post, just so you know, in case the posts following this one seem confusing...

I hastily wrote that and got the mathsy details a bit wrong, sorry. Let me go through this step by step instead, both so you get what I mean and so that I don't mess up. Tongue

Clamping just means to force the value to be no more and no less than the values specified (in this case no less than 0 and no more than 1).

If you look at the amount the stick is being pushed on the x and y axes respectively, you can use these values as a directional vector, indicating the direction the stick is being pushed. Normalising a vector modifies its components (x and y) so that the length (or magnitude) of the vector is 1.

This gives us our direction. We can then multiply this vector by how hard the stick is being pushed (something I forgot to take into account in my previous post) and get the correct magnitude too (should be between 0 and 1).

So...

Step 1: get the directional vector

Put your x and y values into a vector and normalise it to get a magnitude of 1:

Code:
vec dir = normalise(vec(x, y));

Step 2: clamp the magnitude

Get the magnitude from the vector and clamp it against the threshold.

Code:
float mag = magnitude(dir);
if (mag <= threshold) mag = 0;

Step 3: produce final vector

Update to the clamped magnitude:

Code:
dir *= mag;

All in all

Code:
// Get the push direction.
vec dir = normalise(vec(x, y));

// Clamp the magnitude.
float mag = magnitude(dir);
if (mag <= threshold) mag = 0;

// Get the final value!
dir *= mag;
« Last Edit: July 14, 2017, 02:27:50 PM by Prinsessa » Logged

quantumpotato
Quantum Potato
Level 10
*****



View Profile WWW
« Reply #4 on: July 14, 2017, 01:56:08 PM »

max is the highest amount in a direction on that axes, right?

Thinking of this in terms of angle + magnitude makes a lot more sense, thank you.

I think the normalization you talk about is like this:

Code:
var length = x + y;
var xFactor = x / length;
var yFactor = y / length;
So xFactor + yFactor should == 1.

It turns out that gamepad controller in HTML5 does not send 1 on X and 1 on Y for diagonals, like I thought (!). This makes sense in retrospect but it does make things trickier.

Holding down right on an Xbox360, for example, can give 0.82 and 0.56 or 0.52 and 0.90 for the 2 axes.

Maybe I misunderstand

Quote
Code:
float mag = (abs(x) + abs(y)) / max;

We're not interested in negative values (this is encoded in the directional vector), just how much the stick is being pushed on each axis, so we take the absolute (non-negative) value of each first, and then divide by the max value to get a value between 0 and 1.

I'm assuming max is max an axis can go?
Logged

oahda
Level 10
*****



View Profile
« Reply #5 on: July 14, 2017, 02:10:42 PM »

Yep, you've understood vector normalisation perfectly! Hand Thumbs Up Right

I did a big booboo when including the whole max thing, tho, sorry. I was thinking about returning an angle and a strength separately, not a vector at all. You don't need the max thing, it's already in the vector, sorry. Tongue

I'm making lots of brainos today. So all you need is this:

EDIT: moved the solution to the original post where I messed up instead
« Last Edit: July 14, 2017, 02:44:41 PM by Prinsessa » Logged

quantumpotato
Quantum Potato
Level 10
*****



View Profile WWW
« Reply #6 on: July 14, 2017, 02:14:33 PM »

It's working a lot better, thank you!
Here's what I've got now:

Code:
if (pads) {
for (var i = 0; i < pads.length; i++) {
var gp = pads[i];
if (!gp) {
break;
}
var x = gp.axes[0];
    var y = gp.axes[1];

    var m = getm(x, y);

    var length = Math.abs(x) + Math.abs(y);
    if (length > 0) {
    x = x/length;
    y = y/length;
    }
   
    var threshhold = 0.2;
    var r = 0;
    if (m < threshhold) {
    x = 0;
    y = 0;
    m = 0;
    } else {
    r = getr(x, y);
    if (r < 0) {
    r += 360;
    }
    }
   
var input = {'r' : r, 'm': m, 'buttons' : gp.buttons};

    this.game.gamepadCount = i;
    this.game.parseGamepadInput(i, input);
}
}

and the supporting get rotation and get magnitude

Code:
getr = function (x, y) {
  return (Math.atan2(y, x) * 180 / Math.PI);
}

getm = function(a, b) {
  var x = a * a;
  var y = b * b;
  if (x + y == 0){
    return 0;
  }
  var res = Math.sqrt((x+y));
  if (res > 1) {
    res = 1;
  }
  return res;
}
Logged

quantumpotato
Quantum Potato
Level 10
*****



View Profile WWW
« Reply #7 on: July 14, 2017, 02:16:04 PM »

The only issue I'm seeing now is the movement tends to jump -- unfortunately these 360 controllers a .15 threshhold is too low (!!!) so I set it at .2 But then when I start moving, I move very quickly.

I think I need to scale the x and y somehow and divide against ~1.5 as the max magnitude, maybe.. not sure, that wouldn't work well for straight lines up or down.
Logged

oahda
Level 10
*****



View Profile
« Reply #8 on: July 14, 2017, 02:24:44 PM »

Yeah, I was just going to say, you might have to smooth it out with a curve or something so that it doesn't jump straight from nothing to something. So instead of just setting it to zero if below the threshold, you could do something more complex.

I'm sure ThemsAllTook or someone is going to ride in on a white horse with some industry-standard solution for that. Tongue
Logged

quantumpotato
Quantum Potato
Level 10
*****



View Profile WWW
« Reply #9 on: July 14, 2017, 02:37:56 PM »

Yeah, I was just going to say, you might have to smooth it out with a curve or something so that it doesn't jump straight from nothing to something. So instead of just setting it to zero if below the threshold, you could do something more complex.

I'm sure ThemsAllTook or someone is going to ride in on a white horse with some industry-standard solution for that. Tongue

I did a brain recall too, and I was multiplying all my movement by 1 instead of m!

Smoothed it out to be the distance past threshhold that counts:

Code:
var threshhold = 0.3;
    var r = 0;
    if (m < threshhold) {
    x = 0;
    y = 0;
    m = 0;
    } else {
    m = (m - threshhold) / (1 - threshhold);
    r = getr(x, y);
    if (r < 0) {
    r += 360;
    }
    }
   

Thank you Prinsessa!
Logged

oahda
Level 10
*****



View Profile
« Reply #10 on: July 14, 2017, 02:43:47 PM »

There you go! Hand Thumbs Up Right Glad someone had an issue I could help with for once.
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic