diff --git a/.classpath b/.classpath
index d82323d..37d4dc5 100644
--- a/.classpath
+++ b/.classpath
@@ -7,5 +7,6 @@
+
diff --git a/bin/tfg/AnimatedSprite.class b/bin/tfg/AnimatedSprite.class
new file mode 100644
index 0000000..c2cc485
Binary files /dev/null and b/bin/tfg/AnimatedSprite.class differ
diff --git a/bin/tfg/Direction.class b/bin/tfg/Direction.class
index 246995b..c7296d8 100644
Binary files a/bin/tfg/Direction.class and b/bin/tfg/Direction.class differ
diff --git a/bin/tfg/Game.class b/bin/tfg/Game.class
index 096f174..531999c 100644
Binary files a/bin/tfg/Game.class and b/bin/tfg/Game.class differ
diff --git a/bin/tfg/Location.class b/bin/tfg/Location.class
index 8832977..0e24d1a 100644
Binary files a/bin/tfg/Location.class and b/bin/tfg/Location.class differ
diff --git a/bin/tfg/Map.class b/bin/tfg/Map.class
index d96b208..e7f8884 100644
Binary files a/bin/tfg/Map.class and b/bin/tfg/Map.class differ
diff --git a/bin/tfg/Player.class b/bin/tfg/Player.class
index 378b102..2fd159f 100644
Binary files a/bin/tfg/Player.class and b/bin/tfg/Player.class differ
diff --git a/bin/tfg/TextUIElement.class b/bin/tfg/TextUIElement.class
new file mode 100644
index 0000000..4a30a41
Binary files /dev/null and b/bin/tfg/TextUIElement.class differ
diff --git a/bin/tfg/Tile.class b/bin/tfg/Tile.class
index 32013b1..9f22357 100644
Binary files a/bin/tfg/Tile.class and b/bin/tfg/Tile.class differ
diff --git a/bin/tfg/UITextElement.class b/bin/tfg/UITextElement.class
deleted file mode 100644
index ce9f62e..0000000
Binary files a/bin/tfg/UITextElement.class and /dev/null differ
diff --git a/src/tfg/AnimatedSprite.java b/src/tfg/AnimatedSprite.java
new file mode 100644
index 0000000..8fba2f4
--- /dev/null
+++ b/src/tfg/AnimatedSprite.java
@@ -0,0 +1,164 @@
+package tfg;
+
+import org.jsfml.graphics.IntRect;
+import org.jsfml.graphics.Sprite;
+import org.jsfml.system.Vector2f;
+import org.jsfml.system.Vector2i;
+
+/**
+ * A sprite that supports animation.
+ * @author Ritchie Cunningham
+ */
+public class AnimatedSprite {
+ /**
+ * Speed of animation in Hz.
+ */
+ private final float animationSpeed = 15;
+ /**
+ * The frame (stage) in the animation.
+ */
+ private int animationFrame = 0;
+ /**
+ * Keeps track of time between frame changes.
+ */
+ private int frameCounter = 0;
+ /**
+ * Whether or not the sprite is currently animated.
+ */
+ private boolean animating = false;
+ /**
+ * The position at which the sprite is rendered.
+ */
+ private Vector2f spritePosition = new Vector2f(0,0);
+ /**
+ * The sprite to animate.
+ */
+ private Sprite animatedSprite;
+ /**
+ * Location of the entity this sprite represents.
+ */
+ private Location entityLoc;
+ /**
+ * Size of the sprite.
+ */
+ private final Vector2i spriteSize = new Vector2i(32, 48);
+
+ /**
+ * Create a new animated sprite.
+ * @param s The sprite to animate.
+ * @param l The location of the entity the sprite represents.
+ */
+ public AnimatedSprite(Sprite s, Location l) {
+ animatedSprite = s;
+ entityLoc = l;
+ }
+
+ /**
+ * Start the animation.
+ */
+ public void startWalkingAnimation() {
+ startAnimation();
+ }
+
+ /**
+ * Update animation.
+ */
+ public void animate() {
+ if(animating) { /* Then update animation. */
+ if(frameCounter >= animationSpeed) { /* If 15Hz has passed. */
+ stopAnimation();
+ } else {
+ /* Get the position between old and new. */
+ spritePosition = entityLoc.interpolate(animationSpeed, frameCounter);
+ Vector2i entityPos = entityLoc.getPosition(); /* Get the entity position. */
+ /* Subtract new position from starting position to get a number from 0.0 - 1.0. */
+ Vector2f animationProgress =
+ Vector2f.sub(spritePosition, new Vector2f(entityPos.x,entityPos.y));
+ /* Take the abs value because we're measuring distance. */
+ float change = Math.abs(animationProgress.x+animationProgress.y);
+ /* Determine frame based on position. */
+ if(change >= 0.f && change < .25f) {
+ animationFrame = 0;
+ } else if(change >= .25f && change < .5f) {
+ animationFrame = 1;
+ } else if(change >=.5f && change < .75f) {
+ animationFrame = 2;
+ } else if(change >= .75f && change <= 1.0f) {
+ animationFrame = 3;
+ }
+ }
+ frameCounter++; /* Increment on each update to keep track of time. */
+ }
+ }
+
+ /**
+ * Stop the animation.
+ */
+ private void stopAnimation() {
+ /* Reset the following. */
+ animating = false;
+ frameCounter = 0;
+ animationFrame = 0;
+ }
+
+ /**
+ * Start the animation.
+ */
+ private void startAnimation() {
+ animating = true;
+ }
+
+ /**
+ * Determine whether or not the animation completed.
+ * @return If the animation finished.
+ */
+ public boolean finishedAnimating() {
+ return !animating;
+ }
+
+ /**
+ * Update the entity position.
+ * @param l The new location.
+ */
+ public void updatePosition(Location l) {
+ entityLoc = l; /* Update the entity lcoation. */
+ Vector2i entityPos = entityLoc.getPosition(); /* Get Pos from last location. */
+ spritePosition = new Vector2f(entityPos.x, entityPos.y); /* Update sprite pos. */
+ }
+
+ /**
+ * Get the animated sprite to draw it.
+ * @return The anumated sprite.
+ */
+ public Sprite getSprite() {
+ /* Set texture based on direction and animation frame. */
+ animatedSprite.setTextureRect(getTextureCoords());
+ /* Set sprite position */
+ animatedSprite.setPosition(new Vector2f(spritePosition.x * spriteSize.x,
+ (spritePosition.y*spriteSize.x)-(spriteSize.y-spriteSize.x)));
+ return animatedSprite;
+ }
+
+ private IntRect getTextureCoords() {
+ int topX = animationFrame * 32; /* x coord of the image. */
+ int topY = 0; /* Top y coord of the image. */
+ /* Match the correct image to the direction. */
+ switch(entityLoc.getDirection()) {
+ case NORTH:
+ topY = 144;
+ break;
+ case SOUTH:
+ topY = 0;
+ break;
+ case EAST:
+ topY = 96;
+ break;
+ case WEST:
+ topY = 48;
+ break;
+ }
+ /* Create and return an int rectangle. */
+ IntRect textureCoordsRect = new IntRect(topX, topY, spriteSize.x, spriteSize.y);
+ return textureCoordsRect;
+ }
+}
diff --git a/src/tfg/Direction.java b/src/tfg/Direction.java
index 53a114d..422a9b0 100644
--- a/src/tfg/Direction.java
+++ b/src/tfg/Direction.java
@@ -1,3 +1,7 @@
package tfg;
+/**
+ * The cardinal directions.
+ * @author Ritchie Cunningham
+ */
public enum Direction { NORTH, SOUTH, EAST, WEST };
diff --git a/src/tfg/Game.java b/src/tfg/Game.java
index 78af144..42e5b15 100644
--- a/src/tfg/Game.java
+++ b/src/tfg/Game.java
@@ -1,7 +1,6 @@
package tfg;
import org.jsfml.graphics.Color;
-import org.jsfml.graphics.Font;
import org.jsfml.graphics.RenderWindow;
import org.jsfml.graphics.TextStyle;
import org.jsfml.system.Clock;
@@ -13,93 +12,117 @@ import org.jsfml.window.event.Event;
/**
* TFG Game's main class.
+ * This class ideally should be as short as possible.
* @author Ritchie Cunningham
*/
public class Game {
- private RenderWindow renderWindow = new RenderWindow();
- private final String renderWindowTitle = "TFG Game";
- private final Vector2i renderWindowDimensions = new Vector2i(640, 480);
- private Player player;
- private Camera camera;
- private boolean renderWindowFocused = true;
- private Font pixel = new Font();
- private int fps;
- private static boolean limitFPS = false;
- private UITextElement fpsCounter;
+ /**
+ * Main window where everything will be rendered to and handle input.
+ */
+ private RenderWindow window = new RenderWindow();
+ /**
+ * Set's the window title.
+ */
+ private final String windowTitle = "TFG Game";
+ /**
+ * Set dimensions (resolution) the window is created with.
+ */
+ private final Vector2i windowDimensions = new Vector2i(640, 480);
+ /**
+ * Main object representing the player.
+ */
+ private Player player = new Player();
+ /**
+ * UI Element responsible for displaying the FPS.
+ */
+ private final TextUIElement fpsUI =
+ new TextUIElement(InterfacePosition.TOP_LEFT, Color.YELLOW,24,TextStyle.BOLD);
+
+ /**
+ * Repesents whether or not the user has the window opened and in focus.
+ */
+ private boolean windowFocus = true;
+
+ /**
+ * Create an instance of the game and run it.
+ * @param args Command line arguments passed in at run-time.
+ */
public static void main(String[] args) {
Game g = new Game(); /* Create temp object of self. */
g.run(); /* Invoke run. */
}
+ /**
+ * Configure one-time settings at start-up.
+ */
public void handleInitialization() {
- renderWindow.create(new VideoMode(renderWindowDimensions.x,
- renderWindowDimensions.y), renderWindowTitle);
-
- fpsCounter = new UITextElement(InterfacePosition.TOP_LEFT,
- Color.YELLOW,24,TextStyle.BOLD);
-
- if(limitFPS) {
- renderWindow.setFramerateLimit(60);
- }
-
- player = new Player();
-
+ window.create(new VideoMode(windowDimensions.x, windowDimensions.y),
+ windowTitle);
player.changeMap(new Map(10, 10, Tile.SAND));
- camera = new Camera(renderWindow);
}
/**
- * Run the game.
+ * Initializes the game and holds the main loop.
*/
public void run() {
handleInitialization();
- int framesDrawn = 0;
- float updateRate = 20.0f; /* 20hz */
- long maxUpdates = 1;
- Clock updateClock = new Clock();
- Clock frameClock = new Clock();
- updateClock.restart();
+ int framesDrawn = 0; /* Count each frame that is rendered */
+ float updateRate = 20.0f; /* Limit the logic loop to update at 20hz */
+ Clock updateClock = new Clock(); /* Clock used to restrict update loop rate */
+ Clock frameClock = new Clock(); /* Used to calc average FPS. */
+ updateClock.restart(); /* Reset update clock. */
+ /* Calc next update time in millisecs. */
long nextUpdate = updateClock.getElapsedTime().asMilliseconds();
- while(renderWindow.isOpen()) {
- long updates = 0;
+ /* As long as window is open, run main loop. */
+ while(window.isOpen()) {
handleInput();
+ /* Make note of the current update time. */
long updateTime = updateClock.getElapsedTime().asMicroseconds();
- while ((updateTime-nextUpdate) >= updateRate && updates++ < maxUpdates) {
+ while ((updateTime-nextUpdate) >= updateRate) {
handleLogic();
- nextUpdate += updateRate;
+ nextUpdate += updateRate; /* Compute next appropriate update time. */
}
handleDrawing();
- framesDrawn++;
+ framesDrawn++; // Frame has rendered, increment.
+ /* How long has it been since last calulating FPS? */
float elapsedTime = frameClock.getElapsedTime().asSeconds();
- if(elapsedTime >= 1.0f) {
- fps = (int)(framesDrawn/elapsedTime);
- fpsCounter.updateString("FPS: " + fps);
- framesDrawn = 0;
- frameClock.restart();
+ if(elapsedTime >= 1.0f) { /* If one second */
+ /* Divide the frames rendered by one second. */
+ fpsUI.updateString("FPS: " + (int) (framesDrawn/elapsedTime));
+ framesDrawn = 0; /* Reset the count. */
+ frameClock.restart(); /* Reset the frame clock. */
}
}
}
public void handleInput() {
- for(Event event : renderWindow.pollEvents()) {
+ /*
+ * Window based event queue.
+ * Good for single-press actions, not for repeated actions though.
+ */
+ for(Event event : window.pollEvents()) {
switch(event.type) {
case CLOSED:
- renderWindow.close();
+ window.close();
break;
case GAINED_FOCUS:
- renderWindowFocused = true;
+ windowFocus = true;
break;
case LOST_FOCUS:
- renderWindowFocused = false;
+ windowFocus = false;
default:
break;
}
}
- if(renderWindowFocused) {
+ /*
+ * Real-time input.
+ * Good for repeated actions, bad for single-press actions.
+ */
+ if(windowFocus) {
if(Keyboard.isKeyPressed(Key.W)) {
player.move(Direction.NORTH);
} else if(Keyboard.isKeyPressed(Key.S)) {
@@ -109,22 +132,29 @@ public class Game {
} else if(Keyboard.isKeyPressed(Key.D)) {
player.move(Direction.EAST);
} else if(Keyboard.isKeyPressed(Key.ESCAPE)) {
- renderWindow.close();
+ window.close();
}
}
}
+ /**
+ * Update at a fixed rate (20Hz).
+ */
public void handleLogic() {
player.update();
- Camera.MoveTo(player.getPosition(), 0.5f);
}
+ /**
+ * Updates as fast as possible. Draws all objects onto the screen.
+ */
public void handleDrawing() {
- renderWindow.clear();
- renderWindow.draw(player.getMap());
- renderWindow.draw(player);
- renderWindow.draw(fpsCounter);
- renderWindow.display();
+ /* The window has automatic double-buffering. */
+ window.clear();
+ /* Draw each object like layers, background to foreground. */
+ window.draw(player.getMap());
+ window.draw(player);
+ window.draw(fpsUI);
+ window.display();
}
}
\ No newline at end of file
diff --git a/src/tfg/Location.java b/src/tfg/Location.java
index 0249f42..3b8abef 100644
--- a/src/tfg/Location.java
+++ b/src/tfg/Location.java
@@ -1,4 +1,6 @@
package tfg;
+
+import org.jsfml.system.Vector2f;
import org.jsfml.system.Vector2i;
/**
@@ -6,52 +8,100 @@ import org.jsfml.system.Vector2i;
* @author Ritchie Cunningham
*/
public class Location implements Cloneable {
+ /**
+ * Vector position (x,y).
+ */
private Vector2i locPosition = new Vector2i(0,0);
+ /**
+ * Cardinal direction (N,S,E,W).
+ */
private Direction locDirection;
+ /**
+ * Create a new location at x, y facing south.
+ * @param x New x-coorindate.
+ * @param y New y-coordinate.
+ */
public Location(int x, int y) {
this(x, y, Direction.SOUTH);
}
+ /**
+ * Create a new location at x, y with with specified direction.
+ * @param x New x-coordinate.
+ * @param y New y-coordinate.
+ * @param d New direction.
+ */
public Location(int x, int y, Direction d) {
locPosition = new Vector2i(x,y);
locDirection = d;
}
+ /**
+ * Get position.
+ * @return Integer vector of position.
+ */
public Vector2i getPosition() {
return locPosition;
}
+ /**
+ * Get the direction.
+ * @return The direction.
+ */
public Direction getDirection() {
return locDirection;
}
+ /**
+ * Set the position.
+ * @param newPosition the new position vector.
+ */
public void setPosition(Vector2i newPosition) {
locPosition = newPosition;
}
-
+
+ /**
+ * Set the direction.
+ * @param newDirection The new direction.
+ */
public void setDirection(Direction newDirection) {
locDirection = newDirection;
}
+ /**
+ * Add another position with the current position.
+ * @param position The position to add with the current position.
+ */
public void addPosition(Vector2i position) {
locPosition = Vector2i.add(locPosition, position);
}
+ /**
+ * Subtract another position from the current position.
+ * @param position The position to subtract from the current position.
+ */
public void subtractPosition(Vector2i position) {
locPosition = Vector2i.sub(locPosition, position);
}
+ /**
+ * Get the location in the direction from the current position.
+ * @param d The relative direction.
+ * @return The position in the relative direction.
+ */
Location getRelativeLocation(Direction d) {
Location thisLocation = this;
Location newLocation = new Location(0,0);
+ /* Copy the current location. */
try {
newLocation = thisLocation.clone();
} catch(CloneNotSupportedException ex) {
ex.printStackTrace();
}
+ /* Modify the new location based on the direction. */
switch(d) {
case NORTH:
newLocation.subtractPosition(new Vector2i(0,1));
@@ -70,6 +120,43 @@ public class Location implements Cloneable {
return newLocation;
}
+
+ /**
+ * Return a position in-between two locations.
+ * @param speed Size of the step.
+ * @param step Current step.
+ * @return Vector position in-between two locations.
+ */
+ public Vector2f interpolate(float speed, int step) {
+ Vector2f interpolateAddition = new Vector2f(0,0); /* Init new vector. */
+ /*
+ * 1.0f / speed calculated the step increments
+ * Multiplying it by step calculates the current increment.
+ */
+ float additionAmount = (1.0f/speed)*step; /* Current increment. */
+ /* Apply the increment to the vector based on the direction. */
+ switch(locDirection) {
+ case NORTH:
+ interpolateAddition = new Vector2f(0, -additionAmount);
+ break;
+ case SOUTH:
+ interpolateAddition = new Vector2f(0, additionAmount);
+ break;
+ case EAST:
+ interpolateAddition = new Vector2f(additionAmount, 0);
+ break;
+ case WEST:
+ interpolateAddition = new Vector2f(-additionAmount, 0);
+ break;
+ }
+ Vector2i currentPos = getPosition();
+ /* Add the increment to the base vector */
+ return Vector2f.add(new Vector2f(currentPos.x, currentPos.y), interpolateAddition);
+ }
+
+ /**
+ * Allows location to be copied.
+ */
protected Location clone() throws CloneNotSupportedException {
return (Location) super.clone();
}
diff --git a/src/tfg/Map.java b/src/tfg/Map.java
index 0e5a37b..b9d1a11 100644
--- a/src/tfg/Map.java
+++ b/src/tfg/Map.java
@@ -20,76 +20,128 @@ import java.util.Arrays;
* @author Ritchie Cunningham
*/
public class Map implements Drawable {
- private Vector2i mapDimensions = new Vector2i(0, 0);
+ /**
+ * The size (length, width) of the map.
+ */
+ private Vector2i dimensions = new Vector2i(0, 0);
private Tile[][] tileArray;
- private Texture mapTilesheetTexture = new Texture();
- private VertexArray mapVertexArray = new VertexArray();
+ /**
+ * The tilesheet texture.
+ */
+ private Texture tilesheetTexture = new Texture();
+ /**
+ * Vertex array for the tile map.
+ * This is a streamlined way to draw many sprites at once.
+ */
+ private VertexArray vertexArray = new VertexArray();
+ /**
+ * Create a new map of specified size and tile type.
+ * @param l Map length.
+ * @param w Map width.
+ * @param t Default tile to auto-pupulate map.
+ */
public Map(int l, int w, Tile t) {
- mapDimensions = new Vector2i(l,w);
- tileArray = new Tile[mapDimensions.x][mapDimensions.y];
+ dimensions = new Vector2i(l,w);
+ tileArray = new Tile[dimensions.x][dimensions.y];
try {
- mapTilesheetTexture.loadFromFile(Paths.get("res/terrain.png"));
+ tilesheetTexture.loadFromFile(Paths.get("res/terrain.png"));
} catch(IOException ex) {
ex.printStackTrace();
}
- mapVertexArray.setPrimitiveType(PrimitiveType.QUADS);
- initializeMap(t);
+ /* Set vertex array to use quads because the tiles are square. */
+ vertexArray.setPrimitiveType(PrimitiveType.QUADS);
+ initializeMap(t); /* Auto-populate the map with specified tile. */
}
+ /**
+ * Create a new map of speceified size that default to grass.
+ * @param l Map length.
+ * @param w Map width.
+ */
public Map(int l, int w) {
this(l,w,Tile.GRASS);
}
+ /**
+ * Fill the entire map to the specified tile.
+ * @param initialTile The tile to populate the map with.
+ */
public void initializeMap(Tile initialTile) {
+ /**
+ * Arrays.fill does not work with matrices,
+ * therefore we have to loop through the outer level to fill the inner.
+ */
for(Tile[] row : tileArray) {
+ /* Set each tile in the row to the specified tile. */
Arrays.fill(row,initialTile);
}
}
+ /**
+ * Draw the map onto the window.
+ */
public void draw(RenderTarget target, RenderStates states) {
- mapVertexArray.clear();
+ vertexArray.clear(); /* empty array from previous draw. */
final int tileSize = Tile.getSize();
- for(int i = 0; i < mapDimensions.x; i++) {
- for(int j = 0; j < mapDimensions.y; j++) {
- Tile tileType = tileArray[i][j];
+ /* Iterate through every tile in the map. */
+ for(int i = 0; i < dimensions.x; i++) {
+ for(int j = 0; j < dimensions.y; j++) {
+ Tile tileType = tileArray[i][j]; /* Grab current tile in the loop. */
+ /* Grab texture coord to render the tile. */
Vector2f textureCoords = Tile.getTextureCoords(tileType);
+ /*
+ * Add each corner of the tile to the vertex array
+ * counter-clockwise.
+ */
/* Top left. */
- mapVertexArray.add(new Vertex(
+ vertexArray.add(new Vertex(
new Vector2f(i*tileSize, j* tileSize), textureCoords));
/* Bottom left. */
- mapVertexArray.add(new Vertex(
+ vertexArray.add(new Vertex(
new Vector2f(i*tileSize,(j*tileSize)+tileSize),
Vector2f.add(textureCoords, new Vector2f(tileSize,tileSize))));
/* Bottom right. */
- mapVertexArray.add(new Vertex(
+ vertexArray.add(new Vertex(
new Vector2f((i*tileSize)+tileSize, (j*tileSize)+tileSize),
Vector2f.add(textureCoords, new Vector2f(tileSize,tileSize))));
/* Top right. */
- mapVertexArray.add(new Vertex(
+ vertexArray.add(new Vertex(
new Vector2f((i*tileSize)+tileSize, j*tileSize),
Vector2f.add(textureCoords, new Vector2f(tileSize,0))));
}
}
- RenderStates newStates = new RenderStates(mapTilesheetTexture);
- mapVertexArray.draw(target, newStates);
+ /* Apply texture to the vertex array. */
+ RenderStates newStates = new RenderStates(tilesheetTexture);
+ vertexArray.draw(target, newStates); /* Draw vertex array. */
}
+ /**
+ * Return the tile at specified location.
+ * @param l Location of the tile to get.
+ * @return Tile at the specified location.
+ */
public Tile getTile(Location l) {
- Vector2i position = l.getPosition();
- Tile t = tileArray[position.x][position.y];
+ Vector2i position = l.getPosition(); /* Get pos from loc. */
+ Tile t = tileArray[position.x][position.y]; /* Get tile at this pos. */
return t;
}
+ /**
+ * Determine whether or not the location fits within the map.
+ * @param l Location to test.
+ * @return If location is valid.
+ */
public boolean isValidLocation(Location l) {
- Vector2i coordinates = l.getPosition();
+ Vector2i coordinates = l.getPosition(); /* Pos from last location. */
+ /* Return true if the location is greater than 0. */
return ((coordinates.x >= 0) && (coordinates.y >=0) &&
- (coordinates.x < mapDimensions.x) &&
- (coordinates.y < mapDimensions.y) && Tile.getCanWalkOn(getTile(l)));
+ (coordinates.x < dimensions.x) &&
+ (coordinates.y < dimensions.y) && Tile.getCanWalkOn(getTile(l)));
}
}
diff --git a/src/tfg/Player.java b/src/tfg/Player.java
index 6865f91..6aff622 100644
--- a/src/tfg/Player.java
+++ b/src/tfg/Player.java
@@ -4,156 +4,137 @@ import org.jsfml.audio.Sound;
import org.jsfml.audio.SoundBuffer;
import org.jsfml.audio.SoundSource;
import org.jsfml.graphics.Drawable;
-import org.jsfml.graphics.IntRect;
import org.jsfml.graphics.RenderStates;
import org.jsfml.graphics.RenderTarget;
import org.jsfml.graphics.Sprite;
import org.jsfml.graphics.Texture;
-import org.jsfml.system.Vector2f;
-import org.jsfml.system.Vector2i;
import java.io.IOException;
import java.nio.file.Paths;
/**
- * Player Class
+ * Holds everything that pertains to the player (you?).
* @author Ritchie Cunningham
*/
public class Player implements Drawable {
+ /**
+ * The players location.
+ */
private Location playerLoc;
+ /**
+ * The texture for the sprite.
+ */
private Texture playerSpritesheetTexture = new Texture();
- private Sprite playerSprite = new Sprite();
+ /**
+ * The player sprite that supports animation.
+ */
+ private final AnimatedSprite playerSprite;
+ /**
+ * The map the player is currently on.
+ */
private Map currentMap;
- private final Vector2i playerSize = new Vector2i(32, 48);
+ /**
+ * The action the player is currently performing.
+ */
private PlayerAction currentAction = PlayerAction.NONE;
- private Location newPlayerLoc;
- private int frameCounter = 0;
- private final float animationSpeed = 15.f;
- private int animationFrame = 0;
- private SoundBuffer cannotMoveBuffer = new SoundBuffer();
- private Sound cannotMove = new Sound();
- private Vector2f tempPosition = new Vector2f(0,0); /* Horrible Hack. */
+ /**
+ * The buffer that allows the 'stuck' sound to be loaded.
+ */
+ private final SoundBuffer cannotMoveBuffer = new SoundBuffer();
+ /**
+ * The object that plays the 'stuck' sound.
+ */
+ private final Sound cannotMove = new Sound();
- public Player() {
- playerLoc = new Location(0, 0);
+ /**
+ * Create a new player with a location at (x,y).
+ * @param x Starting x pos.
+ * @param y Starting y pos.
+ */
+ public Player(int x, int y) {
+ playerLoc = new Location(x, y);
+ /* Load sprite texture and 'stuck' sound. */
try {
playerSpritesheetTexture.loadFromFile(Paths.get("res/player.png"));
cannotMoveBuffer.loadFromFile(Paths.get("res/stuck.wav"));
} catch(IOException ex) {
ex.printStackTrace();
}
- playerSprite = new Sprite(playerSpritesheetTexture);
+ Sprite sprite = new Sprite(); /* Create a new regular sprite. */
+ sprite.setTexture(playerSpritesheetTexture);
+ /* Create a new animated sprite from the regular sprite. */
+ playerSprite = new AnimatedSprite(sprite, playerLoc);
cannotMove.setBuffer(cannotMoveBuffer);
}
+ /**
+ * Create a new player at(0,0).
+ */
+ public Player() {
+ this(0,0);
+ }
+
+ /**
+ * Change the map the is on.
+ * @param m New map.
+ */
public void changeMap(Map m) {
currentMap = m;
}
+ /**
+ * Get the map the player is currently on.
+ * @return Map player is currently on.
+ */
public Map getMap() {
return currentMap;
}
+ /**
+ * Move the player in the specified direction.
+ * @param d Direction to move the player.
+ */
public void move(Direction d) {
if(currentAction == PlayerAction.NONE) {
+ /*
+ * Get the location relative to the current location.
+ * e.g. NORTH would return the location above the current location.
+ */
Location newLoc = playerLoc.getRelativeLocation(d);
playerLoc.setDirection(d);
if(currentMap.isValidLocation(newLoc)) {
currentAction = PlayerAction.MOVING;
- newPlayerLoc = newLoc;
+ playerLoc.setDirection(d);
+ playerSprite.startWalkingAnimation();
} else if(cannotMove.getStatus() == SoundSource.Status.STOPPED) {
cannotMove.play();
}
}
}
- public void resetLocation() {
- playerLoc = new Location(0, 0);
- }
-
- public Vector2f getPosition() {
- return playerSprite.getPosition();
- }
-
- public IntRect getTextureCoords() {
- IntRect textureCoordsRect = new IntRect(0,0,0,0);
-
- switch(playerLoc.getDirection()) {
- case NORTH:
- textureCoordsRect =
- new IntRect(0+(animationFrame*32), 144, playerSize.x, playerSize.y);
- break;
- case SOUTH:
- textureCoordsRect =
- new IntRect(0+(animationFrame*32),0,playerSize.x,playerSize.y);
- break;
- case EAST:
- textureCoordsRect =
- new IntRect(0+(animationFrame*32),96,playerSize.x,playerSize.y);
- break;
- case WEST:
- textureCoordsRect =
- new IntRect(0+(animationFrame*32),48,playerSize.x,playerSize.y);
- break;
- }
- return textureCoordsRect;
- }
+ /**
+ * Update the animation progress.
+ */
public void update() {
- /* Dirty dirty hack and should be fixed asap. */
- tempPosition = new Vector2f(0,0);
- Vector2i currentPlayerPosition = playerLoc.getPosition();
if(currentAction == PlayerAction.MOVING) {
- if(frameCounter >= animationSpeed) {
- frameCounter = 0;
+ if(playerSprite.finishedAnimating()) {
currentAction = PlayerAction.NONE;
- playerLoc = newPlayerLoc;
- newPlayerLoc = null;
- Vector2i newPlayerPosition = playerLoc.getPosition();
- tempPosition = new Vector2f(newPlayerPosition.x, newPlayerPosition.y);
- animationFrame = 0;
+ /* Actually move the location. */
+ playerLoc = playerLoc.getRelativeLocation(playerLoc.getDirection());
+ /* Update the sprite with new location. */
+ playerSprite.updatePosition(playerLoc);
} else {
- float additionX = 0.0f;
- float additionY = 0.0f;
- switch(playerLoc.getDirection()) {
- case NORTH:
- additionY = -(1.0f / animationSpeed) * (frameCounter);
- break;
- case SOUTH:
- additionY = (1.0f / animationSpeed) * (frameCounter);
- case EAST:
- additionX = (1.0f / animationSpeed) * (frameCounter);
- break;
- case WEST:
- additionX = -(1.0f / animationSpeed) * (frameCounter);
- break;
- }
- float change = Math.abs(additionX + additionY);
- if(change >= 0.f && change < .25f) {
- animationFrame = 0;
- } else if(change >= .25f && change < .5f) {
- animationFrame = 1;
- } else if(change >= .5f && change < .75f) {
- animationFrame = 2;
- } else if(change >= .75f && change <= 1.0f) {
- animationFrame = 3;
- }
- tempPosition = new Vector2f(currentPlayerPosition.x + additionX,
- currentPlayerPosition.y + additionY);
+ playerSprite.animate(); /* Proceed with the animation. */
}
- frameCounter++;
- } else {
- tempPosition = new Vector2f(currentPlayerPosition.x, currentPlayerPosition.y);
}
}
+ /**
+ * Draw the player on screen.
+ */
public void draw(RenderTarget target, RenderStates states) {
- playerSprite.setPosition(
- new Vector2f(tempPosition.x*playerSize.x,
- (tempPosition.y*playerSize.x)-(playerSize.y-playerSize.x)));
-
- playerSprite.setTextureRect(getTextureCoords());
- RenderStates newStates = new RenderStates(playerSpritesheetTexture);
- playerSprite.draw(target, newStates);
+ /* Get the animated sprite and draw it. */
+ playerSprite.getSprite().draw(target, states);
}
}
diff --git a/src/tfg/PlayerAction.java b/src/tfg/PlayerAction.java
index 3628952..12b72da 100644
--- a/src/tfg/PlayerAction.java
+++ b/src/tfg/PlayerAction.java
@@ -1,7 +1,7 @@
package tfg;
/**
- * Actions the player can carry out.
+ * Potential actions the player can carry out.
* @author Ritchie Cunningham
*/
public enum PlayerAction {NONE, MOVING }
diff --git a/src/tfg/TextUIElement.java b/src/tfg/TextUIElement.java
new file mode 100644
index 0000000..61099b8
--- /dev/null
+++ b/src/tfg/TextUIElement.java
@@ -0,0 +1,88 @@
+package tfg;
+
+import org.jsfml.graphics.Color;
+import org.jsfml.graphics.Drawable;
+import org.jsfml.graphics.Font;
+import org.jsfml.graphics.RenderStates;
+import org.jsfml.graphics.RenderTarget;
+import org.jsfml.graphics.Text;
+import org.jsfml.graphics.TextStyle;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+
+/**
+ * UI Elements containing a dynamic text label.
+ * @author Ritchie Cunningham
+ */
+public class TextUIElement implements Drawable {
+ /**
+ * The font for the text.
+ */
+ private Font font = new Font();
+ /**
+ * The text object containing style and content.
+ */
+ private Text text = new Text();
+
+ /**
+ * Create a new Text UI Element with the specified options and a default style.
+ * @param p Position on the screen to display the label.
+ * @param c The Color to set the text.
+ * @param size The size to render the text.
+ */
+ public TextUIElement(InterfacePosition p, Color c, int size) {
+ this(p, c, size, TextStyle.REGULAR);
+ }
+
+ /**
+ * Create a new Text UI element with the specified options and style.
+ * @param p Position on the screen to display the label.
+ * @param c Color to set the text.
+ * @param size Size to render the text.
+ * @param style Style to apply to the text.
+ */
+ public TextUIElement(InterfacePosition p, Color c, int size, int style) {
+ /* Attempt to load font. */
+ try {
+ font.loadFromFile(Paths.get("res/kpixel.ttf"));
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+
+ text.setFont(font); /* Apply the selected font. */
+ text.setCharacterSize(size); /* Update the character size. */
+ text.setColor(c); /* Change the color. */
+ text.setStyle(style); /* Set the desired style. */
+ setPosition(p); /* Position the label. */
+ }
+
+ /**
+ * Update the value of the text.
+ * @param s New value for the text.
+ */
+ public void updateString(String s) {
+ text.setString(s);
+ }
+
+ /**
+ * Position the element on the screen.
+ * @param p Position on screen.
+ */
+ public void setPosition(InterfacePosition p) {
+ switch(p) {
+ case TOP_LEFT:
+ text.setPosition(0,0); /* Default (0,0) if pos is top left. */
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Draw the UI elements to screen.
+ */
+ public void draw(RenderTarget target, RenderStates states) {
+ text.draw(target, states);
+ }
+}
diff --git a/src/tfg/Tile.java b/src/tfg/Tile.java
index b73455d..b77964a 100644
--- a/src/tfg/Tile.java
+++ b/src/tfg/Tile.java
@@ -3,14 +3,26 @@ package tfg;
import org.jsfml.system.Vector2f;
/**
- * Map tiles for the Map class.
+ * Enumeration of tiles comprising maps.
* @author Ritchie Cunningham
*/
public enum Tile {
- WATER, SAND, GRASS;
+ WATER, /* Wet and wild tile. */
+ SAND, /* Urgh, it gets between my toes. */
+ GRASS; /* Soft, green, vivid, inviting.. */
+ /**
+ * Length or width of a map tile.
+ * Each tile *MUST* be a square, therefore the area of
+ * the tile is this number squared.
+ */
private static int tileSize = 32;
+ /**
+ * Get the coords of a specific tile from the tile-set image.
+ * @param t The tole to get the coordinates of.
+ * @return Coords of the tile image in the tile-set.
+ */
public static Vector2f getTextureCoords(Tile t) {
Vector2f textureCoordinates = new Vector2f(0, 0);
switch(t) {
@@ -21,29 +33,39 @@ public enum Tile {
textureCoordinates = new Vector2f(576, 352);
break;
case GRASS:
- textureCoordinates = new Vector2f(448, 252);
+ textureCoordinates = new Vector2f(448, 352);
break;
}
return textureCoordinates;
}
+ /**
+ * Get the side length of a square tile.
+ * The tile area is tileSize^2.
+ * @return Length of a square map tile.
+ */
public static int getSize() {
return tileSize;
}
+ /**
+ * Determine if this tile is safe for the player to walk on.
+ * @param t Tile to test.
+ * @return If the player can walk on it.
+ */
public static boolean getCanWalkOn(Tile t) {
- boolean able = false;
+ boolean canWalk = false;
switch(t) {
case WATER:
- able = false;
+ canWalk = false;
break;
case SAND:
- able = true;
+ canWalk = true;
break;
case GRASS:
- able = true;
+ canWalk = true;
break;
}
- return able;
+ return canWalk;
}
}
diff --git a/src/tfg/UITextElement.java b/src/tfg/UITextElement.java
deleted file mode 100644
index 509ee00..0000000
--- a/src/tfg/UITextElement.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package tfg;
-
-import org.jsfml.graphics.Color;
-import org.jsfml.graphics.Drawable;
-import org.jsfml.graphics.Font;
-import org.jsfml.graphics.RenderStates;
-import org.jsfml.graphics.RenderTarget;
-import org.jsfml.graphics.Text;
-import org.jsfml.graphics.TextStyle;
-
-import java.io.IOException;
-import java.nio.file.Paths;
-
-/**
- * GUI Elements
- * @author Ritchie Cunningham
- */
-public class UITextElement implements Drawable {
- private Font font = new Font();
- private Text text = new Text();
- private String value;
-
- public UITextElement(InterfacePosition p, Color c, int size) {
- this(p, c, size, TextStyle.REGULAR);
- }
-
- public UITextElement(InterfacePosition p, Color c, int size, int style) {
- try {
- font.loadFromFile(Paths.get("res/kpixel.ttf"));
- } catch (IOException ex) {
- ex.printStackTrace();
- }
-
- text = new Text("", font, size);
- text.setColor(c);
- text.setStyle(style);
- setPosition(p);
- }
-
- public void updateString(String s) {
- text.setString(s);
- }
-
- public void setPosition(InterfacePosition p) {
- switch(p) {
- case TOP_LEFT:
- text.setPosition(0,0);
- break;
- }
- }
-
- public void draw(RenderTarget target, RenderStates states) {
- text.draw(target, states);
- }
-}