I'm going to implement the first three pieces in PlayerInputSystem.java, and the last part in MapRenderSystem.java.
First let's look at PlayerInputSystem.java
package com.gamexyz.systems; import com.artemis.Aspect; import com.artemis.ComponentMapper; import com.artemis.Entity; import com.artemis.annotations.Mapper; import com.artemis.systems.EntityProcessingSystem; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.math.Vector3; import com.gamexyz.components.Player; import com.gamexyz.components.Position; import com.gamexyz.utils.MapTools; public class PlayerInputSystem extends EntityProcessingSystem implements InputProcessor { @Mapper ComponentMapper<Position> pm; private OrthographicCamera camera; private Vector3 mouseVector; @SuppressWarnings("unchecked") public PlayerInputSystem(OrthographicCamera camera) { super(Aspect.getAspectForAll(Player.class)); this.camera=camera; } @Override protected void initialize() { Gdx.input.setInputProcessor(this); } @Override protected void process(Entity e) { mouseVector = new Vector3(Gdx.input.getX(),Gdx.input.getY(),0); camera.unproject(mouseVector); } @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) { int x = (int)((mouseVector.x - 6f) / (float)MapTools.col_multiple); int y = (int)((mouseVector.y - (float)MapTools.row_multiple*(x%2)/2) / (float)MapTools.row_multiple); return false; } @Override public boolean touchUp(int screenX, int screenY, int pointer, int button) { return false; } @Override public boolean touchDragged(int screenX, int screenY, int pointer) { Vector3 delta = new Vector3(-camera.zoom*Gdx.input.getDeltaX(), camera.zoom*Gdx.input.getDeltaY(),0); camera.translate(delta); return false; } @Override public boolean mouseMoved(int screenX, int screenY) { return false; } @Override public boolean scrolled(int amount) { if ((camera.zoom > 0.2f || amount == 1) && (camera.zoom < 8 || amount == -1)) camera.zoom += amount*0.1; return false; } }The touchDown() method computes which cell is being clicked, and stores the coordinates in x,y. It looks a little hideous because you have to be careful whether you are in an even or odd column. Remember, because it's a hex map, if you are in an odd column all the cells are drawn down a little lower.
touchDragged() handles clicking and dragging. It's kind of awesome that libgdx just comes with built in methods for Gdx.input.getDeltaX() and Y. I multiply them both by camera.zoom, because when we are zoomed very far in or very far out, the distance the camera moves should change accordingly. camera.translate() just moves the x,y, and z coordinate of the camera, but we're not touching z, so that component is 0.
scrolled() handles scrolling, and if you scroll up it zooms in (up to 0.2) whereas if you scroll down it scrolls out (up to 8). You can adjust those points, but be wary of what can happen if you get a negative zoom! Also, the fact that zoom changes by amount*0.1 sets how fine tuned you can adjust the zoom. If it were 0.01, you would have finer control.
If you implement these straight away, you might notice something funny... I sure did! The HUD which displays FPS and so on stays at a fixed point on the MAP, not on the screen. As you zoom out, it gets smaller and smaller. As you zoom in it does the same. That is silly, so let's fix it!
The problem in HudRenderSystem.java is that we run batch.setProjectionMatrix(camera.combined). This lets things move as you move your camera, exactly what we want to avoid. Get rid of this line. In fact, you don't really need anything to do with a camera, so this is what my new HudRenderSystem looks like (I know I left the camera as an argument, but I was just too lazy to change the main code where I initialize it).
package com.gamexyz.systems; import com.artemis.systems.VoidEntitySystem; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.gamexyz.GameXYZ; public class HudRenderSystem extends VoidEntitySystem { private SpriteBatch batch; private BitmapFont font; public HudRenderSystem(OrthographicCamera camera) { } @Override protected void initialize() { batch = new SpriteBatch(); Texture fontTexture = new Texture(Gdx.files.internal("fonts/normal_0.png")); fontTexture.setFilter(TextureFilter.Linear, TextureFilter.MipMapLinearLinear); TextureRegion fontRegion = new TextureRegion(fontTexture); font = new BitmapFont(Gdx.files.internal("fonts/normal.fnt"), fontRegion, false); font.setUseIntegerPositions(false); } @Override protected void begin() { batch.begin(); } @Override protected void processSystem() { batch.setColor(1, 1, 1, 1); font.draw(batch, "FPS: " + Gdx.graphics.getFramesPerSecond(), 20, GameXYZ.WINDOW_HEIGHT - 20); font.draw(batch, "Active entities: " + world.getEntityManager().getActiveEntityCount(), 20, GameXYZ.WINDOW_HEIGHT - 40); font.draw(batch, "Total created: " + world.getEntityManager().getTotalCreated(), 20, GameXYZ.WINDOW_HEIGHT - 60); font.draw(batch, "Total deleted: " + world.getEntityManager().getTotalDeleted(), 20, GameXYZ.WINDOW_HEIGHT - 80); } @Override protected void end() { batch.end(); } }
With that, our HUD should stay up where it belongs... silly HUD...
The last bit we want right now is frustum culling: to only draw the tiles in our camera's view. Libgdx makes this pretty easy too, but to give you super clear idea of what a frustum is, here's wikipedia.
What we're going to do is get the coordinates of the corners of the far plane, and use them to limit what we render. In MapRenderSystem, where we defined int x0, x1, y0, and y1, change the code to look like this:
// Get bottom left and top right coordinates of camera viewport and convert // into grid coordinates for the map int x0 = MathUtils.floor(camera.frustum.planePoints[0].x / (float)MapTools.col_multiple) - 1; int y0 = MathUtils.floor(camera.frustum.planePoints[0].y / (float)MapTools.row_multiple) - 1; int x1 = MathUtils.floor(camera.frustum.planePoints[2].x / (float)MapTools.col_multiple) + 2; int y1 = MathUtils.floor(camera.frustum.planePoints[2].y / (float)MapTools.row_multiple) + 1; // Restrict the grid coordinates to realistic values if (x0 % 2 == 1) x0 -= 1; if (x0 < 0) x0 = 0; if (x1 > gameMap.width) x1 = gameMap.width; if (y0 < 0) y0 = 0; if (y1 > gameMap.height) y1 = gameMap.height;And voila! We probably don't notice a huge difference yet, but when we start playing with HUGE maps later on, this will mean a world of difference. It handles zooming in and out as well.
This is almost starting to look like it could become a game. Almost...
You have gained 50 XP. Progress to Level 3: 250/600
No comments:
Post a Comment