libGDX zoom implementation isn't working right - libgdx

I would like to achieve a zoom effect that is similar to "Clash of Clans". I'm currently implementing zoom in a way that pretty much all the tutorials online do. There are of course a my camera specific constants to make it zoom at good speeds for my camera.The code DOES WORK.
#Override
public boolean zoom(float initialDistance, float distance) {
float zoomAmount = -1*(distance - initialDistance)/10000;
cameraHelper.addZoom(zoomAmount);
return true;
}
The camera helper class does this simple stuff:
public void addZoom(float amount) {
setZoom(zoom + amount);
}
public void apply(OrthographicCamera camera) {
camera.zoom = zoom;
camera.update();
}
So this does work. When I fling two fingers in one continuous motion and then stop moving the fingers on the screen, let's say I zoom in with zoomAmount = x, zoom gets called once and the zoom is done.
Problem:
However, if I then wiggle one, or both, of the fingers just a tiny bit, zoom gets called again with that same value for zoomAmount = x. So by wiggling a finger, it will simply keep adding to zoom and keep zooming in or out (depending on the current state of zoomAmount).
Question:
How do I make libgdx or java remember my original camera.zoom much like initialDistance in the zoom() method. OR, is there any other trick that I can do to have a responsive and smooth zoom (without removing fingers from the screen) much like the game "Clash of Clans".
Extra info:
I have tried pinch, same effect. I have thought about having a base zoom value and then some kind of percentage multiplier, however because I have to update the camera by setting its zoom (camera.zoom = zoom;) I can't think of a clever trick with adding or subtracting any values that will not be effected by zoom() being constantly called. Any thoughts anyone?

Related

Libgdx - how to zoom a rectangle/line (shaperenderer)

Let's say that I have an Actor which has a draw method:
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch,parentAlpha);
sr.begin(ShapeRenderer.ShapeType.Line);
sr.setColor(Color.RED);
sr.rectLine(getX(),getY(),getX() + getWidth(),getY(),3);
sr.end();
}
As you can see, the actor also draws a red line below itself.
Now, when I zoom the camera on a stage, where the actor is, he becomes bigger - it works but his red rectangle doesn't.
When I set debug() on the Actor, it shows the debug lines and zooms them aswell.
What I'm trying to achieve?
I need to zoom the line too, along the actor.
If there's anyone who knows how to achieve this, I would be grateful ;)
Shaperenderer is similar to batch. It has own coordinate matrix.
So you need to set projection matrix on shaperenderer too.
shaperenderer.setProjectionMatrix(camera.combined);
It will probably work for you.
But while shaperenderer drawing via vertexes, lines' width may not change even when you zoom in/out.
You can change line width with openGL function.
Gdx.gl.glLineWidth(width);
And additionaly im suggesting not to use Shaperenderer because its quite expensive class and only recommended for debug drawings.

I want to scale ParticleEffect - Libgdx

Basically my problem is simple - I have a ParticleEffect which looks nice on small screen but on my private phone (Samsung S4) it looks very small. My question is: How can I scale the effect?
I have already done something like this:
// Callback method
public void addEffect(Vector2 position, boolean special) {
// Callback Method for the particle effects
PooledEffect effect;
if (!special)
effect = (MathUtils.random(0, 1) == 0) ? yellow_star_pool.obtain()
: green_star_pool.obtain();
else
effect = special_star_pool.obtain();
effect.setPosition(position.x, position.y);
effect.scaleEffect(Values.Scalar_Width*1.3f);
effects.add(effect);
}
But the result is that the effect is scaling exponentially, something I don't want.
I just want the effect to be scaled so that it will look the same on all screens. How can I achieve that?
First, I want to explain why current approach doesn't work. Note that the method you use for scaling is named scaleEffect, not setScale. Every time you call it the effect will be scaled relative to its current state. When you use pooled particle effects you scale the effect as many times as the particle had been reused. That's why you see it scaling exponentially.
So, the solution is to scale only the particle effect that you use as a prototype for the pool. Something like:
prototypeEffect.scaleEffect(Values.Scalar_Width * 1.3f);
ParticleEffectPool yellow_star_pool = new ParticleEffectPool(prototypeEffect, 10, 100);
Then you didn't have to scale every particle effect you obtain.

