6
\$\begingroup\$

Recently I found the need to implement a normalization formula in a couple of places in my basketball game, so I created some static methods inside of a class to do so.

public class Normalize {

    //explanation of normalization formula:
    /*
    Suppose you have a range or scale from A to B and you want to convert 
    it to a scale of 1 to 10, where A maps to 1 and B maps to 10.  
    Furthermore, we want to do this with a linear function, so that for 
    example the point midway between A and B maps to halfway between 1 and 
    10, or 5.5.

    Then the following (linear) equation can be applied to any number 
    x on the A-B scale:
    */  
    //y = 1 + (x-A)*(10-1)/(B-A)

    public static Vector2 normalizedVector(Vector2 velocity, float min, float max, float normalizedMin, float normalizedMax) {
        float inputX = velocity.x;
        float inputY = velocity.y;
        float normalizedX = normalizedMin + (inputX-min) * (normalizedMax - normalizedMin) / (max-min);
        float normalizedY = normalizedMin + (inputY-min) * (normalizedMax - normalizedMin) / (max-min);
        return new Vector2(normalizedX, normalizedY);
    }

    public static float normalizedStrength (Vector2 velocity, float min, float max, float normalizedMin, float normalizedMax) {
        float inputX = velocity.x;
        float inputY = velocity.y;
        float inputTotal = inputX + inputY;
        return normalizedMin + (inputTotal-min) * (normalizedMax - normalizedMin) / (max-min);
    }
}

I use this code in a couple of places in the game. First, I'm using it to reduce the strength of the impact force of a ball when it hits the hoop, in order to make sure that it does not move too far as a result of the collision:

public void ballCollided(float angle, Vector2 velocity) {

    //the angle is not currently used in the calculation

    //the 0 and 100 are hard coded here
    //the value represents the power of the ball velocity
    //that i got from shooting balls at max power
    //usually the value was 88 to 95 ish
    //if the ball was moving at much higher speeds or something this would need to change
    float min = 0;
    float max = 100;
    Vector2 normalizedVector = Normalize.normalizedVector(velocity, min, max, this.normalizedMinPower, this.normalizedMaxPower);
    this.amountToMoveForBallHitX += normalizedVector.x;
    this.amountToMoveForBallHitY += normalizedVector.y;
}

In anther place, I am using it to determine the amount to shake the screen when the ball collides with the hoop, as well as to determine the correct sound effect to play:

public void ballHitHoop(Vector2 velocity) {
    //these values observed from watching the logs of ball strength
    float min = 0;
    float max = 100f;

    //these values affect how much it shakes
    float minPower = 0;
    float maxPower = 1f;

    float shakeTime = 40;

    float power = Normalize.normalizedStrength(velocity, min, max, minPower, maxPower);
    this.screenShake.rumble(power, shakeTime, this.camCenterX, this.camCenterY);

    this.playThumpForPower(power);
}

Is there a more efficient or more readable way to accomplish this?

Edit: As requested, here is an example test. Much more complicated tests are possible, but this test just shows normalizing 0 to 100 to be between 0.0 and 1.0.

@Test
public void normalizeToBetween0And1() {
    Vector2 strength100a = new Vector2(50, 50);
    Vector2 strength100b = new Vector2(100, 0);
    Vector2 strength50a = new Vector2(25, 25);
    Vector2 strength50b = new Vector2(20, 30);
    Vector2 strength20a = new Vector2(10, 10);
    Vector2 strength20b = new Vector2(11, 9);

    //origin values will be between 0 and 100
    float min = 0;
    float max = 100;

    //target values are between 0.0 and 1.0
    float normalizedMin = 0;
    float normalizedMax = 1;

    float normalized100a = Normalize.normalizedStrength(strength100a, min, max, normalizedMin, normalizedMax);
    System.out.println(normalized100a);
    assertEquals(normalized100a, 1.0f, 0.0f);

    float normalized100b = Normalize.normalizedStrength(strength100b, min, max, normalizedMin, normalizedMax);
    System.out.println(normalized100b);
    assertEquals(normalized100b, 1.0f, 0.0f);

    float normalized50a = Normalize.normalizedStrength(strength50a, min, max, normalizedMin, normalizedMax);
    System.out.println(normalized50a);
    assertEquals(normalized50a, 0.5f, 0.0f);

    float normalized50b = Normalize.normalizedStrength(strength50b, min, max, normalizedMin, normalizedMax);
    System.out.println(normalized50b);
    assertEquals(normalized50b, 0.5f, 0.0f);

    float normalized20a = Normalize.normalizedStrength(strength20a, min, max, normalizedMin, normalizedMax);
    System.out.println(normalized20a);
    assertEquals(normalized20a, 0.2f, 0.0f);

    float normalized20b = Normalize.normalizedStrength(strength20b, min, max, normalizedMin, normalizedMax);
    System.out.println(normalized20b);
    assertEquals(normalized20b, 0.2f, 0.0f);
}
\$\endgroup\$
  • \$\begingroup\$ Better by which exact means please? \$\endgroup\$ – πάντα ῥεῖ Dec 15 '15 at 21:13
  • \$\begingroup\$ Edited to clarify. \$\endgroup\$ – bazola Dec 15 '15 at 21:16
  • \$\begingroup\$ Not of a great clarification though :-( ... \$\endgroup\$ – πάντα ῥεῖ Dec 15 '15 at 21:18
  • \$\begingroup\$ Not sure what else you are looking for? \$\endgroup\$ – bazola Dec 15 '15 at 21:26
  • \$\begingroup\$ How are you using these methods in your code? I'm a bit confused by the normalizedStrength method \$\endgroup\$ – Simon Forsberg Dec 15 '15 at 21:30
5
\$\begingroup\$

I have just some small tips with regards to this simple code.

Avoid duplication like this:

float normalizedX = normalizedMin + (inputX-min) * (normalizedMax - normalizedMin) / (max-min);
float normalizedY = normalizedMin + (inputY-min) * (normalizedMax - normalizedMin) / (max-min);

I re-read 3 times these long statements to make sure that the expression at the right are identical. If you put that in a local variable, then:

  • it will be easier to read, since it will be obvious that the expressions are the same
  • it will be easier to read without scrolling to the right
  • it will be easier to modify, in case you need to, as you can change in one place

Look, now the piece of code fits within the page here too:

float coef = (normalizedMax - normalizedMin) / (max - min);
float normalizedX = normalizedMin + (inputX-min) * coef;
float normalizedY = normalizedMin + (inputY-min) * coef;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.