If I understand properly, you are approximating your physics calculations perhaps too much. When making non-representative physics calculations, it makes it very challenging to keep things straight, and causes problems down the road if you build off them.
When you compute position in a game, you do so by multiplying velocity by a small increment of time, then add to the current position:
float positionX = positionX + velocityX * elapsedTimeMillis;
When you begin to introduce acceleration, you now need to compute velocity before computing your position. Do this by multiplying your current acceleration by a small increment of time, and then add that to the current velocity.
float velocityX = velocityX + accelerationX * elapsedTimeMillis;
In the code you provided, you have kept your velocity at a constant, and are not adjusting it based of the accelerometer's acceleration reading.
It is also important to keep your axes consistent. When using these formulas, you still need to adjust your acceleration so that it points in the correct direction (adjust by changing sign).
Now, it looks like you are computing the actual screen coordinate by using normalized position and scaling to the screen. This should work very nice for cross-device compatibility. However, if you normalize the position, you also need to normalize the velocity & acceleration.
Make sure that all of your units are consistent.
Also, I see that you've said:
when I run it on an old Galaxy Ace, the sprite accelerates way too fast
This screams out to me it is because of the improper physics computations. It's difficult to tell what the exact cause is from what you've described, but I'd take a look at first fixing your physics computations and see if it changes.
So in sum, you must:
- Keep your position, velocity, and acceleration units consistent and all must be normalized it one dimension is normalized. Or otherwise, the values must be scaled to one other (it doesn't have to be normalized between 0 to 1 or -1 to +1).
- Make sure you compute velocity depending on acceleration before computing position. Your position variable should be completely unaware that you are accelerating (only velocity should care).
If you need to have further control, set the default values for velocity & position (and sometimes acceleration). If you need just a smooth glide like in your first game, the acceleration should be held at 0, but the velocity should have an initial speed. That way the acceleration doesn't change the velocity.
public void initVariables() {
accelerationX = 0.0f; //0 units / second / second.
velocityX = 10.0f; //10 units / second.
positionX = 50.0f; //50 units.
}
public void mainGameLoop() {
//... compute delta time
velocityX = velocityX + accelerationX * elapsedTimeMillis;
positionX = positionX + velocityX * elapsedTimeMillis;
//... compute screen position based on positionX
}
@Override
public void onSensorChanged(SensorEvent event){
//MAKE SURE THIS IS NORMALIZED AND HAS CORRECT DIRECTION.
//MAKE ADJUSTMENTS AS NEEDED
accelerationX = event.values(axis) / MAX_ACCELERATION_VALUE;
}
An Example for Choosing Values
Since I have no context for what is valid/invalid for you game, I'll mock up an example.
Assume:
- Screen width maps to a 10 meter game-space.
- Screen height remains proportional (game-space size depends on aspect ratio).
- The objective is to create a starship that moves left & right according to the accelerometer's reading. We will scale/normalize the acceleration values so that they vary between -1 & +1. The units are measured in meters/second/second.
- The starship should never move faster than 2m/s. It also cannot exceed the boundaries of a 10m-wide game-space.
- Let's assume the left edge of the screen is at 0m in game-space, and the right edge is 10m.
- Let's also assume the starship is located in the center (5m game-space).
Physics Walkthrough
Trial 1:
- At first the velocity is 0, and the acceleration is 0.
- Synchronized with the game-loop, the acceleration changes to +0.35 m/s^2
- It takes 35millis for the game loop to be re-executed.
- In game-loop, the velocity games:
0m/s + 0.35m/s^2 × 35millis
- The new velocity is 0.01225m/s just after the first 35 milliseconds.
- Now, the position must be updated based on the velocity:
5m + 0.01225m/s × 35millis
- The new position is 5.00042875m, just a small nudge.
Trial 2:
- At first the velocity is 0, and the acceleration is 0.
- Synchronized with the game-loop, the acceleration changes to +1 m/s^2
- It takes 1s for the game loop to be re-executed.
- In game-loop, the velocity games:
0m/s + 1m/s^2 × 1s
- The new velocity is 1m/s just after the first second.
- Now, the position must be updated based on the velocity:
5m + 1m/s × 1s
- The new position is 6m, a much more substantial change.
Trial 3:
- At first the velocity is 0, and the acceleration is 0.
- Synchronized with the game-loop, the acceleration changes to -1 m/s^2
- It takes 0.5s for the game loop to be re-executed.
- In game-loop, the velocity games:
0m/s + -1m/s^2 × 0.5s
- The new velocity is -0.5m/s just after the first 0.5 seconds.
- Now, the position must be updated based on the velocity:
5m + -0.5m/s × 0.5s
- The new position is 4.75m.
Trial 4:
- Now, assume position begins at 10m
- At first the velocity is 0, and the acceleration is 0.
- Synchronized with the game-loop, the acceleration changes to -1 m/s^2
- It takes 1s for the game loop to be re-executed.
- In game-loop, the velocity games:
0m/s + -1m/s^2 × 1s
- The new velocity is -1m/s just after the first second.
- Now, the position must be updated based on the velocity:
10m + -1m/s × 1s = 9m
- The next game loop happens 1 second later (acceleration is held to -1 m/s^2)
- Velocity changes to:
-1m/s + -1m/s^2 × 1s = -2m/s
- Position changes to:
9m + -2m/s * 1s = 7m
Now, I assumed that the width of the screen map to 10m in game-sace
Lets assume that screen dimensions are 320 x 480 (in pixels), and that the width is the larger dimension (480).
So.. we need to map these numbers:
0m, 1m, 2m, ..., 10m
To these numbers:
0px, 1px, 2px, ..., 480px
Well, this should be straightforward to compute the screen location because we know that 480px corresponds to 10m, and every other value depends on a linear relationship between game-space & screen-space:
screenXPosition = gameXPosition / 10m * 480px
//Re-written looks like:
screenXPosition = gameXPosition * (480px / 10m)
//Where your constant factor for the linear relationship is (480px / 10).
If you need to change devices, then you simply change the screen width parameter:
screenXPosition = gameXPosition * (screenWidthInPixels / 10m)
You mentioned you need to place limits on your velocity, that should look like this:
float newVelocityX = velocityX + accelerationX * elapsedTimeMillis;
if (Math.abs(newVelocityX) > MAXIMUM_SPEED_X)
{
//If the new speed exceeds the allowed limit, then we need to make
//the speed go to the limit, while preserving the direction of the
//velocity.
//Direction should be either -1 or 1.
float direction = Math.abs(newVelocityX) / newVelocityX;
newVelocityX = MAXIMUM_SPEED_X * direction;
}
velocityX = newVelocityX;