LibGDX Box2D incorrect sprite position of moving body - libgdx

I'm trying to make a 2D car side scrolling game. I'm using wheel joints to move the car.
Here is the screenshot when car is not moving.
Screenshot of car when not moving
And when the car is moving. You can see sprites are not in correct position.
Screenshot when the car is moving
Here is the constructor of car object.
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
bodyDef.position.set(x, y);
//Chassis
PolygonShape chassisShape = new PolygonShape();
chassisShape.setAsBox(width, height);
chassisFixtureDef.shape = chassisShape;
chassis = world.createBody(bodyDef);
// Car Body Sprite
Sprite body = new Sprite(new Texture(Gdx.files.internal("data/body.png")));
body.setSize(5f, 2f);
body.setPosition(0f, 0);
body.setOrigin(body.getWidth() / 2, body.getHeight() / 2);
chassis.setUserData(body);
chassis.createFixture(chassisFixtureDef);
//Left Wheel
CircleShape wheelShape = new CircleShape();
wheelShape.setRadius(height / 1.5f);
wheelFixtureDef.shape = wheelShape;
leftWheel = world.createBody(bodyDef);
//Sprite Test
wheel = new Sprite(new Texture(Gdx.files.internal("data/wheel.png")));
wheel.setSize(1f, 1f);
wheel.setOrigin(wheel.getWidth() / 2, wheel.getHeight() / 2);
leftWheel.setUserData(wheel);
leftWheel.createFixture(wheelFixtureDef);
//Right Wheel
rightWheel = world.createBody(bodyDef);
rightWheel.setUserData(wheel);
rightWheel.createFixture(wheelFixtureDef);
//Left Axis
WheelJointDef def = new WheelJointDef();
def.bodyA = chassis;
def.bodyB = leftWheel;
def.frequencyHz = chassisFixtureDef.density;
def.localAnchorA.set(-width / 2 * 1.7f + wheelShape.getRadius(), -height / 2 * 2.5f);
def.localAxisA.set(Vector2.Y);
def.maxMotorTorque = chassisFixtureDef.density * 30;
leftAxis = (WheelJoint) world.createJoint(def);
def.bodyB = rightWheel;
def.localAnchorA.x *= -1;
rightAxis = (WheelJoint) world.createJoint(def);
And Here is the code to draw sprites relevant to bodies on screen.
for (Body body : bodies)
if (body.getUserData() != null && body.getUserData() instanceof Sprite){
Sprite sprite = (Sprite)body.getUserData();
sprite.setPosition(body.getPosition().x - sprite.getWidth()/2, body.getPosition().y - sprite.getHeight()/2);
sprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
sprite.draw(batch);
}
Sorry if couldn't make my question very specific or clear. I'm new to Stackoverflow.
Edit [Solved]
Just placed in render method [Correct]
renderer.render(world, camera.combined);
after gl.clear
I was doing it after drawing everything on screen. [Wrong]

seems like you update your physic after rendering it, because your physic-debugged polygon moved already further than the sprite shows up. You could try to first update your physics and then draw your sprites.
There shouldn't be something wrong with the code / calculations itself.
If you're already doing so it's probably because the updates of your physic simulation updates more frequently than your rendering-loop.

Related

How to create smooth motion for a mouse follower along a predefined path?

