smooth scroll/inertial scrolling/momentum scroll - iphone

I have an OpenGL ES View in Android thats controlled by a matrix for translation. Im trying to figure out a way to get a hint of momentum scrolling as seen in the google maps app or the iPhone. Thanks.

If your problem is in 2d, it is quite simple
You need to get the elapsed time in each frame
Your onTouch function will find the acceleration of your finger. I forgot the formula on how to get the acceleration from a distance. It should be the second derivative of position with a time variable. But you should always convert your deltaX, deltaY in acceleration. To make it easy you don't really need to put something accurate there. Edit: I don't know why I didn't see it but the function was all there...
acceleration.x = 2(newposition.x - position.x - speed.x * elapsedTime) / (elapsedTime * elapsedTime);
Once you have your acceleration you can set your new position with that code. This is simple physic dynamics in 2d. With your acceleration you can find your speed and with your speed you can find your next position.
speed.x = (float) (mass * acceleration.x * elapsed + speed.x);
speed.y = (float) (mass * acceleration.y * elapsed + speed.y);
position.x += mass * acceleration.x / 2 * elapsed * elapsed + speed.x * elapsed;
position.y += mass * acceleration.y / 2 * elapsed * elapsed + speed.y * elapsed;
speed.x *= friction;
speed.y *= friction;
Mass and friction will let you define how fast it goes and how fast it will slow down by itself. You probably will have to tweak the code because this dynamic isn't exactly nice if you have to have to scroll backward to slow down.
At the end of each frame, you will have to reset your acceleration to (0,0). And on each new frame after a touch even, the acceleration should be set to something. It should work very well :)

Measure the speed that the view is scrolling at.
Detect when the user stops scrolling.
Gradually decrease the speed that the scroll view is scrolling at.
Something like this:
public void redraw() {
myScrollView.ySpeed = myScrollView.lastY-myScrollView.y;
myScrollView.xSpeed = myScrollView.lastX-myScrollView.x;
if (!userIsScrolling && ySpeed > 0) {
ySpeed--;
}
if (!userIsScrolling && xSpeed > 0) {
xSpeed--;
}
myScrollView.lastY = myScrollView.y;
myScrollView.y += ySpeed;
myScrollView.lastX = myScrollView.x;
myScrollView.x += xSpeed;
}
public void userStoppedScrolling() {
userIsScrolling = false;
}

Related

Unity - Quaternion.Lerp - Wheel rotation

I have one problem in script(Unity) that I made for car controller. When I want to rotate front wheels of car, example I'm pressing 'A' or 'D' wheels will turn on left or right(0,45 or -45,0) and immediately it will turn on starting rotation(0,0,0), I didn't have this problem when I didn't use Quaternion.Lerp, without Quaternion.Lerp it works fine. Any kind of help will be welcome.
//Rotation - WHEELS
CurrentRotation = Horizontal * RotationSpeed * Time.deltaTime;
if (CurrentRotation <= MaximumRotation || CurrentRotation >= -MaximumRotation)
{
Vector3 from_v = new Vector3(Wheels[0].transform.localRotation.x, Wheels[0].transform.localRotation.y, Wheels[0].transform.localRotation.z);
Vector3 to_v = new Vector3(Wheels[1].transform.localRotation.x, CurrentRotation, Wheels[1].transform.localRotation.z);
Quaternion from = Quaternion.Euler(from_v);
Quaternion to = Quaternion.Euler(to_v);
float lerp = 0.5F * (1.0F + Mathf.Sin(Mathf.PI * Time.realtimeSinceStartup * 3f));
Wheels[0].transform.localRotation = Quaternion.Lerp(from, to, lerp);
Wheels[1].transform.localRotation = Quaternion.Lerp(from, to, lerp);
}
I don't fully understand what you want to achieve. Is it the rolling of the wheel plus steering?
Quaternions are great, but here you don't need them. You can get and set the Euler (main axis) rotation directly. Untested code:
Vector3 rot = Wheels[0].localRotation;
rot.x += speed * Time.deltaTime;
rot.y = (current smoothed steering angle)
Wheels[0].localRotation = rot;
Wheels[1].localRotation = rot;
You should orient all your wheel game objects to point x-Axis to the right and Y-Axis to the top. The smoothing / max angles can depend on the input type / device, the game settings and the car speed.
It would be nice if you upvote my answer so I finally can write comments here.
Kind Regards,
Chris

Throw ball using cocos2d but without any physic engine