How can I avoid sprite distortion as the frame's bounding box changes?

I have a spritesheet (packed by the wonderful TexturePacker) that looks like so:
and the animation as an animation works well using the current code.
However, when the animation moves to a frame where the bounding box is smaller (e.g. when the legs are closer, when the figure is kneeling, etc.), the image distorts so that the smaller bounding box fills the bounding box of the sprite.
How can I fix this?
The spritesheet above is a frame-extraction from a sample Adobe flash animation.
Easier way:
You can make all the textureregions the same size.
Hard way:
Keep it like this, but you need to save and calculate the size and position of every textureregion to draw.
I personally recommend you the first option, just make all the image the same size.
cant give you a link but here is what i do for these situations
while making the texture packer strip the white space
make a array of AtlasSprite for images
then in my render method
public void render(float delta) {
atlasSprite = animation.getKeyFrame(statetime, Animation.LOOPING);
atlasSprite.draw(batcher)
};
in my update method, which gives exact rectangular bounds of the current image displayed. Since you have stripped the whitespace and used AtlasSprite so bounds will be without blank whitespace.
public void update(float delta) {
bounds = sprite.getBoundingRectangle();
}
P.S. this is just a pseudo code and many syntatical error could be there its just to give you a idea

Understand LibGDX Coordinate system and drawing sprites

So I am super stoked to start using LibGDX for my first android title for OUYA and PC, but I am running into some snags with LibGDX. (All of my questions can be answered by looking at source, but I am really trying to understand the design choices as well).
To start with, the coordinate system. I created a project using the Project Setup jar, and it creates an OrthographicCamera like so
camera = new OrthographicCamera(1, h/w);
From my reading, I understand that LibGdx uses bottom left corner for 0,0 and yUp. Fine.
I see that it is pretty easy to change to y down if I want to, but I am not understanding the next bit of code that was created.
For the default sprite that gets created the position is set like so.
logoSprite.setOrigin(logoSprite.getWidth()/2, logoSprite.getHeight()/2);
logoSprite.setPosition(-logoSprite.getWidth()/2, -logoSprite.getHeight()/2);
When I run this basic program, I see the logo image I have added is centered on the screen. What I am trying to understand is why the values are negative in set position, and why is it using the sprite width and height instead of the graphics w and h of the view port? If I change to the screen width and height, then the image is drawn in some odd position in the lower right hand side of the screen.
My next question is sprite.setSize vs sprite.setScale. Why is the difference between the two? (They appear to do the same thing, except setScale leaves getWidth and getHeight unchanged).
Since my game will be using a 2D camera heavily for panning, zooming and rotation, I am trying to understand as much as I can about the libgdx framework before I start writing any code.
As a side note, I have a game development and math background and I have made several 2D and 3D games using XNA. I am finding LibGdx a bit frustrating as it does not abstract away OpenGL as much as I was expecting it to, and so far the 2D drawing I have been experimenting with seems to be more confusing than it should be!
I also wanted to note that I am planning to use spine for my animations. Should that change my choice to use y-up or y-down?
If you want to draw a sprite in center of screen, do this in your create method
logosprite.setposition(scrw/2-logosprite.getwidth()/2,scrh/2-logosprite.getheight/2);
here scrw is your viewport's width,
and scrh is your viewport's height,
this way your sprite will be in center of screen
sprite.setsize is used for setting size of the sprite and sprite.setscale is used when we scale a large/small texture so that its quality remains good in all devices(hdpi.mdpi,xhdpi,ldpi)..
no need to worry if you are using spine it works smoothly in libgdx..
You can use just this code if possible
logoSprite.setPosition(Gdx.graphics.getWidth()/2 - image.getWidth()/2,
Gdx.graphics.getHeight()/2 - image.getHeight()/2);
To center the sprite into the middle of the screen Where "image" is the Texture you have loaded/declared initially.
As for why it is coming in a odd position is due to the fact that you are using a camera.
Which changes the view a lot just go through the documentations of libgdx about camera here
In my case, I needed to set position of camera and then call update() method.
Then never forget camera's (0,0) is its center. Everything is being placed that way. My camera code:
private void cameralariUpdateEt() {
cameraGame.position.set(cameraGame.viewportWidth * 0.5f,
cameraGame.viewportHeight * 0.5f, 0);
cameraGame.update();
cameraScore.position.set(cameraScore.viewportWidth * 0.5f,
cameraScore.viewportHeight * 0.5f, 0);
cameraScore.update();
}
Call this method from inside render();
Step 1: Set the sprite origin to the position you would like it to rotate around.
// camera center point is (c.x, c.y)
logoSprite.setOrigin(c.x, c.y);
Step 2: make sure to set your sprite center to origin
logoSprite.setOriginCenter();
Step 3: Rotate your sprite
logoSprite.setRotation(Angle);
Step 4: Set the sprite position, [subtract half the sprites width and height to center the sprite]
logoSprite.setPosition(c.x - logoSprite.getWidth() / 2, c.y - logoSprite.getHeight() / 2)

