Today I ran into a gotcha of libgdx. I was trying to render a NinePatch packed with texture packer by constructing a NinePatch using this:
new NinePatch(assetManaget.get("pack.atlas", TextureAtlas.class).findRegion("path/to/ninepatch"));
Then I tried to render the object using its draw() method. Didn't work. Instead, the NinePatch image was just stretched.
Then I read the documentation about the constructor of new NinePatch(TextureRegion)
Construct a degenerate "nine" patch with only a center component.
What? This is obviously not what I was expecting.
Turned out that the proper way to do this is:
assetManaget.get("pack.atlas", TextureAtlas.class).createPatch("path/to/ninepatch");
I was looking for the code for pinch to zoom on libgdx that zooms with the origin at the center of two fingers. Then I found this. Unfortunately, it seems that the solution there is far from being elegant. Then I figure out a new solution using the camera matrix myself. Here is the code:
this.gestureDetector = new GestureDetector(new GestureAdapter(){
private Vector2 oldInitialFirstPointer=null, oldInitialSecondPointer=null;
private float oldScale;
@Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
game.getCamera().update();
game.getCamera().position.add(
game.getCamera().unproject(new Vector3(0, 0, 0))
.add(game.getCamera().unproject(new Vector3(deltaX, deltaY, 0)).scl(-1f))
);
return true;
}
@Override
public boolean pinch (Vector2 initialFirstPointer, Vector2 initialSecondPointer, Vector2 firstPointer, Vector2 secondPointer){
if(!(initialFirstPointer.equals(oldInitialFirstPointer)&&initialSecondPointer.equals(oldInitialSecondPointer))){
oldInitialFirstPointer = initialFirstPointer.cpy();
oldInitialSecondPointer = initialSecondPointer.cpy();
oldScale = game.getCamera().zoom;
}
Vector3 center = new Vector3(
(firstPointer.x+initialSecondPointer.x)/2,
(firstPointer.y+initialSecondPointer.y)/2,
0
);
zoomCamera(center, oldScale*initialFirstPointer.dst(initialSecondPointer)/firstPointer.dst(secondPointer));
return true;
}
private void zoomCamera(Vector3 origin, float scale){
game.getCamera().update();
Vector3 oldUnprojection = game.getCamera().unproject(origin.cpy()).cpy();
game.getCamera().zoom = scale; //Larger value of zoom = small images, border view
game.getCamera().zoom = Math.min(2.0f, Math.max(game.getCamera().zoom, 0.5f));
game.getCamera().update();
Vector3 newUnprojection = game.getCamera().unproject(origin.cpy()).cpy();
game.getCamera().position.add(oldUnprojection.cpy().add(newUnprojection.cpy().scl(-1f)));
}
});
The method Camera.unproject() is used to convert the coordinate of the points you touch(let's say it's coordinate A) to the coordinate that you use for displaying objects(let's say it's coordinate B). For example, if you draw a object on a SpriteBatch, and the batch has set to use a camera, then the coordinate used there is probably in coordinate B. However, another coordinate system, coordinate A is used for touch input. That's why we need to do an unprojection for the conversion between two coordinate systems. Remember to update the camera before using the unproject method. Otherwise, it may do that unprojection based on an outdated transformation matrix of the camera, which make the calculation inaccurate