Rotating a Box2D light/body based on mouse input?

You flipped [icode]touch_angleCur[/icode] and [icode]touch_angleWant[/icode] on line 5.

When you provide input, you set the desired (wanted) angle. You slowly change the actual (current) angle to match it.

Where/How and exactly what to should I set the current angle? Should I do it in the show() method before everything gets started? If so, what should I set it to?

The initial ‘current angle’ is entirely up to you.

When you are setting the current angle, you do the following:

final Rot2D currAngle = Rot2D.fromVector(target.x - item.x, target.y - item.y);

If I were to set this in the show() method, the target would not exist yet. What would I do instead to set the angle, say, pointing north.

Just make it [icode]Rot2D.fromDegrees(0.0)[/icode] or any other angle you fancy.

It rotates sporadically similar to the previous gifs even with the mentioned changes. I think this is just a sign for me to quit game dev because obviously I am doing something extremely incorrect, and at this point, I don’t even remotely know what I am doing incorrectly.


	private Vector2 touch_target = new Vector2(0, 0);
	private Rot2D touch_angleCur = Rot2D.fromDegrees(0.0);
	private Rot2D touch_angleWant;

Then in render…


			touch_angleWant = Rot2D.fromVector(
				touch_target.x - entity_player.getBody().getPosition().x,
				touch_target.y - entity_player.getBody().getPosition().y
			);
			
			double cross1 = Rot2D.cross(touch_angleCur, touch_angleWant);
			
			if (cross1 > 0.0)
				touch_angleCur.rotate(Rot2D.fromDegrees(1.0));
			else
				touch_angleCur.rotate(Rot2D.fromDegrees(-1.0));
			
			double cross2 = Rot2D.cross(touch_angleCur, touch_angleWant);
			
			if (Math.signum(cross1) != Math.signum(cross2))
				touch_angleCur.load(touch_angleWant);
			
			entity_player.getBody().setTransform(entity_player.getBody().getPosition(), (float) touch_angleCur.getAngle());

Don’t give up so easily :slight_smile:

It might simply be that Box2D has a different coordinate system (other orientation) and you simply need to flip the rotation, and potentially adding/subtracting 90deg:


entity_player.getBody().setTransform(
   entity_player.getBody().getPosition(),
@@   touch_angleCur.getAngle() * -1 + Math.PI * 0.5
);

When you are visually debugging, you shouldn’t click at arbitrary positions. Slowly click in a circular pattern around the light source, and check whether the rotation is applied in the opposite direction, whether there is merely an angle offset, or whether it’s somehow behaving in another undesired way.

Your debugging is fairly bad, I would suggest using the good ol friend System.out.println(); print out the values that you need to debug such as the target position that it thinks the mouse is at, maybe you are trying to find the angle about the bottom left or upper left corner instead of the centre of the person, also use it to output the incremental rotation process. Click in a specific manor (like riven said) so that you can notice to what values that it works and at what values it doesnt work.

If I were doing this there are a few things that I would need to know , the position of rotation , current mouse position , the angular distance between the current angle of the mouse about the rotation point and the current rotation of the direction of the light perspective about the rotation point. The rotation point in your case is the centre of the entity you are moving so simply take that as the rotx and roty values . Calculating angular distance is almost as easy , Code from Riven :


entity_player.getBody().setTransform(
   entity_player.getBody().getPosition(),
   touch_angleCur.getAngle() * -1 + Math.PI * 0.5
);

that is the current angle around the point (thank you riven) of the mouse (hopefully).
I am no master of Box2d but I would assume you can do the same with the angle of the light from the entity position . * This may and most likely is where the bug could be so double check the values given by this section of code.
I assume you have a regular pulse that runs through your game , a tick , so to calculate how far your object should move each tick calculate distance between the two angles (the angle between them) / tickrate , this gives it an accelerated feel if you dont want this then replace this value with a standard speed , then from this rotate the entity by this amount until its current rotation is equal to that of the mouse point.
Hypothetical demo of this:


