New shaders, remake of Android placement partially done

This commit is contained in:
Anuken
2018-05-29 22:03:43 -04:00
parent b434c37f01
commit 0f09a0b152
18 changed files with 508 additions and 76 deletions

View File

@@ -1,17 +1,27 @@
package io.anuke.mindustry.input;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult;
import io.anuke.mindustry.input.PlaceUtils.NormalizeResult;
import io.anuke.mindustry.type.Recipe;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.BuildBlock;
import io.anuke.mindustry.world.blocks.types.BuildBlock.BuildEntity;
import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.graphics.Draw;
@@ -27,20 +37,37 @@ import static io.anuke.mindustry.Vars.*;
public class AndroidInput extends InputHandler implements GestureListener{
private static Rectangle r1 = new Rectangle(), r2 = new Rectangle();
/**Maximum line length.*/
private static final int maxLength = 100;
/**Maximum speed the player can pan.*/
private static final float maxPanSpeed = 1.3f;
/**Distance to edge of screen to start panning.*/
private final float edgePan = Unit.dp.scl(60f);
//gesture data
private Vector2 pinch1 = new Vector2(-1, -1), pinch2 = pinch1.cpy();
private Vector2 vector = new Vector2();
private float initzoom = -1;
private boolean zoomed = false;
/**Position where the player started dragging a line.*/
private int lineStartX, lineStartY;
/**Animation scale for line.*/
private float lineScale;
/**List of currently selected tiles to place.*/
private Array<PlaceRequest> placement = new Array<>();
/**Place requests to be removed.*/
private Array<PlaceRequest> removals = new Array<>();
/**Whether or not the player is currently shifting all placed tiles.*/
private boolean selecting;
/**Whether the player is currently in line-place mode.*/
private boolean lineMode;
public AndroidInput(Player player){
super(player);
Inputs.addProcessor(new GestureDetector(20, 0.5f, 2, 0.15f, this));
Inputs.addProcessor(new GestureDetector(20, 0.5f, 0.4f, 0.15f, this));
}
/**Returns whether this tile is in the list of requests, or at least colliding with one.*/
@@ -88,6 +115,32 @@ public class AndroidInput extends InputHandler implements GestureListener{
return null;
}
Tile tileAt(float x, float y){
Vector2 vec = Graphics.world(x, y);
return world.tileWorld(vec.x, vec.y);
}
void removeRequest(PlaceRequest request){
placement.removeValue(request, true);
removals.add(request);
}
void drawRequest(PlaceRequest request){
Tile tile = request.tile();
float offset = request.recipe.result.offset();
TextureRegion[] regions = request.recipe.result.getBlockIcon();
Draw.alpha(Mathf.clamp((1f - request.scale) / 0.5f));
Draw.tint(Color.WHITE, Palette.breakInvalid, request.redness);
for(TextureRegion region : regions){
Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset,
region.getRegionWidth() * request.scale, region.getRegionHeight() * request.scale,
request.recipe.result.rotate ? request.rotation * 90 : 0);
}
}
@Override
public void buildUI(Group group) {
@@ -97,10 +150,7 @@ public class AndroidInput extends InputHandler implements GestureListener{
defaults().size(60f);
//Add a cancel button, which clears the selection.
new imagebutton("icon-cancel", 14*2f, () -> {
placement.clear();
selecting = false;
});
new imagebutton("icon-cancel", 14*2f, () -> recipe = null);
//Add an accept button, which places everything.
new imagebutton("icon-check", 14*2f, () -> {
@@ -114,22 +164,83 @@ public class AndroidInput extends InputHandler implements GestureListener{
}
}
removals.addAll(placement);
placement.clear();
selecting = false;
});
}}.visible(this::isPlacing).end();
}}.visible(() -> isPlacing() && placement.size > 0).end();
}
@Override
public void draw(){
Draw.color(Palette.accent);
public void drawBottom(){
//Draw all placement requests as squares
Shaders.mix.color.set(Palette.accent);
Graphics.shader(Shaders.mix);
//draw removals
for(PlaceRequest request : removals){
Tile tile = request.tile();
if(tile == null) continue;
request.scale = Mathf.lerpDelta(request.scale, 0f, 0.2f);
request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f);
drawRequest(request);
}
//draw normals
for(PlaceRequest request : placement){
Tile tile = request.tile();
if(tile == null) continue;
Lines.square(tile.worldx() + request.recipe.result.offset(), tile.worldy() + request.recipe.result.offset(),
request.recipe.result.size * tilesize/2f);
if(validPlace(tile.x, tile.y, request.recipe.result)){
request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f);
request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f);
}else{
request.scale = Mathf.lerpDelta(request.scale, 0.5f, 0.1f);
request.redness = Mathf.lerpDelta(request.redness, 1f, 0.2f);
}
drawRequest(request);
}
Graphics.shader();
Draw.color(Palette.accent);
//Draw lines
if(lineMode){
Tile tile = tileAt(control.gdxInput().getX(), control.gdxInput().getY());
if(tile != null){
NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale);
Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y);
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, maxLength);
//go through each cell and draw the block to place if valid
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX());
int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX());
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result)){
Draw.color();
TextureRegion[] regions = recipe.result.getBlockIcon();
for(TextureRegion region : regions){
Draw.rect(region, x *tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(),
region.getRegionWidth() * lineScale, region.getRegionHeight() * lineScale, recipe.result.rotate ? rotation * 90 : 0);
}
}else{
Draw.color(Palette.breakInvalid);
Lines.square(x*tilesize + recipe.result.offset(), y*tilesize + recipe.result.offset(), recipe.result.size * tilesize/2f);
}
}
}
}
Draw.color();
@@ -140,13 +251,13 @@ public class AndroidInput extends InputHandler implements GestureListener{
if(state.is(State.menu)) return false;
//get tile on cursor
Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize));
Tile cursor = tileAt(screenX, screenY);
//ignore off-screen taps
if(cursor == null || ui.hasMouse(screenX, screenY)) return false;
//only begin selecting if the tapped block is a request
selecting = hasRequest(cursor);
selecting = hasRequest(cursor) && isPlacing();
Tile linked = cursor.target();
@@ -157,6 +268,13 @@ public class AndroidInput extends InputHandler implements GestureListener{
frag.config.hideConfig();
}
//when tapping a build block, select it
if(linked.block() instanceof BuildBlock){
BuildEntity entity = linked.entity();
player.replaceBuilding(linked.x, linked.y, linked.getRotation(), entity.recipe);
}
//call tapped() event
//TODO net event for block tapped
linked.block().tapped(linked, player);
@@ -164,36 +282,73 @@ public class AndroidInput extends InputHandler implements GestureListener{
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button){
//place down a line if in line mode
if(lineMode) {
Tile tile = tileAt(screenX, screenY);
if(tile == null) return false;
//normalize area
NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100);
rotation = result.rotation;
//place blocks on line
for(int i = 0; i <= result.getLength(); i += recipe.result.size){
int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX());
int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX());
if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result)){
PlaceRequest request = new PlaceRequest(x * tilesize, y * tilesize, recipe, result.rotation);
request.scale = 1f;
placement.add(request);
}
}
lineMode = false;
}
return false;
}
@Override
public boolean longPress(float x, float y) {
if(state.is(State.menu)) return false;
//get tile on cursor
Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize));
Tile cursor = tileAt(x, y);
//ignore off-screen taps
if(cursor == null || ui.hasMouse(x, y)) return false;
//remove request if it's there
if(hasRequest(cursor)){
placement.removeValue(getRequest(cursor), true);
}
//long pressing enables line mode otherwise
lineStartX = cursor.x;
lineStartY = cursor.y;
lineMode = true;
Effects.effect(Fx.tapBlock, cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe.result.size);
return false;
}
@Override
public boolean tap(float x, float y, int count, int button) {
if(state.is(State.menu)) return false;
if(state.is(State.menu) || lineMode) return false;
//get tile on cursor
Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize));
Tile cursor = tileAt(x, y);
//ignore off-screen taps
if(cursor == null || ui.hasMouse(x, y)) return false;
//add to placement queue if it's a valid place position
if(isPlacing() && validPlace(cursor.x, cursor.y, recipe.result) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){
//remove if request present
if(hasRequest(cursor)) {
removeRequest(getRequest(cursor));
}else if(isPlacing() && validPlace(cursor.x, cursor.y, recipe.result) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){
//add to placement queue if it's a valid place position
placement.add(new PlaceRequest(cursor.worldx(), cursor.worldy(), recipe, rotation));
}
@@ -203,14 +358,68 @@ public class AndroidInput extends InputHandler implements GestureListener{
@Override
public void update(){
//reset state when not placing
if(!isPlacing()){
selecting = false;
lineMode = false;
removals.addAll(placement);
placement.clear();
}
if(lineMode){
lineScale = Mathf.lerpDelta(lineScale, 1f, 0.1f);
//When in line mode, pan when near screen edges automatically
if(Gdx.input.isTouched(0) && lineMode){
float screenX = Graphics.mouse().x, screenY = Graphics.mouse().y;
float panX = 0, panY = 0;
if(screenX <= edgePan){
panX = -(edgePan - screenX);
}
if(screenX >= Gdx.graphics.getWidth() - edgePan){
panX = (screenX - Gdx.graphics.getWidth()) + edgePan;
}
if(screenY <= edgePan){
panY = -(edgePan - screenY);
}
if(screenY >= Gdx.graphics.getHeight() - edgePan){
panY = (screenY - Gdx.graphics.getHeight()) + edgePan;
}
vector.set(panX, panY).scl((Core.camera.viewportWidth * Core.camera.zoom) / Gdx.graphics.getWidth());
vector.limit(maxPanSpeed);
player.x += vector.x;
player.y += vector.y;
player.targetAngle = vector.angle();
}
}else{
lineScale = 0f;
}
//remove place requests that have disappeared
for(int i = removals.size - 1; i >= 0; i --){
PlaceRequest request = removals.get(i);
if(request.scale <= 0.0001f){
removals.removeIndex(i);
i --;
}
}
}
@Override
public boolean pan(float x, float y, float deltaX, float deltaY){
//can't pan in line mode with one finger!
if(lineMode && !Gdx.input.isTouched(1)){
return false;
}
float dx = deltaX * Core.camera.zoom / Core.cameraScale, dy = deltaY * Core.camera.zoom / Core.cameraScale;
if(selecting){
@@ -282,6 +491,10 @@ public class AndroidInput extends InputHandler implements GestureListener{
Recipe recipe;
int rotation;
//animation variables
float scale;
float redness;
PlaceRequest(float x, float y, Recipe recipe, int rotation) {
this.x = x;
this.y = y;

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.input;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.entities.BlockBuilder.BuildRequest;
@@ -25,11 +24,9 @@ import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*;
public abstract class InputHandler extends InputAdapter{
float playerSelectRange = Unit.dp.scl(60f);
float playerSelectRange = Unit.dp.scl(80f);
Translator stackTrns = new Translator();
private float mx, my;
public final Player player;
public final OverlayFragment frag = new OverlayFragment(this);
@@ -50,11 +47,11 @@ public abstract class InputHandler extends InputAdapter{
}
public float getMouseX(){
return mx;
return control.gdxInput().getX();
}
public float getMouseY(){
return my;
return control.gdxInput().getY();
}
public void resetCursor(){
@@ -65,16 +62,19 @@ public abstract class InputHandler extends InputAdapter{
return false;
}
public void updateController(){
mx = Gdx.input.getX();
my = Gdx.input.getY();
}
public void buildUI(Group group){
}
public void draw(){
public void updateController(){
}
public void drawBottom(){
}
public void drawTop(){
}

View File

@@ -158,12 +158,12 @@ public enum PlaceMode{
void process(int tilex, int tiley, int endx, int endy){
/*
if(Math.abs(endx - tilex) > maxlen){
endx = Mathf.sign(endx - tilex) * maxlen + tilex;
if(Math.abs(x2 - tilex) > maxlen){
x2 = Mathf.sign(x2 - tilex) * maxlen + tilex;
}
if(Math.abs(endy - tiley) > maxlen){
endy = Mathf.sign(endy - tiley) * maxlen + tiley;
if(Math.abs(y2 - tiley) > maxlen){
y2 = Mathf.sign(y2 - tiley) * maxlen + tiley;
}*/
if(endx < tilex){
@@ -289,18 +289,18 @@ public enum PlaceMode{
//todo hold shift to snap
/*
if(Math.abs(tilex - endx) > Math.abs(tiley - endy)){
endy = tiley;
if(Math.abs(tilex - x2) > Math.abs(tiley - y2)){
y2 = tiley;
}else{
endx = tilex;
x2 = tilex;
}
if(Math.abs(endx - tilex) > maxlen){
endx = Mathf.sign(endx - tilex) * maxlen + tilex;
if(Math.abs(x2 - tilex) > maxlen){
x2 = Mathf.sign(x2 - tilex) * maxlen + tilex;
}
if(Math.abs(endy - tiley) > maxlen){
endy = Mathf.sign(endy - tiley) * maxlen + tiley;
if(Math.abs(y2 - tiley) > maxlen){
y2 = Mathf.sign(y2 - tiley) * maxlen + tiley;
}*/
int dx = endx - tilex, dy = endy - tiley;

View File

@@ -0,0 +1,139 @@
package io.anuke.mindustry.input;
import io.anuke.mindustry.world.Block;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.tilesize;
public class PlaceUtils {
private static final NormalizeResult result = new NormalizeResult();
private static final NormalizeDrawResult drawResult = new NormalizeDrawResult();
/**Normalizes a placement area and returns the result, ready to be used for drawing a rectangle.
* Returned x2 and y2 will <i>always</i> be greater than x and y.
*
* @param block block that will be drawn
* @param startx starting X coordinate
* @param starty starting Y coordinate
* @param endx ending X coordinate
* @param endy ending Y coordinate
* @param snap whether to snap to a line
* @param maxLength maximum length of area
*/
public static NormalizeDrawResult normalizeDrawArea(Block block, int startx, int starty, int endx, int endy, boolean snap, int maxLength, float scaling){
normalizeArea(startx, starty, endx, endy, 0, snap, maxLength);
float offset = block.offset();
drawResult.x = result.x * tilesize;
drawResult.y = result.y * tilesize;
drawResult.x2 = result.x2 * tilesize;
drawResult.y2 = result.y2 * tilesize;
drawResult.x -= block.size * scaling * tilesize/2;
drawResult.x2 += block.size * scaling * tilesize/2;
drawResult.y -= block.size * scaling * tilesize/2;
drawResult.y2 += block.size * scaling * tilesize/2;
drawResult.x += offset;
drawResult.y += offset;
drawResult.x2 += offset;
drawResult.y2 += offset;
return drawResult;
}
/**Normalizes a placement area and returns the result.
* Returned x2 and y2 will <i>always</i> be greater than x and y.
*
* @param tilex starting X coordinate
* @param tiley starting Y coordinate
* @param endx ending X coordinate
* @param endy ending Y coordinate
* @param snap whether to snap to a line
* @param rotation placement rotation
* @param maxLength maximum length of area
*/
public static NormalizeResult normalizeArea(int tilex, int tiley, int endx, int endy, int rotation, boolean snap, int maxLength){
if(snap) {
if (Math.abs(tilex - endx) > Math.abs(tiley - endy)) {
endy = tiley;
} else {
endx = tilex;
}
if (Math.abs(endx - tilex) > maxLength) {
endx = Mathf.sign(endx - tilex) * maxLength + tilex;
}
if (Math.abs(endy - tiley) > maxLength) {
endy = Mathf.sign(endy - tiley) * maxLength + tiley;
}
}
int dx = endx - tilex, dy = endy - tiley;
if(Math.abs(dx) > Math.abs(dy)){
if(dx >= 0){
rotation = 0;
}else{
rotation = 2;
}
}else if(Math.abs(dx) < Math.abs(dy)){
if(dy >= 0){
rotation = 1;
}else{
rotation = 3;
}
}
if(endx < tilex){
int t = endx;
endx = tilex;
tilex = t;
}
if(endy < tiley){
int t = endy;
endy = tiley;
tiley = t;
}
result.x2 = endx;
result.y2 = endy;
result.x = tilex;
result.y = tiley;
result.rotation = rotation;
return result;
}
static class NormalizeDrawResult {
float x, y, x2, y2;
}
static class NormalizeResult{
int x, y, x2, y2, rotation;
boolean isX(){
return Math.abs(x2 - x) > Math.abs(y2 - y);
}
/**Returns length of greater edge of the selection.*/
int getLength(){
return Math.max(x2 - x, y2 - y);
}
/**Returns the X position of a specific index along this area as a line.*/
int getScaledX(int i){
return x + (x2 - x > y2 - y ? i : 0);
}
/**Returns the Y position of a specific index along this area as a line.*/
int getScaledY(int i){
return y + (x2 - x > y2 - y ? 0 : i);
}
}
}