M core/src/net/dermetfan/jigsawPuzzle/JigsawPuzzle.java +102 -6
@@ 14,29 14,125 @@
package net.dermetfan.jigsawPuzzle;
-import com.badlogic.gdx.Game;
+import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Input.TextInputListener;
+import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.g2d.Batch;
+import com.badlogic.gdx.graphics.g2d.PolygonRegion;
+import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
+import com.badlogic.gdx.scenes.scene2d.Stage;
+import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
-import net.dermetfan.jigsawPuzzle.screens.PlayScreen;
+import com.badlogic.gdx.scenes.scene2d.ui.Table;
+import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup;
+import com.badlogic.gdx.scenes.scene2d.utils.DragAndDrop;
+import com.badlogic.gdx.utils.viewport.ScreenViewport;
+import net.dermetfan.gdx.scenes.scene2d.ui.JigsawPuzzle.Piece;
+import net.dermetfan.gdx.scenes.scene2d.ui.JigsawPuzzle.Source;
+import net.dermetfan.gdx.scenes.scene2d.ui.JigsawPuzzle.Target;
+import net.dermetfan.gdx.scenes.scene2d.utils.PolygonRegionDrawable;
/** entry-point / the actual game
* @author dermetfan */
-public class JigsawPuzzle extends Game {
+public class JigsawPuzzle extends ApplicationAdapter {
- /** loads all levels and shows the {@link PlayScreen} */
+ private Batch batch;
+ private Stage stage;
+
@Override
public void create() {
Assets.loadAllLevels();
Assets.manager.load(Assets.uiskin, Skin.class);
Assets.manager.finishLoading();
- setScreen(new PlayScreen());
+
+ batch = new PolygonSpriteBatch(); // PolygonSpriteBatch needed to draw pieces correctly, but doesn't crash with a SpriteBatch
+ stage = new Stage(new ScreenViewport(), batch); // make the stage use the batch (by default, it creates a SpriteBatch, but we need a PolygonSpriteBatch)
+ stage.setDebugAll(true);
+ Gdx.input.getTextInput(new TextInputListener() { // temporary solution - ask user for name of the level directory to load psh files (pieces) from
+ @Override
+ public void input(String level) {
+ start(level);
+ }
+
+ @Override
+ public void canceled() {
+ Gdx.app.log("PlayScreen", "no level selected, exiting");
+ Gdx.app.exit();
+ }
+ }, "select level", "", "enter level name");
+ Gdx.input.setInputProcessor(stage); // let the stage receive input events
+ }
+
+ /** creates the puzzle
+ * @param level the name of the level directory in {@link Assets#puzzlesDir} to load the puzzle from */
+ private void start(String level) {
+ stage.clear(); // clear everything for subsequent calls
+ Table table = new Table();
+ table.setFillParent(true); // make table fill the whole stage
+ stage.addActor(table);
+
+ // dialog to contain the puzzle
+ final Dialog dialog = new Dialog(level, Assets.manager.get(Assets.uiskin, Skin.class));
+ dialog.setModal(false);
+ dialog.setResizable(true);
+
+ final net.dermetfan.gdx.scenes.scene2d.ui.JigsawPuzzle puzzle = new net.dermetfan.gdx.scenes.scene2d.ui.JigsawPuzzle();
+ final WidgetGroup board = new WidgetGroup(); // the group that pieces will be dropped on
+ float nextX = 0, tallest = 0;
+ for(FileHandle psh : Assets.puzzlePSHs(level)) { // set up the pieces on the bank
+ Piece piece = new Piece(new PolygonRegionDrawable(Assets.manager.get(psh.path(), PolygonRegion.class)));
+ puzzle.add(piece);
+ board.addActor(piece);
+ piece.setX(nextX);
+ nextX += piece.getWidth();
+ if(piece.getHeight() > tallest)
+ tallest = piece.getHeight();
+ }
+
+ DragAndDrop dnd = new DragAndDrop();
+ dnd.addSource(new Source(board, dnd, puzzle));
+ dnd.addTarget(new Target(board, puzzle, 10) {
+ @Override
+ protected void solved() {
+ dialog.hide();
+ Gdx.input.getTextInput(new TextInputListener() {
+ @Override
+ public void input(String level) {
+ start(level);
+ }
+
+ @Override
+ public void canceled() {
+ Gdx.app.exit();
+ }
+
+ }, "Puzzle solved!", "", "Play again? Enter level name");
+ }
+ });
+
+ dialog.getContentTable().add(board).expand().fill().minSize(nextX, tallest);
+
+ dialog.show(stage);
+ }
+
+ @Override
+ public void resize(int width, int height) {
+ stage.getViewport().update(width, height, true);
}
@Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // clear the screen
- super.render(); // draw current screen
+ stage.act();
+ stage.draw();
+ }
+
+ @Override
+ public void dispose() {
+ stage.dispose();
+ batch.dispose();
}
}
R core/src/net/dermetfan/jigsawPuzzle/puzzle/JigsawPuzzle.java => +0 -379
@@ 1,379 0,0 @@
-/** Copyright 2014 Robin Stumm (serverkorken@gmail.com, http://dermetfan.net)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License. */
-
-package net.dermetfan.jigsawPuzzle.puzzle;
-
-import com.badlogic.gdx.math.Intersector;
-import com.badlogic.gdx.math.Vector2;
-import com.badlogic.gdx.scenes.scene2d.Actor;
-import com.badlogic.gdx.scenes.scene2d.Group;
-import com.badlogic.gdx.scenes.scene2d.InputEvent;
-import com.badlogic.gdx.scenes.scene2d.actions.Actions;
-import com.badlogic.gdx.scenes.scene2d.ui.Image;
-import com.badlogic.gdx.scenes.scene2d.utils.DragAndDrop;
-import com.badlogic.gdx.scenes.scene2d.utils.DragAndDrop.Payload;
-import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
-import com.badlogic.gdx.utils.Array;
-import com.badlogic.gdx.utils.Pools;
-import net.dermetfan.gdx.scenes.scene2d.Scene2DUtils;
-import net.dermetfan.gdx.scenes.scene2d.utils.PolygonRegionDrawable;
-
-/** represents a puzzle and manages {@link Piece Pieces}
- * @author dermetfan */
-public class JigsawPuzzle {
-
- /** the {@link Piece pieces} of the puzzle */
- private final Array<Piece> pieces;
-
- public JigsawPuzzle() {
- pieces = new Array<>(Piece.class);
- }
-
- /** @param pieces the amount of pieces that will probably be in this puzzle */
- public JigsawPuzzle(int pieces) {
- this.pieces = new Array<>(pieces);
- }
-
- /** @param pieces the {@link #pieces} */
- public JigsawPuzzle(Piece... pieces) {
- this.pieces = new Array<>(pieces);
- }
-
- /** solves the puzzle by letting all {@link #pieces} {@link Piece#place(Piece) snap} into their spot
- * @param relativeTo the piece relative to which the puzzle should be solved */
- public void solve(Piece relativeTo) {
- if(!pieces.contains(relativeTo, true))
- throw new IllegalArgumentException("the reference piece is not part of the puzzle");
- for(Piece piece : pieces) {
- if(piece == relativeTo)
- continue;
- piece.place(relativeTo);
- }
- }
-
- /** @param piece the piece to add to {@link #pieces} */
- public void add(Piece piece) {
- if(!pieces.contains(piece, true))
- pieces.add(piece);
- }
-
- /** @param piece the piece to remove from {@link #pieces}
- * @return if the piece was found and removed */
- public boolean remove(Piece piece) {
- return pieces.removeValue(piece, true);
- }
-
- /** @param piece the piece to which to find the closest other piece
- * @return the piece most correctly placed in relation to the given piece
- * @deprecated no known use case */
- @Deprecated
- public Piece findClosest(Piece piece) {
- float distance = Float.POSITIVE_INFINITY;
- Piece closest = null;
- for(Piece other : pieces) {
- if(other == piece)
- continue;
- float dist = Vector2.dst(other.getX() + other.getSlotX(), other.getY() + other.getSlotY(), piece.getX() + piece.getSlotX(), piece.getY() + piece.getSlotY());
- if(dist < distance) {
- distance = dist;
- closest = other;
- }
- }
- return closest;
- }
-
- /** @param tolerance the distance by which each piece is allowed to be off
- * @return if the puzzle is solved */
- public boolean isSolved(float tolerance) {
- Piece reference = pieces.first();
- for(Piece piece : pieces)
- if(!piece.isPlacedCorrectly(reference, tolerance))
- return false;
- return true;
- }
-
- /** @return the {@link #pieces} */
- public Array<Piece> getPieces() {
- return pieces;
- }
-
- /** a piece on a {@link JigsawPuzzle}
- * @author dermetfan */
- public static class Piece extends Image {
-
- /** the position of the piece on the puzzle (the {@link PolygonRegionDrawable#getPolygonX() minX} and {@link PolygonRegionDrawable#getPolygonY() minY} of its vertices) */
- private float slotX, slotY;
-
- public Piece(Drawable drawable) {
- super(drawable);
- }
-
- /** @param drawable Sets the {@link #setSlot(float, float) slot} to the drawable's {@link PolygonRegionDrawable#getPolygonX() polygonX} and {@link PolygonRegionDrawable#getPolygonY() polygonY} if the given drawable is a {@link PolygonRegionDrawable}. Otherwise sets it to 0:0. */
- @Override
- public void setDrawable(Drawable drawable) {
- super.setDrawable(drawable);
- if(drawable instanceof PolygonRegionDrawable) {
- PolygonRegionDrawable pd = (PolygonRegionDrawable) drawable;
- slotX = pd.getPolygonX();
- slotY = pd.getPolygonY();
- } else
- slotX = slotY = 0;
- }
-
- @Override
- public Actor hit(float x, float y, boolean touchable) {
- Actor hit = super.hit(x, y, touchable);
- PolygonRegionDrawable drawable = getDrawable() instanceof PolygonRegionDrawable ? (PolygonRegionDrawable) getDrawable() : null;
- if(hit == this && drawable != null) {
- float[] vertices = drawable.getRegion().getVertices();
- if(!Intersector.isPointInPolygon(vertices, 0, vertices.length, x / getWidth() * drawable.getPolygonWidth() + slotX, y / getHeight() * drawable.getPolygonHeight() + slotY))
- return null;
- }
- return hit;
- }
-
- /** @param reference the piece in relation to which to this piece should snap in its spot */
- public void place(Piece reference) {
- Vector2 refPuzzlePoint = Pools.obtain(Vector2.class).set(reference.getX(), reference.getY()).sub(reference.slotX, reference.slotY);
- setPosition(refPuzzlePoint.x + slotX, refPuzzlePoint.y + slotY);
- Pools.free(refPuzzlePoint);
- }
-
- /** @param reference the piece in relation to which this piece's position should be checked
- * @param tolerance the distance by which each piece is allowed to be off
- * @return if this piece is placed correctly in relation to the given reference piece with the given tolerance */
- public boolean isPlacedCorrectly(Piece reference, float tolerance) {
- // get bottom left corner of each puzzle
- Vector2 puzzlePos = Pools.obtain(Vector2.class).set(-slotX, -slotY), refPuzzlePoint = Pools.obtain(Vector2.class).set(-reference.slotX, -reference.slotY);
- localToStageCoordinates(puzzlePos);
- reference.localToStageCoordinates(refPuzzlePoint);
-
- // see if they're the same
- boolean rel = puzzlePos.epsilonEquals(refPuzzlePoint, tolerance);
-
- Pools.free(puzzlePos);
- Pools.free(refPuzzlePoint);
-
- return rel;
- }
-
- // getters and setters
-
- /** @param slotX the {@link #slotX} to set
- * @param slotY the {@link #slotY} to set */
- public void setSlot(float slotX, float slotY) {
- this.slotX = slotX;
- this.slotY = slotY;
- }
-
- /** @return the {@link #slotX} */
- public float getSlotX() {
- return slotX;
- }
-
- /** @param slotX the {@link #slotX} to set */
- public void setSlotX(float slotX) {
- this.slotX = slotX;
- }
-
- /** @return the {@link #slotY} */
- public float getSlotY() {
- return slotY;
- }
-
- /** @param slotY the {@link #slotY} to set */
- public void setSlotY(float slotY) {
- this.slotY = slotY;
- }
-
- }
-
- /** @author dermetfan */
- public static class Source extends DragAndDrop.Source {
-
- /** the DragAndDrop currently using this Source */
- private DragAndDrop dragAndDrop;
-
- /** the puzzle */
- private JigsawPuzzle puzzle;
-
- /** the time it takes for the piece to move back */
- private float moveBackDuration = .5f;
-
- /** temporary Payload for internal use */
- private final Payload payload = new Payload();
-
- /** temporary Vector2 for internal use */
- private final Vector2 vec2 = new Vector2();
-
- /** @param dragAndDrop the {@link #dragAndDrop}
- * @param puzzle the {@link #puzzle} */
- public Source(Group board, DragAndDrop dragAndDrop, JigsawPuzzle puzzle) {
- super(board);
- this.dragAndDrop = dragAndDrop;
- this.puzzle = puzzle;
- }
-
- /** @param moveBackDuration the {@link #moveBackDuration}
- * @see #Source(Group, DragAndDrop, JigsawPuzzle) */
- public Source(final Group board, final DragAndDrop dragAndDrop, JigsawPuzzle puzzle, float moveBackDuration) {
- this(board, dragAndDrop, puzzle);
- this.moveBackDuration = moveBackDuration;
- }
-
- @Override
- public Payload dragStart(InputEvent event, float x, float y, int pointer) {
- Actor actor = getActor().hit(x, y, true); // get actor under pointer
- // don't drag the board itself or anything that's not a piece of the puzzle
- if(actor == getActor() || !(actor instanceof Piece) || !puzzle.getPieces().contains((Piece) actor, true))
- return null;
- payload.setDragActor(actor);
-
- // put the actor in the right position under the pointer
- ((Group) getActor()).localToDescendantCoordinates(actor, vec2.set(x, y));
- dragAndDrop.setDragActorPosition(-vec2.x, -vec2.y + actor.getHeight());
-
- vec2.set(actor.getX(), actor.getY()); // set vec2 for dragStop (to the current position to be able to move the piece back)
- return payload;
- }
-
- @Override
- public void dragStop(InputEvent event, float x, float y, int pointer, Payload payload, DragAndDrop.Target target) {
- final Actor actor = payload.getDragActor();
- if(actor.getParent() == null) { // move back to where the piece was dragged from
- // put it on the stage in the correct position
- getActor().getStage().addActor(actor);
- getActor().localToStageCoordinates(vec2);
- // move back
- actor.addAction(Actions.sequence(Actions.moveTo(vec2.x, vec2.y, moveBackDuration), Actions.run(new Runnable() {
- @Override
- public void run() {
- ((Group) getActor()).addActor(actor);
- // set position correctly on the board
- getActor().stageToLocalCoordinates(vec2);
- actor.setPosition(vec2.x, vec2.y);
- }
- })));
- }
- }
-
- // getters and setters
-
- /** @return the {@link #dragAndDrop} */
- public DragAndDrop getDragAndDrop() {
- return dragAndDrop;
- }
-
- /** @param dragAndDrop the {@link #dragAndDrop} to set */
- public void setDragAndDrop(DragAndDrop dragAndDrop) {
- this.dragAndDrop = dragAndDrop;
- }
-
- /** @return the {@link #puzzle} */
- public JigsawPuzzle getPuzzle() {
- return puzzle;
- }
-
- /** @param puzzle the {@link #puzzle} to set */
- public void setPuzzle(JigsawPuzzle puzzle) {
- this.puzzle = puzzle;
- }
-
- /** @return the {@link #moveBackDuration} */
- public float getMoveBackDuration() {
- return moveBackDuration;
- }
-
- /** @param moveBackDuration the {@link #moveBackDuration} to set */
- public void setMoveBackDuration(float moveBackDuration) {
- this.moveBackDuration = moveBackDuration;
- }
-
- }
-
- /** @author dermetfan */
- public static class Target extends DragAndDrop.Target {
-
- /** the puzzle */
- private JigsawPuzzle puzzle;
-
- /** the distance by which each piece is allowed to be off */
- private float tolerance;
-
- /** @param tolerance the {@link #tolerance} */
- public Target(Group group, JigsawPuzzle puzzle, float tolerance) {
- super(group);
- this.puzzle = puzzle;
- this.tolerance = tolerance;
- }
-
- @Override
- public boolean drag(DragAndDrop.Source source, Payload payload, float x, float y, int pointer) {
- return true;
- }
-
- @Override
- public void drop(DragAndDrop.Source source, Payload payload, float x, float y, int pointer) {
- Actor dragged = payload.getDragActor();
- Scene2DUtils.addAtStageCoordinates(dragged, (Group) getActor());
- if(dragged instanceof Piece) {
- Piece piece = (Piece) dragged;
- for(int i = 0; i < puzzle.getPieces().size; i++) {
- Piece ref = puzzle.getPieces().get(i);
- if(ref == piece)
- continue;
- if(piece.isPlacedCorrectly(ref, tolerance)) {
- piece.place(ref);
- placed(piece);
- break;
- }
- }
- }
- }
-
- /** called by {@link #drop(DragAndDrop.Source, Payload, float, float, int) drop} when a piece is placed
- * @param piece the placed piece */
- protected void placed(Piece piece) {
- if(puzzle.isSolved(tolerance))
- solved();
- }
-
- /** called by {@link #placed(Piece) placed} when all pieces are placed correctly */
- protected void solved() {}
-
- // getters and setters
-
- /** @return the {@link #puzzle} */
- public JigsawPuzzle getPuzzle() {
- return puzzle;
- }
-
- /** @param puzzle the {@link #puzzle} to set */
- public void setPuzzle(JigsawPuzzle puzzle) {
- this.puzzle = puzzle;
- }
-
- /** @return the {@link #tolerance} */
- public float getTolerance() {
- return tolerance;
- }
-
- /** @param tolerance the {@link #tolerance} to set */
- public void setTolerance(float tolerance) {
- this.tolerance = tolerance;
- }
-
- }
-
-}
R core/src/net/dermetfan/jigsawPuzzle/screens/PlayScreen.java => +0 -136
@@ 1,136 0,0 @@
-/** Copyright 2014 Robin Stumm (serverkorken@gmail.com, http://dermetfan.net)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License. */
-
-package net.dermetfan.jigsawPuzzle.screens;
-
-import com.badlogic.gdx.Gdx;
-import com.badlogic.gdx.Input.TextInputListener;
-import com.badlogic.gdx.ScreenAdapter;
-import com.badlogic.gdx.files.FileHandle;
-import com.badlogic.gdx.graphics.g2d.Batch;
-import com.badlogic.gdx.graphics.g2d.PolygonRegion;
-import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
-import com.badlogic.gdx.scenes.scene2d.Stage;
-import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
-import com.badlogic.gdx.scenes.scene2d.ui.Skin;
-import com.badlogic.gdx.scenes.scene2d.ui.Table;
-import com.badlogic.gdx.scenes.scene2d.ui.WidgetGroup;
-import com.badlogic.gdx.scenes.scene2d.utils.DragAndDrop;
-import com.badlogic.gdx.utils.viewport.ScreenViewport;
-import net.dermetfan.gdx.scenes.scene2d.utils.PolygonRegionDrawable;
-import net.dermetfan.jigsawPuzzle.Assets;
-import net.dermetfan.jigsawPuzzle.puzzle.JigsawPuzzle;
-import net.dermetfan.jigsawPuzzle.puzzle.JigsawPuzzle.Piece;
-import net.dermetfan.jigsawPuzzle.puzzle.JigsawPuzzle.Source;
-import net.dermetfan.jigsawPuzzle.puzzle.JigsawPuzzle.Target;
-
-/** a UI for a {@link JigsawPuzzle}
- * @author dermetfan */
-public class PlayScreen extends ScreenAdapter {
-
- private Batch batch = new PolygonSpriteBatch(); // PolygonSpriteBatch needed to draw pieces correctly, but doesn't crash with a SpriteBatch
- private Stage stage;
-
- public PlayScreen() {
- stage = new Stage(new ScreenViewport(), batch); // make the stage use the batch (by default, it creates a SpriteBatch, but we need a PolygonSpriteBatch)
- stage.setDebugAll(true);
- Gdx.input.getTextInput(new TextInputListener() { // temporary solution - ask user for name of the level directory to load psh files (pieces) from
- @Override
- public void input(String level) {
- start(level);
- }
-
- @Override
- public void canceled() {
- Gdx.app.log("PlayScreen", "no level selected, exiting");
- Gdx.app.exit();
- }
- }, "select level", "", "enter level name");
- }
-
- @Override
- public void show() {
- Gdx.input.setInputProcessor(stage); // let the stage receive input events
- }
-
- /** creates the puzzle
- * @param level the name of the level directory in {@link Assets#puzzlesDir} to load the puzzle from */
- private void start(String level) {
- stage.clear(); // clear everything for subsequent calls
- Table table = new Table();
- table.setFillParent(true); // make table fill the whole stage
- stage.addActor(table);
-
- // dialog to contain the puzzle
- final Dialog dialog = new Dialog(level, Assets.manager.get(Assets.uiskin, Skin.class));
- dialog.setModal(false);
- dialog.setResizable(true);
-
- final JigsawPuzzle puzzle = new JigsawPuzzle();
- final WidgetGroup board = new WidgetGroup(); // the group that pieces will be dropped on
- float nextX = 0, tallest = 0;
- for(FileHandle psh : Assets.puzzlePSHs(level)) { // set up the pieces on the bank
- Piece piece = new Piece(new PolygonRegionDrawable(Assets.manager.get(psh.path(), PolygonRegion.class)));
- puzzle.add(piece);
- board.addActor(piece);
- piece.setX(nextX);
- nextX += piece.getWidth();
- if(piece.getHeight() > tallest)
- tallest = piece.getHeight();
- }
-
- DragAndDrop dnd = new DragAndDrop();
- dnd.addSource(new Source(board, dnd, puzzle));
- dnd.addTarget(new Target(board, puzzle, 10) {
- @Override
- protected void solved() {
- dialog.hide();
- Gdx.input.getTextInput(new TextInputListener() {
- @Override
- public void input(String level) {
- start(level);
- }
-
- @Override
- public void canceled() {
- Gdx.app.exit();
- }
-
- }, "Puzzle solved!", "", "Play again? Enter level name");
- }
- });
-
- dialog.getContentTable().add(board).expand().fill().minSize(nextX, tallest);
-
- dialog.show(stage);
- }
-
- @Override
- public void resize(int width, int height) {
- stage.getViewport().update(width, height, true);
- }
-
- @Override
- public void render(float delta) {
- stage.act(delta);
- stage.draw();
- }
-
- @Override
- public void dispose() {
- stage.dispose();
- batch.dispose();
- }
-
-}