I want to make a tracing game. I want my circle to follow the path as the user traces the letter (path of the letter). The user can not go back to the area which is already traced
import flash.events.Event;
import flash.geom.Point;
var i: Number;
var size: int = 80;
var down: Boolean = false;
var up: Boolean = true;
var inside: Boolean = true;
var outside: Boolean = true;
var circle: Shape = new Shape();
stage.addEventListener(Event.ENTER_FRAME, loop);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseup);
char.addEventListener(MouseEvent.MOUSE_DOWN, mousedown);
function loop(e: Event) {
if (down == true) {
// Checks if mouse pointer is on path i.e 'S' alphabet
if (s.hitTestPoint(stage.mouseX, stage.mouseY, true)) {
inside = true;
outside = true;
var point: Point = maskobj.globalToLocal(new Point(stage.mouseX, stage.mouseY));
var point2: Point = new Point();
//Checks if mouse pointer is completely outside of drawn area
for (i = 0; i < 2 * Math.PI; i += (2 * Math.PI) / 10) {
point2.x = stage.mouseX + (size / 3) * Math.cos(i);
point2.y = stage.mouseY + (size / 3) * Math.sin(i);
if ((maskobj.hitTestPoint(point2.x, point2.y, true))) {
outside = false;
break;
}
}
//Checks if mouse pointer is completely inside drawn area
for (i = 0; i < 2 * Math.PI; i += (2 * Math.PI) / 10) {
point2.x = stage.mouseX + (size / 3) * Math.cos(i);
point2.y = stage.mouseY + (size / 3) * Math.sin(i);
if (!(maskobj.hitTestPoint(point2.x, point2.y, true))) {
inside = false;
break;
}
}
//Character will be moved only if mouse position not to far from current position
if (outside == false) {
if (inside == false) {
//Increases drawn area by drawing a circle shape in 'maskobj' MovieClip
circle.graphics.beginFill(0x0000ff);
circle.graphics.drawCircle(point.x, point.y, size);
circle.graphics.endFill();
maskobj.addChild(circle);
//Moves character to new position
char.x = stage.mouseX;
char.y = stage.mouseY;
}
}
}
}
}
function mouseup(e: MouseEvent): void {
up = true;
down = false;
}
function mousedown(e: MouseEvent): void {
down = true;
up = false;
}
When I trace the path,the motion is not smooth. Can someone please suggest a way to make the motion smooth OR suggest another way to achieve the same. Thank you in advance.
I've created a drawing game before that allowed the user to draw a path.
Not sure why Wicked's answer was down-voted, as the first thing you need to do is to use the highest frame rate that you can get away with. The higher the frame rate, the smoother your curve.
I see that your code draws a circle at the current position if the conditions are met. It might be better to draw a line from the last point.x/point.y to the current one instead of just a circle, so that you don't have any holes in your path.
I couldn't get around the fact that the line was jagged (a series of straight lines) as it was being drawn, but as soon as the user lifted their finger I was able to take the points along the line they had drawn and replace them with a smooth bezier Path (a series of simple bezier curves), which worked well. You could also do this on-the-fly once you have 3 points (you need 3 points to draw a curve).
Here is a good reference on how to achieve this, with theory and code samples. See further down the page for bezier paths. You'll need to convert to AS3, but it shouldn't be difficult.
Another tip is to do as little calculation as possible within the ENTER_FRAME. You could pre-calculate the two values used by your loops (2 * Math.PI) and ((2 * Math.PI) / 10) as these are constants. You could also calculate (size/3) once at the top of the function, and especially pre-calculate the 10 values for Math.sin(i) and Math.cos(i) and store them in an Array (basically a LUT - Look Up Table) as these are the heaviest math ops you're doing.
My final tip is that your code doesn't check if the point being drawn is very close to the last point that was drawn. I would recommend you do this, and only draw a point after the mouse has moved a minimum distance (e.g. 2 pixels). Otherwise you could get the mouse sitting still in one spot and your code is drawing circle upon circle on top of itself needlessly.
Try increasing the FPS in your document to atleast double what you currently have
Modify>Document...>Frame Rate

as3 - How to access child's child?

