RontoKing
RontoKing

Reputation: 49

LibGDX / Java weird behaviour

This is a game being made in LibGDX. The user can spawn sheep, which then are supposed to wander around randomly. There are two problems.

  1. The sheep do not always change their "right" variable.
  2. They seem to always move in the same way, which should not be happening.

Here's the code for the MainClass and Entity classes. The others shouldn't be the causes of this bug.

Main:

package com.god.game;

import com.badlogic.gdx.*;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class MainClass extends ApplicationAdapter {
    public static final int width = 1600;
    public static final int height = 1000;
    public static final String title = "God Game";

    public static EntityType[] entityTypes;
    public static float rand, cameraSpeed;
    public static Comparator<Entity> layerComp;
    public static int selectedId;
    public static String aiStr;
    public static InputMultiplexer input;

    SpriteBatch batch;

    public static ArrayList<Entity> entities;
    public static OrthographicCamera camera;
    public static Vector3 mousePos;

    @Override
    public void create () {
        input = new InputMultiplexer();
        input.addProcessor(new com.god.game.Input());
        input.addProcessor(new GestureDetector(new Gestures()));
        Gdx.input.setInputProcessor(input);

        entityTypes = new EntityType[]{new EntityType("Sheep", new Vector2(55, 38), new TextureAtlas(Gdx.files.internal("Textures/sheep.atlas")), 20, new AI(new String[]{"Wander"}))};
        entities = new ArrayList<Entity>();

        layerComp = new Comparator<Entity>() {
            @Override
            public int compare(Entity e1, Entity e2) {
                if (e1.getPosition().y > e2.getPosition().y)
                    return -1;
                if (e1.getPosition().y < e2.getPosition().y)
                    return 1;
                return 0;
            }
        };

        camera = new OrthographicCamera(width, height);
        cameraSpeed = 500;
        selectedId = 0;

        batch = new SpriteBatch();
    }

    public void update(float deltaTime){

        handleInput(deltaTime);

        for(Entity e : entities){
            e.update(deltaTime);
        }

        Collections.sort(entities, layerComp);

        camera.update();
    }

    @Override
    public void render () {

        update(Gdx.graphics.getDeltaTime());

        Gdx.gl.glClearColor(0, 0.8f, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.setProjectionMatrix(camera.combined);
        batch.begin();
        for(Entity e : entities){
            if(!e.isRight())
                entityTypes[e.getId()].getAtlas().findRegion(e.getFrame()).flip(true, false);
            batch.draw(entityTypes[e.getId()].getAtlas().findRegion(e.getFrame()), e.getPosition().x, e.getPosition().y);
            if(!e.isRight())
                entityTypes[e.getId()].getAtlas().findRegion(e.getFrame()).flip(true, false);
        }
        batch.end();
    }

    @Override
    public void dispose(){
        batch.dispose();
        for(EntityType e : entityTypes)
            e.dispose();
    }

    public void handleInput(float deltaTime){

        mousePos = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
        camera.unproject(mousePos);

        if(Gdx.input.isKeyPressed(Input.Keys.W))
            camera.translate(0, cameraSpeed*deltaTime);
        if(Gdx.input.isKeyPressed(Input.Keys.A))
            camera.translate(-cameraSpeed*deltaTime, 0);
        if(Gdx.input.isKeyPressed(Input.Keys.S))
            camera.translate(0, -cameraSpeed*deltaTime);
        if(Gdx.input.isKeyPressed(Input.Keys.D))
            camera.translate(cameraSpeed*deltaTime, 0);
    }
}

Entity:

package com.god.game;

import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;

public class Entity {
    private int id;
    private Vector2 position, goalPos;
    private boolean right;
    private String frame;
    private float animationTimer;

    public Entity(int id, Vector2 position) {
        this.id = id;
        this.position = position;
        this.right = true;
        this.frame = "stand";
        this.animationTimer = 0;
        this.goalPos = Vector2.Zero;
    }

    public void update(float deltaTime){
        MainClass.aiStr = MainClass.entityTypes[id].getAi().getBrainList()[0];
        if(MainClass.aiStr == "Wander"){
            MainClass.rand = MathUtils.random(1000);

            if(MainClass.rand == 1){
                goalPos.x = -1;
                right = false;
            }
            else if(MainClass.rand == 2)
                goalPos.x = 0;
            else if(MainClass.rand == 3){
                goalPos.x = 1;
                right = true;
            }

            MainClass.rand = MathUtils.random(1000);

            if(MainClass.rand == 1)
                goalPos.y = -1;
            else if(MainClass.rand == 2)
                goalPos.y = 0;
            else if(MainClass.rand == 3)
                goalPos.y = 1;

            moveForward(deltaTime);
        }
        else{
            frame = "stand";
            animationTimer = 0;
        }
        animationTimer += deltaTime;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Vector2 getPosition() {
        return position;
    }

    public void setPosition(Vector2 position) {
        this.position = position;
    }

    public boolean isRight() {
        return right;
    }

    public void setRight(boolean right) {
        this.right = right;
    }

    public String getFrame() {
        return frame;
    }

    public void setFrame(String frame) {
        this.frame = frame;
    }

    public float getAnimationTimer() {
        return animationTimer;
    }

    public void setAnimationTimer(float animationTimer) {
        this.animationTimer = animationTimer;
    }

    public Vector2 getGoalPos() {
        return goalPos;
    }

    public void setGoalPos(Vector2 goalPos) {
        this.goalPos = goalPos;
    }

    public void moveForward(float deltaTime){
        position.x += MainClass.entityTypes[id].getMoveSpeed() * deltaTime * goalPos.x;
        position.y += MainClass.entityTypes[id].getMoveSpeed() * deltaTime * goalPos.y;
        if(goalPos.x == 0 && goalPos.y == 0){
            frame = "stand";
            animationTimer = 0;
        }
        else if(animationTimer >= 0.2f){
            if(frame.equals("stand"))
                frame = "move";
            else
                frame = "stand";
            animationTimer = 0;
        }
    }
}

The sheep SHOULD all be setting their right variables to true/false every time they change their goalPos.x to -1 or 1, but they don't seem to do so. They also don't seem to move independently, instead they're like a formation, moving in the same direction each time. They do independently set their right variables to true/false, but it doesn't match their movement. Ex: A sheep is moving right, and then randomly turns left while still moving right, etc.

Any help would be greatly appreciated, thank you!

Upvotes: 0

Views: 219

Answers (3)

Hllink
Hllink

Reputation: 918

First of all, you are acessing variables on static way, like

         [...]
    MainClass.aiStr = MainClass.entityTypes[id].getAi().getBrainList()[0];
         [...]
    MainClass.rand = MathUtils.random(1000);
         [...]

so all the sheeps are gonna use this same value and move like the same, just make those calls on the same instance.

If you are math rand 1000 and comparing if the value is 1 its 0,1% chance per frame, if your game is locked on 30fps the chances for it to trigger is very, very low.

You are checking twice if(!e.isRight()) on the render method, so it will flip twice.

 for(Entity e : entities){
            if(!e.isRight())
                entityTypes[e.getId()].getAtlas().findRegion(e.getFrame()).flip(true, false);
            batch.draw(entityTypes[e.getId()].getAtlas().findRegion(e.getFrame()), e.getPosition().x, e.getPosition().y);
            if(!e.isRight())
                entityTypes[e.getId()].getAtlas().findRegion(e.getFrame()).flip(true, false);
        }

Could you provide more info about EntityType class? So we can find out what are you doing when you're setting the movingspeed on the entity.

[Edit] as stated by mbrlabs always uses .equals to compare strings, because even they look the same they actually are 2 different objects on the memory containing the same text, so using == will never be true.

Upvotes: 1

mbrlabs
mbrlabs

Reputation: 1

In your Entity class change this if(MainClass.aiStr == "Wander") to MainClass.aiStr.equals("Wander").

When you compare strings, you should always do it this way. By using == you are comparing the reference, but you want to compare the actual content of the string. The code between the if statement is most likely never executed, because you are comparing references.

Upvotes: 0

Madmenyo
Madmenyo

Reputation: 8584

Firstly, you should never say Java is behaving weird but rather that you code is not behaving as you expected. If you blame some inner java functionality for your bugs you won't ever find them. Now let's take a look what happens if your ai is set to wander.

if(MainClass.aiStr == "Wander"){
        MainClass.rand = MathUtils.random(1000);
//Random number in the range of 0 to 1000.

        if(MainClass.rand == 1){ //happens .1% of the time
            goalPos.x = -1;
            right = false;
        }
        else if(MainClass.rand == 2) //happens .1% of the time
            goalPos.x = 0;
        else if(MainClass.rand == 3){ //happens .1% of the time
            goalPos.x = 1;
            right = true;
        }
 //since there is no else nothing happens 99.7% of the time.
    }

My guess is that your code is behaving as you should expect and there is no black voodoo going on inside your computer. So in the future blame yourself and inspect your code, it's really not that much code and only a tiny part of what you posted is responsible for steering your entities.

Now what you probably want is something like this MathUtils.random(1, 4) since maximum is exclusive. Or MathUtils.random(1, 5) if you want 25% of the time nothing to happen.

A more scalable way in terms of chance I always prefer something like this:

int randomNumber = MathUtils.random(1, 101);
if (randomNumber <= 33)
{
  //do something 33% of the time
}
else if (randomNumber <= 66)
{
  //do something 33% of the time
}
else
{
  //do something 34% of the time
}

Now this is easier to alter since all you need to do is change the percentages and you can easily add another case.

if (randomNumber <= 20)
{
  //do something 20% of the time
}
else if (randomNumber <= 60)
{
  //do something 40% of the time
}
else if (randomNumber <= 58)
{
  //do something 18% of the time
}
else
{
  //do something 22% of the time
}

Upvotes: 2

Related Questions