So, I just want to draw Box2DDebugRenderer same with sprite size.
I have use same SpriteBatch same Camera and same Viewport. It took me 8 hours, tried finding a solution around google and still not solve the problem.
Here what I got:
I just change this line:
PolygonShape shape = new PolygonShape();
shape.setAsBox(32, 32);
Some tutorial said to div by 2. I tried that I got result like 2nd picture.
Here my script
PlayScreen.java:
public class PlayScreen implements Screen {
private SpriteBatch batch;
private TiledMap tileMap;
private TiledMapRenderer tiledMapRenderer;
private OrthographicCamera cam;
private Player player;
private World world;
private Box2DDebugRenderer debugRenderer;
private FitViewport gamePort;
public PlayScreen(HookaHookaGame game) {
this.batch = game.getSpriteBatch();
this.world = new World(new Vector2(0, -20), true);
// Create cam
cam = new OrthographicCamera();
gamePort = new FitViewport(800, 600, cam);
gamePort.apply(true);
//initially set our gamcam to be centered correctly at the start of of map
// cam.position.set(400, 300, 0);
// cam.update();
// Load tilemap
tileMap = new TmxMapLoader().load("simulation01.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tileMap, batch);
// Create box2d debug renderer
debugRenderer = new Box2DDebugRenderer();
// Create player sprite
player = new Player(this.world);
}
#Override
public void show() {
}
#Override
public void render(float delta) {
//cam.update();
// Update sprite
player.update(delta);
//Clear the game screen with Black
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Draw tilemap
tiledMapRenderer.setView(cam);
tiledMapRenderer.render();
// Set camera to spritebatch
batch.setProjectionMatrix(cam.combined);
// Draw sprite
batch.begin();
batch.draw(player.getKeyFrame(), 300, 300);
player.draw(batch);
batch.end();
// Draw box2d debug renderer
debugRenderer.render(world, cam.combined);
}
#Override
public void resize(int width, int height) {
gamePort.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
}
Player.java, extended from Sprite Class:
public class Player extends Sprite {
private Animation anim;
private float stateTimer;
private World world;
private Body body;
// Debug
public Player(World world) {
stateTimer = 0;
this.world = world;
loadAnim();
}
private void loadAnim() {
Array<TextureRegion> temp = new Array<TextureRegion>(40);
Texture texture = new Texture("sprite.png");
temp.add(new TextureRegion(texture, 3*32, 3*32, 32, 32));
temp.add(new TextureRegion(texture, 2*32, 3*32, 32, 32));
temp.add(new TextureRegion(texture, 32, 3 * 32, 32, 32));
temp.add(new TextureRegion(texture, 0, 3*32, 32, 32));
anim = new Animation(0.1f, temp, Animation.PlayMode.LOOP);
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(100, 100);
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox(32, 32);
FixtureDef fixture = new FixtureDef();
fixture.shape = shape;
body.createFixture(fixture);
setBounds(0, 0, 32, 32);
setPosition(100, 100);
}
public void update(float delta) {
stateTimer += delta;
setRegion(getKeyFrame());
setSize(32, 32);
}
public TextureRegion getKeyFrame() {
return anim.getKeyFrame(stateTimer, true);
}
}
Could you explain what happen exactly?
Box2d and Textures have different origins.
The origin of the body is its center.
The origin of the Texture is the bottom left corner.
As you can see, the center of the box2d object is exactly at the bottom left corner of the texture, if you draw them both at the same position.
Pseudo code:
batch.draw(texture, body.x - texture.width / 2, body.y - texture.heigth / 2);
Otherwise you could set the origin of the box2d body to "the bottom left corner", but that might give you trouble if you follow other tutorials.
you can also put the origin of your sprite to center by calling the function setOriginCenter() of your Sprites before drawing them
so the spritebatch will draw them from center just like your box
Related
Before I decided that I wanted to use box2d for all my game physics, I had set up all the non-animated shapes using shaperenderer. I have converted my box2d world and shape to meters at a scale of 32 ppm but all my regular shapes are still drawn with pixels.
Since my box2d circle has been scaled along with the camera, it renders fine with the right size. However, all the shapes that I drew with pixels using shaperenderer are way out of view.
Do I need to redo all of my original shapes with box2d shapes?
Can I just scale all the original shapes with my PPM scaling?
From what I understand, I should be able to just scale all the dimensions of the original shapes by the PPM since I have already scaled the camera?
public class Controller extends Game {
public OrthographicCamera cam;
public ShapeRenderer shape;
public ShapeRenderer circle;
public FitViewport viewport;
public Stage stage;
public World world;
public Body ball;
public Box2DDebugRenderer box2DDebugRenderer;
#Override
public void create() {
cam = new OrthographicCamera();
viewport = new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), cam);
cam.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
shape = new ShapeRenderer();
circle = new ShapeRenderer(); //balls shot from cannon
world = new World(new Vector2(0, 0), true);
box2DDebugRenderer = new Box2DDebugRenderer();
setScreen(new GameScreen(this));
//setScreen(new TitleScreen(this));
}
#Override
public void dispose() {
shape.dispose();
stage.dispose();
circle.dispose();
box2DDebugRenderer.dispose();
world.dispose();
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void resize(int width, int height) {
viewport.update(width, height);
}
public Body createBall(){
Body bBody;
BodyDef def = new BodyDef();
def.type = BodyDef.BodyType.DynamicBody;
def.position.set(500,500);
def.fixedRotation = true;
bBody = world.createBody(def);
return bBody;
}
}
And here is is the actual game screen that implements all the original shapes and the box2d shapes:
import static com.mygdx.game.Utils.Constants.PPM;
public class GameScreen extends ScreenAdapter {
Controller game;
//x-axis length for top/bottom bar
private float goalWidth;
//y-axis height for back bar
private float goalHeight;
private float goalPostThickness;
//Screen height and width
private float screenWidth;
private float screenHeight;
//How far down/up posts are from edge of screen
private float goalPostTopOffset;
private float goalPostBottomOffset;
private float cannonOriginX; //Variables used for starting position of balls
private float cannonOriginY;
float ballX;
float ballY;
public GameScreen (Controller game){
this.game = game;
}
#Override
public void show(){
game.ball = game.createBall();
// Create a circle shape and set its radius to 6
CircleShape circle = new CircleShape();
circle.setRadius(32/2/PPM);
// Create a fixture definition to apply our shape to
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = 0.5f;
fixtureDef.friction = 0.4f;
fixtureDef.restitution = 0.6f; // Make it bounce a little bit
// Create our fixture and attach it to the body
Fixture fixture = game.ball.createFixture(fixtureDef);
circle.dispose();
}
#Override
public void render(float delta){
//Logic
screenWidth = Gdx.graphics.getWidth();
screenHeight = Gdx.graphics.getHeight();
goalPostTopOffset = screenHeight/7;
goalPostBottomOffset = goalPostTopOffset * 3;
goalHeight = screenHeight - (goalPostTopOffset + goalPostBottomOffset);
goalWidth = screenWidth / 6;
goalPostThickness = screenWidth / 75;
cannonOriginX = goalWidth / 2; //Variables used for starting position of balls
cannonOriginY = (goalPostThickness*5) / 2;
ballX = 0 + cannonOriginX;
ballY = (goalPostBottomOffset - (goalPostBottomOffset / 4)) + cannonOriginY;
game.world.step(1/60f, 6, 2);
//Draw
game.cam.update();
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.box2DDebugRenderer.render(game.world, game.cam.combined.scl(PPM));
game.shape.setProjectionMatrix(game.cam.combined);
drawNonAnimated();
drawAnimated();
}
#Override
public void hide(){
}
//draws stationary objects such as goal posts
public void drawNonAnimated(){
//Top goal bar
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(screenWidth - goalWidth, screenHeight - goalPostTopOffset, goalWidth, goalPostThickness);
game.shape.end();
//Bottom goal bar
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(screenWidth - goalWidth, goalPostBottomOffset, goalWidth, goalPostThickness);
game.shape.end();
//Back goal bar
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(screenWidth - goalPostThickness, goalPostBottomOffset, goalPostThickness, goalHeight);
game.shape.end();
//Cannon platform
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(0, goalPostBottomOffset - (goalPostBottomOffset / 4),goalWidth, goalPostThickness*2);
game.shape.end();
//Cannon
game.shape.setColor(1, 1, 1, 1);
game.shape.begin(ShapeRenderer.ShapeType.Filled);
game.shape.rect(0, goalPostBottomOffset - (goalPostBottomOffset / 4), cannonOriginX,
cannonOriginY, goalWidth, goalPostThickness*5, 1, 1, 45);
game.shape.end();
}
public void drawAnimated(){
//Ball(s)
game.circle.setColor(1, 1, 1, 1);
game.circle.begin(ShapeRenderer.ShapeType.Filled);
game.circle.circle(ballX, ballY, goalPostThickness * 2.5f);
game.circle.end();
game.cam.update();
ballX += 1;
}
}
As I mentioned earlier, the circle shape rendered with box2d shows fine with the PPM scale. However, all the shapes that I drew with shaperenderer are off the screen.
So I am just trying to make my game character, which is a texture (ball), to jump up in the air and then return back down to the position that it started at when the screen is pressed. I was just wondering if someone could give me a code example or help me to do this with my current code which is below. I have basically just drawn the background and the ball texture and positioned the ball where I want it to start the jump. The ball texture is what I want to make jump straight up.
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
Texture background;
Texture ball;
#Override
public void create () {
batch = new SpriteBatch();
background = new Texture("gamebackground.png");
ball = new Texture("ball2.png");
ball.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
}
#Override
public void render () {
batch.begin();
float scaleFactor = 2.0f;
batch.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.draw(ball, 80, 145, ball.getWidth() * scaleFactor, ball.getHeight() * scaleFactor);
batch.end();
}
#Override
public void dispose () {}
}
There are a million ways to do this.
Here's a simple (and not very flexible way). Create a Ball class that has variables for x and y position, velocity, and acceleration. Then give it an update method for applying the acceleration and velocity to the position:
public class Ball {
public static final float GRAVITY = -100; // size depends on your world scale
public static final float BOUNCE_DAMPENING = 0.6f;
public final Vector2 position = new Vector2();
public final Vector2 velocity = new Vector2();
public final Vector2 acceleration = new Vector2(0, GRAVITY);
public void update (){
float dt = Gdx.graphics.getDeltaTime();
velocity.add(acceleration.x * dt, acceleration.y * dt));
position.add(velocity.x * dt, velocity.y * dt);
if (position.y <= 0){ // hit ground, so bounce
position.y = -position.y * BOUNCE_DAMPENING;
velocity.y = -velocity.y * BOUNCE_DAMPENING;
}
}
}
This is a very rudimentary way of handling physics. It would be more sophisticated to use Box2D, but the above is fine if you're just learning.
Now, you need to create a ball instance and use it to track your ball position. Use the Ball object's position when drawing it. And you can react to taps to apply a velocity.
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
Texture background;
Texture ballTexture;
Ball ball;
#Override
public void create () {
batch = new SpriteBatch();
background = new Texture("gamebackground.png");
ballTexture = new Texture("ball2.png");
ballTexture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
ball = new Ball();
}
#Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // don't forget to clear screen
if (Gdx.input.justTouched())
ball.velocity.y += 100;
ball.update();
batch.begin();
float scaleFactor = 2.0f;
batch.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.draw(ballTexture, ball.position.x, ball.position.y, ballTexture.getWidth() * scaleFactor, ballTexture.getHeight() * scaleFactor);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
background.dispose();
ballTexture.dispose();
}
}
You also need to read up on pixel units vs. world units and how to solve the scale problem with Viewports. See https://xoppa.github.io/blog/pixels/ and https://github.com/libgdx/libgdx/wiki/Viewports
I'm very new to programming so bear with me here...
I'm making a basic 2d android game. I am trying to simply make a sprite jump up in the air a bit and then land back to where it originally started. The sprite that I want to jump is "ball2". This is my code so far (Basically just added textures and the sprite in the correct position to start the jump but haven't done anything else).
Any help is appreciated.
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
Texture background;
Texture ball;
Texture spike1;
Texture spike2;
#Override
public void create () {
batch = new SpriteBatch();
background = new Texture("gamebackground.png");
ball = new Texture("ball2.png");
ball.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
spike1 = new Texture("spike1.png");
spike1.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
spike2 = new Texture("spike2.png");
}
#Override
public void render () {
batch.begin();
float scaleFactor = 2.0f;
batch.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.draw(ball, 80, 145, ball.getWidth() * scaleFactor, ball.getHeight() * scaleFactor);
batch.end();
}
#Override
public void render () {
batch.begin();
float scaleFactor = 2.0f;
batch.draw(background, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.draw(ball, 80, 145, ball.getWidth() * scaleFactor, ball.getHeight() * scaleFactor);
batch.end();
Gdx.input.setInputProcessor(new InputAdapter () {
#Override
public boolean keyDown (int keycode) {
if(keycode==Keys.UP)
{
ball.setHeight(ball.getHeight()+50);
}
return true;
}
}
}
Then you need to add the timer down down or you could add down button. Not sure if you have spikes on the top as well. I don't know what it's supposed to look like.
I have a Circle shape that is going to represent the character I will play and control. I have a tmx map, and in the map there are red squares that are meant to represent "fences". The fences are in each corner of the map, inside the fences I have the ground that looks the same as the rest of the ground in the map, but I want to make it so that the game knows when the character is standing on those tiles in the map. can anyone help me? I don't know if it will help but here is the code:
public class PlayScreen implements Screen{
private Game game;
private OrthographicCamera gamecam;
private Viewport gamePort;
private TmxMapLoader mapLoader;
private TiledMap map;
OrthogonalTiledMapRenderer renderer;
//Box#d variables
private World world;
private Box2DDebugRenderer b2dr;
private Hero hero;
private ArrayList<Rectangle> specGroundList;
private boolean heroOnGround;
public PlayScreen(BasicGame game){
this.game = game;
gamecam = new OrthographicCamera();
gamePort = new FitViewport(BasicGame.V_WIDTH, BasicGame.V_HEIGHT, gamecam);
mapLoader = new TmxMapLoader();
map = mapLoader.load("Basic Map.tmx");
renderer = new OrthogonalTiledMapRenderer(map);
gamecam.position.set(gamePort.getWorldWidth() / 2, gamePort.getWorldHeight() / 2, 0);
world = new World(new Vector2(0, 0), true);
b2dr = new Box2DDebugRenderer();
hero = new Hero(world, this);
specGroundList = new ArrayList<Rectangle>();
world.setContactListener(new WorldContactListener());
BodyDef bdef = new BodyDef();
PolygonShape shape = new PolygonShape();
FixtureDef fdef = new FixtureDef();
Body body;
//create fence bodies/fixtures
for(MapObject object: map.getLayers().get(3).getObjects().getByType(RectangleMapObject.class)){
Rectangle rect = ((RectangleMapObject) object).getRectangle();
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set(rect.getX() + rect.getWidth() / 2, rect.getY() + rect.getHeight() / 2);
body = world.createBody(bdef);
shape.setAsBox(rect.getWidth() / 2, rect.getHeight() / 2);
fdef.shape = shape;
body.createFixture(fdef);
}
//create special ground bodies/fixtures
for(MapObject object: map.getLayers().get(2).getObjects().getByType(RectangleMapObject.class)) {
Rectangle rect = ((RectangleMapObject) object).getRectangle();
bdef.type = BodyDef.BodyType.StaticBody;
bdef.position.set(rect.getX() + rect.getWidth() / 2, rect.getY() + rect.getHeight() / 2);
body = world.createBody(bdef);
shape.setAsBox(rect.getWidth() / 2, rect.getHeight() / 2);
fdef.shape = shape;
body.createFixture(fdef);
}
}
#Override
public void render(float delta) {
update(delta);
// Clear the game screen with Black
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
renderer.render();
b2dr.render(world, gamecam.combined);
}
#Override
public void show() {
}
public void handleInput(float dt){
if(Gdx.input.isTouched()) {
// gamecam.position.x += 100 * dt;
gamecam.position.y -= 100 * dt;
}
}
public void update(float dt){
handleInput(dt);
world.step(1/60f, 6, 0);
hero.update(dt);
for(Rectangle rect: specGroundList){
}
gamecam.update();
renderer.setView(gamecam);
}
#Override
public void resize(int width, int height) {
gamePort.update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
public Viewport getGamePort() {
return gamePort;
}
}
Assuming your hero has a known position, and your regions of special ground are oriented rectangles then can't you just use a boolean function that returns something along the lines of:
Hero.x > rectangle.left & Hero.x < rectangle.right & hero.y > rectangle.bottom & hero.y < rectangle.top
I have an image that I want to move to the right. To do this I increase its x position by 200f*Gdx.graphics.getDeltaTime(); every time render is called. I've read other posts where people had similar problems but were they were not multiplying by delta or Gdx.graphics.getDeltaTime(). Because I obviously do this why is my image flickering/vibrating when its moving???
My code:
public class gm extends Game{
OrthographicCamera cam;
SpriteBatch batch;
Texture img;
float x = 0;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
cam = new OrthographicCamera();
cam.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
#Override
public void render () {
super.render();
//x += 200f*Gdx.graphics.getDeltaTime();
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
batch.draw(img, x += 100f*Gdx.graphics.getDeltaTime(), 0);
batch.end();
}
}