Drawer.java
package com.vikingz.campustycoon.Util;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.vikingz.campustycoon.UI.Components.Component;
import com.vikingz.campustycoon.UI.Components.Sprite;
import com.vikingz.campustycoon.Util.Types.Coordinate;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
public class Drawer {
private static List<DrawInfo> drawQueue = new LinkedList<>();
public static SpriteBatch spriteBatch = new SpriteBatch();
private static BitmapFont font = new BitmapFont();
private static Map<String, Texture> textures = new HashMap<String, Texture>(); // Note: this exists because I learned that generating hundreds of new textures every second is NOT a good idea
private static Map<Texture, Map<Integer, TextureRegion>> textureRegions = new HashMap<Texture, Map<Integer, TextureRegion>>();
// Added stage
public static Stage stage = new Stage();
/**
* Sets the sprite batch
* @param batch
*/
public static void setSpriteBatch(SpriteBatch batch) {
spriteBatch = batch;
}
/**
* DrawInfo
* Used as a data store
*/
public static class DrawInfo { // Cursed static class with non-static members
public int layer; // Used to determine draw order
public Component component;
public DrawInfo(int Layer, Component Component) {
layer = Layer;
component = Component;
}
}
/**
* Gets the last draw item
* @return
*/
public static DrawInfo getLastDrawItem(){
return drawQueue.get(drawQueue.size()-1);
}
public static void addDrawItem(DrawInfo drawInfo){
drawQueue.add(drawInfo);
}
/**
* Pops all components from a given layer
* @param <T>
* @param layer
* @param type
* @return
*/
public static <T> List<T> popLayer(int layer, T type) {
List<T> layerComponents = new ArrayList<T>();
int left = binarySearch(layer - 1);
int right = binarySearch(layer);
int count = right - left;
for (int i = 0; i < count; i++) {
if (drawQueue.get(left).component.getClass() == type.getClass()) {
@SuppressWarnings("unchecked")
T component = (T)drawQueue.get(left).component; // Should be a safe cast
layerComponents.add(component);
drawQueue.remove(left);
}
}
return layerComponents;
}
public static void clear() {
drawQueue.clear();
}
/**
* Draws all components in the drawQueue
*/
public static void drawAll() {
spriteBatch.begin();
//Drawing the components in the drawQueue
for (int i = 0; i < drawQueue.size(); i++) {
// drawQueue is pre-sorted, so this draws primarily in order of layer (ascending)
// Followed by the time the component was added to the queue (within each layer)
Component component = drawQueue.get(i).component;
if (component.sprite.usesSpriteSheet) {
drawRegion(component);
}
else if (component.isText) {
drawText(component);
}
else {
draw(component);
}
}
stage.draw();
spriteBatch.end();
}
/**
* Draws text components
* @param component
*/
private static void drawText(Component component) {
//font.setFixedWidthGlyphs(component.text);
font.getRegion().getTexture().setFilter(
TextureFilter.Linear, TextureFilter.Linear);
font.getData().setScale(component.width, component.height);
font.draw(spriteBatch, component.text,
component.x, component.y);
}
/**
* Draws sprite components
* @param component
*/
private static void draw(Component component) {
Sprite sprite = component.sprite;
sprite.stepAnimation();
// Important to note that the animation is stepped and then immediately retrieved for drawing
// This is to prevent the 1 frame of visual delay that would be caused by stepping after returning the image
String imagePath = sprite.getImagePath();
Texture image = getTexture(imagePath);
spriteBatch.draw(
image, // Image texture to draw
component.x, component.y, // Coordinates to draw at
component.width, component.height); // Size of the image
}
/**
* Draws sprite components that use a spritesheet
*/
private static void drawRegion(Component component) {
Sprite sprite = component.sprite;
sprite.stepAnimation();
String sheetPath = sprite.getImagePath();
Texture sheet = getTexture(sheetPath);
TextureRegion image = getTextureRegion(sheet, sprite);
spriteBatch.draw(
image, // Image texture (region) to draw
component.x, component.y, // Coordinates to draw at
component.width, component.height); // Size of the image
}
/**
* Gets the texture region of a sprite from a spritesheet
* @param sheet
* @param sprite
* @return
*/
private static TextureRegion getTextureRegion(Texture sheet, Sprite sprite) {
int spriteID = sprite.getID();
// If a mapping of the spriteSheet does not exist -> creates new mapping
if (!textureRegions.containsKey(sheet)) {
Map<Integer, TextureRegion> regions = new HashMap<Integer, TextureRegion>();
textureRegions.put(sheet, regions);
}
Map<Integer, TextureRegion> sheetRegions = textureRegions.get(sheet);
// If a mapping of the spriteID to a TextureRegion does not exist -> adds that mapping
if (!sheetRegions.containsKey(spriteID)) {
Coordinate spriteCoords = sprite.spriteSheet.getRegionCoords(spriteID);
sheetRegions.put(spriteID, new TextureRegion(
sheet, // Spritesheet to get sprite from
spriteCoords.x, spriteCoords.y, // Coordinates of the sprite within the spritesheet
sprite.spriteSheet.spriteWidth, sprite.spriteSheet.spriteHeight)); // Sprite dimensions
}
return sheetRegions.get(spriteID);
}
private static Texture getTexture(String imagePath) {
if (!textures.containsKey(imagePath)) {
textures.put(imagePath, new Texture(imagePath));
}
return textures.get(imagePath);
}
// Updates every component in the drawQueue (usually needed due to screen resolution changes)
public static void updateAll() {
for (int i = 0; i < drawQueue.size(); i++) {
drawQueue.get(i).component.update();
}
}
// Adds the new component to the drawQueue at the end of the given layer
public static void add(int layer, Component component) {
drawQueue.add(binarySearch(layer), new DrawInfo(layer, component));
}
public static void remove(int layer, Component component) {
remove(component, binarySearch(layer - 1));
}
public static void remove(Component component) {
remove(component, 0);
}
// Removes the given component from the drawQueue
// Does a linear search of the drawQueue starting from the index given by startIndex
private static void remove(Component component, int startIndex) {
for (int index = startIndex; index < drawQueue.size(); index++) {
if (drawQueue.get(index).component.equals(component)) {
drawQueue.remove(index);
return;
}
}
}
// Searches the drawQueue to find the index of the end of a given layer
// e.g. in {-1, 0, 0, 1, 1, 1, 2, 2}, searching for '1' would give the index of the first 2
private static int binarySearch(int target) {
// haha could have used this
Collections.binarySearch(null, null);
int left = 0;
int right = drawQueue.size() - 1;
int midpoint = 0;
if (drawQueue.size() <= 1) {
return drawQueue.size();
}
if (drawQueue.get(left).layer > target) {
return 0;
}
if (drawQueue.get(right).layer < target) {
return right + 1;
}
while (right - left > 1) {
midpoint = (left + right) / 2;
int value = drawQueue.get(midpoint).layer;
if (value <= target) {
left = midpoint;
continue;
}
if (value > target) {
right = midpoint;
continue;
}
}
midpoint = (left + right) / 2;
return midpoint + 1;
}
}