diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/GameCore.java b/core/src/com/gamejolt/mikykr5/ceidecpong/GameCore.java index 03add00..b95d741 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/GameCore.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/GameCore.java @@ -37,10 +37,27 @@ import com.gamejolt.mikykr5.ceidecpong.states.LogoScreenState; import com.gamejolt.mikykr5.ceidecpong.states.MainMenuState; import com.gamejolt.mikykr5.ceidecpong.utils.AsyncAssetLoader; +/** + * This is the central class of the Game. It is in charge of maintaining the game's + * life cycle and switching between the different application states. It also renders + * the fade effects when switching states. + * + * @author Miguel Astor + */ public class GameCore extends Game { + /** + * Tag used for logging. + */ private static final String TAG = "GAME_CORE"; + + /** + * Class name used for logging. + */ private static final String CLASS_NAME = GameCore.class.getSimpleName(); + /** + * An enumerated type used for state switching. + */ public enum game_states_t { LOGO_SCREEN(0), MAIN_MENU(1), IN_GAME(2), QUIT(3), LOADING(4); @@ -59,18 +76,55 @@ public class GameCore extends Game { } }; + /** + * A pointer to the currently active state. + */ private game_states_t currState; + + /** + * A pointer to the state to switch to. Usually null. + */ public game_states_t nextState; + + /** + * An array to hold all application states. + */ private BaseState[] states; + /** + * The {@link SpriteBatch} used to render all 2D graphics in the game. + */ public SpriteBatch batch; + + /** + * A pixel perfect camera used to render the fade effects. + */ private OrthographicCamera pixelPerfectCamera; // Fade in/out effect fields. + /** + * The fade graphic. + */ private Texture fadeTexture; + + /** + * A {@link MutableFloat} used to interpolate the transparency of {@link GameCore#fadeTexture}. + */ private MutableFloat alpha; + + /** + * A {@link Tween} instance used to interpolate between full transparency to no transparency. + */ private Tween fadeOut; + + /** + * A {@link Tween} instance used to interpolate between no transparency to full transparency. + */ private Tween fadeIn; + + /** + * A flag to indicate that a fade effect is in progress. + */ private boolean fading; @Override @@ -78,7 +132,7 @@ public class GameCore extends Game { AsyncAssetLoader loader = AsyncAssetLoader.getInstance(); // Set up rendering fields and settings. - ShaderProgram.pedantic = false; + ShaderProgram.pedantic = false; // Not passing all variables to a shader will not close the game. batch = new SpriteBatch(); batch.enableBlending(); batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); @@ -91,6 +145,7 @@ public class GameCore extends Game { fadeTexture = new Texture(pixmap); pixmap.dispose(); + // Create the initial interpolators and start with a fade in effect. alpha = new MutableFloat(1.0f); fadeOut = Tween.to(alpha, 0, 0.5f).target(1.0f).ease(TweenEquations.easeInQuint); fadeIn = Tween.to(alpha, 0, 2.5f).target(0.0f).ease(TweenEquations.easeInQuint); @@ -112,6 +167,7 @@ public class GameCore extends Game { return; } + // Register every state as an AssetsLoadedListener if the state implements the interface. for(BaseState state : states){ if(state != null && state instanceof AssetsLoadedListener) loader.addListener((AssetsLoadedListener)state); @@ -138,6 +194,7 @@ public class GameCore extends Game { // If the current state set a value for nextState then switch to that state. if(nextState != null){ + // First disable the current state so that it will no longer catch user inputs. states[currState.getValue()].onStateDisabled(); if(!fadeOut.isStarted()){ @@ -198,12 +255,13 @@ public class GameCore extends Game { public void dispose(){ super.dispose(); - // Dispose screens. + // Dispose all states. for(BaseState state : states){ if(state != null) state.dispose(); } + // Dispose other graphics. fadeTexture.dispose(); batch.dispose(); } diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ProjectConstants.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ProjectConstants.java index 6edcbb3..9826644 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ProjectConstants.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ProjectConstants.java @@ -15,10 +15,34 @@ */ package com.gamejolt.mikykr5.ceidecpong; +/** + * This class holds some project-wise constants. + * + * @author Miguel Astor + */ public abstract class ProjectConstants{ + /** + * What to return when the application terminates successfully. + */ public static final int EXIT_SUCCESS = 0; + + /** + * What to return when the application terminates closes due to an error. + */ public static final int EXIT_FAILURE = 1; - public static final boolean DEBUG = true; + + /** + * Enable/disable logging. + */ + public static final boolean DEBUG = false; + + /** + * Logical screen width. + */ public static final int FB_WIDTH = 1920; + + /** + * Logical screen height. + */ public static final int FB_HEIGHT = 1080; } \ No newline at end of file diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/BoundingBoxComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/BoundingBoxComponent.java index 49defcd..24d080c 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/BoundingBoxComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/BoundingBoxComponent.java @@ -19,7 +19,15 @@ import com.badlogic.ashley.core.Component; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.utils.Pool.Poolable; +/** + * A 2D bounding rectangle component. + * + * @author Miguel Astor + */ public class BoundingBoxComponent extends Component implements Poolable { + /** + * The bounding rectangle. + */ public Rectangle bbox; public BoundingBoxComponent() { diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/Mappers.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/Mappers.java index 8cb9008..ad4746e 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/Mappers.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/Mappers.java @@ -17,10 +17,14 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.components; import com.badlogic.ashley.core.ComponentMapper; +/** + * A holder class for all {@link ComponentMapper} instances used by the game. + * + * @author Miguel Astor + */ public abstract class Mappers { public static final ComponentMapper positionMapper = ComponentMapper.getFor(PositionComponent.class); public static final ComponentMapper velocityMapper = ComponentMapper.getFor(VelocityComponent.class); - public static final ComponentMapper textureMapper = ComponentMapper.getFor(TextureComponent.class); public static final ComponentMapper spriteMapper = ComponentMapper.getFor(SpriteComponent.class); public static final ComponentMapper bboxMapper = ComponentMapper.getFor(BoundingBoxComponent.class); public static final ComponentMapper scoreMapper = ComponentMapper.getFor(ScoreComponent.class); diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PlayerComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PlayerComponent.java index 243ac05..f7c8c88 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PlayerComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PlayerComponent.java @@ -18,10 +18,18 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.components; import com.badlogic.ashley.core.Component; import com.badlogic.gdx.utils.Pool.Poolable; +/** + * A Player identity {@link Component} + * + * @author Miguel Astor + */ public class PlayerComponent extends Component implements Poolable { - public static int HUMAN_PLAYER = 0; - public static int COMPUTER_PLAYER = 1; + public static final int HUMAN_PLAYER = 0; + public static final int COMPUTER_PLAYER = 1; + /** + * An identifier used to determine the type of player. + */ public int id = -1; @Override diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PositionComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PositionComponent.java index 162ccba..1a276dc 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PositionComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/PositionComponent.java @@ -18,10 +18,28 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.components; import com.badlogic.ashley.core.Component; import com.badlogic.gdx.utils.Pool.Poolable; +/** + * A 2D position {@link Component}. + * + * @author Miguel Astor. + */ public class PositionComponent extends Component implements Poolable { + /** + * The X coordinate. + */ public float x = 0; + + /** + * The Y coordinate. + */ public float y = 0; + /** + * Sets both coordinates simultaneously. + * + * @param x + * @param y + */ public void setXY(float x, float y){ this.x = x; this.y = y; diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/ScoreComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/ScoreComponent.java index adad132..eb0896b 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/ScoreComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/ScoreComponent.java @@ -18,7 +18,15 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.components; import com.badlogic.ashley.core.Component; import com.badlogic.gdx.utils.Pool.Poolable; +/** + * A {@link Component} to hold a player's score. + * + * @author Miguel Astor + */ public class ScoreComponent extends Component implements Poolable{ + /** + * The score. + */ public int score = 0; @Override diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SoundComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SoundComponent.java index 00c8a4b..94261c1 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SoundComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SoundComponent.java @@ -1,9 +1,33 @@ +/* + * Copyright (c) 2014, Miguel Angel Astor Romero + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Read the LICENSE file for more details. + */ package com.gamejolt.mikykr5.ceidecpong.ecs.components; import com.badlogic.ashley.core.Component; import com.badlogic.gdx.utils.Pool.Poolable; +import com.gamejolt.mikykr5.ceidecpong.utils.managers.CachedSoundManager; +/** + * A sound effect {@link Component} + * + * @author Miguel Astor + */ public class SoundComponent extends Component implements Poolable { + /** + * The path of the sound effect. Used as a key by {@link CachedSoundManager}. + */ public String path = ""; @Override diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SpriteComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SpriteComponent.java index b8c0ba6..d8a42aa 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SpriteComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/SpriteComponent.java @@ -19,7 +19,15 @@ import com.badlogic.ashley.core.Component; import com.badlogic.gdx.graphics.g2d.Sprite; import com.badlogic.gdx.utils.Pool.Poolable; +/**} + * A 2D renderable {@link Component}. + * + * @author Miguel Astor + */ public class SpriteComponent extends Component implements Poolable { + /** + * The renderable sprite. + */ public Sprite sprite; public SpriteComponent(){ diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/TextureComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/TextureComponent.java deleted file mode 100644 index 74a4af4..0000000 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/TextureComponent.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2014, Miguel Angel Astor Romero - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Read the LICENSE file for more details. - */ -package com.gamejolt.mikykr5.ceidecpong.ecs.components; - -import com.badlogic.ashley.core.Component; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.utils.Pool.Poolable; - -public class TextureComponent extends Component implements Poolable{ - public Texture texture; - - public TextureComponent(){ - texture = null; - } - - public TextureComponent(Texture texture){ - this.texture = texture; - } - - @Override - public void reset() { - if(texture != null) - texture.dispose(); - texture = null; - } -} diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/VelocityComponent.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/VelocityComponent.java index 17d3b1f..f24389c 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/VelocityComponent.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/components/VelocityComponent.java @@ -18,10 +18,27 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.components; import com.badlogic.ashley.core.Component; import com.badlogic.gdx.utils.Pool.Poolable; +/** + * A 2D velocity {@link Component} + * + * @author Miguel Astor + */ public class VelocityComponent extends Component implements Poolable { + /** + * The velocity in the X axis. + */ public float vx = 0; + + /** + * The velocity in the Y axis. + */ public float vy = 0; + /** + * Sets both fields simultaneously. + * @param vx + * @param vy + */ public void setXY(float vx, float vy){ this.vx = vx; this.vy = vy; diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/EntityInitializerBase.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/EntityInitializerBase.java index ffbeb59..7c70fa9 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/EntityInitializerBase.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/EntityInitializerBase.java @@ -15,10 +15,29 @@ */ package com.gamejolt.mikykr5.ceidecpong.ecs.entities; +import com.badlogic.ashley.core.Entity; import com.badlogic.ashley.core.PooledEngine; import com.badlogic.gdx.utils.Disposable; +import com.gamejolt.mikykr5.ceidecpong.states.InGameState; +/** + * Base class for entity initializers. Implementations must create all initial {@link Entity} objects + * needed by their respective games. + * + * @author Miguel Astor + */ public abstract class EntityInitializerBase implements Disposable{ + /** + * Creates all {@link Entity} objects and loads the needed assets. + * @param engine A {@link PooledEngine} instance as used by {@link InGameState} + */ public abstract void createAllEntities(PooledEngine engine); + + /** + * Associates all assets loaded to their respective entities. + * + * @param engine A {@link PooledEngine} instance as used by {@link InGameState} + * @throws IllegalStateException If the entities have not been created before calling this method. + */ public abstract void setLoadableAssets(PooledEngine engine) throws IllegalStateException; } diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/PongEntityInitializer.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/PongEntityInitializer.java index b2cf3af..3cf06f3 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/PongEntityInitializer.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/entities/PongEntityInitializer.java @@ -34,17 +34,61 @@ import com.gamejolt.mikykr5.ceidecpong.ecs.components.VelocityComponent; import com.gamejolt.mikykr5.ceidecpong.utils.AsyncAssetLoader; import com.gamejolt.mikykr5.ceidecpong.utils.managers.CachedSoundManager; -public class PongEntityInitializer extends EntityInitializerBase { +/** + * A concrete implementation of a {@link EntityInitializerBase} that creates all the entities + * needed by the Pong game. + * + * @author Miguel Astor + */ +public class PongEntityInitializer extends EntityInitializerBase{ + /** + * An assets loader instance. + */ private AsyncAssetLoader loader; + + /** + * An entity that plays a sound when the user scores. + */ private Entity victorySound; + + /** + * An entity that plays a sound when the computer scores. + */ private Entity defeatSound; + + /** + * An entity that represents the ball in the game. + */ private Entity ball; + + /** + * An entity that represents the human player. + */ private Entity paddleUser; + + /** + * An entity that represents the computer player. + */ private Entity paddleComp; + + /** + * An entity that is used to render the background. + */ private Entity background; + + /** + * Flag that indicates that all entities have been created. + */ private boolean entitiesCreated; + + /** + * Flag that indicates that all assets associated to entities have been loaded. + */ private boolean assetsLoaded; + /** + * Create the initializer and set the flags to false. + */ public PongEntityInitializer() { entitiesCreated = false; assetsLoaded = false; @@ -52,25 +96,29 @@ public class PongEntityInitializer extends EntityInitializerBase { @Override public void createAllEntities(PooledEngine engine){ + // Get instances of the needed asset loaders. loader = AsyncAssetLoader.getInstance(); CachedSoundManager soundManager = CachedSoundManager.getInstance(); + // Load all textures and sound effects. loader.addAssetToLoad("data/gfx/textures/pong_atlas.atlas", TextureAtlas.class); loader.addAssetToLoad("data/gfx/textures/bckg.png", Texture.class); soundManager.loadSound("data/sfx/BounceYoFrankie.ogg"); soundManager.loadSound("data/sfx/oh_yeah_wav_cut.ogg"); soundManager.loadSound("data/sfx/atari_boom.ogg"); + // Create the entities related to the sound effects. victorySound = engine.createEntity(); victorySound.add(engine.createComponent(SoundComponent.class)); - defeatSound = engine.createEntity(); defeatSound.add(engine.createComponent(SoundComponent.class)); + // Create the background. background = engine.createEntity(); background.add(engine.createComponent(PositionComponent.class)); background.add(engine.createComponent(SpriteComponent.class)); + // Create the ball. ball = engine.createEntity(); ball.add(engine.createComponent(PositionComponent.class)); ball.add(engine.createComponent(VelocityComponent.class)); @@ -78,6 +126,7 @@ public class PongEntityInitializer extends EntityInitializerBase { ball.add(engine.createComponent(BoundingBoxComponent.class)); ball.add(engine.createComponent(SoundComponent.class)); + // Create the human player. paddleUser = engine.createEntity(); paddleUser.add(engine.createComponent(PositionComponent.class)); paddleUser.add(engine.createComponent(VelocityComponent.class)); @@ -86,6 +135,7 @@ public class PongEntityInitializer extends EntityInitializerBase { paddleUser.add(engine.createComponent(ScoreComponent.class)); paddleUser.add(engine.createComponent(PlayerComponent.class)); + // Create the computer player. paddleComp = engine.createEntity(); paddleComp.add(engine.createComponent(PositionComponent.class)); paddleComp.add(engine.createComponent(VelocityComponent.class)); @@ -94,6 +144,7 @@ public class PongEntityInitializer extends EntityInitializerBase { paddleComp.add(engine.createComponent(ScoreComponent.class)); paddleComp.add(engine.createComponent(PlayerComponent.class)); + // Register all entities. engine.addEntity(victorySound); engine.addEntity(defeatSound); engine.addEntity(background); @@ -101,6 +152,7 @@ public class PongEntityInitializer extends EntityInitializerBase { engine.addEntity(paddleUser); engine.addEntity(paddleComp); + // Mark the flag. entitiesCreated = true; } @@ -109,33 +161,42 @@ public class PongEntityInitializer extends EntityInitializerBase { if(!entitiesCreated) throw new IllegalStateException("Entities have not been created before setting assets."); + // Some variables used to initialize the ball. Vector2 randomVector = new Vector2().set(Vector2.X).setAngle(MathUtils.random(-60, 60)); int randomSign = MathUtils.random(-1, 1) >= 0 ? 1 : -1; + + // Fetch the assets. TextureAtlas atlas = loader.getAsset("data/gfx/textures/pong_atlas.atlas", TextureAtlas.class); Texture bckg = loader.getAsset("data/gfx/textures/bckg.png", Texture.class); + // Add the sound effects to the entities. Mappers.soundMapper.get(victorySound).path = "data/sfx/oh_yeah_wav_cut.ogg"; Mappers.soundMapper.get(defeatSound).path = "data/sfx/atari_boom.ogg"; + // Set up the background. Mappers.spriteMapper.get(background).sprite = new Sprite(bckg); Mappers.positionMapper.get(background).setXY(-(ProjectConstants.FB_WIDTH / 2.0f), -(ProjectConstants.FB_HEIGHT / 2.0f)); + // Set up the ball. Mappers.spriteMapper.get(ball).sprite = atlas.createSprite("ball"); Mappers.positionMapper.get(ball).setXY(-(Mappers.spriteMapper.get(ball).sprite.getWidth() / 2), -(Mappers.spriteMapper.get(ball).sprite.getHeight() / 2)); Mappers.velocityMapper.get(ball).setXY(randomVector.x * 475.0f * randomSign, randomVector.y * 475.0f * randomSign); Mappers.bboxMapper.get(ball).bbox.set(Mappers.spriteMapper.get(ball).sprite.getBoundingRectangle()); Mappers.soundMapper.get(ball).path = "data/sfx/BounceYoFrankie.ogg"; + // Set up the human player. Mappers.spriteMapper.get(paddleUser).sprite = atlas.createSprite("glasspaddle2"); Mappers.positionMapper.get(paddleUser).setXY(-(ProjectConstants.FB_WIDTH / 2) + 100, -(Mappers.spriteMapper.get(paddleUser).sprite.getHeight() / 2)); Mappers.bboxMapper.get(paddleUser).bbox.set(Mappers.spriteMapper.get(paddleUser).sprite.getBoundingRectangle()); Mappers.playerMapper.get(paddleUser).id = PlayerComponent.HUMAN_PLAYER; + // Set up the computer player. Mappers.spriteMapper.get(paddleComp).sprite = atlas.createSprite("paddle"); Mappers.positionMapper.get(paddleComp).setXY(((ProjectConstants.FB_WIDTH / 2) - 1) - 100 - Mappers.spriteMapper.get(paddleComp).sprite.getWidth(), -(Mappers.spriteMapper.get(paddleComp).sprite.getHeight() / 2)); Mappers.bboxMapper.get(paddleComp).bbox.set(Mappers.spriteMapper.get(paddleComp).sprite.getBoundingRectangle()); Mappers.playerMapper.get(paddleComp).id = PlayerComponent.COMPUTER_PLAYER; + // Release the assets loader instance and mark the flag. AsyncAssetLoader.freeInstance(); assetsLoaded = true; } @@ -148,6 +209,7 @@ public class PongEntityInitializer extends EntityInitializerBase { if(!assetsLoaded) throw new IllegalStateException("Assets have not been loaded before disposing."); + // Release the sound manager instance. CachedSoundManager.freeInstance(); } } diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessage.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessage.java index 98f0751..ef09d98 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessage.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessage.java @@ -3,10 +3,30 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.systems.messaging; import java.util.HashMap; import java.util.Map; +import com.badlogic.ashley.core.EntitySystem; + +/** + * A message from a {@link EntitySystem }to another. + * + * @author Miguel Astor + */ public class InterSystemMessage{ + /** + * A string to identify the receiver of the message. Can contain anything so long as the intended receiver + * knows. + */ public final String target; + + /** + * A holder for arbitrary data. + */ public final Map data; + /** + * Creates a new message object. + * + * @param target The receiver of the message. + */ public InterSystemMessage(String target){ this.target = target; this.data = new HashMap(); diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessagingQueue.java b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessagingQueue.java index deaeab8..b610a84 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessagingQueue.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/ecs/systems/messaging/InterSystemMessagingQueue.java @@ -3,11 +3,26 @@ package com.gamejolt.mikykr5.ceidecpong.ecs.systems.messaging; import java.util.LinkedList; import java.util.Queue; +import com.badlogic.ashley.core.EntitySystem; import com.badlogic.gdx.Gdx; +/** + * A messaging queue to communicate two {@link EntitySystem} instances. + * + * @author Miguel Astor + */ public abstract class InterSystemMessagingQueue{ + /** + * The message queue. + */ private static Queue queue = new LinkedList(); + /** + * Adds a message to the queue. + * + * @param message The message to add. + * @throws IllegalArgumentException If message is null. + */ public static synchronized void pushMessage(InterSystemMessage message) throws IllegalArgumentException{ if(message == null) throw new IllegalArgumentException("Message is null"); @@ -15,6 +30,14 @@ public abstract class InterSystemMessagingQueue{ queue.add(message); } + /** + * Fetches a message from the queue if it's intended receiver is the caller of this method. A message is removed from the + * queue only if it was successfully retrieved. + * + * @param receiver The intended receiver. + * @return The message. + * @throws IllegalArgumentException If receiver is null. + */ public static synchronized InterSystemMessage popMessage(String receiver) throws IllegalArgumentException{ InterSystemMessage message = null; diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/effects/ScrollingBackground.java b/core/src/com/gamejolt/mikykr5/ceidecpong/effects/ScrollingBackground.java index 97a3a39..78be27c 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/effects/ScrollingBackground.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/effects/ScrollingBackground.java @@ -26,92 +26,198 @@ import com.badlogic.gdx.utils.Disposable; import com.gamejolt.mikykr5.ceidecpong.interfaces.AssetsLoadedListener; import com.gamejolt.mikykr5.ceidecpong.utils.AsyncAssetLoader; +/** + * A reusable scrolling infinite background similar to those used commonly in PSX games. + * + * @author Miguel Astor + */ public class ScrollingBackground implements Disposable, AssetsLoadedListener{ + /** + * Tag used for logging. + */ private static final String TAG = "SCROLLING_BACKGROUND"; + + /** + * Class name used for logging. + */ private static final String CLASS_NAME = ScrollingBackground.class.getSimpleName(); + + /** + * The path of the shader used by the effect. + */ private static final String SHADER_PATH = "shaders/movingBckg/movingBckg"; + /** + * The assets loader instance. + */ private AsyncAssetLoader loader; + + /** + * The texture to render. + */ private Texture backgroundTexture; + + /** + * An sprite to hold the texture. + */ private Sprite background; + + /** + * A compiled shader object used during rendering. + */ private ShaderProgram shader; + + /** + * Holds the location of the u_scaling variable of the shader. + */ private int u_scaling; + + /** + * Holds the location of the u_displacement variable of the shader. + */ private int u_displacement; + + /** + * The scaling to apply to the texture. + */ private float scaling; + + /** + * The displacement to apply to the texture. + */ private float displacement; private String texturePath; + /** + * Creates a new effect using a default scaling and displacement. The + * texture is loaded with {@link AsyncAssetLoader}. + * + * @param texturePath The internal path of the texture to use. + */ public ScrollingBackground(String texturePath){ this(texturePath, 2.0f, 0.0f, true); } + /** + * Creates a new effect using a default scaling and displacement. + * + * @param texturePath The internal path of the texture to use. + * @param loadAsync Whether to use {@link AsyncAssetLoader} or not. + */ public ScrollingBackground(String texturePath, boolean loadAsync){ this(texturePath, 2.0f, 0.0f, loadAsync); } + /** + * Creates a new effect using a default displacement. The + * texture is loaded with {@link AsyncAssetLoader}. + * + * @param texturePath The internal path of the texture to use. + * @param scaling The scaling to apply to the texture. + */ public ScrollingBackground(String texturePath, float scaling){ this(texturePath, scaling, 0.0f, true); } + /** + * Creates a new effect using a default displacement. + * + * @param texturePath The internal path of the texture to use. + * @param scaling The scaling to apply to the texture. + * @param loadAsync Whether to use {@link AsyncAssetLoader} or not. + */ public ScrollingBackground(String texturePath, float scaling, boolean loadAsync){ this(texturePath, scaling, 0.0f, loadAsync); } + /** + * Creates a new effect. + * + * @param texturePath The internal path of the texture to use. + * @param scaling The scaling to apply to the texture. + * @param displacement The displacement to apply to the texture. + * @param loadAsync Whether to use {@link AsyncAssetLoader} or not. + */ public ScrollingBackground(String texturePath, float scaling, float displacement, boolean loadAsync){ if(loadAsync){ + // If an asynchronous load was requested then use the assets loader. loader = AsyncAssetLoader.getInstance(); loader.addAssetToLoad(texturePath, Texture.class); loader.addListener(this); }else{ + // Else load the texture manually. backgroundTexture = new Texture(Gdx.files.internal(texturePath)); initGraphics(); } + // Load and compile the shader. If the shader failed to load or compile the disable the effect. shader = new ShaderProgram(Gdx.files.internal(SHADER_PATH + "_vert.glsl"), Gdx.files.internal(SHADER_PATH + "_frag.glsl")); if(!shader.isCompiled()){ Gdx.app.error(TAG, CLASS_NAME + ".ScrollingBackground() :: Failed to compile the shader."); Gdx.app.error(TAG, CLASS_NAME + shader.getLog()); shader = null; + u_scaling = 0; + u_displacement = 0; + }else{ + // If the shader compiled fine then cache it's uniforms. + u_scaling = shader.getUniformLocation("u_scaling"); + u_displacement = shader.getUniformLocation("u_displacement"); } - u_scaling = shader.getUniformLocation("u_scaling"); - u_displacement = shader.getUniformLocation("u_displacement"); - + // Set all other fields. this.texturePath = texturePath; this.scaling = scaling; this.displacement = displacement; } + /** + * Render this effect. + * + * @param batch The {@link SpriteBatch} to use for the rendering. + * @throws IllegalStateException If the {@link SpriteBatch} did not call {@link SpriteBatch#begin()} befor this method was called. + */ public void render(SpriteBatch batch) throws IllegalStateException{ if(!batch.isDrawing()) throw new IllegalStateException("Must be called between SpriteBatch.begin() and SpriteBatch.end()"); + // If the shader was loaded then set it as the current shader. if(shader != null){ batch.setShader(shader); shader.setUniformf(u_scaling, scaling); shader.setUniformf(u_displacement, displacement); } + + // Render. background.draw(batch); - if(shader != null) batch.setShader(null); + // If the shader was loaded then disable it. + if(shader != null) + batch.setShader(null); + // Update the displacement a little bit. + // GOTCHA: This will look slower or faster depending on the speed of the computer. displacement = displacement < 0.0f ? 1.0f : displacement - 0.0005f; } @Override public void dispose(){ + // Dispose all graphic assets. backgroundTexture.dispose(); - if(shader != null) shader.dispose(); + if(shader != null) + shader.dispose(); } @Override public void onAssetsLoaded(){ + // Get the graphics and initialize them. Then release the assets loader. backgroundTexture = loader.getAsset(texturePath, Texture.class); initGraphics(); - AsyncAssetLoader.freeInstance(); } + /** + * Sets the graphic asset's parameters and creates the sprite. + */ private void initGraphics(){ // Set up the texture. backgroundTexture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat); diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/interfaces/AssetsLoadedListener.java b/core/src/com/gamejolt/mikykr5/ceidecpong/interfaces/AssetsLoadedListener.java index 17d661b..f1db04f 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/interfaces/AssetsLoadedListener.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/interfaces/AssetsLoadedListener.java @@ -15,6 +15,16 @@ */ package com.gamejolt.mikykr5.ceidecpong.interfaces; -public interface AssetsLoadedListener { +import com.gamejolt.mikykr5.ceidecpong.utils.AsyncAssetLoader; + +/** + * An interface for objects that want to be notified when {@link AsyncAssetLoader} has finished loading all requested assets. + * + * @author Miguel Astor + */ +public interface AssetsLoadedListener{ + /** + * Called when {@link AsyncAssetLoader} has finished loading so that observers can fetch their assets. + */ public void onAssetsLoaded(); } diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/states/BaseState.java b/core/src/com/gamejolt/mikykr5/ceidecpong/states/BaseState.java index 66b3e9f..74a83b6 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/states/BaseState.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/states/BaseState.java @@ -19,19 +19,53 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.Screen; import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.gamejolt.mikykr5.ceidecpong.GameCore; +/** + * Base class for all the game states. + * + * @author Miguel Astor + */ public abstract class BaseState implements Screen, InputProcessor{ + /** + * Class used for logging. + */ private static final String CLASS_NAME = BaseState.class.getSimpleName(); + /** + * A {@link GameCore} instance to use it's {@link SpriteBatch} + */ protected GameCore core; + + /** + * A flag to indicate that the state is the currently active state. + */ protected boolean stateEnabled; + + /** + * A pixel perfect camera used to render all 2D assets for a concrete state instance. + */ protected OrthographicCamera pixelPerfectCamera; - protected Vector3 win2world; - protected Vector2 touchPointWorldCoords; + + /** + * An auxiliary vector used to convert screen coordinates to world coordinates. + */ + protected final Vector3 win2world; + + /** + * An auxiliary vector used to hold the unprojected world coordinates of a touch or click. + */ + protected final Vector2 touchPointWorldCoords; + /** + * Sets up all the general state fields. + * + * @param core The game core. + * @throws IllegalArgumentException If core is null. + */ public BaseState(final GameCore core) throws IllegalArgumentException{ if(core == null) throw new IllegalArgumentException(CLASS_NAME + ": Core is null."); diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/states/InGameState.java b/core/src/com/gamejolt/mikykr5/ceidecpong/states/InGameState.java index b2971e0..687edcb 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/states/InGameState.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/states/InGameState.java @@ -15,6 +15,7 @@ */ package com.gamejolt.mikykr5.ceidecpong.states; +import com.badlogic.ashley.core.Engine; import com.badlogic.ashley.core.PooledEngine; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; @@ -40,31 +41,85 @@ import com.gamejolt.mikykr5.ceidecpong.ecs.systems.messaging.InterSystemMessage; import com.gamejolt.mikykr5.ceidecpong.ecs.systems.messaging.InterSystemMessagingQueue; import com.gamejolt.mikykr5.ceidecpong.interfaces.AssetsLoadedListener; +/** + * The state in charge of executing and handling the game itself. + * + * @author Miguel Astor + */ public class InGameState extends BaseState implements AssetsLoadedListener{ + /** + * The {@link Engine} responsible for handling the ECS design pattern. + */ private PooledEngine engine; + + /** + * The entity creator. + */ private EntityInitializerBase entityInitializer; + + /** + * A {@link FrameBuffer} used to render to a logical screen. This way the game can be + * designed for a single screen resolution and scaled to the running device's screen resolution. + */ private FrameBuffer frameBuffer; + + /** + * The screen width. + */ private int w; + + /** + * The screen height. + */ private int h; - private final float oldRatio; + + /** + * The aspect ratio of the logical screen. + */ + private final float fbAspectRatio; + + /** + * Flag to indicate that all assets have been successfully loaded. + */ private boolean assetsLoaded; + + /** + * A pixel perfect camera used for rendering to the frame buffer. + */ private OrthographicCamera fbCamera; + + /** + * The bounding rectangle of the frame buffer. + */ private Rectangle fbBounds; + + /** + * An auxiliary vector for input calculations. + */ private final Vector3 temp; + /** + * Creates the state and the entity processing systems. + * + * @param core A GameCore instance. See {@link BaseState#BaseState(GameCore)}. + * @throws IllegalArgumentException If core is null; + */ public InGameState(final GameCore core) throws IllegalArgumentException{ super(core); + // Initialize all fields. engine = new PooledEngine(); - frameBuffer = new FrameBuffer(Format.RGB565, ProjectConstants.FB_WIDTH, ProjectConstants.FB_HEIGHT, false); - fbBounds = new Rectangle(); w = Gdx.graphics.getWidth(); w = Gdx.graphics.getHeight(); - oldRatio = aspectRatio(ProjectConstants.FB_WIDTH, ProjectConstants.FB_HEIGHT); assetsLoaded = false; - fbCamera = new OrthographicCamera(ProjectConstants.FB_WIDTH, ProjectConstants.FB_HEIGHT); temp = new Vector3(); + // Create the framebuffer. + frameBuffer = new FrameBuffer(Format.RGB565, ProjectConstants.FB_WIDTH, ProjectConstants.FB_HEIGHT, false); + fbAspectRatio = aspectRatio(ProjectConstants.FB_WIDTH, ProjectConstants.FB_HEIGHT); + fbCamera = new OrthographicCamera(ProjectConstants.FB_WIDTH, ProjectConstants.FB_HEIGHT); + fbBounds = new Rectangle(); + // Create all entities. entityInitializer = new PongEntityInitializer(); entityInitializer.createAllEntities(engine); @@ -103,7 +158,7 @@ public class InGameState extends BaseState implements AssetsLoadedListener{ // Scale the frame buffer to the current screen size. renderW = w; - renderH = renderW / oldRatio; + renderH = renderW / fbAspectRatio; // Set the rendering position of the frame buffer. x = -(renderW / 2.0f); @@ -135,10 +190,13 @@ public class InGameState extends BaseState implements AssetsLoadedListener{ @Override public boolean keyDown(int keycode){ + // If the user pressed the escape key (the back button in Android) then go back + // to the main menu. Else ignore the key. if(keycode == Input.Keys.BACK || keycode == Input.Keys.ESCAPE){ core.nextState = game_states_t.MAIN_MENU; return true; } + return false; } @@ -146,6 +204,7 @@ public class InGameState extends BaseState implements AssetsLoadedListener{ public boolean touchDown(int screenX, int screenY, int pointer, int button){ InterSystemMessage message; + // If the user touched the screen inside the frame buffer then notify the player positioning system. if(touchInsideFrameBuffer(screenX, screenY)){ message = new InterSystemMessage(HumanPlayerPositioningSystem.class.getCanonicalName()); message.data.put("INPUT_Y", convertWorldYToFrameBufferY(screenY)); @@ -159,6 +218,7 @@ public class InGameState extends BaseState implements AssetsLoadedListener{ public boolean touchDragged(int screenX, int screenY, int pointer){ InterSystemMessage message; + // If the user touched the screen inside the frame buffer then notify the player positioning system. if(touchInsideFrameBuffer(screenX, screenY)){ message = new InterSystemMessage(HumanPlayerPositioningSystem.class.getCanonicalName()); message.data.put("INPUT_Y", convertWorldYToFrameBufferY(screenY)); @@ -174,12 +234,19 @@ public class InGameState extends BaseState implements AssetsLoadedListener{ assetsLoaded = true; } + /** + * Checks if the user clicked or touched a point inside the frame buffer. + * + * @param screenX The X coordinate of the touch point. + * @param screenY The Y coordinate of the touch point. + * @return True if the touch point is inside the frame buffer. + */ private boolean touchInsideFrameBuffer(int screenX, int screenY){ float fbW, fbH; unprojectTouch(screenX, screenY); fbW = w; - fbH = fbW / oldRatio; + fbH = fbW / fbAspectRatio; fbBounds.set(-(fbW / 2.0f), -(fbH / 2.0f), fbW, fbH); if(fbBounds.contains(touchPointWorldCoords)){ @@ -189,8 +256,14 @@ public class InGameState extends BaseState implements AssetsLoadedListener{ } } + /** + * Converts the Y coordinate of a touch point in screen coordinates to the world coordinates of the framebuffer. + * + * @param y The Y coordinate of a touch point. + * @return The Y coordinate converted to world coordinates. + */ private float convertWorldYToFrameBufferY(float y){ - float fbH = h / oldRatio; + float fbH = h / fbAspectRatio; temp.set(0, y + (fbH / 2.0f), 0); fbCamera.unproject(temp, 0, 0, w, fbH); diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/states/LoadingState.java b/core/src/com/gamejolt/mikykr5/ceidecpong/states/LoadingState.java index a5c9f52..2cf134e 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/states/LoadingState.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/states/LoadingState.java @@ -77,7 +77,6 @@ public class LoadingState extends BaseState{ if(!loadingDone && loader != null){ if(loader.loadAssets()){ - loader.notifyListeners(); loadingDone = true; } } diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/utils/AsyncAssetLoader.java b/core/src/com/gamejolt/mikykr5/ceidecpong/utils/AsyncAssetLoader.java index 33fe72c..5fd5259 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/utils/AsyncAssetLoader.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/utils/AsyncAssetLoader.java @@ -22,35 +22,80 @@ import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.utils.Disposable; import com.gamejolt.mikykr5.ceidecpong.interfaces.AssetsLoadedListener; +/** + * An singleton wrapper around an {@link AssetManager} object. + * + * @author Miguel Astor + * + */ public final class AsyncAssetLoader implements Disposable{ + /** + * A list of all listeners registered with this loader. + */ private LinkedList listeners; + + /** + * The {@link AssetManager} used to load the assets. + */ private AssetManager manager; + /** + * Creates the listeners list and the assets manager. Made private so that this class cannot be + * instantiated outside of itself. + */ private AsyncAssetLoader(){ listeners = new LinkedList(); manager = new AssetManager(); } + /** + * A holder for the singleton instance of {@link AsyncAssetLoader}. + */ private static final class SingletonHolder{ + /** + * How many references to the singleton instance there are. + */ public static int REF_COUNT = 0; + + /** + * The singleton instance. + */ public static AsyncAssetLoader INSTANCE; } + /** + * Gets a reference to the singleton instance of this class. + * + * @return The singleton instance. + */ public static AsyncAssetLoader getInstance(){ + // If the instance does not exists then create it and update it's reference counter. if(SingletonHolder.REF_COUNT == 0) SingletonHolder.INSTANCE = new AsyncAssetLoader(); SingletonHolder.REF_COUNT++; + return SingletonHolder.INSTANCE; } + /** + * Releases a reference to the singleton instance of this class. + */ public static void freeInstance(){ SingletonHolder.REF_COUNT--; + + // If there are no more references to the instance then delete it. if(SingletonHolder.REF_COUNT <= 0){ SingletonHolder.INSTANCE.dispose(); SingletonHolder.INSTANCE = null; } } + /** + * Adds a new listener to the listeners queue. + * + * @param listener An {@link AssetsLoadedListener} instance. + * @throws IllegalArgumentException if listener is null. + */ public void addListener(AssetsLoadedListener listener) throws IllegalArgumentException{ try{ checkParametes(listener, "listener"); @@ -61,6 +106,13 @@ public final class AsyncAssetLoader implements Disposable{ listeners.add(listener); } + /** + * Requests a new asset to be loaded. + * + * @param path The internal path of the asset. + * @param assetClass The class of the asset to load. Must be a class recognized by {@link AssetManager}. + * @throws IllegalArgumentException If either argument is null. + */ public void addAssetToLoad(String path, Class assetClass) throws IllegalArgumentException{ try{ checkParametes(path, "path"); @@ -72,6 +124,14 @@ public final class AsyncAssetLoader implements Disposable{ manager.load(path, assetClass); } + /** + * Fetches an asset from the manager after it has been loaded. + * + * @param path The internal path of the asset. + * @param assetClass The class of the asset to load. Must be a class recognized by {@link AssetManager}. + * @return The asset. + * @throws IllegalArgumentException If either argument is null. + */ public T getAsset(String path, Class assetClass) throws IllegalArgumentException{ try{ checkParametes(path, "path"); @@ -83,17 +143,37 @@ public final class AsyncAssetLoader implements Disposable{ return manager.get(path, assetClass); } + /** + * Updates the {@link AssetManager}. Notifies all the registered listeners if the loading finished. + * + * @return True if all assets are loaded. + */ public boolean loadAssets(){ - return manager.update(); + boolean done = manager.update(); + + if(done) + notifyListeners(); + + return done; } - public void notifyListeners(){ + /** + * Notifies all listener objects that this loader has finished it's work. + */ + private void notifyListeners(){ for(AssetsLoadedListener listener : listeners) listener.onAssetsLoaded(); listeners.clear(); } + /** + * Checks if the given parameter is null. + * + * @param parameter The parameter to check. + * @param paramName The name of the parameter to append to the exception if it is null. + * @throws IllegalArgumentException If parameter is null. + */ private void checkParametes(Object parameter, String paramName) throws IllegalArgumentException{ if(parameter == null) throw new IllegalArgumentException("Parameter: " + paramName + " is null."); } diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedFontManager.java b/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedFontManager.java index 58b93fe..15ea082 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedFontManager.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedFontManager.java @@ -23,62 +23,125 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; +/** + * A {@link BitmapFont} loader and manager with a cache. + * + * @author Miguel Astor + */ public class CachedFontManager{ + /** + * The characters used by all the {@link BitmapFont} objects loaded. + */ public static final String FONT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890:,"; + + /** + * The standar font size for all loaded {@link BitmapFont} objects. + */ public static final int BASE_FONT_SIZE = 40; + /** + * The cache of {@link BitmapFont} objects. + */ private Map fonts; + /** + * Creates the cache. Made private so that this class cannot be instantiated outside of itself. + */ private CachedFontManager(){ fonts = new HashMap(); } + /** + * A holder for the singleton instance of {@link CachedFontManager}. + */ private static final class SingletonHolder{ + /** + * How many references to the singleton instance there are. + */ public static int REF_COUNT = 0; + + /** + * The singleton instance. + */ public static CachedFontManager INSTANCE; } + /** + * Gets a reference to the singleton instance of this class. + * + * @return The singleton instance. + */ public static CachedFontManager getInstance(){ + // If the instance does not exists then create it and update it's reference counter. if(SingletonHolder.REF_COUNT == 0) SingletonHolder.INSTANCE = new CachedFontManager(); SingletonHolder.REF_COUNT++; + return SingletonHolder.INSTANCE; } + /** + * Releases a reference to the singleton instance of this class. + */ public static void freeInstance(){ SingletonHolder.REF_COUNT--; + + // If there are no more references to the instance then delete it. if(SingletonHolder.REF_COUNT <= 0){ SingletonHolder.INSTANCE.dispose(); SingletonHolder.INSTANCE = null; } } + /** + * Loads a {@link BitmapFont} with the given path and {@link CachedFontManager#BASE_FONT_SIZE} as default size. + * + * @param path The internal path of the font to load. + * @return The font. + */ public BitmapFont loadFont(String path){ return loadFont(path, BASE_FONT_SIZE); } + /** + * Loads a {@link BitmapFont} with the given path and size. + * + * @param path The internal path of the font to load. + * @param size The size of the font to load. + * @return + */ public BitmapFont loadFont(String path, int size){ + // If the font is already in the cache the return it. + // GOTCHA: The same font cannot be loaded with two different sizes. if(fonts.containsKey(path)) return fonts.get(path); + // Declare all variables used to create a font. FreeTypeFontGenerator fontGenerator; FreeTypeFontParameter fontParameters; BitmapFont font; + // Set the parameters of the font to load. fontParameters = new FreeTypeFontParameter(); fontParameters.characters = FONT_CHARS; fontParameters.size = size; fontParameters.flip = false; + // Create a new bitmap font from the given TrueType font. fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal(path)); font = fontGenerator.generateFont(fontParameters); fonts.put(path, font); + // Clean the font generator. fontGenerator.dispose(); return font; } + /** + * Removes a specific {@link BitmapFont} from the cache and disposes it. + * @param path + */ public void unloadFont(String path){ if(fonts.containsKey(path)){ fonts.get(path).dispose(); @@ -86,9 +149,13 @@ public class CachedFontManager{ } } + /** + * Removes and disposes all the loaded fonts. + */ private void dispose(){ Gdx.app.log("FONT_MANAGER", "Disposing fonts."); + // for(BitmapFont font : fonts.values()) font.dispose(); fonts.clear(); diff --git a/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedSoundManager.java b/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedSoundManager.java index f71d99d..ce97c39 100644 --- a/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedSoundManager.java +++ b/core/src/com/gamejolt/mikykr5/ceidecpong/utils/managers/CachedSoundManager.java @@ -21,33 +21,72 @@ import java.util.Map; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.audio.Sound; +/** + * A {@link Sound} effect loader and manager with a cache. + * + * @author Miguel Astor + */ public class CachedSoundManager { + /** + * The cache of {@link Sound} objects. + */ private Map sounds; + /** + * Creates the cache. Made private so that this class cannot be instantiated outside of itself. + */ private CachedSoundManager(){ sounds = new HashMap(); } + /** + * A holder for the singleton instance of {@link CachedSoundManager}. + */ private static final class SingletonHolder{ + /** + * How many references to the singleton instance there are. + */ public static int REF_COUNT = 0; + + /** + * The singleton instance. + */ public static CachedSoundManager INSTANCE; } + /** + * Gets a reference to the singleton instance of this class. + * + * @return The singleton instance. + */ public static CachedSoundManager getInstance(){ + // If the instance does not exists then create it and update it's reference counter. if(SingletonHolder.REF_COUNT == 0) SingletonHolder.INSTANCE = new CachedSoundManager(); SingletonHolder.REF_COUNT++; + return SingletonHolder.INSTANCE; } + /** + * Releases a reference to the singleton instance of this class. + */ public static void freeInstance(){ SingletonHolder.REF_COUNT--; + + // If there are no more references to the instance then delete it. if(SingletonHolder.REF_COUNT <= 0){ SingletonHolder.INSTANCE.dispose(); SingletonHolder.INSTANCE = null; } } + /** + * Loads a {@link Sound} effect with the given path. + * + * @param path The internal path of the sound effect to load. + * @return The sound effect. + */ public Sound loadSound(String path){ if(sounds.containsKey(path)) return sounds.get(path); @@ -58,6 +97,10 @@ public class CachedSoundManager { return s; } + /** + * Removes a specific {@link Sound} from the cache and disposes it. + * @param path + */ public void unloadSound(String path){ if(sounds.containsKey(path)){ sounds.get(path).dispose(); @@ -65,6 +108,9 @@ public class CachedSoundManager { } } + /** + * Removes and disposes all the loaded sounds. + */ private void dispose(){ Gdx.app.log("SOUND_MANAGER", "Disposing sounds.");