Rotating a Model in Away3D

Here's what i want to do:
I want to load a model (most likely .3ds) into my .swf and be able to rotate it with the mouse.
This works fine at first glance, but there's problem, the rotations 'switch' over. Or to say it differently:
Imagine we have a model in the shape of a pipe.
If we drag the mouse to the right, the model rotates along its X-Axis to the left, mouse to the left, X-Axis rotation to the right, moving the mouse up, Y-Axis rotation downward, mouse down, Y-Axis rotation upward.
Now, lets say we turn the pipe to the right or left, until we face the (former) 'backside' of the pipe, and then we move the mouse down. The model will rotate downward instead of upward.
I hope you understand what i mean with this. I've been looking around for a good while now and never found a satisfying solution. There was talk about quaternions, but i can't grasp them.
Another suggestion i read somewhere is the following:
create a Matrix3D object, apply rotation on it, then multiply it with the desired Matrix3D of my 3d-Model.
I tried to do it, but the result stays the same, the directions of rotation switches depending on what side i'm facing.
private function update(e:Event):void
{
xCalc = (0.3*(stage.mouseX - lastMouseX));
yCalc = (0.3*(stage.mouseY - lastMouseY));
if(move)
{
var objTransform:Matrix3D = new Matrix3D();
objTransform.prependRotation(xCalc, Vector3D.Y_Axis, objC.pivotPoint);
objTransform.prependRotation(yCalc, Vector3D.X_Axis, objC.pivotPoint);
mesh.transform = multiply3DMatrices(mesh.transform, objTransform);
}
lastMouseX = stage.mouseX;
lastMouseY = stage.mouseY;
view.render();
}
multiply3DMatrices simply multiplies two 4x4 Matrices together.
objC is the ObjectContainer3D that holds my model. For some reason i cannot rotate it properly, unless i manipulate the rotationX/Y/Z properties.
mesh is the mesh inside of the Model (which is very simple, a single mesh).
Also, i'm aware that i could try another Framework for this (like papervision) but this project requires me to use Away3D.
Solved it by myself, the problem was that i created a new Matrix3D Object every time. The fixed code looks like this:
private function update(e:Event):void
{
...
if(move)
{
var objTransform:Matrix3D = mesh.transform;
objTransform.appendRotation(xCalc, Vector3D.Y_Axis, objC.pivotPoint);
objTransform.appendRotation(yCalc, Vector3D.X_Axis, objC.pivotPoint);
mesh.transform = objTransform;
}
...
}
And yes, the user bwroga was actually right, i should've used appendRotation instead of prependRotation, as well.