I have such screen: very long world which I draw on stage and I need to scroll it. Now I move my camera in touchDrag()
method. But I want to move my camera like I scroll it. E.g. I fling screen fast and my camera smoothly fly for some distance on y-axis. Just like it works in ScrollPane. How can I do this?
2 Answers
Yes, you understood me exactly. The question is not already actual. But thank you very much for your answer, this is another implementation of one idea. I did probably the same things, now I show my realization
public class CameraScroller extends InputAdapter implements GestureListener {
public static final String TAG = "SCROLL_TAG";
public static final float TIME_TO_SCROLL = 2.0f;
private Camera mCamera;
private final float mLowerPosition;
private final float mUpperPosition;
private GestureDetector mGestureDetector;
private float mTimer;
private float mVelocityY;
/**
* @param camera {@link Camera} that have to be scrolled
* @param upperPosition the upper limit position of camera
* @param lowerPosition the lower limit position of camera
*/
public CameraScroller(Camera camera, float lowerPosition, float upperPosition) {
mUpperPosition = upperPosition;
mLowerPosition = lowerPosition;
mGestureDetector = new GestureDetector(this);
mCamera = camera;
}
/**
* Call in Screen.render()
*/
public void act(float deltaTime) {
if (mTimer > 0) {// if timer is not 0
float acceleration_y = mVelocityY * deltaTime;// calculate acceleration (the rate of change of velocity)
mTimer -= deltaTime;// decreasing timer
mVelocityY -= acceleration_y;// decreasing velocity
mCamera.position.y += mVelocityY;
checkCameraPosition();// check if camera position is in not less or more than some value else stop camera (mTimer = 0)
}
}
private void checkCameraPosition() {
if (mCamera.position.y > mUpperPosition) {
mCamera.position.y = mUpperPosition;
mTimer = 0;
}
if (mCamera.position.y < mLowerPosition) {
mCamera.position.y = mLowerPosition;
mTimer = 0;
}
}
@Override
public boolean fling(float velocityX, float velocityY, int button) {
mTimer = TIME_TO_SCROLL;
mVelocityY = velocityY * Gdx.graphics.getDeltaTime();
return true;
}
@Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
float cam_y = mCamera.position.y;
System.out.println();
if (cam_y >= mLowerPosition && cam_y <= mUpperPosition) {
mCamera.position.y += deltaY;
}
if (mCamera.position.y > mUpperPosition) mCamera.position.y = mUpperPosition;
if (mCamera.position.y < mLowerPosition) mCamera.position.y = mLowerPosition;
return false;
}
@Override
public boolean panStop(float x, float y, int pointer, int button) {
mVelocityY = 0;
return true;
}
@Override
public boolean touchDown(float x, float y, int pointer, int button) {
mTimer = 0;
return true;
}
public GestureDetector getGestureDetector() {
return mGestureDetector;
}
@Override
public boolean zoom(float initialDistance, float distance) {
return false;
}
@Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
return false;
}
@Override
public boolean tap(float x, float y, int count, int button) {
return false;
}
@Override
public boolean longPress(float x, float y) {
return false;
}
}
I'm having a bit of trouble understanding the exact question, but I think your solution is to add some basic physics to your camera:
If you're looking for a way to have your camera keep moving after you drag it, have it record the velocity you drag it with as a vector. To make it slowly come to a stop, you modify the velocity after release with posNew = posOld + finalVelocity*t + 1/2 * a * t^2, where "a" is a (negative!) constant of friction (you'll have to experiment to find a good value for this) and t is the time between frames or updates.
Now I'll get a bit more specific:
- Recording final velocity:
When you drag the screen, you will record positions over time. To get a velocity, you simply find v = (posEnd - posStart)/t, where t is the time between frames. If you want to be able to scroll on just the x-axis, you only need to put x-values into the position variables. If you want x and y scrolling, you need to compute separate final velocities for both x and y and use them independently.
- Movement of camera after you release:
If you need to move in both the x and y directions, record a final velocity for both x and y and use the same formula as above to compute the next frame's velocity. To find the frame position, use the formula above.
- Movement after finalVelocity * t = -a*t^2:
You will get to a point eventually where this equation becomes true. This is the point where your camera stops moving. This is also the point you want to stop using physics. If you continue after this point, t^2 will increase faster than just t, and you will end up moving your camera in the opposite direction. I would suggest using an if-statement with a simple inequality wrapping the whole drag method.
- About using vectors:
If you must move in both the x and y directions, I would suggest using some sort of vector class. I'm not quite sure what language you're using or what libgdx is, but vectors should work in the same ways for any programming language, and they sometimes have built in functions for what I described above. If you use vectors, it might be helpful to get some background understanding of vector math when it comes to physics, which can be done through even a simple Wikipedia search.
I hope this answers your question, but if it doesn't, please comment and let me know what needs to be specified.