public float calc_rotation(Vertex2d entity_position,float rotation_entity , Vertex2d mouse_position,int tickrate){//returns the rotation increment 
		float rotation_mouse = rotate_about_point(entity_position,mouse_position);//fairly simple calculate the angle of point b about point a. Assume 0 is directly upward , we will work in degrees for now
		float absolute_distance = Math.max(rotation_entity,rotation_mouse) - Math.min(rotation_mouse,rotation_entity);//calculate the maximum possible distance between the two points
		if(absolute_distance <= 180){//if were going anti clockwise
			return -(absolute_distance / tickrate);
		}
		else{//if not
			absolute_distance = 360 - absolute_distance;//
			return absolute_distance/ tickrate;
		}
		
	}
	private float rotation_rate = 0;
	private boolean rotating = false;//update this as you require when the mouse is clicked
	private int rotate_tick = 0;//as we are doing this per second( ticks per second is the tickrate) then we use this to track the value
	public void setup(){
		rotation_rate = calc_rotation(your entity position, the amount its rotated, the current mouse position , current tickrate);
	}
	public void tick(){
		if(rotating){
			if(rotate_tick < tickrate){
				Entity.rotate_by(rotation_rate);//IE increase its current angle this can also be done by Entity.rotation += rotation_rate; Entity.rotate(Entity.rotation);
			}
			if(rotate_tick >= tickrate){//reset the variable once it reaches this point
				rotating = false;
				rotate_tick = 0;
			}
		}
	}

Hope this helps.
Thanks.

Added print statements to the code to get some insight into what is going on, but from what I can see, the wanted angle and the current angle do event meet up with each other, but they do not go to the right place. I am going to try to figure out what is going on, but I figured I’d post this while I do that in case anyone has anything else to say.

This is the output after one click.

http://pastebin.com/uebW4qdq

This is the code:


		if (Gdx.input.isTouched())
		{
			touch_target = new Vector2(Gdx.input.getX(), Gdx.input.getY());
			System.out.printf("[Touch] Just touched, setting new touch target.\n");
			System.out.printf("New touch target at (%f, %f)\n", touch_target.x, touch_target.y);
		}
		
		/** Touch handling */
			touch_angleWant = Rot2D.fromVector(
				touch_target.x - entity_player.getBody().getPosition().x,
				touch_target.y - entity_player.getBody().getPosition().y
			);
			
			System.out.printf("[Angle] touch_angleWant.getAngle() = %f\n", (float) touch_angleWant.getAngle());
			
			double cross1 = Rot2D.cross(touch_angleCur, touch_angleWant);
			
			if (cross1 > 0.0)
				touch_angleCur.rotate(Rot2D.fromDegrees(1.0));
			else
				touch_angleCur.rotate(Rot2D.fromDegrees(-1.0));
			
			double cross2 = Rot2D.cross(touch_angleCur, touch_angleWant);
			
			if (Math.signum(cross1) != Math.signum(cross2))
				touch_angleCur.load(touch_angleWant);
			
			System.out.printf("[Angle] touch_angleCur.getAngle() = %f\n", (float) touch_angleCur.getAngle());
			
			entity_player.getBody().setTransform(entity_player.getBody().getPosition(), (float) (touch_angleCur.getAngle()));

Does it always rotate in the wrong direction, or does it depend on the angles?

Anyway, can you run my Swing code? Does the problem show there? I cannot reproduce your issue.

I just ran your Swing code and it works fine and as intended.

I’ll go through the things that could possibly be wrong with my code.

I am pretty sure Body.setTransform(…) takes the angle in Radians due to this code I found in the tutorials, and your class is returning in Radians, so that’s most likely fine.


#define DEGTORAD 0.0174532925199432957f
#define RADTODEG 57.295779513082320876f
  
  dynamicBody->SetTransform( b2Vec2( 10, 20 ), 45 * DEGTORAD );

Every time the screen is touched or dragged or any kind of movement involving touch happens, I update a Vector2 object containing the mouse position, like this

touch_target = new Vector2(Gdx.input.getX(), Gdx.input.getY());

Then after that it finds the wanted angle like this


if (Gdx.input.isTouched())
{
	touch_target = new Vector2(Gdx.input.getX(), Gdx.input.getY());
	System.out.printf("[Touch] Just touched, setting new touch target.\n");
	System.out.printf("New touch target at (%f, %f)\n", touch_target.x, touch_target.y);
}

touch_angleWant = Rot2D.fromVector(
	touch_target.x - entity_player.getBody().getPosition().x,
	touch_target.y - entity_player.getBody().getPosition().y
);

