0
\$\begingroup\$

After creating a few bodies and a map with collisions, the app performance clearly goes down. However, RAM and CPU don't seem fully stuck, but the game experience is horrible nonetheless.

GameScreen class:

public class GameScreen implements Screen {
    Main main;
    Joystick joystickL, joystickR;
    public static Player player;
    public static Array<Bullet> bullets;
    public static float angle;
    private Text ammo_view;
    public static Array<Enemy> enemies;
    private TiledMap map;
    private OrthogonalTiledMapRenderer mapRenderer;
    public static World world;
    private Box2DDebugRenderer b2dr;
    private HitContactListener hcl = new HitContactListener();
    public GameScreen(Main main){
        this.main = main;
    }
    @Override
    public void show()  {
        loadActors();
        Gdx.input.setInputProcessor(new InputProcessor() {
                                            @Override
                                            public boolean keyDown(int keycode) {
                                                return false;
                                            }

                                            @Override
                                            public boolean keyUp(int keycode) {
                                                return false;
                                            }

                                            @Override
                                            public boolean keyTyped(char character) {
                                                return false;
                                            }

                                            @Override
                                            public boolean touchDown(int screenX, int screenY, int pointer, int button) {
                                                screenY = (Gdx.graphics.getHeight() - screenY);
                                                multitouch( screenX, screenY, true, pointer);
                                                return false;
                                            }

                                            @Override
                                            public boolean touchUp(int screenX, int screenY, int pointer, int button) {
                                                screenY =  (Gdx.graphics.getHeight() - screenY);
                                                multitouch(screenX, screenY, false, pointer);
                                                return false;
                                            }

                                            @Override
                                            public boolean touchDragged(int screenX, int screenY, int pointer) {
                                                screenY = (Gdx.graphics.getHeight() - screenY);
                                                multitouch(screenX, screenY, true, pointer);
                                                return false;
                                            }


                                            @Override
                                            public boolean mouseMoved(int screenX, int screenY) {
                                                return false;
                                            }

                                            @Override
                                            public boolean scrolled(float amountX, float amountY) {
                                                return false;
                                            }
                                        }
            );
        }

    public void loadActors() {
        bullets = new Array<Bullet>();
        enemies = new Array<Enemy>();
        //MAP
        b2dr = new Box2DDebugRenderer();
        world = new World(new Vector2(0, 0), false);
        world.setContactListener(hcl);
        map = new TmxMapLoader().load("map/kartapapka/суперготовая карта.tmx");
        mapRenderer = new OrthogonalTiledMapRenderer(map, 1.5f);
        MapLoader.buildShapes(map.getLayers().get("Obstacles").getObjects(), world);

        //PLAYER
        player = new Player(Main.mainChar_ak,  new Vector2(52.8f, 7.5f).scl(PPM), 7, world);
        player.getGun(new Ak(player));

        //GRIPS
        joystickR = new Joystick(Main.joystickImg, Main.gripImg, new Vector2((float) (Main.WIDTH - (Main.WIDTH * 0.15)), (Main.HEIGHT / 3) / 2 + (Main.HEIGHT / 3) / 4), Main.HEIGHT / 3);
        joystickL = new Joystick(Main.joystickImg, Main.gripImg, new Vector2((float)(Main.WIDTH - (Main.WIDTH * 0.85)), (Main.HEIGHT / 3) / 2 + (Main.HEIGHT / 3) / 4), Main.HEIGHT / 3);

        //FIRST BULLET TO AVOID SPAWN TROUBLES
        bullets.add(new Bullet(new Vector2(), new Vector2(), 0, 0, world));
        //world.destroyBody(bullets.get(0).body);

        //ENEMIES GENERATION
        enemies.add(new Enemy(new Sprite(new Texture(Gdx.files.internal("main_sprites/enemy_handgun.png"))), new Vector2(21.6f,27f).scl(PPM), 6, world));
        enemies.add(new Enemy(new Sprite(new Texture(Gdx.files.internal("main_sprites/enemy_handgun.png"))), new Vector2(61.4f,18f).scl(PPM), 6, world));
        enemies.add(new Enemy(new Sprite(new Texture(Gdx.files.internal("main_sprites/enemy_handgun.png"))), new Vector2(80.6f,35f).scl(PPM), 6, world));
        enemies.add(new Enemy(new Sprite(new Texture(Gdx.files.internal("main_sprites/enemy_handgun.png"))), new Vector2(58f, 38.4f).scl(PPM), 6, world));
        enemies.add(new Enemy(new Sprite(new Texture(Gdx.files.internal("main_sprites/enemy_handgun.png"))), new Vector2(31.5f, 39f).scl(PPM), 6, world));
        enemies.add(new Enemy(new Sprite(new Texture(Gdx.files.internal("main_sprites/enemy_handgun.png"))), new Vector2(39.2f, 54f).scl(PPM), 6, world));

        //GIVING GUNS TO ENEMIES
        for (int i = 0; i < enemies.size; i++) {
            enemies.get(i).getGun(new Enemy_handgun(enemies.get(i)));
        }

        //FONT GENERATION
        ammo_view = new Text("fonts/ammo.ttf",(float) Main.WIDTH - 100, (float) Main.HEIGHT - 80);

        //ADDING ACTORS
        Main.stage.addActor(joystickR);
        Main.stage.addActor(joystickL);
        Main.stage.addActor(ammo_view);
        Main.stage.addActor(fm);
    }

