📜 ⬆️ ⬇️

Creating a Zero Player Game using libgdx

Idea


  1. Game space - checkered field limited by a frame
  2. Existing cell types:
    • Empty cage - white
    • Wall - black
    • The beast is red
    • Track - brown
    • House - green
  3. Moving the beast leaves a non-vanishing mark.
  4. A maze is generated on launch.
  5. The beast knows the state of the neighboring cells and on the basis of this builds a map when moving
  6. When moving, the animal spends forces that are restored in the house (+5) or on any cage (+1)
  7. In a collision, the animals unite in flocks (houses are transferred to neighboring points), if several animals simultaneously rest in the house their cards unite
  8. (Not implemented) Different "clans" randomly unite or fight
  9. (Not implemented) Genetic algorithm for various animal behaviors, randomly mixed during reproduction (+ mutations), more promising species will survive in wars


Why libgdx?

I developed from different devices, a home computer on ubuntu and a tablet on win8, a bunch of java + eclipse allowed to do this without problems. Libgdx is used for the convenience of working with the camera, the possibility of adding graphics in the future, as well as for creating a version for android.

In this article


Procurement game in which implemented:


Result:
')


Start


After creating and importing a project in eclipse, add the required fields (depending on the version of libgdx, some may already be added)

SpriteBatch batch;//       OrthographicCamera camera;//    Texture texture;// (   png  -     ) 


In the create () method, we initialize them:

 batch = new SpriteBatch(); batch.disableBlending(); camera = new OrthographicCamera(FIELD_SIZE, FIELD_SIZE); 


Add the necessary constants:

 public static final int FIELD_SIZE = 51;// ( ) public static float UPDATE_TIME = 0.001f;//  ""  


Next comes the abstract Cell class, which will describe the general functionality of the cells.

 public abstract class Cell { public Color color; Sprite sprite; public Cell(Texture texture, Color color){ this.color = color; sprite = new Sprite(texture); sprite.setColor(color); sprite.setSize(1, 1); } public abstract void update(Cell[][] map, int x, int y, Texture texture); public void setColor(Color color){ this.color = color; sprite.setColor(color); } public void draw(SpriteBatch batch,int x, int y){ sprite.setPosition(x-Main.FIELD_SIZE/2-sprite.getWidth()/2, y-Main.FIELD_SIZE/2-sprite.getHeight()/2); sprite.draw(batch); } } 


Immediately consider his two heirs Wall and Empty.

 public class Wall extends Cell { public Wall(Texture texture) { super(texture, new Color(0f, 0f, 0f, 1)); } @Override public void update(Cell[][] map, int x, int y, Texture texture) { } } public class Empty extends Cell { public Empty(Texture texture) { super(texture, new Color(1, 1, 1, 1)); } @Override public void update(Cell[][] map, int x, int y, Texture texture) { } } 


Now you need to create a maze. I will not explain the algorithm, it is well described here . I allocated this algorithm to a separate class MazeGenerator with a single getMaze method (int size), which returns a two-dimensional array of zeros and ones, where 0 is an empty cell, 1 is a wall.

The game field will be stored in a simple two-dimensional array:

 Cell[][] map; 


Creating a field looks like this:

 map = new Cell[FIELD_SIZE][FIELD_SIZE]; texture = new Texture(Gdx.files.internal("tile.png"));//    char[][] bmap = (new MazeGenerator()).getMaze(FIELD_SIZE - 1); for (int i = 0; i < FIELD_SIZE; i++) for (int j = 0; j < FIELD_SIZE; j++) { if (bmap[i][j] == 0) map[i][j] = new Empty(texture); if (bmap[i][j] == 1) map[i][j] = new Wall(texture); } 


Now we have a random maze every time we start the program. You can play with the constants and determine for yourself the best combination.



Yes, on this screenshot, tile.png is just a white square.

The beast


It is time to fill the world with life.