I dispatched an event for a bullet to spawn on the page. However the bullet should be located in the area where the page's child child should be. There is a Page One MovieClip, which has a child named player and the player's child is gun. So I'm trying to keep the location and rotation of the bullet the same as the player's gun. So I need to access Page One's child's child. The player and the turret are using instance names and not variables.
I tried this code, but the bullet spawns in the page but will not spawn itself on the turret's location. This event is located in the PageOne class.
function fire(e:Event)
{
var b:Bullet = new Bullet();
b.rotation = player.turret.rotation;
b.x = player.turret.x + player.turret.width * Math.cos(player.turret.rotation / 180 * Math.PI);
b.y = player.turret.y + player.turret.width * Math.sin(player.turret.rotation / 180 * Math.PI);
addChild(b);
}
You can access to the turret child. The problem is that you need to convert the coords inside the turret MovieClip to the coords inside the PageOne MovieClip:
var b: Bullet = new Bullet();
var turretpoint:Point = new Point(player.turret.width, 0);
var pagepoint: Point = this.globalToLocal(player.turret.localToGlobal(turretpoint));
b.rotation = (player.scaleX > 0) ? player.turret.rotation : 180 - player.turret.rotation;
b.x = pagepoint.x;
b.y = pagepoint.y;
addChild(b);

body falls slowly in any gravity

