Box2d Lighting different behavior with different screen size

191 Views Asked by At

I tried to play a little with lighting, but very quickly I came upon weird behavior that I dont understand. Could anybody explain to me, why this happens, if it is on purpose, or I do something wrong.

On the first image, light goes little trough obstacle (which is also weird).

Game screen wider

When the screen size is changed to lower width, the light goes even more through obstacle.

The red background is "black bar", since I use FitViewport. The "real" world has badlogic.jpg all over it.

Game screen narrow

On the next pictures you can see, how it works without light rendering - the screen renders the same world regardless of screen size (only scaled). Thats how I would expect it to work even with lighting.

enter image description here

enter image description here

The project is standard, generated by libgdx project generator. All my code is just one class, so I can upload it here:

package com.gobanit.sandbox.main;

import com.badlogic.ashley.core.Engine;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;

import box2dLight.PointLight;
import box2dLight.RayHandler;

public class SandboxTestGame extends ApplicationAdapter {
    SpriteBatch batch;
    World world;
    Engine engine;
    Viewport viewport;
    Box2DDebugRenderer debugRenderer;
    Texture texture;
    RayHandler rayHandler;

    Body playerBody;

    @Override
    public void create () {
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("badlogic.jpg"));
        engine = new Engine();
        world = new World(new Vector2(0, 0), true);
        viewport = new FitViewport(1000, 500);
        viewport.getCamera().position.set(new Vector3(0, 0, 0));
        debugRenderer = new Box2DDebugRenderer();   
        rayHandler = new RayHandler(world);
        rayHandler.setShadows(true);

        createPlayerBody();
        createObstacleBody();       

        PointLight l1 = new PointLight(rayHandler, 100, null, 500, 0, 0);
        l1.attachToBody(playerBody);
        //l1.setSoft(true);

        //new PointLight(rayHandler, 100, null, 500, 0, 200);

    }

    private void createObstacleBody() {
        BodyDef bodyDef = new BodyDef();
        bodyDef.position.add(new Vector2(-10000, 100));
        bodyDef.type = BodyType.StaticBody;

        Body body = world.createBody(bodyDef);

        FixtureDef fixDef = new FixtureDef();
        PolygonShape shape = new PolygonShape();
        shape.set(new float[] {0f,0f,0f,40f,20000f,40f,20000f,0f});
        fixDef.shape = shape;

        body.createFixture(fixDef);

        body.setActive(true);       
    }

    private void createPlayerBody() {
        BodyDef bodyDef = new BodyDef();
        bodyDef.position.add(new Vector2(100, 50));
        bodyDef.type = BodyType.DynamicBody;

        Body body = world.createBody(bodyDef);

        FixtureDef fixDef = new FixtureDef();
        CircleShape circle = new CircleShape();
        circle.setRadius(20);
        fixDef.shape = circle;

        body.createFixture(fixDef);

        body.setActive(true);

        playerBody = body;      
    }



    @Override
    public void render () {
        update();
        draw();
    }

    private void draw() {
        Gdx.gl.glClearColor(100, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());

        batch.setProjectionMatrix(viewport.getCamera().combined);
        batch.begin();
        batch.draw(texture, -1000, -1000, 2000, 2000);
        batch.end();

        debugRenderer.render(world, viewport.getCamera().combined);

        rayHandler.setCombinedMatrix((OrthographicCamera) viewport.getCamera());
        rayHandler.render();    
    }

    private void update() {
        world.step(1f/60f, 8, 3);
        rayHandler.update();

        if(Gdx.input.isKeyPressed(Keys.D)) {
            //viewport.getCamera().position.add(new Vector3(1,0,0));
            playerBody.applyForceToCenter(100, 0, true);
        }
        if(Gdx.input.isKeyPressed(Keys.A)) {
            //viewport.getCamera().position.add(new Vector3(-1,0,0));
            playerBody.applyForceToCenter(-100, 0, true);
        }
        if(Gdx.input.isKeyPressed(Keys.W)) {
            //viewport.getCamera().position.add(new Vector3(0,1,0));
            playerBody.applyForceToCenter(0, 100, true);
        }
        if(Gdx.input.isKeyPressed(Keys.S)) {
            //viewport.getCamera().position.add(new Vector3(0,-1,0));
            playerBody.applyForceToCenter(0, -100, true);
        }   

        viewport.getCamera().position.set(new Vector3(playerBody.getPosition().x, playerBody.getPosition().y, 0));
    }

    @Override
    public void dispose () {
        batch.dispose();
        texture.dispose();
        world.dispose();
        debugRenderer.dispose();
        rayHandler.dispose();
    }
}

Thanks a lot. If more information is needed, tell me and I will edit the question.

1

There are 1 best solutions below

0
Apex On

useCustomViewport function in the documentation

to confirm what @Mikhail Churbanov said and bring more visibility. The documentation mention that it is our responsibility to update the viewport on resize. I thought it was the point of the fit viewport. I am confused.

Anyway, i ended up using this function in my resize() like the following :

    if(viewport.getRightGutterWidth() > 0){
        rayHandler.useCustomViewport(viewport.getRightGutterWidth()-5,viewport.getBottomGutterHeight()-5, (int)(height*SCREEN_RATIO)+10,height+10);
    }else{
        rayHandler.useCustomViewport(viewport.getRightGutterWidth()-5,viewport.getBottomGutterHeight()-5, width+10,(int)(width/SCREEN_RATIO)+10);
    }
  • The first condition allow to differentiate from a vertical/horizontal resize
  • using getRightGutterWidth and getBottomGutterHeight to position the viewport
  • Using the ratio of the screen constant to deduce the width or height depending on the case
  • Last but not least, sadly had to add a tiny padding (5s & 10s) otherwise you would sometime see a 1px line on the viewport without the light applied.

Doing so give me mixed vibes. I don't give much confidence that's the way to do, but hey, it works !