System.out.printf("touch_angleWant = Rot2D.fromVector(\n\t%f - %f,\n\t%f - %f\n);\n",
	touch_target.x, entity_player.getBody().getPosition().x,
	touch_target.y, entity_player.getBody().getPosition().y
);

System.out.printf("touch_angleWant.getAngle() = %f\n", touch_angleWant.getAngle());

This is a section of the output of this code, right as I click: http://pastebin.com/Vt7QEaJa

It must be something where your code meets Box2D. Maybe it has something to do with transfering world coordinates to screen coordinates? I am very new to Box2D so I don’t know much about it but Burnt mentioned something about unproject? That may be the issue? I’ll look into what unproject does and if its useful here.

Render a sprite at touch_target and verify it actually is rendered where you click / drag.

Yeah… change this line:


if (Gdx.input.isTouched())
{
-   touch_target = new Vector2(Gdx.input.getX(), Gdx.input.getY());
+   Vector3 tmp = camera.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0));
+   touch_target = new Vector2(tmp.x, tmp.y);
   System.out.printf("[Touch] Just touched, setting new touch target.\n");
   System.out.printf("New touch target at (%f, %f)\n", touch_target.x, touch_target.y);
}

Box2D does everything in radians and meters. Gdx.input.getX() etc. return in pixels. Camera.unproject does the required conversion from pixels to meters (ever heard of dimensional analysis?) so that Box2D correctly interprets everything. Otherwise you might click at (500, 400) on your screen right next to your sprite, but Box2D thinks you just clicked a point 500 meters away in “world space,” when it’s actually less than a meter away.

Oh lord… This was what was wrong the entire time. It works properly now. - bangs head against desk -

Thanks for all of your help Riven and BurntPizza. I really appreciate it. This forum is a great place and has a great community.

Now to work on getting it to change position as well.

Also, a similar trick that can be of help, is setting your sprites’ size in meters rather than pixels:


// width and height are in meters
public static Sprite makeSprite(String name, float width, float height) {
    Sprite s = atlas.createSprite(name); // this is using a texture atlas, but it doesn't matter where the sprite comes from
    s.setScale(width / s.getWidth(), height / s.getHeight()); // the sprite width/height methods return pixels
    return s;
}

So then to render you just:


box2Dcamera.update(); // or viewport.apply();
batch.setProjectionMatrix(box2Dcamera.combined);

sprite.setCenter(box2Dbody.getPosition());
sprite.setRotation(Math.toDegrees(box2Dbody.getAngle()));

batch.begin();
sprite.draw(batch);
// more stuff, loop through and render entities, etc.
batch.end();

And it all just works. You can use the Box2D debug renderer to see that the sprites are the same size and in the same position as the bodies.

So, just to make sure I am understanding fully, I have a question.

I am setting up my camera like this:


camera = new OrthographicCamera();
viewport = new ScreenViewport(camera);
viewport.setUnitsPerPixel(1 / 32f);

And then loading the sprite like this:


sprite_floor = new Sprite(new Texture(Gdx.files.internal("wood.png")));
sprite_floor.setPosition(0, 0);

And then rendering the sprite like this.


batch.begin();
	sprite_floor.setSize(50, 50);
	sprite_floor.draw(batch);
batch.end();

If I use the setSize(50, 50), it looks fairly normal on the screen, as seen in the image below. But if I do not set a size, it is extremely large and pixelated.

I do not understand what is going on here. When feeding numbers to setSize(), what unit is being used? And how does this relate to .setUnitsPerPixel(1 / 32f)? If this is too much to explain you don’t have to, I understand, I just don’t have a very good conceptual grasp on the units, converting them, and how it works with the camera and sprites.

It’s in pixels, although how that relates to the camera/viewport is indeterminable from here as I do not know if you actually update the camera/viewport when needed.

So if it takes pixels then how is only 50 pixels so large? That is what I am confused about.

Also:


@Override
public void resize(int width, int height)
{
    viewport.update(width, height);
}

Here again. The rotating code works fine, but I have tried and failed to create a way for the player to move towards the mouse click.

Using setTransform and some kind of interpolation would have it going straight through walls, so I figured I should apply some kind of force to the body. I tried doing this, but of course, I am no good at math, and it did not end up going too well. I’ve followed a few online tutorials, but none seem to work/do what I need.

Can anyone help?