I've created a World with earth gravity and I place an entity in the scene (contains a sprite and a Body) and it falls down slowly like a balloon.
Here's how I set the World:
world = new World(new Vector2(0, -GRAVITY_EARTH), true);
and here's the relevant Box2D code for the Body etc:
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(positionX, positionY);
// Create our body in the world
body = world.createBody(bodyDef);
// Grab the first idle sprite to use as initial
Sprite sprite = idleSprites.get(0);
// Create a box shape to represent our hit box
PolygonShape box = new PolygonShape();
box.setAsBox(sprite.getWidth() / 2f, sprite.getHeight() / 2f);
// Create a fixture definition to apply our shape
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = box;
fixtureDef.density = 1f; // Give it full density
fixtureDef.friction = 0f; // Give it no friction
fixtureDef.restitution = 0f; // Make it not bouncy
// Create our fixture and attach it to the body
fixture = body.createFixture(fixtureDef);
// Remember to dispose of any shapes after you're done with them!
// BodyDef and FixtureDef don't need disposing, but shapes do.
box.dispose();
and how I draw the sprite:
TextureRegion keyFrame = idleAnimation.getKeyFrame(stateTimeSeconds, true);
Vector2 position = body.getPosition();
batch.draw(keyFrame, position.x - keyFrame.getRegionWidth() / 2f, position.y - keyFrame.getRegionHeight() / 2f);
and the relevant code in the render() method:
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
final float deltaTime = Gdx.graphics.getDeltaTime();
camera.update();
spriteBatch.setProjectionMatrix(camera.combined);
spriteBatch.begin();
spriteBatch.draw(sky, 0, 0);
tim.animate(spriteBatch, deltaTime);
spriteBatch.draw(floor, 0, 0);
spriteBatch.end();
// Render physics for debug
debugRenderer.render(world, camera.combined);
// Run physics
doPhysicsStep(deltaTime);
}
private void doPhysicsStep(float deltaTime) {
// fixed time step
// max frame time to avoid spiral of death (on slow devices)
float frameTime = Math.min(deltaTime, 0.25f);
accumulator += frameTime;
while (accumulator >= TIME_STEP) {
world.step(TIME_STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
accumulator -= TIME_STEP;
}
}
I've tried changing the density of the fixture, and I've tried changing the gravity value, and I've tried changing the TIME_STEP and nothing is having an effect. The body just falls down slowly like a balloon.
It looks to me like you're using pixels as your units, box2d treats every unit as a meter and so you're hitting the internal limit of 2.0 units per time step, see http://www.iforce2d.net/b2dtut/gotchas. You can get around this by setting up your camera in world units instead of pixels, you have to scale all your sprites and positions to fit into world units instead of pixels though.
Something like this may do:
float w = (float) Gdx.graphics.getWidth();
float h = (float) Gdx.graphics.getHeight();
camera = new OrthographicCamera(30, 30 * (h / w));
the way the camera is set up here allows the height of the viewport to be variable based on the screens' aspect ratio.
Then to setup the sprite change it by a set factor
sprite.setSize(sprite.getWidth / PIX2M, sprite.getHeight / PIX2M);
where PIX2M is a static field defining how many pixels are a meter in box2d
Alternatively you can set the dimensions of the sprite explicitly to a value which makes physical sense and with the aspect ratio of the original image(my personal preference) . So an image of a person which is 100 x 500 for example could be set like this.
sprite.setSize(.4f, 2f);
meaning the person is 2 meters high and .4 meters wide. Also with this method you don't need a PIX2M conversion factor and you will always know the exact size of your body. Since you set the camera to a specific number of world units, 30 in this case, the sprite will take up the same amount of room on the screen no matter the resolution of the display.

ActionScript - Drawing BitmapData While Maintaining Center Registration of Display Object

maintaining the center registration point of a circle shape, or any other display object with center registration, while being converted to a bitmap object is proving to be difficult.
the following code converts a circle shape into a bitmap object and positions it in the center of the stage and subsequently removes its center registration poin.
the x and y origin of a new bitmapData object (top left) is the same as the x and y origin of the circle (center), but it's not possible to translate the x and y position of the bitmapData.draw() - its parameters only accept width, height, transparency and fill color.
var myCircle:Shape = new Shape();
myCircle.graphics.beginFill(0xFF0000, 1.0);
myCircle.graphics.drawCircie(0, 0, 100);
myCircle.graphics.endFill();
var matrix:Matrix = new Matrix();
matrix.tx = myCircle.width / 2;
matrix.ty = myCircle.height / 2;
var myCircleBitmapData:BitmapData = new BitmapData(myCircle.width, myCircle.height, true, 0x00FFFFFF);
myCircleBitmapData.draw(myCircle, matrix);
var result:Bitmap = new Bitmap(myCircleBitmapData, PixelSnapping.AUTO, true);
result.x = stage.stageWidth / 2 - matrix.tx;
result.y = stage.stageHeight / 2 - matrix.ty;
addChild(result);
with the help of a matrix translation, the new bitmap object will appear centered in the stage, but applying a regular or 3D rotation, etc., will clearly demonstrate that the registration point is now the top left corner instead of the center.
how can i convert a center registered display object into a bitmap while maintaining its center registration?
it appears the most common approach is to simply add the bitmap as a child of a sprite container and rotate the sprite container rather than the bitmap itself.
var myCircle:Shape = new Shape();
myCircle.graphics.beginFill(0xFF0000, 1.0);
myCircle.graphics.drawCircie(0, 0, 100);
myCircle.graphics.endFill();
var matrix:Matrix = new Matrix();
matrix.tx = myCircle.width / 2;
matrix.ty = myCircle.height / 2;
var myCircleBitmapData:BitmapData = new BitmapData(myCircle.width, myCircle.height, true, 0x00FFFFFF);
myCircleBitmapData.draw(myCircle, matrix);
var myCircleBitmap:Bitmap = new Bitmap(myCircleBitmapData, PixelSnapping.AUTO, true);
myCircleBitmap.x -= matrix.tx;
myCircleBitmap.y -= matrix.ty;
var circleContainer:Sprite = new Sprite();
circleContainer.addChild(myCircleBitmap);
alternatively, for those using Flash Professional IDE, there is the option to employ fl.motion.MatrixTransformer.rotateAroundInternalPoint instead of using a container sprite.
The following tutorial looks like what you're trying to do.
http://www.8bitrocket.com/2007/10/30/Actionscript-3-Tutorial-BitmapData-rotation-with-a-matrix/

ActionScript 3 - Tweening rotateAroundExternalPoint

i'm unsuccessfully trying to rotate a rectangle around an external point while tweening. i'm trying to lock the top of the red rectangle to the line while it tweens from left to right and rotates from 0º to 90º.
alt text http://www.freeimagehosting.net/uploads/0b937c92e6.png
the image above shows 3 states of the tween. state 1 shows the red rectangle at the start of the line with no angle. state 2 shows the red rectangle has tweened half way along the line with an angle of 45º that is also half the total angle of 90º. state 3 shows the final position of the tween where the red rectangle has an angle of 90º and is placed at the edge of the line.
it seems the problem i'm experiencing is that while tweening, the rotation causes the top of the red rectangle to lose sync with the black line.
here is my code that doesn't work, but hopefully will give you a clearer picture of what i'm attempting.
var angle:Number = 90;
var previousAngle:Number = 0;
var distanceObject:Object = new Object();
distanceObject.distance = line.width;
distanceTween = new Tween(distanceObject, "distance", None.easeNone, 0, distanceObject.distance, 5, true);
distanceTween.addEventListener(TweenEvent.MOTION_CHANGE, tweenHandler);
function tweenHandler(evt:TweenEvent):void
{
var angleShift:Number = (angle / distance) * distanceObject.distance;
//1:tween RedBox position
redBox.x = line.x + line.width * distanceObject.distance;
//2:tween RedBox angle
var externalPointMatrix:Matrix = redBox.transform.matrix;
MatrixTransformer.rotateAroundExternalPoint(externalPointMatrix, 0 + redBox.width * distanceObject.distance, 0, angleShift - previousAngle);
redBox.transform.matrix = externalPointMatrix;
previousAngle = angleShift;
}
I don't think you have specified the problem well enough for a generic solution. There are 3 things changing here: x, y and rotation. Each of these is calculated as a result of a point on the rectangle (the blue "x" in your diagram) that changes over time. That means the thing you need to focus on first is the point on the rectangle that changes over time. Next you need to know that the x and y can be calculated using that point along with the rotation.
So break it down into steps.
find the location of the "x" point on the line
rotate the object
find the location of the "x" point wrt to the rectangle
based on the angle of rotation and the known location of the "x" point calculate the x and y position of the rectangle (SOHCAHTOA)
Here is some code to illustrate:
package
{
import com.greensock.TweenNano;
import flash.display.Sprite;
import flash.events.Event;
[SWF(width='500', height='300', backgroundColor='#ffffff', frameRate='30')]
public class BoxAnim extends Sprite
{
private static const LINE_WIDTH:int = 350;
private static const RECT_WIDTH:int = 150;
private static const RECT_HEIGHT:int = 100;
private static const FINAL_ROTATION:Number = Math.PI/2;
public var point:Number;
private var line:Sprite;
private var rect:Sprite;
private var cross:Sprite;
public function BoxAnim()
{
addEventListener(Event.ADDED_TO_STAGE, addedToStage);
}
private function addedToStage(event:Event):void
{
line = new Sprite();
addChild(line);
line.graphics.lineStyle(10, 0x0);
line.graphics.lineTo(LINE_WIDTH, 0);
line.x = 50;
line.y = 175;
rect = new Sprite();
addChild(rect);
rect.graphics.lineStyle(4, 0xFF0000);
rect.graphics.beginFill(0xFF0000, 0.5);
rect.graphics.drawRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
rect.x = 50;
rect.y = 175;
cross = new Sprite();
addChild(cross);
cross.graphics.lineStyle(5, 0x41a9f4);
cross.graphics.moveTo(-5, -5);
cross.graphics.lineTo(5, 5);
cross.graphics.moveTo(5, -5);
cross.graphics.lineTo(-5, 5);
cross.x = 50;
cross.y = 175;
point = 0;
TweenNano.to(this, 3, {point: 1, onUpdate: tick});
}
private function tick():void
{
// first calculate where the point should be on the line
cross.x = (point * LINE_WIDTH) + line.x;
// calculate the angle of rotation
var rotationRadians:Number = (point * FINAL_ROTATION);
rect.rotation = rotationRadians*180/Math.PI;
// calculate where on the rectangle the point would be
var rectCrossX:Number = (point * RECT_WIDTH);
// use trig to find the x & y points
rect.x = cross.x - Math.cos(rotationRadians)*rectCrossX;
rect.y = cross.y - Math.sin(rotationRadians)*rectCrossX;
}
}
}
I'm just using the variable point as a percentage that goes from 0 to 1. I then scale it to find the position of the "x" point on the line. Scale it again to figure out the rotation. Scale it again to find where it lies along the top of the rectangle. Then trig solves the location of the corner of the rectangle wrt the point.