I want to throw a ball with swipe speed but do not want to use any physic engine. so please can any one suggest me how i do this.
thanks
A simple yet effective approach without having to explicitly use any physics engine is to step the velocity and position of the ball sprite manually in your update loop, Euler-style.
In the typical case (with downward gravity), you will have non-constant velocity in the y-direction and constant velocity in the x-direction, hence the following code:
-(void) update: (ccTime) dt
{
// Step only the y-velocity
velocity_y += GRAVITY * dt;
// Step the position values and update the ball sprite position accordingly
ball.position.x += velocity_x * dt;
ball.position.y += velocity_y * dt;
}
Then when a swipe event is detected,
Capture the swipe velocity (you will have to compute the change in position of the finger touch in the current and last frame)
Multiply the velocity with a scaling factor if necessary.
Set *velocity_x* and *velocity_y* to these initial values.
I am using projectile motion formulas to do this, http://en.wikipedia.org/wiki/Projectile_motion, along with cocos actions. Here you have a possible approach:
First
Implement a CCActionInterval subclass that receives the projectile formula parameters and updates the projectile position. In this case, ProjectileAction. These are the key methods you need to over-ride:
-(void) startWithTarget:(id)target
{
self.initialPosition = [target position];
self.elapsedTime = 0;
[super startWithTarget:target];
}
-(void) update: (ccTime) tt
{
self.elapsedTime += tt;
float t = self.elapsedTime;
float theta = CC_DEGREES_TO_RADIANS(self.angle);
float v0 = self.velocity;
float g = self.gravitationalAcceleration;
double x = v0 * cos(theta) * t;
double y = v0 * sin(theta) * t - 0.5 * g * t * t ;
[self.target setPosition: ccp(self.initialPosition.x + (float)x, self.initialPosition.y + (float)y)];
}
Second
Use your swipe gesture to recognize the speed, that will translate to the ball's initial velocity in the equation above and to the theVelocity parameter bellow. I leave that part to you.
Third
Run the action. i.e.
ProjectileLaunch* action = [ProjectileLaunch actionWithDuration:10
angle:45
initialVelocity:theVelocity
g:9.8];
[sprite runAction:action];
I hope this helps you.
Regards.

Accelerometer direction detection

I have the following code
playerPosition.x += acceleration.x * 10;
playerPosition.y += acceleration.y * 20;
acceleration currently works, I would like to detect if the player should move backwards(y decreasing) and if so change it to
playerPosition.y += acceleration.y * 10;
How does one detect if acceleration from the accelerometer is negative?
How does one retrieve the direction of the movement? left right etc?
You just compare the acceleration with 0
acceleration.y < 0.0
or
acceleration.y > 0.0
There is a free app iSimulate where you can see what values accelerometer outputs.

How to get colliding effect or bouncy when ball hits the track

