477 lines
15 KiB
Java
477 lines
15 KiB
Java
/**
|
|
* GladiatorBrawler is a 2D swordfighting game.
|
|
* Copyright (C) 2015 Jeasonfire/Allexit
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.saltosion.gladiator.systems;
|
|
|
|
import com.badlogic.ashley.core.ComponentMapper;
|
|
import com.badlogic.ashley.core.ComponentType;
|
|
import com.badlogic.ashley.core.Engine;
|
|
import com.badlogic.ashley.core.Entity;
|
|
import com.badlogic.ashley.core.EntitySystem;
|
|
import com.badlogic.ashley.core.Family;
|
|
import com.badlogic.ashley.utils.ImmutableArray;
|
|
import com.badlogic.gdx.Gdx;
|
|
import com.badlogic.gdx.graphics.Camera;
|
|
import com.badlogic.gdx.graphics.Color;
|
|
import com.badlogic.gdx.graphics.GL20;
|
|
import com.badlogic.gdx.graphics.OrthographicCamera;
|
|
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
|
import com.badlogic.gdx.graphics.g2d.Sprite;
|
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
|
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
|
|
import com.badlogic.gdx.math.Vector2;
|
|
import com.saltosion.gladiator.components.CAI;
|
|
import com.saltosion.gladiator.components.CCombat;
|
|
import com.saltosion.gladiator.components.CParticle;
|
|
import com.saltosion.gladiator.components.CPhysics;
|
|
import com.saltosion.gladiator.components.CRenderedObject;
|
|
import com.saltosion.gladiator.gui.nodes.GUINode;
|
|
import com.saltosion.gladiator.gui.properties.ImageProperty;
|
|
import com.saltosion.gladiator.gui.nodes.TextNode;
|
|
import com.saltosion.gladiator.gui.properties.TextProperty;
|
|
import com.saltosion.gladiator.util.AppUtil;
|
|
import com.saltosion.gladiator.util.AudioLoader;
|
|
import com.saltosion.gladiator.util.Global;
|
|
import com.saltosion.gladiator.util.Name;
|
|
import com.saltosion.gladiator.util.SpriteLoader;
|
|
import com.saltosion.gladiator.util.SpriteSequence;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class RenderingSystem extends EntitySystem {
|
|
|
|
private final ComponentMapper<CRenderedObject> rom = ComponentMapper.getFor(CRenderedObject.class);
|
|
private final ComponentMapper<CPhysics> pm = ComponentMapper.getFor(CPhysics.class);
|
|
private final ComponentMapper<CCombat> cm = ComponentMapper.getFor(CCombat.class);
|
|
private final ComponentMapper<CAI> aim = ComponentMapper.getFor(CAI.class);
|
|
private final ComponentMapper<CParticle> pam = ComponentMapper.getFor(CParticle.class);
|
|
private ImmutableArray<Entity> entities;
|
|
|
|
private SpriteBatch batch;
|
|
private BitmapFont font;
|
|
private ShapeRenderer debugRenderer, particleRenderer;
|
|
private OrthographicCamera camera, fontCamera;
|
|
|
|
public float aspectratio;
|
|
public int screenHeight = 0;
|
|
public int screenWidth = 0;
|
|
|
|
private boolean debug = false;
|
|
private final Color debugColor = new Color(0, 1, 0, 1), debugAIColor = new Color(1, 0, 0, 1);
|
|
|
|
private float deltaDelay = 0;
|
|
private double deltaAvgSum;
|
|
private long deltaAvgTimes;
|
|
private String deltaString = "0";
|
|
|
|
private List<TextObject> drawableText;
|
|
|
|
private Sprite[] healthbar;
|
|
private boolean healthbarLoaded = false;
|
|
private float xMin = -15, xMax = 15;
|
|
|
|
@Override
|
|
public void addedToEngine(Engine engine) {
|
|
updateEntities(engine);
|
|
|
|
batch = new SpriteBatch();
|
|
|
|
font = new BitmapFont(Gdx.files.internal("fonts/font.fnt"));
|
|
font.setUseIntegerPositions(false);
|
|
|
|
debugRenderer = new ShapeRenderer();
|
|
particleRenderer = new ShapeRenderer();
|
|
|
|
camera = new OrthographicCamera();
|
|
camera.setToOrtho(false, 1, 1);
|
|
|
|
fontCamera = new OrthographicCamera();
|
|
fontCamera.setToOrtho(false, Global.FONT_SCALE, Global.FONT_SCALE);
|
|
|
|
drawableText = new ArrayList<TextObject>();
|
|
}
|
|
|
|
public void setViewport(int width, int height) {
|
|
camera.setToOrtho(false, width, height);
|
|
fontCamera.setToOrtho(false, width * Global.FONT_SCALE, height * Global.FONT_SCALE);
|
|
}
|
|
|
|
@Override
|
|
public void update(float deltaTime) {
|
|
camera.update();
|
|
fontCamera.update();
|
|
|
|
Gdx.gl.glClearColor(0, 0, 0, 0);
|
|
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
|
|
|
|
updateEntityAnimations(deltaTime);
|
|
renderEntities(deltaTime);
|
|
renderParticles();
|
|
renderDebug();
|
|
renderGUI(new Vector2(0, 0));
|
|
|
|
if (debug) {
|
|
drawString("FPS: " + Gdx.graphics.getFramesPerSecond(), new Vector2(camera.position.x - 12, camera.position.y + 8));
|
|
drawString("Delta (ms): " + getDeltaWithDelay(deltaTime, 0.1f), new Vector2(camera.position.x - 12, camera.position.y + 7));
|
|
}
|
|
renderFont(fontCamera);
|
|
}
|
|
|
|
/**
|
|
* A debugging function for easier performance logging.
|
|
*
|
|
* @param deltaTime The delta of the current frame
|
|
* @param delay The delay between deltaString updates
|
|
* @return A string that has the delta formatted
|
|
*/
|
|
private String getDeltaWithDelay(float deltaTime, float delay) {
|
|
this.deltaDelay += deltaTime;
|
|
this.deltaAvgSum += Gdx.graphics.getDeltaTime() * 1000;
|
|
this.deltaAvgTimes++;
|
|
if (this.deltaDelay >= delay) {
|
|
this.deltaDelay = 0;
|
|
this.deltaString = String.format("%.2f", this.deltaAvgSum / this.deltaAvgTimes);
|
|
this.deltaAvgSum = 0;
|
|
this.deltaAvgTimes = 0;
|
|
}
|
|
return deltaString;
|
|
}
|
|
|
|
private void updateEntityAnimations(float deltaTime) {
|
|
for (int i = 0; i < entities.size(); i++) {
|
|
updateEntityAnimation(entities.get(i), deltaTime);
|
|
}
|
|
}
|
|
|
|
private void updateEntityAnimation(Entity entity, float deltaTime) {
|
|
CRenderedObject ro = rom.get(entity);
|
|
CPhysics po = pm.get(entity);
|
|
CCombat co = cm.get(entity);
|
|
if (ro == null || po == null || co == null) {
|
|
return;
|
|
}
|
|
|
|
boolean moving = false, combat = false;
|
|
String dirMove = po.movedLeftLast ? "Left" : "Right";
|
|
String dirSwing = "";
|
|
|
|
if (po.movingLeft || po.movingRight) {
|
|
moving = true;
|
|
}
|
|
if (co.swingCdCounter > 0) {
|
|
combat = true;
|
|
switch (co.getSwingDirection()) {
|
|
default:
|
|
case RIGHT:
|
|
dirSwing += "Right";
|
|
break;
|
|
case LEFT:
|
|
dirSwing += "Left";
|
|
break;
|
|
case UP:
|
|
if (dirMove.equals("Left")) {
|
|
dirSwing = "Left-";
|
|
} else {
|
|
dirSwing = "Right-";
|
|
}
|
|
dirSwing += "Up";
|
|
break;
|
|
case DOWN:
|
|
if (dirMove.equals("Left")) {
|
|
dirSwing = "Left-";
|
|
} else {
|
|
dirSwing = "Right-";
|
|
}
|
|
dirSwing += "Down";
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Play animations & play sounds
|
|
if (po.stepCD > 0) {
|
|
po.stepCD -= deltaTime;
|
|
}
|
|
|
|
if (moving && combat) {
|
|
ro.playAnimation("torso", "Torso-Combat-" + dirSwing);
|
|
ro.playAnimation("legs", "Legs-Run-" + dirMove);
|
|
tryToMakeStepSound(po);
|
|
} else if (combat) {
|
|
ro.playAnimation("torso", "Torso-Combat-" + dirSwing);
|
|
ro.playAnimation("legs", "Legs-Idle-" + dirMove);
|
|
} else if (moving) {
|
|
ro.playAnimation("torso", "Torso-Run-" + dirMove);
|
|
ro.playAnimation("legs", "Legs-Run-" + dirMove);
|
|
tryToMakeStepSound(po);
|
|
} else {
|
|
ro.playAnimation("torso", "Torso-Idle-" + dirMove);
|
|
ro.playAnimation("legs", "Legs-Idle-" + dirMove);
|
|
}
|
|
if (!po.isGrounded()) {
|
|
ro.playAnimation("legs", "Legs-Jump-" + dirMove);
|
|
}
|
|
}
|
|
|
|
private void tryToMakeStepSound(CPhysics po) {
|
|
if (po.stepCD > 0 || !po.isGrounded()) {
|
|
return;
|
|
}
|
|
po.stepCD = 0.3f;
|
|
AppUtil.jukebox.playSound(AudioLoader.getSound(Name.SOUND_STEP), AppUtil.sfxVolume / 3 * 2);
|
|
}
|
|
|
|
private void renderEntities(float deltaTime) {
|
|
if (AppUtil.player == null) {
|
|
return;
|
|
}
|
|
CPhysics playerPhys = pm.get(AppUtil.player);
|
|
batch.setProjectionMatrix(camera.combined);
|
|
batch.begin();
|
|
for (int i = 0; i < entities.size(); i++) {
|
|
CPhysics physics = pm.get(entities.get(i));
|
|
CRenderedObject renderedObject = rom.get(entities.get(i));
|
|
if (renderedObject == null) {
|
|
continue;
|
|
}
|
|
// Draw entity
|
|
for (String channel : renderedObject.getChannels()) {
|
|
SpriteSequence currSequence = renderedObject.getSequence(renderedObject.getCurrentSequence(channel));
|
|
int currFrame = (int) Math.floor(renderedObject.getCurrentFrame(channel));
|
|
Sprite currSprite = currSequence.getSprite(currFrame);
|
|
|
|
int spriteHeight = currSprite.getRegionHeight();
|
|
int spriteWidth = currSprite.getRegionWidth();
|
|
|
|
currSprite.setPosition(((physics.getPosition().x - spriteWidth / 2) + getCameraOffset(playerPhys, physics).x),
|
|
(physics.getPosition().y - spriteHeight / 2) + getCameraOffset(playerPhys, physics).y);
|
|
currSprite.draw(batch);
|
|
|
|
float nextFrame = renderedObject.getCurrentFrame(channel) + deltaTime * currSequence.getPlayspeed();
|
|
renderedObject.setCurrentFrame(channel, nextFrame % currSequence.frameCount());
|
|
}
|
|
|
|
// Draw healthbars
|
|
CCombat combat = cm.get(entities.get(i));
|
|
if (combat != null) {
|
|
if (!healthbarLoaded) {
|
|
loadHealthbarSprites();
|
|
}
|
|
float spriteWidth = healthbar[0].getWidth();
|
|
float spriteHeight = healthbar[0].getHeight();
|
|
float hp = (float) combat.getHealth() / (float) combat.getMaxHealth();
|
|
|
|
healthbar[0].setPosition(((physics.getPosition().x - spriteWidth / 2) + getCameraOffset(playerPhys, physics).x),
|
|
(physics.getPosition().y - spriteHeight / 2 + 2.5f) + getCameraOffset(playerPhys, physics).y);
|
|
healthbar[0].draw(batch);
|
|
healthbar[1].setPosition(((physics.getPosition().x - spriteWidth / 2) + getCameraOffset(playerPhys, physics).x),
|
|
(physics.getPosition().y - spriteHeight / 2 + 2.5f) + getCameraOffset(playerPhys, physics).y);
|
|
healthbar[1].setSize(spriteWidth * hp, spriteHeight);
|
|
healthbar[1].draw(batch);
|
|
healthbar[2].setPosition(((physics.getPosition().x - spriteWidth / 2) + getCameraOffset(playerPhys, physics).x),
|
|
(physics.getPosition().y - spriteHeight / 2 + 2.5f) + getCameraOffset(playerPhys, physics).y);
|
|
healthbar[2].draw(batch);
|
|
}
|
|
}
|
|
batch.end();
|
|
}
|
|
|
|
private void renderGUI(Vector2 rootPosition) {
|
|
batch.setProjectionMatrix(camera.combined);
|
|
batch.begin();
|
|
renderGUINode(AppUtil.guiManager.getRootNode(), rootPosition);
|
|
batch.end();
|
|
}
|
|
|
|
private void renderGUINode(GUINode node, Vector2 position) {
|
|
if (!node.isVisible()) {
|
|
return;
|
|
}
|
|
position.add(node.getPosition());
|
|
if (node instanceof ImageProperty) {
|
|
Sprite s = ((ImageProperty) node).getImage();
|
|
s.setPosition(position.x * AppUtil.VPHEIGHT_CONST * aspectratio - s.getWidth() / 2 + camera.position.x,
|
|
position.y * AppUtil.VPHEIGHT_CONST - s.getHeight() / 2 + camera.position.y);
|
|
s.draw(batch);
|
|
}
|
|
if (node instanceof TextNode) {
|
|
drawString(((TextProperty) node).getText(), new Vector2(position.x * AppUtil.VPHEIGHT_CONST * aspectratio + camera.position.x,
|
|
position.y * AppUtil.VPHEIGHT_CONST + camera.position.y));
|
|
}
|
|
for (GUINode child : node.getChildren()) {
|
|
renderGUINode(child, new Vector2(position));
|
|
}
|
|
}
|
|
|
|
private void renderParticles() {
|
|
if (AppUtil.player == null) {
|
|
return;
|
|
}
|
|
CPhysics playerPhys = pm.get(AppUtil.player);
|
|
particleRenderer.setProjectionMatrix(camera.combined);
|
|
particleRenderer.begin(ShapeType.Filled);
|
|
for (int i = 0; i < entities.size(); i++) {
|
|
CParticle particle = pam.get(entities.get(i));
|
|
if (particle == null) {
|
|
continue;
|
|
}
|
|
|
|
particleRenderer.setColor(particle.getColor());
|
|
particleRenderer.rect(particle.getPosition().x - particle.getSize().x / 2 + getCameraOffset(playerPhys).x,
|
|
particle.getPosition().y - particle.getSize().y / 2 + getCameraOffset(playerPhys).y,
|
|
particle.getSize().x, particle.getSize().y);
|
|
}
|
|
particleRenderer.end();
|
|
}
|
|
|
|
private void renderDebug() {
|
|
if (debug) {
|
|
if (AppUtil.player == null) {
|
|
return;
|
|
}
|
|
CPhysics playerPhys = pm.get(AppUtil.player);
|
|
debugRenderer.setProjectionMatrix(camera.combined);
|
|
debugRenderer.begin(ShapeType.Line);
|
|
for (int i = 0; i < entities.size(); i++) {
|
|
CPhysics physics = pm.get(entities.get(i));
|
|
if (physics == null) {
|
|
continue;
|
|
}
|
|
|
|
float x0 = physics.getPosition().x - physics.getSize().x / 2 + getCameraOffset(playerPhys, physics).x;
|
|
float x1 = physics.getPosition().x + physics.getSize().x / 2 + getCameraOffset(playerPhys, physics).x;
|
|
float y0 = physics.getPosition().y - physics.getSize().y / 2 + getCameraOffset(playerPhys, physics).y;
|
|
float y1 = physics.getPosition().y + physics.getSize().y / 2 + getCameraOffset(playerPhys, physics).y;
|
|
|
|
debugRenderer.setColor(debugColor);
|
|
debugRenderer.line(x0, y0, x1, y0);
|
|
debugRenderer.line(x1, y0, x1, y1);
|
|
debugRenderer.line(x1, y1, x0, y1);
|
|
debugRenderer.line(x0, y1, x0, y0);
|
|
|
|
CAI ai = aim.get(entities.get(i));
|
|
if (ai == null) {
|
|
continue;
|
|
}
|
|
float x = physics.getPosition().x + getCameraOffset(playerPhys, physics).x;
|
|
float y = physics.getPosition().y + getCameraOffset(playerPhys, physics).y;
|
|
|
|
debugRenderer.setColor(debugAIColor);
|
|
debugRenderer.circle(x, y, ai.getReactDistance());
|
|
}
|
|
debugRenderer.end();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is the main method that actually _renders_ the text. Use
|
|
* "drawString(str, pos)" method to add a string to a list that will be
|
|
* rendered here.
|
|
*
|
|
* @param camera
|
|
*/
|
|
private void renderFont(Camera fontCamera) {
|
|
batch.setProjectionMatrix(fontCamera.combined);
|
|
batch.begin();
|
|
for (TextObject obj : drawableText) {
|
|
font.draw(batch, obj.text, obj.position.x * Global.FONT_SCALE, obj.position.y * Global.FONT_SCALE);
|
|
}
|
|
drawableText.clear();
|
|
batch.end();
|
|
}
|
|
|
|
public void drawString(String text, Vector2 position) {
|
|
drawableText.add(new TextObject(text, position));
|
|
}
|
|
|
|
public void updateEntities(Engine engine) {
|
|
entities = engine.getEntitiesFor(Family.getFor(ComponentType.getBitsFor(),
|
|
ComponentType.getBitsFor(CPhysics.class, CParticle.class), ComponentType.getBitsFor()));
|
|
}
|
|
|
|
public void loadHealthbarSprites() {
|
|
healthbarLoaded = true;
|
|
healthbar = new Sprite[3];
|
|
healthbar[0] = SpriteLoader.loadSprite(Name.HEALTHBARIMG, 0, 0, 32, 8);
|
|
healthbar[1] = SpriteLoader.loadSprite(Name.HEALTHBARIMG, 0, 1, 32, 8);
|
|
healthbar[2] = SpriteLoader.loadSprite(Name.HEALTHBARIMG, 0, 2, 32, 8);
|
|
}
|
|
|
|
public boolean getDebug() {
|
|
return this.debug;
|
|
}
|
|
|
|
public void setDebug(boolean debug) {
|
|
this.debug = debug;
|
|
}
|
|
|
|
public Vector2 getCameraLocation() {
|
|
return new Vector2(this.camera.position.x, this.camera.position.y);
|
|
}
|
|
|
|
private Vector2 getCameraOffset(CPhysics playerPhys) {
|
|
Vector2 offset = new Vector2(Math.max(xMin + camera.viewportWidth / 2, Math.min(xMax - camera.viewportWidth / 2,
|
|
-playerPhys.getPosition().x)) + camera.viewportWidth / 2,
|
|
-playerPhys.getPosition().y + camera.viewportHeight / 3);
|
|
return offset;
|
|
}
|
|
|
|
private Vector2 getCameraOffset(CPhysics playerPhys, CPhysics currPhys) {
|
|
Vector2 offset = new Vector2(Math.max(xMin + camera.viewportWidth / 2, Math.min(xMax - camera.viewportWidth / 2,
|
|
-playerPhys.getPosition().x)) / currPhys.getZParallax() + camera.viewportWidth / 2,
|
|
-playerPhys.getPosition().y / currPhys.getZParallax() + camera.viewportHeight / 3);
|
|
return offset;
|
|
}
|
|
|
|
public float getXMin() {
|
|
return xMin;
|
|
}
|
|
|
|
public RenderingSystem setXMin(float xMin) {
|
|
this.xMin = xMin;
|
|
return this;
|
|
}
|
|
|
|
public float getXMax() {
|
|
return xMax;
|
|
}
|
|
|
|
public RenderingSystem setXMax(float xMax) {
|
|
this.xMax = xMax;
|
|
return this;
|
|
}
|
|
|
|
public void dispose() {
|
|
batch.dispose();
|
|
debugRenderer.dispose();
|
|
particleRenderer.dispose();
|
|
font.dispose();
|
|
}
|
|
|
|
private class TextObject {
|
|
|
|
public String text;
|
|
public Vector2 position;
|
|
|
|
public TextObject(String text, Vector2 position) {
|
|
this.text = text;
|
|
this.position = position;
|
|
}
|
|
}
|
|
|
|
}
|