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); - } -}