Java tutorial
/* * Copyright (C) 2014, 2015 Helix Engine Developers * (http://github.com/fauu/HelixEngine) * * This software is licensed under the GNU General Public License * (version 3 or later). See the COPYING file in this distribution. * * You should have received a copy of the GNU Library General Public License * along with this software. If not, see <http://www.gnu.org/licenses/>. * * Authored by: Piotr Grabowski <fau999@gmail.com> */ package com.github.fauu.helix.system; import com.artemis.ComponentMapper; import com.artemis.Entity; import com.artemis.annotations.Wire; import com.artemis.managers.TagManager; import com.artemis.systems.VoidEntitySystem; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Timer; import com.github.fauu.helix.AreaType; import com.github.fauu.helix.Direction; import com.github.fauu.helix.PassageAction; import com.github.fauu.helix.TilePermission; import com.github.fauu.helix.component.*; import com.github.fauu.helix.datum.Ambience; import com.github.fauu.helix.datum.Move; import com.github.fauu.helix.datum.Tile; import com.github.fauu.helix.datum.TileAreaPassage; import com.github.fauu.helix.displayable.DecalDisplayable; import com.github.fauu.helix.displayable.ModelDisplayable; import com.github.fauu.helix.graphics.AnimationType; import com.github.fauu.helix.graphics.HelixCamera; import com.github.fauu.helix.manager.AreaManager; import com.github.fauu.helix.manager.LocalAmbienceManager; import com.github.fauu.helix.manager.PlayerManager; import com.github.fauu.helix.manager.WeatherMan; import com.github.fauu.helix.util.IntVector2; import com.github.fauu.helix.util.IntVector3; import com.google.common.collect.Range; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; public class PlayerMovementSystem extends VoidEntitySystem { private static final float PASSAGE_BLACKOUT_DURATION = .5f; @Wire private AreaManager areaManager; @Wire private TagManager tagManager; @Wire private PlayerManager playerManager; @Wire private ComponentMapper<AreaTypeComponent> areaTypeMapper; @Wire private ComponentMapper<DimensionsComponent> dimensionsMapper; @Wire private ComponentMapper<DisplayableComponent> displayableMapper; @Wire private ComponentMapper<MovementSpeedComponent> movementSpeedMapper; @Wire private ComponentMapper<OrientationComponent> orientationMapper; @Wire private ComponentMapper<PositionComponent> positionMapper; @Wire private ComponentMapper<TilesComponent> tilesMapper; @Wire private HelixCamera camera; private static final float START_DELAY; private static final HashMap<Direction, Integer> DIRECTION_KEYS; private static final AnimationType[] WALK_ANIMATION_CYCLE; private boolean enabled; private Move currentMove; private float startDelayCounter; private int walkAnimationCycleCounter; private LinkedList<Move> queue; private Move lastMove; private Entity player; private DecalDisplayable displayable; static { START_DELAY = 0.1f; DIRECTION_KEYS = new HashMap<Direction, Integer>(); DIRECTION_KEYS.put(Direction.NORTH, Input.Keys.W); DIRECTION_KEYS.put(Direction.EAST, Input.Keys.D); DIRECTION_KEYS.put(Direction.SOUTH, Input.Keys.S); DIRECTION_KEYS.put(Direction.WEST, Input.Keys.A); WALK_ANIMATION_CYCLE = new AnimationType[2]; WALK_ANIMATION_CYCLE[0] = AnimationType.STEP_RIGHT_LEG; WALK_ANIMATION_CYCLE[1] = AnimationType.STEP_LEFT_LEG; } @Override public void initialize() { enabled = true; queue = new LinkedList<Move>(); } @Override protected boolean checkProcessing() { return enabled; } @Override protected void processSystem() { player = playerManager.getPlayer(); displayable = (DecalDisplayable) displayableMapper.get(player).get(); if (!isMoving()) { Direction pressedDirection = pollPressedDirection(); if (pressedDirection != null) { if (startDelayCounter == 0) { orientationMapper.get(player).set(pressedDirection); displayable.orientate(pressedDirection); } if (queue.size() == 0 && startDelayCounter >= START_DELAY) { tryMoving(pressedDirection); } else { startDelayCounter += Gdx.graphics.getDeltaTime(); } } else { startDelayCounter = 0; } Move nextMove = queue.poll(); if (nextMove != null) { currentMove = nextMove; } } if (isMoving()) { advanceMove(); } } private void tryMoving(Direction direction) { orientationMapper.get(player).set(direction); displayable.orientate(direction); IntVector3 position = positionMapper.get(player).get(); IntVector2 targetCoords = position.toIntVector2().add(direction.getVector()); Entity area = areaManager.getArea(); if (!coordsInsideArea(targetCoords, area)) { return; } Tile[][] tiles = tilesMapper.get(area).get(); Tile currentTile = Tile.get(tiles, position.toIntVector2()); Tile targetTile = Tile.get(tiles, targetCoords); switch (targetTile.getPermissions()) { case OBSTACLE: break; case PASSAGE: moveThroughAreaPassage(targetTile.getAreaPassage(), targetCoords, direction); break; case RAMP: moveOnRamp(currentTile, findRampEnd(tiles, targetCoords, direction), direction); break; default: Move move = new Move(); move.setDirection(direction); if (currentTile.getPermissions() == TilePermission.RAMP) { float moveVectorZSgn = direction == lastMove.getDirection() ? 1 : -1; move.setVectorZ(moveVectorZSgn * lastMove.getVector().z); } move.setSpeed(movementSpeedMapper.get(player).get()); queue.push(move); } } private void moveOnRamp(Tile currentTile, Tile rampEnd, Direction direction) { float moveVectorZ; if (currentTile.getPermissions() == TilePermission.RAMP) { moveVectorZ = lastMove.getVector().z; } else { int currentElevation = currentTile.getPermissions().getElevation(); int finalElevation = rampEnd.getPermissions().getElevation(); moveVectorZ = currentElevation < finalElevation ? .5f : -.5f; } Move move = new Move(); move.setDirection(direction); move.setVectorZ(moveVectorZ); move.setSpeed(movementSpeedMapper.get(player).get()); queue.push(move); } private Tile findRampEnd(Tile[][] tiles, IntVector2 startCoords, Direction direction) { IntVector2 coords = startCoords.cpy(); Tile end; do { end = Tile.get(tiles, coords); coords.add(direction.getVector()); } while (end.getPermissions() == TilePermission.RAMP); return end; } private void moveThroughAreaPassage(final TileAreaPassage passage, final IntVector2 passageCoords, final Direction direction) { float entryDuration = moveIntoAreaPassage(passageCoords, direction); Timer.schedule(new Timer.Task() { @Override public void run() { areaManager.unloadCurrent(); areaManager.load(passage.getTargetAreaName()); // TODO: Get this out of here if (areaTypeMapper.get(areaManager.getArea()).get() == AreaType.OUTDOOR) { world.getManager(LocalAmbienceManager.class) .setAmbience(world.getManager(WeatherMan.class).getWeather()); } else { world.getManager(LocalAmbienceManager.class).setAmbience(new Ambience()); } IntVector3 exitPosition = new IntVector3(passage.getTargetCoords()); positionMapper.get(player).set(exitPosition); displayable.updatePosition(exitPosition); // TODO: Camera stuff out of here? camera.updateTargetPosition(exitPosition); moveOutOfAreaPassage(passage, direction); } }, entryDuration + PASSAGE_BLACKOUT_DURATION); } private float moveIntoAreaPassage(IntVector2 passageCoords, Direction direction) { final ModelDisplayable areaDisplayable = (ModelDisplayable) displayableMapper.get(areaManager.getArea()) .get(); String entryAnimationId = AreaManager.constructPassageAnimationId(passageCoords, PassageAction.ENTRY); float entryDelay = areaDisplayable.animateIfAnimationExists(entryAnimationId) ? 1 : 0; enabled = false; final Move entryMove = new Move(); entryMove.setDirection(direction); entryMove.setSpeed(movementSpeedMapper.get(player).get()); Timer.schedule(new Timer.Task() { @Override public void run() { queue.push(entryMove); world.getSystem(ScreenFadingSystem.class).fade(ScreenFadingSystem.FadeType.FADE_OUT, .2f); enabled = true; } }, entryDelay); return entryDelay + entryMove.getDuration(); } private void moveOutOfAreaPassage(TileAreaPassage passage, Direction direction) { float exitAnimationWaitTime = 0; String exitAnimationId = AreaManager.constructPassageAnimationId(passage.getTargetCoords(), PassageAction.EXIT); ModelDisplayable areaDisplayable = (ModelDisplayable) displayableMapper.get(areaManager.getArea()).get(); areaDisplayable.animateIfAnimationExists(exitAnimationId); final Move exitMove = new Move(); exitMove.setDirection(direction); exitMove.setSpeed(movementSpeedMapper.get(player).get()); Timer.schedule(new Timer.Task() { @Override public void run() { world.getSystem(ScreenFadingSystem.class).fade(ScreenFadingSystem.FadeType.FADE_IN, .2f); queue.push(exitMove); } }, exitAnimationWaitTime); } private void advanceMove() { if (!currentMove.hasFinished()) { if (!currentMove.hasStarted()) { IntVector3 position = positionMapper.get(player).get(); IntVector2 targetPosition = position.toIntVector2().add(currentMove.getDirection().getVector()); Tile[][] tiles = tilesMapper.get(areaManager.getArea()).get(); Tile targetTile = tiles[targetPosition.y][targetPosition.x]; displayable.animate(WALK_ANIMATION_CYCLE[walkAnimationCycleCounter++], currentMove.getDirection(), currentMove.getDuration()); walkAnimationCycleCounter %= 2; PositionComponent positionComponent = positionMapper.get(player); IntVector3 newPosition = new IntVector3(targetPosition.x, targetPosition.y, targetTile.getPermissions().getElevation()); positionComponent.set(newPosition); } // TODO: Find a way for the movement to be less choppy while remaining accurate float delta = currentMove.willHaveBeenFinishedIn(Gdx.graphics.getDeltaTime()) ? currentMove.getRemaining() : Gdx.graphics.getDeltaTime(); Vector3 translation = currentMove.getVector().cpy().scl(delta * currentMove.getSpeed()); displayable.move(translation); camera.translateTargetPosition(translation); currentMove.setElapsed(currentMove.getElapsed() + delta); } else { lastMove = currentMove; currentMove = null; } } private Direction pollPressedDirection() { Direction pressedDirection = null; for (Map.Entry<Direction, Integer> dk : DIRECTION_KEYS.entrySet()) { if (Gdx.input.isKeyPressed(dk.getValue())) { pressedDirection = dk.getKey(); break; } } return pressedDirection; } private boolean coordsInsideArea(IntVector2 coords, Entity area) { IntVector2 areaDimensions = dimensionsMapper.get(area).get(); return (Range.closedOpen(0, areaDimensions.x).contains(coords.x) && Range.closedOpen(0, areaDimensions.y).contains(coords.y)); } private boolean isMoving() { return currentMove != null; } }