[Change] Refactored Animation
[Add] Documentation Comments.
This commit is contained in:
parent
9f7127d9bd
commit
e150b70113
@ -7,5 +7,6 @@
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="C:/Users/user/Downloads/jsfml/jsfml.jar"/>
|
||||
<classpathentry kind="src" path="Bruh_Example"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
BIN
bin/tfg/AnimatedSprite.class
Normal file
BIN
bin/tfg/AnimatedSprite.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
bin/tfg/TextUIElement.class
Normal file
BIN
bin/tfg/TextUIElement.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
164
src/tfg/AnimatedSprite.java
Normal file
164
src/tfg/AnimatedSprite.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
package tfg;
|
||||
|
||||
/**
|
||||
* The cardinal directions.
|
||||
* @author Ritchie Cunningham
|
||||
*/
|
||||
public enum Direction { NORTH, SOUTH, EAST, WEST };
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
|
88
src/tfg/TextUIElement.java
Normal file
88
src/tfg/TextUIElement.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user