    @Override
    public void render(float delta) {
        world.step(1 / 45f, 6, 2);
        Gdx.gl.glClearColor(1,0,1,1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        gameUpdate(delta);
        main.shapeRenderer.setProjectionMatrix(main.camera.combined);
        main.camera.update();
        main.camera.position.set(player.body.getPosition().x * PPM, player.body.getPosition().y * PPM, 0);
        //MAP RENDERING
        mapRenderer.setView(main.camera);
        mapRenderer.render();
        b2dr.render(world, new Matrix4(main.camera.combined).scl(PPM));
        Main.batch.setProjectionMatrix(main.camera.combined);
        Main.batch.begin();
        gameRender(Main.batch);
        Main.batch.end();
        //STAGE DRAW
        Main.stage.act(delta);
        Main.stage.draw();

        main.shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
        main.shapeRenderer.setColor(Color.GRAY);
        for (int i = 0; i < bullets.size; i++) {
            main.shapeRenderer.circle(bullets.get(i).position.x, bullets.get(i).position.y, bullets.get(i).size);
        }
        main.shapeRenderer.end();

    }

    @Override
    public void resize(int width, int height) {
        main.camera.viewportWidth = width;
        main.camera.viewportHeight = height;
        main.camera.update();
    }

    @Override
    public void dispose() {
        map.dispose();
        mapRenderer.dispose();
        main.dispose();
    }
    public void gameRender(SpriteBatch batch) {
        //PLAYER ROTATION
        if (joystickL.direction.angleDeg() != 0) angle = joystickL.direction.angleDeg();
        if (joystickR.direction.angleDeg() != 0) angle = joystickR.direction.angleDeg();
        player.img.setRotation(angle);
        ammo_view.update(player);


        //ENEMY ROTATION
        for(int i = 0; i < enemies.size; i++) {
            enemies.get(i).img.setRotation(enemies.get(i).direction.angleDeg());
            enemies.get(i).draw(batch);
        }

        player.draw(batch);
    }

    private void sweepDeadBodies() {
        Array<Body> bodiesToDestroy = new Array<>(hcl.getBodiesToDestroy());
        Set<Body> bodiesToDestroySet = new HashSet<>();
        for (Body body : bodiesToDestroy) {
            if (body.getUserData() != null && body != null) {
                bodiesToDestroySet.add(body);
            }
        }
        //System.out.println(bodiesToDestroySet);
        for (Body body : bodiesToDestroySet) {
            world.destroyBody(body);

        }
    }
    public void gameUpdate(float delta){
        for(int i = 0; i < enemies.size; i++) {
            //enemies.get(i).player_detection(player);
            enemies.get(i).update();
            enemies.get(i).handgun.update(joystickR);
            bullets.addAll(enemies.get(i).handgun.getBulletsArray());
        }
        bullets.addAll(player.gun.getBulletsArray());
        Gdx.app.postRunnable(new Runnable() {
            @Override
            public void run() {
                sweepDeadBodies();
            }
        });
        player.update();
        //sc.update();
        player.direction.set(joystickL.direction);
        player.gun.update(joystickR);
        for (int i = 0; i < bullets.size; i++) {
            //bullets.get(i).direction.rotateDeg(bullets.get(i).direction.angleDeg() + (float)getRandomDoubleBetweenRange(-10, 10));
            bullets.get(i).update();
        }
        if (player.gun.isFire && player.gun.ammo > 0) {
            if (Rumble.getRumbleTimeLeft() > 0){
                Rumble.tick(delta);
                main.camera.translate(Rumble.getPos());
            }
        }

        hit_detection();
    }
    private void multitouch(float x, float y, boolean isDownTouched, int pointer) {
        for (int i = 0; i < 5; i++) {
            joystickL.update(x, y, isDownTouched, pointer);
            joystickR.update(x, y, isDownTouched, pointer);
        }
    }
    private void hit_detection() {
        for (int j = 0; j < enemies.size; j++) {
            for (int i = 0; i < bullets.size; i++) {

                //HIT DETECTION
                    if (bullets.get(i).bounds.overlaps(enemies.get(j).bounds) && enemies.get(j).isAlive) { 
                        bullets.removeIndex(i--);
                        enemies.get(j).die();
                    }
            }
        }
    }
}

I want to increase efficiency or follow any other suggestions. I can append any other classes, if necessary.

\$\endgroup\$
8
  • 2
    \$\begingroup\$ Did you try running your game through a profiler? \$\endgroup\$
    – Vaillancourt
    Commented Jun 2, 2023 at 21:27
  • \$\begingroup\$ yes, i did when fps goes down to 4 frames CPU is stucked on 10% and RAM on 116mb \$\endgroup\$
    – ognevo
    Commented Jun 3, 2023 at 7:33
  • \$\begingroup\$ A good profiler will tell you the "hot path", i.e. the chunks of code where your program spends the most time. This tells you where you need to optimize. \$\endgroup\$
    – Vaillancourt
    Commented Jun 3, 2023 at 11:37
  • \$\begingroup\$ FWIW, if you have a lot of enemies and bullets, the slow down may come from the hit_detection function. Doing checks in O(n^2) like that may be very inefficient compared to using a better spacial partition approach. Also, I don't know how this works in details in Java, but removing the bullets the way it is done now may cause another performance issue; in c++ you would swap the object to remove with the last object in the vector, then you'd remove it from the last position; this avoid having to shift all the objects repeatedly. \$\endgroup\$
    – Vaillancourt
    Commented Jun 3, 2023 at 11:51
  • 1
    \$\begingroup\$ You are already using a Box2D ContactListener, which is good. Why are you implementing your own hit_detection(), additionally? This is exactly the intention of Box2D ContactListeners. \$\endgroup\$ Commented Jun 3, 2023 at 13:44

1 Answer 1

0
\$\begingroup\$
  • You are loading 6 times the same texture of the enemy, you can load it only once and give the reference to your enemy instances.
  • You should use the AssetManager to store and load your textures.
  • Try to draw as much things with one SpriteBatch as possible, you can merge some of the batch processes to one.
  • Check if the map you are loading isn't too big. If it's too big, try to render/update visible tiles only.
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .