I am implementing 3D modeling with LibGDX, and I want to manually rotate objects with the mouse, but I just I can't find any tutorials and examples that are right.
EDIT: Originally I only asked this question regarding rotating models, but I've discovered the same problem exists when rotating the camera.
Click here for the source code of a fully functional demo in github.com
Here's a snapshot of rotating the view and a model:
I want the object to rotate in the direction it is dragged by the mouse, no matter which direction it happens to be orientated at the time. As it is now, when I first drag the mouse to the right, the object rotates to the right about the screen Y axis as expected; but then when I drag the mouse upward I want the object to rotate upward about the screen X axis, but instead it spins to the left about the screen Z axis. Think of it like a floating ball in a bowl of water - whichever way you swipe at it, it rotates in that direction.
It seems to me that the mouse movement is transforming the objects directly in their local coordinate system; but instead I think it needs to transform the axis of rotation itself from the Screen Coordinate System into the Object Coordinate System before applying it to the object. I don't know, but it may be even more complicated than that.
I would really appreciate any insight or help to resolve this; I'm running out of hair to pull out... Thanks in advance.
LibGDX implements OpenGL. The terminology we use in OpenGL can help us to know how LibGDX works behind the scene. The other technology that implements OpenGL is WebGL, WebGL uses JavaScript. LibGDX uses Java. Once we know how OpenGL works, drawing objects and rotating objects should be fun. Of course depending on what we are drawing. OpenGL is well
documented. OpenGL itself always works the same. The first question, should be what are we drawing? And what are the objectives of the project. So, you want to draw the cube and rotate it. Cool. Once we can draw one cube and rotate it, we can add more objects in the scene. Cool. Strategically you can divide your project into parts.
Draw your object.
Rotate it.
Add more objects.
Rotate them.
We are done.
If you want to rotate the view as well, you can use the same process as above with some modifications. For example:
Draw the view.
Draw objects inside the view.
Rotate the objects.
Rotate the view.
On the other hand you can just use the camera and move it around the scene.
Done.
To make things worse LibGDX can extend many different classes, the programmer have to implement all abstract methods. Your code might look differently or some functions behave differently depending on which class you extend or implement in your project. The documentation about those classes is stochastic. Each and every abstract class comes with its abstract methods. The programmer should release any other resources allocated by LibGDX by using dispose() method. With just few changes your code should work as expected.
For Example:
//
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Input.Buttons;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.InputProcessor;
//import etc...
public class Space extends ApplicationAdapter implements ApplicationListener, InputProcessor {
SpriteBatch batch;
BitmapFont font;
float backgroundColor[];
Cube cubes[];
ModelBatch modelBatch;
int selectedCube;
PerspectiveCamera camera;
Environment environment;
int xCubes = 27;
int touchedButton;
int lastX, lastY;
float Theta, Phi, dX, dY;
Vector3 screenAOR;
float screenAng;
float point[];
int side[];
int front[];
float w;
float h;
Model viewM;
ModelInstance viewMin;
Vector3 position;
ColorAttribute attrib;
Vector3 cubePositions[];
boolean drag;
#Override
public void create () {
drag = false;
Theta = 0;
Phi = 0;
batch = new SpriteBatch();
font = new BitmapFont();
font.setColor(Color.FOREST);
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
modelBatch = new ModelBatch();
screenAOR = new Vector3();
camera = new PerspectiveCamera(67f, 3f, 2f);
camera.position.set(10f, -10f, 70f);
camera.lookAt(Vector3.Zero);
camera.up.set(Vector3.Y);
camera.near = 1f;
camera.far = 500f;
camera.update();
backgroundColor = new float[]{.9f, .9f, .7f};
environment = new Environment();
environment.set(new ColorAttribute( ColorAttribute.AmbientLight, .6f, .6f, .6f, 1f));
environment.add(new DirectionalLight().set(.8f, .8f, .8f, 50f, 50f, 50f));
environment.add(new DirectionalLight().set(.5f, .5f, .5f, -50f, -50f, 50f));
spaceModel();
Gdx.input.setInputProcessor(this);
//Gdx.graphics.requestRendering();
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
//Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//Gdx.gl.glClearColor(backgroundColor[0], backgroundColor[1], backgroundColor[2], 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
Gdx.gl.glEnable(GL20.GL_DEPTH_TEST);
Gdx.gl.glEnable(GL20.GL_CULL_FACE);
batch.begin();
modelBatch.begin(camera);
modelBatch.render(viewMin, environment);
for(int i = 0; i < cubes.length; i++){
modelBatch.render(cubes[i].modelInstance, environment);
}
font.draw(batch, "Space pro...", 10, 100);
batch.end();
modelBatch.end();
}
#Override
public void dispose () {
batch.dispose();
modelBatch.dispose();
font.dispose();
viewM.dispose();
}
/*///////////////////////////////////
//Implements all abstract methods.
//
*/////////////////////////////////
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
//lastX -= screenX;
//lastY -= screenY;
//float aspRatio = w/h;
//float angle = 40.0f;
moveModel(screenX, screenY);
// distance of mouse movement
//screenAng = (float) Math.sqrt( ((lastX * lastX) + (lastY * lastY)) );
//screenAng = (float) Math.tan((angle * 0.5)* (Math.PI/180));
// direction vector of the AOR
//screenAOR.set((lastY/screenAng), (lastX/screenAng), 0f );
//screenAOR.set(projection(angle,aspRatio,h,w));
//public Vector3 set(float x, float y, float z)
screenAOR.set(dX, dY, 0f);
if ( touchedButton == 0 ){
//public Matrix4 rotate(Vector3 axis, float degrees)
//cubes[ selectedCube ].modelInstance.transform.rotate( screenAOR, screenAng );
//public Matrix4 rotate(float axisX, float axisY, float axisZ, float degrees)
cubes[ selectedCube ].modelInstance.transform.rotate(dX, dY, 0f, Theta);
cubes[ selectedCube ].modelInstance.transform.rotate(dX, dY, 0f, Phi);
}
else{
//public void rotateAround(Vector3 point, Vector3 axis, float angle)
//camera.rotateAround( Vector3.Zero, screenAOR, (-screenAng/5.5f) );
//public void rotate(float angle, float axisX, float axisY, float axisZ)
//camera.rotate(Theta, dX, dY, 0f);
//camera.rotate(Phi, dX, dY, 0f);
//camera.rotateAround(position, screenAOR, Theta);
camera.rotateAround(Vector3.Zero, screenAOR, Theta);
camera.update();
//camera.rotateAround(position, screenAOR, Phi);
camera.rotateAround(Vector3.Zero, screenAOR, Phi);
camera.update();
viewMin.transform.rotate(dX, dY, 0f, Theta);
viewMin.transform.rotate(dX, dY, 0f, Phi);
}
//Gdx.graphics.requestRendering();
//Gdx.app.log("touchDragged:", screenAng+" : "+screenAOR+" : "+touchedButton);
return true;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
drag = true;
if(button == Buttons.LEFT){
touchedButton = 0;
}
else{
touchedButton = button;
}
Gdx.app.log("touchDown:", button+" : "+screenX+" : "+screenY+" : "+pointer);
return true;
}
#Override
public boolean keyDown(int i) {
float move = 1.0f;
float pX = w/10;
float pY = h/10;
if(i == Keys.LEFT){
pX -= move;
//public void rotate(float angle, float axisX, float axisY, float axisZ)
//camera.rotate(-45, pX, 0f, 0f);
camera.rotate(-45, 0f, pY, 0f);
//camera.update();
//public void translate(float x, float y, float z)
//camera.translate(move, 0f, 0f);
}
if(i == Keys.RIGHT){
pX += move;
//camera.rotate(45, pX, 0f, 0f);
camera.rotate(45, 0f, pY, 0f);
//camera.update();
//camera.translate(-move, 0f, 0f);
}
if(i == Keys.DOWN){
pY -= move;
//camera.rotate(-45, 0f, pY, 0f);
//camera.rotate(-45, pX, 0f, 0f);
camera.rotate(45, pX, 0f, 0f);
//camera.update();
//camera.translate(0f, 0f, move);
//camera.update();
//camera.translate(0f, move, 0f);
}
if(i == Keys.UP){
pY += move;
//camera.rotate(45, 0f, pY, 0f);
//camera.rotate(45, pX, 0f, 0f);
camera.rotate(-45, pX, 0f, 0f);
//camera.update();
//camera.translate(0f, 0f, -move);
//camera.update();
//camera.translate(0f, -move, 0f);
}
camera.update();
Gdx.app.log("KeyDown: ", pX+" : "+pY+" : "+i);
return true;
}
#Override
public boolean keyUp(int i) {
Gdx.app.log("KeyUp: ",i+" : ");
return false;
}
#Override
public boolean keyTyped(char c) {
//Gdx.app.log("KeyTyped: ",c+" : ");
return false;
}
#Override
public boolean touchUp(int i, int i1, int i2, int i3) {
drag = false;
Gdx.app.log("touchUp: ",i+" : "+i1+" : "+i2+" : "+i3);
return true;
}
#Override
public boolean mouseMoved(int i, int i1) {
if(!drag)
{
dX *= 0.96;
dY *= 0.96;
Theta += dX;
Phi += dY;
return false;
}
Gdx.app.log("mouseMoved: ", i+" : "+i1);
return false;
}
#Override
public boolean scrolled(int i) {
return false;
}
public void moveModel(int x2, int y2){
dX = (float) ((x2 - lastX)*2*(Math.PI/w));
dY = (float) ((y2 - lastY)*2*(Math.PI/h));
Theta += dX;
Phi += dY;
lastX = x2;
lastY = y2;
}
public void spaceModel(){
xCubes = 27;
selectedCube = 14;
ModelBuilder modelB = new ModelBuilder();
attrib = new ColorAttribute(1,Color.VIOLET);
Material m = new Material();
m.set(attrib);
//public Model createXYZCoordinates(float axisLength, Material material, long attributes)
viewM = modelB.createXYZCoordinates(w, m , 1);
cubePositions = new Vector3[xCubes];
for(int i = 0; i < xCubes; i++){
cubePositions[i] = new Vector3((i/9), ((i%9)/3), (i%3)).scl(20f).add(-20f, -20f, -20f);
}
cubes = new Cube[xCubes];
for(int i = 0; i < xCubes; i++){
cubes[i] = new Cube(cubePositions[i], (i == selectedCube));
}
viewMin = new ModelInstance(viewM);
position = cubePositions[0];
viewMin.transform.setTranslation(position);
Gdx.app.log("viewModel: ", w+" : "+h+" : "+w/h);
}
float[] projection(float angle, float a, float z1, float z2){
float ang = (float) Math.tan((angle * 0.5)* (Math.PI/180));
float[] proj = {
(float)0.5/ang, 0, 0, 0,
0, (float)0.5*a/ang, 0, 0,
0, 0, -(z2+z1)/(z2-z1), -1,
0, 0, (-2*z2*z1)/(z2-z1), 0
};
return proj;
}
}
/*////////////////////
//Draw cubes.
//
*/////////////////
class Cube{
Vector3 position;
Model model;
ModelInstance modelInstance;
Cube(Vector3 cubePosition, boolean selected) {
position = cubePosition;
compose(selected);
}
//etc...
}
//
When you are rotating the camera and rotating the objects, the direction can change or sometimes reversed. Depending at what angle is the object and the camera are at, at that point in time. It's like looking at the review mirror facing the opposite direction. So user's position and orientation in the scene is also important.
//
//
When you are looking at an object spinning “ < ---” around the circle, logically it is going to change direction “ ---> ”. When it reaches the far end “ <---> ”. i.e. From right to left and from left to right. Of course the user will still be using the same button. When you press the other buttons the same logic follows. Different sequences of rotations can result in different images as well. The other option which is time consuming is: translate(rotate(scale(geometry))). Eventually the entire image will be a single whole that is composed of its various parts. This process mighty help you to debug your code, and figuring out what caused the errors. With a little bit of maths, things can't get any worse. The science behind the object you are rotating is also important. When you are looking at the object rotating, the beauty is on the observers eye. e.g. Are you looking at the front side or the back side? Finally you have to use maths to get your model to behave the way you want it to behave.
Enjoy.
I asked the same question on one of the other sister forums, and got an answer that I was able to implement.
See the discussion here.
Here is the change to the code that made the whole thing work correctly:
Here is the change to the code that made the whole thing work correctly:
#Override public boolean touchDragged( int screenX, int screenY, int pointer )
{
lastX -= screenX;
lastY -= screenY;
// distance of mouse movement
screenAng = Vector3.len( lastX, lastY, 0f );
// direction vector of the AOR
screenAOR.set( lastY/screenAng, lastX/screenAng, 0f );
if ( touchedButton == 0 )
{ // rotate the part
// transform the screen AOR to a model rotation
Matrix4 camT, camR, camRi, modT, modR;
camT = new Matrix4();
camR = new Matrix4();
modT = new Matrix4();
modR = new Matrix4();
decompose( camera.view, camT, camR );
camRi = camR.cpy().inv();
decompose( cubes[ selectedCube ].modelInstance.transform, modT, modR );
tempMat.idt()
.mul( modT )
.mul( camRi )
.rotate( screenAOR, -screenAng )
.mul( camR )
.mul( modR );
cubes[ selectedCube ].modelInstance.transform.set( tempMat );
}
else if ( touchedButton == 1 )
{ // rotate the camera
// transform the AOR from screen CS to camera CS
// get the camera transformation matrix
tempMat.set( camera.view );
tempMat.translate( camera.position );
tempMat.inv();
// transform the screen AOR to a world AOR
worldAOR = transform( tempMat, screenAOR, worldAOR ).nor();
// apply the rotation of the angle about the world AOR to the camera
camera.rotateAround( Vector3.Zero, worldAOR, screenAng/5.5f );
camera.update();
}
lastX = screenX;
lastY = screenY;
Gdx.graphics.requestRendering();
return true;
}
Vector3 transform( Matrix4 mat, Vector3 from, Vector3 to )
{
// transform a vector according to a transformation matrix
to.x = from.dot( mat.val[ Matrix4.M00 ], mat.val[ Matrix4.M01 ],
mat.val[ Matrix4.M02 ] )+mat.val[ Matrix4.M03 ];
to.y = from.dot( mat.val[ Matrix4.M10 ], mat.val[ Matrix4.M11 ],
mat.val[ Matrix4.M12 ] )+mat.val[ Matrix4.M13 ];
to.z = from.dot( mat.val[ Matrix4.M20 ], mat.val[ Matrix4.M21 ],
mat.val[ Matrix4.M22 ] )+mat.val[ Matrix4.M23 ];
return to;
}
void decompose( Matrix4 m, Matrix4 t, Matrix4 r )
{
Matrix4 I4 = new Matrix4(); // Identity
for ( int i = 0; i < 4; i++ )
{
for ( int j = 0; j < 4; j++ )
{
if (i == 3 || j == 3)
{
r.val[ i*4+j ] = I4.val[ i*4+j ];
t.val[ i*4+j ] = m.val[ i*4+j ];
}
else
{
r.val[ i*4+j ] = m.val[ i*4+j ];
t.val[ i*4+j ] = I4.val[ i*4+j ];
}
}
}
}
How come my rect does not rotate during draw()?
When I enable box2d debug I see the actual box2d object rotating correctly but the actual rendered shape does not rotate ..
Tween.to(getPlayer(), ShapeAccessor.ANGLE, 0.7f)
.target(0.4f)
.ease(Elastic.OUT)
.repeatYoyo(0, 0)
.start(getLevel().getTweenManager());
Player has the following draw() method ..
public void draw(ShapeRenderer sRenderer, Vector2 pos) {
sRenderer.begin(ShapeType.Filled);
sRenderer.setColor(getColor().r, getColor().g, getColor().b, getColor().a);
sRenderer.identity();
sRenderer.rotate(0, 0, 1, getAngle());
sRenderer.translate(pos.x - getWidth(), pos.y - getHeight(), 0);
sRenderer.rect(0, 0, getWidth() * 2, getHeight() * 2);
sRenderer.end();
}
Heres my TweenAccessor ..
public class ShapeAccessor implements TweenAccessor<Shape> {
public static final int ANGLE = 5;
public int getValues(Shape target, int tweenType, float[] returnValues) {
switch (tweenType) {
case ANGLE: // animate object by rotation
returnValues[0] = ((Shape) target).getBody().getAngle();
return 1;
default:
assert false;
return -1;
}
}
public void setValues(Shape target, int tweenType, float[] newValues) {
switch (tweenType) {
case ANGLE: // animate object by rotation
((Shape) target).getBody().setTransform(
((Shape) target).getBody().getPosition(),
newValues[0]);
break;
default:
assert false;
}
}
}
Needed to scale angle up by 60 and modify the .rect coordinates ..
sRenderer.begin(ShapeType.Filled);
sRenderer.setColor(getColor().r, getColor().g, getColor().b, getColor().a);
sRenderer.identity();
sRenderer.translate(pos.x, pos.y, 0);
sRenderer.rotate(0, 0, 1, getBody().getAngle() * 60.0f);
sRenderer.rect(-getWidth(), -getHeight(), getWidth() * 2, getHeight() * 2);
sRenderer.end();
I'm currently trying to make a game, and I'm still novice with using cameras, and I'm thinking that two OrthographicCameras may be necessary, but I'm not sure if that's the most efficient way, or even how to do so.
Basically, I want this to be the layout for it:
The Main Area is where the main stuff is, which is a Server Interface. The Game Level is where the actual game part is in. I am currently using a ScissorStack to cut the region, but with this demo, results make me question how to do this:
public class TestScissorStackAndCamera extends ApplicationAdapter {
private SpriteBatch batch;
private OrthographicCamera camera;
private Sprite sprite;
private int width, height;
#Override
public void create() {
Gdx.gl.glClearColor(0, 0, 0, 0);
width = Gdx.graphics.getWidth();
height = Gdx.graphics.getHeight();
batch = new SpriteBatch();
camera = new OrthographicCamera(width, height);
camera.position.set(width / 2, height / 2, 0);
camera.update();
createSprite();
}
private void createSprite() {
Pixmap map = new Pixmap(width, height, Format.RGBA8888);
map.setColor(Color.RED);
map.fillRectangle(0, 0, width, height);
map.setColor(Color.BLUE);
map.drawLine(width / 2, 0, width / 2, height);
map.drawLine(0, height / 2, width, height / 2);
Texture texture = new Texture(map);
sprite = new Sprite(texture);
}
#Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined); // The Question!
batch.begin();
{
Rectangle scissors = new Rectangle();
Rectangle area = new Rectangle(10, 10, width - 20, height - 20);
ScissorStack.calculateScissors(camera, batch.getTransformMatrix(), area, scissors);
ScissorStack.pushScissors(scissors);
batch.draw(sprite, 0, 0);
batch.flush();
ScissorStack.popScissors();
}
batch.end();
}
public static void main(String[] args) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = "ScissorStack & Camera Test";
config.resizable = false;
new LwjglApplication(new TestScissorStackAndCamera(), config);
}
}
Questioning batch.setProjectionMatrix(camera.combined)
I marked a line in the code with a comment, The Question!, which is what is affecting the results. If I don't have it, using the camera.translate(...) method, the image is drawn at (0, 0) but what it does is it moves what part is viewed. If I do have that line, when I use the camera.translate(...) method, the image is drawn respectively to the position of the camera.
In respect to the game that I'm currently developing, it behaves awkwardly without the projectionMatrix not being set, but when I do set it, it messes up the positioning of the rest of the game. I even added some testing features, and it's not rendering inside of the correct ScissorStack
How could I go about setting up two cameras, or what could I do to set up what I'm trying to correctly and efficiently?
With my actual game (not the mock-up) this is what it is doing. It should be rendering inside of the red lines, but it's not:
If you'd like to see my current code for my GameLevel that is handling the ScissorStack and OrthographicCamera:
public GameLevel(int x, int y, int displayWidth, int displayHeight) {
this.x = x; // x = 10
this.y = y; // y = 10
this.displayWidth = displayWidth; // displayWidth = Gdx.graphics.getWidth() - x - 10
this.displayHeight = displayHeight; // displayHeight = Gdx.graphics.getHeight() - y - 120
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(displayWidth / 2, displayHeight / 2, 0);
// FBLAGame.batch.setProjectionMatrix(camera.combined);
camera.update();
init();
}
...
#Override
public void render() {
Rectangle area = new Rectangle(x, y, displayWidth, displayHeight);
Rectangle scissor = new Rectangle();
Matrix4 matrix = FBLAGame.batch.getTransformMatrix();
ScissorStack.calculateScissors(camera, matrix, area, scissor);
ScissorStack.pushScissors(scissor);
renderLevel();
FBLAGame.batch.flush();
ScissorStack.popScissors();
Pixmap map = new Pixmap(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), Format.RGBA8888);
map.setColor(Color.RED);
map.drawRectangle((int) area.x, (int) area.y, (int) area.width, (int) area.height);
Texture t = new Texture(map);
map.dispose();
FBLAGame.batch.draw(t, 0, 0);
}
So I understand the concept. The idea is that box2d more or less works in meters, so you need to do a conversion from pixels to it. Makes sense. I was following the tutorial/intro to box2d here. It mentions to do the conversion and gives you some example amounts to use. Now, that's all well and good, but I find when I'm using such techniques, the debugger box doesn't seem to render where they should. The collision does work as expected however.
In my GameScreen class, here's how I initialize the ground:
ground = new BodyDef();
// set the position half way up the ground
ground.position.set(0,16 * GameScreen.WORLD_TO_BOX);
groundBody = world.createBody(ground);
groundShape = new PolygonShape();
// make the height 16px so it doubles to 32
groundShape.setAsBox(Gdx.graphics.getWidth() * GameScreen.WORLD_TO_BOX, 16.0f * GameScreen.WORLD_TO_BOX);
groundBody.createFixture(groundShape, 0.0f);
My render method in that screen is like so:
public void render(float delta) {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
camera.update();
stateTime += Gdx.graphics.getDeltaTime();
batch.begin();
batch.draw(background, 0, Gdx.graphics.getHeight() - 512, 512, 512);
batch.draw(trailingBackground, 512, Gdx.graphics.getHeight() - 512);
int heightToCover = Gdx.graphics.getHeight() - 512;
int widthToCover = Gdx.graphics.getWidth();
for(int w = 0; w < widthToCover; w += 32) {
for(int h = 0; h < heightToCover; h += 32) {
batch.draw(lightBackgroundTile, w, h, 32, 32);
}
}
player.update();
player.render(stateTime, batch);
batch.end();
levels.get(currentLevel).render(camera);
// physics updates
world.step(1/60f, 6, 2);
debugRenderer.render(world, camera.combined);
}
Here's the constructor of the player class, so you can see how im setting up its collision box2d objects. I also pasted the update method which is called in the above render loop to adjust the sprites position.
public Player(int x, int y, World world) {
super();
playerTexture = new Texture(Gdx.files.internal("assets/hero.png"));
init(x, y, 128, 128, playerTexture, false, world);
bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(x * GameScreen.WORLD_TO_BOX, y * GameScreen.WORLD_TO_BOX);
body = getWorld().createBody(bodyDef);
collisionBox = new PolygonShape();
collisionBox.setAsBox(32 * GameScreen.WORLD_TO_BOX, 64 * GameScreen.WORLD_TO_BOX, new Vector2(64 * GameScreen.WORLD_TO_BOX, 64 * GameScreen.WORLD_TO_BOX), 0.0f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = collisionBox;
fixtureDef.density = 10f;
fixtureDef.friction = 0.4f;
fixtureDef.restitution = 0f;
body.createFixture(fixtureDef);
collisionBox.dispose();
addFrame(0, 0, 128, 128);
}
public void update() {
this.setX((int) ((body.getPosition().x) * GameScreen.BOX_TO_WORLD));
this.setY((int) ((body.getPosition().y) * GameScreen.BOX_TO_WORLD));
}
Now, when I remove the multiplication of those static floats in the various calculations, sizes, etc, the collision remains correct and the debugger box shows up. However passing the raw pixels to box2d feels wrong. Is there something I'm missing here as to why the debugging boxes don't show up as is?
I do like you are doing but might be a little different.
Might be a little overkill but here's my jucl port
jucl/Android - pastebin
Then I just have utility classes ie:
public class Pixel {
public static float toMeter(float pixels) {
return (float)LengthConversions.Pixel2SIf(pixels);
}
public static Vector2 toMeter(Vector2 vecPixel) {
return new Vector2(Pixel.toMeter(vecPixel.x), Pixel.toMeter(vecPixel.y));
}
}
public class Meter {
public static final float METERS_PER_PIXEL = (float) LengthConversions.SI_PIXEL;
public static float toPixel(float meter) {
return (float)LengthConversions.SI2Pixelf(meter);
}
}
In my initialize:
int graphicsWidth = Gdx.graphics.getWidth();
int graphicsHeight = Gdx.graphics.getHeight();
CAMERA_WIDTH_METERS = Pixel.toMeter(graphicsWidth);
CAMERA_HEIGHT_METERS = Pixel.toMeter(graphicsHeight);
Then in my game classes (like your Player class). In my case it was a pinball game so i have Flipper, Ball, Bumper, etc. I have a #Override render() method where I sync up the sprite with the physics body.
Here's an example file..sry it's messy but might be helpful.
Pinball Engine Class file
I'm trying to make an object appear where the person last touched. However when I try to do so it appears in the wrong place. I assume this is because of the fact the coordinates that the input returns is different to the display coordinates, my code is as follows:
public class Core implements ApplicationListener, InputProcessor
{ //Has to be here otherwise the code formatting becomes buggy
private Mesh squareMesh;
private PerspectiveCamera camera;
private Texture texture;
private SpriteBatch spriteBatch;
Sprite sprite;
float moveX = 0;
private final Matrix4 viewMatrix = new Matrix4();
private final Matrix4 transformMatrix = new Matrix4();
#Override
public void create()
{
Gdx.input.setInputProcessor(this);
texture = new Texture(Gdx.files.internal("door.png"));
spriteBatch = new SpriteBatch();
sprite = new Sprite(texture);
sprite.setPosition(0, 0);
viewMatrix.setToOrtho2D(0, 0, 480, 320);
float x = 0;
float y = 0;
}
#Override
public void dispose()
{
}
#Override
public void pause()
{
}
#Override
public void render()
{
viewMatrix.setToOrtho2D(0, 0, 480, 320);
spriteBatch.setProjectionMatrix(viewMatrix);
spriteBatch.setTransformMatrix(transformMatrix);
spriteBatch.begin();
spriteBatch.disableBlending();
spriteBatch.setColor(Color.WHITE);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
//spriteBatch.draw(texture, 0, 0, 1, 1, 0, 0, texture.getWidth(),
// texture.getHeight(), false, false);
sprite.draw(spriteBatch);
spriteBatch.end();
update();
}
#Override
public void resize(int width, int height)
{
float aspectRatio = (float) width / (float) height;
camera = new PerspectiveCamera(67, 2f * aspectRatio, 2f);
}
#Override
public void resume()
{
}
public void update()
{
float delta = Gdx.graphics.getDeltaTime();
if(Gdx.input.isTouched())
{
Vector3 worldCoordinates = new Vector3(sprite.getX(), sprite.getY(), 0);
camera.unproject(worldCoordinates);
sprite.setPosition(Gdx.input.getX(), Gdx.input.getY());
float moveX = 0;
float moveY = 0;
}
}
I cropped this code for sake of simplicty.
I also made a video demonstrating the bug:
http://www.youtube.com/watch?v=m89LpwMkneI
Camera.unproject converts screen coordinates to world coordinates.
Vector3 pos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(pos);
sprite.setPosition(pos.x, pos.y);
Firstly, Gdx.input.getX() and Gdx.input.getY() return "screen coordinates". You want to transform these to your "camera coordinates". Screen coordinates typically have (0,0) in the top left corner of the window. I think your camera coordinates have (0,0) at the bottom left corner (either libgdx or opengl are doing that). Your video seems to suggest that this true. So you will need to multiply the Y value by -1. Secondly, I suspect the scale of the screen is different from the scale of the camera. I think you can fix the scale by multiplying by (world/screen).
Let's say your screen has width=800, height=600 and your world has width=480 height=320. Then your new X,Y for your sprite should be:
X = Gdx.input.getX()*(480/800)
Y = Gdx.input.getY()*(320/600)*-1
you should check your touch cordinates.
Gdx.app.log("", "hello x"+touchx);
Gdx.app.log("", "hello x"+touchy);
here touchx and touchy are your input x and input y variables
then do calculation where touch should work
like if u touched x=100,y=100
and touchx is coming 120
and touch y is coming 120
soo in your update method do this
sprite.setPosition(Gdx.input.getX()-20, Gdx.input.getY()-20);
i think this will help
I figured out the screen size/ game ratio and multiplied it to the current screen size:
rect.x=((((1024/Gdx.graphics.getWidth()))* Gdx.graphics.getWidth())
for a screen width of 1024 pixels
There must be an easier way however this works for me.