** STILL NOT WORKING **
I am using below formula to move the ball circular, where accelX and accelY are the values from accelerometer, it is working fine.
But the problem in this code is mRadius (I fixed its value to 50), i need to change mRadius according to accelerometer values and also i need bouncing effect when it touches the track. Currently i am developing code by assuming only one ball is on the board.
float degrees = -atan2(accelX, accelY);
int x = cCentrePoint.x + mRadius * cos(degrees);
int y = cCentrePoint.y + mRadius * sin(degrees);
Here is the snap of the game i want to develop:
Balls Game http://iphront.com/wp-content/uploads/2009/12/bdece528ea334033.jpg.jpg
Updated: I am sending the updated code...
mRadius = 5;
mRange = NSMakeRange(0,60);
-(void) updateBall: (UIAccelerationValue) accelX
withY:(UIAccelerationValue)accelY
{
float degrees = -atan2(accelX, accelY);
int x = cCentrePoint.x + mRadius * cos(degrees);
int y = cCentrePoint.y + mRadius * sin(degrees);
//self.targetRect is rect of ball Object
self.targetRect = CGRectMake(newX, newY, 8, 9);
self.currentRect = self.targetRect;
static NSDate *lastDrawTime;
if(lastDrawTime!=nil)
{
NSTimeInterval secondsSinceLastDraw =
-([lastDrawTime timeIntervalSinceNow]);
ballXVelocity = ballXVelocity + (accelX * secondsSinceLastDraw)
* [self isTouchedTrack:mRadius andRange:mRange];
ballYVelocity = ballYVelocity + -(accelY * secondsSinceLastDraw)
* [self isTouchedTrack:mRadius andRange:mRange];
distXTravelled = distXTravelled + secondsSinceLastDraw
* ballXVelocity * 50;
distYTravelled = distYTravelled + secondsSinceLastDraw
* ballYVelocity * 50;
//Updating the ball rect
CGRect temp = self.targetRect;
temp.origin.x += distXTravelled;
temp.origin.y += distYTravelled;
//calculating new radius after updating ball position
int radius = (temp.origin.x - cCentrePoint.x) /
cos(degreesToRadians(degrees));
if( !NSLocationInRange(abs(radius),mRange))
{
//Colided with the tracks...Need a better logic here
ballXVelocity = -ballXVelocity;
}
else
{
// Need a better logic here
self.targetRect = temp;
}
}
[lastDrawTime release];
lastDrawTime = [ [NSDate alloc] init];
}
In the above code i have initialized mRadius and mRange(indicate track) to some constant for testing, i am not getting the moving of the ball as i expected( bouncing effect when Collided with track ) with respect to accelerometer. Help me to recognize where i went wrong or send some code snippets or links which does the similar job.
I am searching for better logic than my code, if you found share with me.
If I understand your code correctly, then the ball's position is directly controlled by the iPhone's orientation (tilt). So, tilting the iPhone to the right will place the ball at the right side of the track (3 o'clock). I believe you may want the balls acceleration (or, at least, its velocity) to be controlled. Then, you integrate the acceleration to velocity and the velocity to place, taking into account the constraints (the track walls).
The way it is set now, I don't see how you'd control more than one ball (as per the image you posted).
Then, for the bouncing effect: if you mean bouncing by the track's wall, then this will be a small modulation of the mRadius. If you mean bounce by other ball, then you'd modulate the angular position (by means of angular velocity) of the two balls to reflect the reaction.
EDIT: for integration of acceleration to velocity and then to position, for the purpose of this game, you can do with 1st order rectangular integration. Also, it will be more realistic to make the acceleration proportional to the tilt angle. Given the accel values from the iPhone itself, you can assign a 1:1 relation between the balls accel and the device reading. So, you'd like something like:
BallAccX = AccelX * Am_I_NOT_touching_a_wall_in_X_direction() * Ka
BallVelX = BallVelX + BallAccX * dT * Kv
BallPosX = BallPosX + BallVelX * dT * Kp
Note: the above formulae for velocity and position are 1st order approximation but should be sufficient for the purpose of this game.
Ka, Kv, Kp are some proportion coefficients. Choose them to make the relation between the sensed acceleration and the ball movement as you like. dT is the time difference between updates of the state of the ball. The function Am_I_NOT_touching_a_wall_in_X_direction() returns a 1 if the ball is free to move horizontally (in the direction of the tilt) and 0 otherwise.
Calculations for Y movement is symmetrical.
After trying alot I thought it is not easy to produce real time effect without using any physics engine. So its better to use BOX2d or Chipmunks or any other physics engines.

Why do my accelerometers respond so slowly?

I'm asking them at 50Hz / 50 times per second for data. When I suddenly flip the device on the x-axis by 90 degrees while the device was flat on a table with display facing up bevore, the values move pretty slowly to the "target" value for that position.
Now the weird thing is: If I increase the measurement-rate, the value will move faster to that new value upon suddenly flipping the device by 90 degrees. But if I just ask once per second for the new value, it take's very long until the value reaches the target. What can be the reason for this?
I don't do any kind of data aggregation, and don't accumulate anything. I just do some simple filtering to get rid of the noise. My method looks like this:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
// Use a basic low-pass filter to only keep the gravity in the accelerometer values for the X and Y axes
// accelerationX is an instance variable
accelerationX = acceleration.x * 0.05 + accelerationX * (1.0 - 0.05);
// round
int i = accelerationX * 100;
float clippedAccelerationValue = i;
clippedAccelerationValue /= 100;
[self moveViews:clippedAccelerationValue];
}
later on, in my -moveViews: method, I do this:
-(IBAction)moveSceneForPseudo3D:(float)accelerationValue {
if(fabs(lastAccelerationValue - accelerationValue) > 0.02) { // some little treshold to prevent flickering when it lays on a table
float viewAccelerationOffset = accelerationValue * 19 * -1;
newXPos = initialViewOrigin + viewAccelerationOffset;
myView.frame = CGRectMake(newXPos, myView.frame.origin.y, myView.frame.size.width, myView.frame.size.height);
lastAccelerationValue = accelerationValue;
}
}
As a result, of the device gets turned 90 degrees on the x-achsis, or 180 degrees, the view just moves pretty slowly to it's target position. I don't know if that's because of the physics of the accelerometers, or if it's a bug in my filtering code. I only know that there are fast paced games where the accelerometers are used for steering, so I almost can't imagine that's a hardware problem.
This line:
accelerationX = acceleration.x * 0.05 + accelerationX * (1.0 - 0.05);
is a low-pass filter, which works by computing a moving average of the x acceleration. In other words, each time that callback is called, you're only moving the accelerationX by 5% towards the new accelerometer value. That's why it takes many iterations before accelerationX reflects the new orientation.
What you should do is increase the 0.05 value, to say 0.2. I'd make a global #define and play around with different values along with different refresh rates.