Libgdx ,can i attach animation in a Body? - java

im kinda new here, anywho... i am interested in the android gaming app development and i am learning on my own how to do so, using Libgdx as my game-engine and already made a small game not something exciting.
i have recently started to learn how to use the World variable and creating bodies in it, i want to create a class that extends Actor, give it animations (i know how to give animations) and to try and display it on the body, so where ever the body goes, the animation is played on it (basically below the animation is the body that is not displayed ofc)
so my question is : is there a way to give this costume actor into the body so it will play it over the body? (attaching them)
Will appreciate the help!
** NOTE : be aware that my knowledge is still limited, i am a college student for Program Engineering and i am not fully into much depth in java yet (my college not teaching game-engines) **
UPDATE
i have created a costume class, made animations to each action (like herowalk, herosit ... etc...)
i wrote a render method in it :
public void render(SpriteBatch batch) {
float posX = bbody.getPosition().x * PPM;
float posY = bbody.getPosition().y * PPM;
float rotation = (float) Math.toDegrees(bbody.getAngle());
sprite.setPosition(posX,posY);
sprite.setRotation(rotation);
sprite.draw(batch);
}
and created a body, placed it in the middle of my screen and attached the sprite to it using SetUserData() :
BodyDef bdef = new BodyDef();
bdef.position.set(160 / PPM, 200 / PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
PolygonShape shape = new PolygonShape();
shape.setAsBox(5 / PPM, 5 / PPM);
bbody = world.createBody(bdef);
Bodyarray.add(bbody);
FixtureDef fdef = new FixtureDef();
fdef.shape = shape;
fdef.filter.categoryBits = BIT_BOX;
fdef.filter.maskBits = BIT_PLATFORM | BIT_BALL;
bbody.createFixture(fdef);
bbody.setUserData(sprite);
and in my main class where i draw with batch i wrote :
player.sprite.setRegion(player.getAnimationup().getKeyFrame(dt, true));
dt += elapsedTime;
player.render(batch);
to constantly change the animations depends on if the player turns to the right, left, up , down.
the only problem i have is that the sprite it self (the animation works perfectly) is being drawn on the bottom left side of the screen (not on 0,0, it looks like it got the x,y of the body but still away from it) and can see that its attached to it when moving the body (i put controls to control the body movement) and i see the sprite moving with it.
for example im trying to check the coordination of X,Y for both the body and the sprite with Gdx.app.log as usual and both have the SAME exact X and Y. (ofc the sprite has his multiplied by PPM (placed it as 100) since the sprite is not a physical body)
what is causing the wrong location of the sprite?

Welcome to Stack Overflow!
The answer to your question is "Not exactly." Box2D gives you a method called Body#setUserData() which lets you create a reference from the Body to any object (in this case an Animation object, but it is up to you to keep their positions in sync. See the pseudo-code below adapted from the libGDX wiki:
// Create an array to be filled with the bodies
// (better don't create a new one every time though)
Array<Body> bodies = new Array<Body>();
// Now fill the array with all bodies
world.getBodies(bodies);
for (Body b : bodies) {
// Get the body's user data - in this example, our user
// data is an instance of the Entity class
Animation anim = (Animation) b.getUserData();
if (anim != null) {
// Update the entities/sprites position and angle
TextureRegion frame = anim.getKeyFrame( ... );
// Now draw the frame using b.getPosition() and b.getAngle()
}
}
By the way, the libGDX wiki is an excellent resource and reference as you start learning libGDX.

Related

How to draw a sprite within the FitViewport view. libgdx

The basic question here is: how to ALWAYS keep your sprites within the Fitviewport? How to keep a reference to the view in order to have the proper coordinates as to where to draw?
I'm trying to spawn enemies into the gameplay screen. But this is handled by a FitViewport, and enemies and even the player can move outside the FitViewport on certain screen resolutions. So far the problem seems to be in the Y axis.
The FitViewport is made like this:
gameCamera = new OrthographicCamera();
gameCamera.setToOrtho(false);
gameViewport = new FitViewport(MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT,gameCamera);
gameViewport.setScreenBounds(0,0,MyGame.WORLD_WIDTH,MyGame.WORLD_HEIGHT);
Then the camera position gets updated like this at the resize() method:
gameViewport.update(width,height); //not used when using the virtual viewport in the render method.
gameCamera.position.set(player.position.x + 200,player.position.y, 0);
Then the update() method calls the Player's own update() method which includes these lines:
//POSITION UPDATE
if (this.position.x<0) this.position.x=0;
if (this.position.x>Gdx.graphics.getWidth() - width) this.position.x= Gdx.graphics.getWidth() - width;
if (this.position.y<0) this.position.y = 0;
if (this.position.y>PlayScreen.gameViewport.getScreenHeight() - height) this.position.y = PlayScreen.gameViewport.getScreenHeight()- height;
Notice for the X axis I'm still using Gdx.graphics dimensions because I'm yet to make it work with PlayScreen.gameViewport.getScreenHeight() (gameViewport has been set to static for this purpose).
Also on enemy spawn (the problem related here is that they spawn outside of the screen Y in terms of what I see) I have this code inside the update() method of the Screen implementing all these viewports:
//Alien Spawn
if (System.currentTimeMillis() - lastZSpawn >= SpawnTimer){
count++;
lastZSpawn= System.currentTimeMillis();
for (int i=0;i<count;i++){
int x = Gdx.graphics.getWidth();
int y = random.nextInt((int)gameViewport.getScreenHeight() - Alien.height);
if (entities.size()<6){
entities.add(new Alien(new Vector2(x,y),1, alienImages,(float)((0))));
}
}
}
Also using gameViewport.getScreenHeight() here cause Gdx.graphics wasnt giving the correct result (it gave me the same issue really).
The render() method is correctly implemented in terms of the batch and applying the viewport:
MyGame.batch.setProjectionMatrix(gameCamera.combined);
gameViewport.apply();
MyGame.batch.begin();
for (int i = entities.size()-1; i>=0;i--){
entities.get(i).render();
}
You should never change the position of your player or enemies when resizing, that's why a viewport is for, remove all the code that do that first, to make your viewport work as you expected you need to create a new instance of camera passing the new viewport width and height when you resize, i prefer to make my camera static so i can acess its atribbutes from everywhere i want, you should do something like this:
public static OrthographicCamera update(int width,int height){
instance = new OrthographicCamera(width, height);
instance.setToOrtho(false);
return instance;
}
The answer to my problem is posted by myself in another question of mine which was also driven by a confusion in the implementation of FitViewports and using WorldWidth and WorldHeight properties as coordinates of reference when drawing objects into the game, and also correctly setting the camera position taking these values into consideration aswell.
The answer is here even though its text and not code and its mainly what i already wrote in this very post. FitViewport doesnt scale properly Libgdx

Touch event on a specific area of a box2d body

I have these box2d bodies. I want to check for touch events on the bodies. To be specific, touch on a certain area of the body. Please see the image below, within the body, how can I check if the user touched on the blue area of the body (upper right corner).
I can get the touch coordinates, convert it to world coordinates and check if they overlap with that of the box, but that will trigger even if the box is touched anywhere inside it.
Maybe its too late but i will answer your question for who needs help.
You should create a fixture for the body which defines body's edges and attributes. Than you need to add fixtures to the body as sensor. Code sample would be seems like this.
// create bodydef
BodyDef bdef = new BodyDef();
bdef.type = BodyType.DynamicBody;
bdef.position.set(60 / PPM, 120 / PPM);
bdef.fixedRotation = true;
bdef.linearVelocity.set(1f, 0f);
// create body from bodydef
Body body = world.createBody(bdef);
// create box shape for player collision box
PolygonShape shape = new PolygonShape();
shape.setAsBox(13 / PPM, 13 / PPM);
// create fixturedef for player collision box
FixtureDef fdef = new FixtureDef();
fdef.shape = shape;
fdef.density = 1;
fdef.friction = 0;
// create player collision box fixture
body.createFixture(fdef);
shape.dispose();
// create box shape SENSOR for player
shape = new PolygonShape();
shape.setAsBox(13 / PPM, 3 / PPM, new Vector2(0, -13 / PPM), 0);
// create fixturedef SENSOR for player
fdef.shape = shape;
fdef.isSensor = true;
// create player SENSOR fixture
body.createFixture(fdef).setUserData("SENSOR");;
Have a nice day.
The Body itself has nothing common with Libgdx Input/Output operations - this is what is important to understand. Libgdx is handling touching and gestures by
Listeners mechanism - for example InputListener
Scene2d built-in framework
Then you cannot bind body instance to the listener however you can do this with whole screen (and then calculate somehow if coordinates are in body bounds - then if yes in which part exactly) or you can do this by creating a Scene2d actor instance.
If I were you I would create an Actor (for example rectangle shape) with size of part of body you want to be "touchable". The actor should has attached ClickListener with method that you want to fire on touch. Then in the actor's act() method I would update actor position due to the body position.

Libgdx - Having one Entity partially exempt from Phsyics

I'm using LibGDX to make a mobile game, now I'm facing a certain issue.
I have a certain Entity which can collide with the wall, when this happens the wall receives a certain force which then causes it to go off the screen and not be positioned properly anymore.
I have tried using LibGDX's isSensor variable but after doing that my Entity crosses straight through the wall.
How can I make it so the wall does stop the Entity from moving through but isn't affected itself by the force of the Entity?
I'm using the physics body editor for my colission shapes as they are not in normal geometrical formats.
Thank you,
Rene
You just need to make the wall static.
I'm not sure how you do it using the physics body editor as I've never used it, but here's some code I wrote a while back that does something similar...
private void buildRoom()
{
Vector2[] roomCorners = new Vector2[]{new Vector2(0f, 0f),
new Vector2(0f, ROOM_HEIGHT),
new Vector2(ROOM_WIDTH, ROOM_HEIGHT),
new Vector2(ROOM_WIDTH, 0f)};
ChainShape roomShape = new ChainShape();
try
{
roomShape.createLoop(roomCorners);
BodyDef roomDef = new BodyDef();
roomDef.type = BodyDef.BodyType.StaticBody;
Body room = world.createBody(roomDef);
room.createFixture(roomShape, 0);
} finally
{
roomShape.dispose();
}
}

How to create a body with Polygon and Rectangle attached together in Libgdx

I am a newbie in Libgdx. I am working on detecting collisions for two bodies. Have created two bodies using "polygonshape" but unfortunately there isn't any method to detect collision among "PolygonShapes" but for "rectangles" .So I came up with an idea of attaching the rectangle at the front of each of the polygons and then detect collision for rectangles but when I run the code, I don't see any rectangles but only polygons.
Here is my code,
public class Player implements Screen, InputProcessor {
private Body polybody;
private Player player;
private World world;
Array<Rectangle> raindrops;
private Body enemybody;
private Sprite polysprite;
public final float width,height;
private Vector2 movement=new Vector2();
private float speed=580;
public Player(World world,float x,float y,float width)
{
this.width=width; //IMP
height=width*2;
BodyDef polygon=new BodyDef();
polygon.type=BodyType.DynamicBody;
polygon.position.set(x,y); //
PolygonShape poly =new PolygonShape();
poly.setAsBox(width/2,height/2); //
FixtureDef polyfixture=new FixtureDef();
polyfixture.shape=poly;
polyfixture.friction=0.8f; //
polyfixture.restitution=0.1f; //
polyfixture.density=3; //
//creating actual body
polybody=world.createBody(polygon);
polybody.createFixture(polyfixture);
//disposing the body
BodyDef rectangle=new BodyDef();
rectangle.type=BodyType.DynamicBody;
Rectangle rect=new Rectangle();
rect.setWidth(50);
rect.setHeight(50);
rect.setPosition(x, y);
enemybody=world.createBody(rectangle);
polysprite=new Sprite(new Texture("img/car.jpg"));
polysprite.setSize(0.5f, 1);
polysprite.setOrigin(polysprite.getWidth()/2, polysprite.getHeight()/2);
polybody.setUserData(polysprite);
poly.dispose();
}
public void update()
{
polybody.applyForceToCenter(movement, true);
enemybody.applyForceToCenter(movement,true);
}
public Body getBody(){
{
return polybody;
}
}
}
Collision Code:
public Player(World world,float x,float y,float width)
{
this.width=width; //IMP
height=width*2;
BodyDef polygon=new BodyDef();
polygon.type=BodyType.DynamicBody;
polygon.position.set(x,y); //
// polygon.fixedRotation=true;
//polygon shape
//Rectangle poly=new Rectangle();
PolygonShape poly =new PolygonShape();
poly.setAsBox(width/2,height/2); //
//fixture defn
polygon.position.set(5,4);
FixtureDef polyfixture=new FixtureDef();
polyfixture.shape=poly;
polyfixture.friction=0.8f; //
polyfixture.restitution=0.1f; //
polyfixture.density=3; //
//creating actual body
polybody=world.createBody(polygon);
polybody.createFixture(polyfixture);
// polybody.applyAngularImpulse(52, true);
//disposing the body
polysprite=new Sprite(new Texture("img/car.jpg"));
polysprite.setSize(2, 3); //size of mario
polysprite.setOrigin(polysprite.getWidth()/2, polysprite.getHeight()/2);
polybody.setUserData(polysprite);
//2nd body
BodyDef polygons=new BodyDef();
polygons.type=BodyType.DynamicBody;
PolygonShape polys=new PolygonShape();
polys.setAsBox(2,2);
FixtureDef polyxfixture=new FixtureDef();
polyxfixture.shape=polys;
polyxfixture.friction=0.8f;
polyxfixture.restitution=0.1f;
polyxfixture.density=3;
polybodys=world.createBody(polygons);
polybodys.createFixture(polyxfixture);
poly.dispose();
}
#Override
public void beginContact(Contact contact) {
// TODO Auto-generated method stub
Fixture fixtureA=contact.getFixtureA();
Fixture fixtureB=contact.getFixtureB();
System.out.println("collides");
}
This is my code. I have created 2 bodies using "polygonshape" and "rectangle" but rectangle isn't created. I can't find what is the mistake. Please help!! Thanks in advance
It seems like you are a bit confused and you are mixing two absolutely independend things:
Box2D, a physics engine, which does all physic calculations
(including collission detection) for you.
The Rectangle, Polygon and the Intersector class of libgdx.
I try to explain what both of them do and why you can't use them together.
The Polygonshape is part of the Box2D engine. It is used to give a Fixture it's shape, which is then used by the collission detection of Box2D.
You can read something about Box2D here.
The Rectangle class is some kind of "Helperclass" in Libgdx, which let you define a Rectangle, with a given position, width and height. It can be used together with the Intersectorclass, which provides some usefull methods for collission detection and other things.
There is also a class caled Polygon. It is, again, a Libgdx class and can't be mixed with the Box2D PolygonShape. The Libgdx Polygon instead can be used with the Intersector class, which provides a method to check for an overlap between 2 Polygons. Polygons are defined by a float[], giving the position of all it's corner points.
That means, that if you want to use the physics engine Box2D, you need to work with the PolygonShape and other shapes, provided by Box2D.
If you instead don't want to use the physics engine and do the collission detection on your own, you can use Rectangle, Polygon and other shapes from Libgdx and you also can use the Intersector for overlap-testing.
EDIT:
If you take a look at this link, there is a section "Contact Listener". There you can see, that the ContactListener provides the methods beginContact (called when two objects start to collide/overlap) and endContact (called when the contact between the two objects ends).
You need to implement those methods and set it as the worlds ContactListener.
Node, that Box2D automatically handles collisions, by seperating the colliding objects (bounce back). If thats not what you want, you shouldset the isSensor-Flag of your FixtureDef. That way, Box2D will notify you, that there is a collision (beginContact gets called) but won't handle it for you.
EDIT 2:
The Contact object you get in your ContactListener-Methods holds 2 Fixtures: FixtrueA and FixtureB.
Thats because in every contact or collision 2 objects have to be involved, so FixtureA is one of them and FixtureB is the ther one.
The reason, why a Contact contains 2 Fixtures instead of 2 Bodys is the following:
Let's say we have a Player, defined by a Body. This Body contains 2 Fixtures: headFixture, a circle repressenting it's head and bodyFixture, a rectangle, repressenting the hitbox of the Players body and feet.
Now, if a Bullet (a Body with one Fixture) hits our Player, it may make a difference, if it hits its body or its head.
So basicly a Body repressents our whole object, the Player, while the Fixtures repressent some parts of it, which are used for the physics-calculation.
If you only want to handle collissions per Body, you can get the Body by calling fixtureA().getBody().

Libgdx Box2D - Draw Sprite on Body - Heavy problems

I'm heaving heavy problems with drawing a Sprite on a Box2D body.
I'm creating a platformer and I did draw a sprite on a body before but then realized that my gravity is really floaty. After googling I found out that I should work with meters when using Box2D and I changed my code to work with a pixel to meter conversion ratio of 25.
Since then I can't get everything to work though, my sprite just won't draw on my body.
Camera:
float width = Gdx.graphics.getWidth() * PIXELS_TO_METERS;
float height = Gdx.graphics.getHeight() * PIXELS_TO_METERS;
camera = new OrthographicCamera(width / 2, height / 2);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
Here is the code for my body:
idleRegion = new TextureRegion(xeonTexture, 20, 13, 50, 65);
xeonSprite = new Sprite(idleRegion);
//Physics
bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(100 * PIXELS_TO_METERS, 100 * PIXELS_TO_METERS);
bodyDef.fixedRotation = true;
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox((xeonSprite.getWidth() / 2) * PIXELS_TO_METERS, (xeonSprite.getHeight() / 2) * PIXELS_TO_METERS);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
fixtureDef.friction = 1f;
fixtureDef.restitution = 0f;
fixtureDef.isSensor = false;
physicsFixture = body.createFixture(fixtureDef);
Here is how I set the position of my sprite:
final float widthD2 = (xeonSprite.getWidth() / 2);
final float heightD2 = (xeonSprite.getHeight() / 2);
final float angle = this.getBodyAngle();
xeonSprite.setOrigin(widthD2, heightD2);
xeonSprite.setPosition(body.getPosition().x - xeonSprite.getWidth() / 2, body.getPosition().y - xeonSprite.getHeight() / 2);
xeonSprite.setRotation((float) Math.toRadians(angle));
I also tried the following:
xeonSprite.setPosition(body.getPosition().x - xeonSprite.getWidth() / 2 * METERS_TO_PIXELS, body.getPosition().y - xeonSprite.getHeight() / 2 * METERS_TO_PIXELS);
And here is how I draw my Sprite:
penguinBatch.begin();
xeon.getPenguinSprite(stateTime, Gdx.graphics.getDeltaTime()).draw(penguinBatch);
penguinBatch.end();
This is yet another case of "Pixels do not exist in your game world", they are only there to represent you game world to your client. A camera man for TV need to know as much about your TV as you need to know about your clients screens. Just capture what you want to show and let LibGDX do the rest.
So, you should never, ever work with pixels. In the case you want pixel perfect drawing you might want to setup your camera to the amount of pixels of the screen you are targeting but after that you treat these "pixels" as units.
Box2D does not work with pixel per meter. It works with 1 unit == 1 meter and you should stick to that. But how much meters a pixel should represent is still up to you.
If you want to draw a 1m x 1m box you should not multiply by some number you create based of screen pixels like you do now, you should just give it 1 x 1 to represent the box.
shape.setAsBox(1, 1);
Your sprite should be exactly the same
xeonSprite.setSize(1, 1);
And you should position them on the same position. It's really that simple, 1 UNIT == 1m and there is nothing more to it, the camera does the rest. If you want to show 9m x 16m of your game world you setup your camera like this.
camera = new OrthographicCamera(9, 16);
If you want to represent the empire state building you would give the body and sprite a size of 57, 443 and then your camera needs to represent a much larger area, if you don't want to render a small portion of it. If you want to fit the height exactly on screen without stretching you need your own conversion.
float camHeight = 443;
float aspectRatio = Gdx.graphics.width / Gdx.graphics.height
float camWidth = aspectRatio * 443;
Although those Gdx calls give the pixels your clients are running you should still not treat these as pixels about these since you don't know at what screen I will be playing your game.
Now forget about the empire state building example and your camera's center is positioned at 0, 0 world space so bottom left is at -4.5, -8 so you might want to translate it and don't forget to .update the camera. You are currently doing this in your code.
If you start drawing your sprite and updating Box2D you will see a ball drop from the center of your screen. Of course you need to keep updating your sprites position to match the body position for it to move along with the Box2D body.
Just forget pixels, unless you want to code your own camera or viewports, which you probably do not want because you chose LibGDX. The rest of your game code does not need to work with pixels in any way. The OrthographicCamera can calculate world position to screen position for you by camera.unproject(touchVector).

Categories

Resources