Create a descendant of Cell:

 public class Unit extends Cell { Cell[][] my_map = new Cell[3][3];// ,      float update_time = Main.UPDATE_TIME;//  int mapX = 1, mapY = 1;//     Vector<Action> queue = new Vector<Action>();//    enum Action { left, right, up, down//  } public Unit(Texture texture, Cell[][] map, int x, int y) { super(texture, new Color(1f, 0, 0, 1)); for (int i = x - 1; i <= x + 1; i++) for (int j = y - 1; j <= y + 1; j++) my_map[i - x + 1][j - y + 1] = map[i][Main.FIELD_SIZE - j - 1]; my_map[1][1] = this; homeX = 1; homeY = 1; } private int goRight(Cell[][] map, int x, int y, Texture texture) {...}//map - ,   , x,y -     private int goLeft(Cell[][] map, int x, int y, Texture texture) {...} private int goUp(Cell[][] map, int x, int y, Texture texture) {...} private int goDown(Cell[][] map, int x, int y, Texture texture) {...} 


I do not want to download the post code, so I will not give the whole update method.

The operation algorithm is simple: we check the action queue, if it is not empty, then decrease the tact counter, if it is empty, re-increment it and perform the action and update the neighborhood on the map. If there is no action, then we are building a new route, but more on that a bit further, and now consider the character's step.

For convenience, create a separate method for the step in each direction:

 private int goRight(Cell[][] map, int x, int y, Texture texture) {...}//map - ,    private int goLeft(Cell[][] map, int x, int y, Texture texture) {...}//x,y -     private int goUp(Cell[][] map, int x, int y, Texture texture) {...} private int goDown(Cell[][] map, int x, int y, Texture texture) {...} 


A “step” will consist of several actions.


Route definition

In my opinion the simplest solution is a wave algorithm that builds a route to a random empty cell.
To do this, I added a new WavePath class with a static method:

 public static Vector<Action> getPath(Cell[][] my_map, int x, int y, int nx,int ny){...} 


This method returns a sequence of steps to reach a randomly selected point.

Final touches


Now it remains only to draw all this on the screen and, going through the map array, update the state of the cells.

 @Override public void render() { this.update();//  Gdx.gl.glClearColor(0, 0, 0, 1); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); batch.setProjectionMatrix(camera.combined); batch.begin(); for (int i = 0; i < FIELD_SIZE; i++) for (int j = 0; j < FIELD_SIZE; j++) if(!(map[i][j] instanceof Wall))//       map[i][j].draw(batch, i, j); batch.end(); } public void update() { Input input = Gdx.input; for (int i = 0; i < FIELD_SIZE; i++) for (int j = 0; j < FIELD_SIZE; j++) map[i][j].update(map, i, j, texture);// if(input.isKeyPressed(Input.Keys.W))// , , ,  camera.zoom-=Gdx.graphics.getDeltaTime(); if(input.isKeyPressed(Input.Keys.S)) camera.zoom+=Gdx.graphics.getDeltaTime(); if(input.isKeyPressed(Input.Keys.Q)) camera.rotate(Gdx.graphics.getDeltaTime()*90); if(input.isKeyPressed(Input.Keys.E)) camera.rotate(-Gdx.graphics.getDeltaTime()*90); if(input.isKeyPressed(Input.Keys.CONTROL_LEFT)) UPDATE_TIME+=Gdx.graphics.getDeltaTime(); if(input.isKeyPressed(Input.Keys.SHIFT_LEFT)) UPDATE_TIME-=Gdx.graphics.getDeltaTime(); if(input.isKeyPressed(Input.Keys.LEFT)) camera.translate(new Vector2(-Gdx.graphics.getDeltaTime()*50,0)); if(input.isKeyPressed(Input.Keys.RIGHT)) camera.translate(new Vector2(Gdx.graphics.getDeltaTime()*50,0)); if(input.isKeyPressed(Input.Keys.UP)) camera.translate(new Vector2(0,Gdx.graphics.getDeltaTime()*50)); if(input.isKeyPressed(Input.Keys.DOWN)) camera.translate(new Vector2(0,-Gdx.graphics.getDeltaTime()*50)); if(input.isKeyPressed(Input.Keys.SPACE)){//  UPDATE_TIME = 1f; camera = new OrthographicCamera(FIELD_SIZE, FIELD_SIZE); } camera.update(); if (input.isTouched()) {//    float stepX = Gdx.graphics.getWidth() / FIELD_SIZE; float stepY = Gdx.graphics.getHeight() / FIELD_SIZE; float x = input.getX(); float y = input.getY(); for (int i = 0; i < FIELD_SIZE; i++) for (int j = 0; j < FIELD_SIZE; j++) { if (x >= stepX * i && x <= stepX * (i + 1) && y >= stepY * j && y <= stepY * (j + 1)) if (map[i][FIELD_SIZE - j - 1] instanceof Empty) map[i][FIELD_SIZE - j - 1] = new Unit(texture, map, i, j); } } } 


Conclusion


I apologize in advance for the errors, and not a complete presentation of the material. Sources on github .

If someone is interested, I will write a sequel.

Source: https://habr.com/ru/post/224525/


All Articles