diff --git a/android/assets/particles/beateffect.p b/android/assets/particles/beateffect.p index de047be..ad171ec 100755 --- a/android/assets/particles/beateffect.p +++ b/android/assets/particles/beateffect.p @@ -1,31 +1,27 @@ -beat +main - Delay - active: false - Duration - -lowMin: 300.0 -lowMax: 300.0 +lowMin: 150.0 +lowMax: 150.0 - Count - min: 0 -max: 50 +max: 20 - Emission - lowMin: 0.0 -lowMax: 3.0 -highMin: 50.0 -highMax: 60.0 +lowMax: 0.0 +highMin: 100.0 +highMax: 150.0 relative: false -scalingCount: 3 +scalingCount: 1 scaling0: 1.0 -scaling1: 0.19607843 -scaling2: 0.0 -timelineCount: 3 +timelineCount: 1 timeline0: 0.0 -timeline1: 0.20547946 -timeline2: 1.0 - Life - lowMin: 0.0 lowMax: 0.0 -highMin: 750.0 -highMax: 1500.0 +highMin: 2500.0 +highMax: 3000.0 relative: false scalingCount: 2 scaling0: 1.0 @@ -44,8 +40,8 @@ shape: line - Spawn Width - lowMin: 0.0 lowMax: 0.0 -highMin: 64.0 -highMax: 64.0 +highMin: 3840.0 +highMax: 3840.0 relative: false scalingCount: 1 scaling0: 1.0 @@ -63,33 +59,33 @@ timelineCount: 1 timeline0: 0.0 - Scale - lowMin: 0.0 -lowMax: 0.5 -highMin: 0.75 -highMax: 1.25 +lowMax: 0.0 +highMin: 45.0 +highMax: 135.0 relative: false -scalingCount: 5 -scaling0: 0.64705884 +scalingCount: 3 +scaling0: 1.0 scaling1: 1.0 -scaling2: 1.0 -scaling3: 0.74509805 -scaling4: 0.3529412 -timelineCount: 5 +scaling2: 0.25490198 +timelineCount: 3 timeline0: 0.0 -timeline1: 0.13013698 -timeline2: 0.74657536 -timeline3: 0.9246575 -timeline4: 1.0 +timeline1: 0.7876712 +timeline2: 1.0 - Velocity - active: true lowMin: 0.0 lowMax: 0.0 -highMin: 15.0 -highMax: 30.0 +highMin: 540.0 +highMax: 1080.0 relative: false -scalingCount: 1 +scalingCount: 3 scaling0: 1.0 -timelineCount: 1 +scaling1: 1.0 +scaling2: 0.6666667 +timelineCount: 3 timeline0: 0.0 +timeline1: 0.69863015 +timeline2: 0.9931507 - Angle - active: true lowMin: 90.0 @@ -99,75 +95,49 @@ highMax: 135.0 relative: false scalingCount: 3 scaling0: 1.0 -scaling1: 0.0 -scaling2: 0.0 +scaling1: 0.43137255 +scaling2: 0.13725491 timelineCount: 3 timeline0: 0.0 -timeline1: 0.5 -timeline2: 1.0 +timeline1: 0.5684931 +timeline2: 0.8630137 - Rotation - -active: true -lowMin: 0.0 -lowMax: 0.0 -highMin: 0.0 -highMax: 360.0 -relative: false -scalingCount: 2 -scaling0: 0.0 -scaling1: 1.0 -timelineCount: 2 -timeline0: 0.0 -timeline1: 1.0 +active: false - Wind - -active: true -lowMin: 0.0 -lowMax: 0.0 -highMin: 0.0 -highMax: 0.0 -relative: false -scalingCount: 1 -scaling0: 1.0 -timelineCount: 1 -timeline0: 0.0 +active: false - Gravity - active: false - Tint - -colorsCount: 12 -colors0: 0.6431373 -colors1: 0.0 -colors2: 0.0 -colors3: 0.0 -colors4: 0.12941177 -colors5: 1.0 -colors6: 0.8784314 -colors7: 0.0 -colors8: 1.0 -colors9: 1.0 -colors10: 0.9254902 -colors11: 0.0 -timelineCount: 4 +colorsCount: 6 +colors0: 0.050980393 +colors1: 0.74509805 +colors2: 1.0 +colors3: 0.02745098 +colors4: 0.41960785 +colors5: 0.56078434 +timelineCount: 2 timeline0: 0.0 -timeline1: 0.5094081 -timeline2: 0.7872049 -timeline3: 1.0 +timeline1: 1.0 - Transparency - lowMin: 0.0 lowMax: 0.0 highMin: 1.0 highMax: 1.0 relative: false -scalingCount: 5 -scaling0: 0.2982456 -scaling1: 0.64912283 -scaling2: 0.80701756 -scaling3: 0.47368422 -scaling4: 0.0 -timelineCount: 5 +scalingCount: 6 +scaling0: 0.0 +scaling1: 0.19298245 +scaling2: 1.0 +scaling3: 1.0 +scaling4: 0.21052632 +scaling5: 0.0 +timelineCount: 6 timeline0: 0.0 -timeline1: 0.28082192 -timeline2: 0.6643836 -timeline3: 0.8561644 -timeline4: 1.0 +timeline1: 0.23972602 +timeline2: 0.41095892 +timeline3: 0.60958904 +timeline4: 0.79452056 +timeline5: 1.0 - Options - attached: false continuous: false @@ -176,4 +146,4 @@ additive: true behind: false premultipliedAlpha: false - Image Path - -triangle.png +glow-circle.png diff --git a/android/assets/particles/glow-circle.png b/android/assets/particles/glow-circle.png new file mode 100755 index 0000000..96f69a0 Binary files /dev/null and b/android/assets/particles/glow-circle.png differ diff --git a/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java b/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java index 27e9d20..af2988b 100755 --- a/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java +++ b/core/src/zero1hd/rhythmbullet/audio/visualizer/DoubleHorizontalVisualizer.java @@ -21,6 +21,9 @@ public class DoubleHorizontalVisualizer implements Disposable { private int binsPerBar; private float offset; private int boundaryThickness; + private boolean significantBeat; + private float maxAverageAmplitude; + private byte significantFrames; private float targetDelta; private float spacePercentage = 0.7f; private float baseSensitivity = 0.009f; @@ -29,7 +32,7 @@ public class DoubleHorizontalVisualizer implements Disposable { private int smoothRange = 2; private int binsToInclude = 120; private Color colorBegin = new Color(0.5f, 0.6f, 0.8f, 0.46f); - private Color colorEnd = new Color(); + /** * * @param barCount amount of bars this visualizer should have. @@ -54,13 +57,37 @@ public class DoubleHorizontalVisualizer implements Disposable { } public void act(float delta) { + if (pcm.hasAudioChanged()) { + maxAverageAmplitude = 0; + significantBeat = false; + } float[] freqBins = pcm.getFrequencyBins(); + float averageAmplitude = 0; for (int bar = 0; bar < amplitudes.length; bar++) { amplitudes[bar] = 0; for (int freq = bar*binsPerBar; freq < (bar*binsPerBar) + binsPerBar; freq++) { amplitudes[bar] += Math.abs(freqBins[freq]) * baseSensitivity; } amplitudes[bar] /= binsPerBar; + + averageAmplitude += amplitudes[bar]; + } + + averageAmplitude /= amplitudes.length; + if (averageAmplitude > maxAverageAmplitude*0.5f && !significantBeat) { + if (maxAverageAmplitude != 0) { + significantFrames++; + if (significantFrames >= 16) { + significantFrames = 0; + significantBeat = true; + } + } + + if (averageAmplitude > maxAverageAmplitude) { + maxAverageAmplitude = averageAmplitude; + } + } else { + significantBeat = false; } for (int bar = 0; bar < barHeights.length; bar++) { int smoothCount = 1; @@ -147,8 +174,12 @@ public class DoubleHorizontalVisualizer implements Disposable { return y; } + public boolean isSignificantBeat() { + return significantBeat; + } + public void updateMusic() { - pcm.updateMusic(); + pcm.loadMusic(); } @Override diff --git a/core/src/zero1hd/rhythmbullet/audio/visualizer/PCMSystem.java b/core/src/zero1hd/rhythmbullet/audio/visualizer/PCMSystem.java index 8bc3761..8a2e092 100755 --- a/core/src/zero1hd/rhythmbullet/audio/visualizer/PCMSystem.java +++ b/core/src/zero1hd/rhythmbullet/audio/visualizer/PCMSystem.java @@ -8,5 +8,7 @@ public interface PCMSystem extends Disposable { int getWindowSize(); - void updateMusic(); + void loadMusic(); + + boolean hasAudioChanged(); } \ No newline at end of file diff --git a/core/src/zero1hd/rhythmbullet/graphics/ui/components/ScrollText.java b/core/src/zero1hd/rhythmbullet/graphics/ui/components/ScrollingText.java similarity index 87% rename from core/src/zero1hd/rhythmbullet/graphics/ui/components/ScrollText.java rename to core/src/zero1hd/rhythmbullet/graphics/ui/components/ScrollingText.java index c446899..3d3248a 100755 --- a/core/src/zero1hd/rhythmbullet/graphics/ui/components/ScrollText.java +++ b/core/src/zero1hd/rhythmbullet/graphics/ui/components/ScrollingText.java @@ -14,7 +14,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Widget; import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack; -public class ScrollText extends Widget { +public class ScrollingText extends Widget { Rectangle scissors = new Rectangle(); Rectangle clipBounds = new Rectangle(); @@ -35,13 +35,17 @@ public class ScrollText extends Widget { private NinePatch background; private Vector2 coords; - public ScrollText(String text, String text2, Skin skin, boolean scrollOnHover, boolean useBackground) { + + private float targetDelta; + + public ScrollingText(String text, String text2, Skin skin, boolean scrollOnHover, boolean useBackground, float targetDelta) { super(); font = skin.getFont("default-font"); init(text, text2, skin, scrollOnHover, useBackground); + this.targetDelta = targetDelta; } - public ScrollText(String text, String text2, Skin skin, String fontName, Color color, boolean scrollOnHover, boolean useBackground) { + public ScrollingText(String text, String text2, Skin skin, String fontName, Color color, boolean scrollOnHover, boolean useBackground) { super(); font = skin.getFont(fontName); font.setColor(color); @@ -102,21 +106,18 @@ public class ScrollText extends Widget { } } - public void scroll(float delta) { if (text1Offset >= -text1Width) { - text1Offset -= 60*delta; - - + text1Offset -= 60*targetDelta; if ((text1Offset < - Math.abs((text1Width - clipBounds.getWidth())) - 50) || text2Offset != clipBounds.getWidth()) { - text2Offset -= 60*delta; + text2Offset -= 60*targetDelta; if (text2Offset <= -text2Width) { text2Offset = clipBounds.getWidth(); } } } else { - text2Offset -= 60*delta; + text2Offset -= 60*targetDelta; if (text2Offset < - Math.abs((text2Width - clipBounds.getWidth())) - 50) { text1Offset = clipBounds.getWidth(); } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java b/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java index 675a62d..eba05bf 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/audio/PCMObtainer.java @@ -27,6 +27,7 @@ public class PCMObtainer implements Observer, PCMSystem { private int windowSize = 1024; private int sampleRate; private long millisPerWindow; + private boolean songChanged; private DecimalFormat df; private float[] PCM = new float[windowSize]; private float[] frequencyBins = new float[windowSize / 2]; @@ -102,14 +103,14 @@ public class PCMObtainer implements Observer, PCMSystem { synchronizeBufferWithPlayback(); } - private int calcBufferPosition() { + private int calculateBufferPosition() { int offset = (int) alGetSourcef(sourceID, AL11.AL_SAMPLE_OFFSET); offset = (offset / windowSize) * windowSize; return offset; } private boolean synchronizeBufferWithPlayback() { - int bufferPos = calcBufferPosition(); + int bufferPos = calculateBufferPosition(); synchronized (this) { if (bufferPos <= playingBuffer.limit() && bufferPos >= 0) { playingBuffer.position(bufferPos); @@ -121,7 +122,7 @@ public class PCMObtainer implements Observer, PCMSystem { } @Override - public void updateMusic() { + public void loadMusic() { Gdx.app.debug("PCMObtainer", "music set."); sourceID = -1; @@ -129,8 +130,8 @@ public class PCMObtainer implements Observer, PCMSystem { sampleRate = mc.getCurrentMusicHeader().getSampleRate(); String millisPerWindowF = df.format(windowSize/(float) sampleRate); millisPerWindow = (long) (Float.valueOf(millisPerWindowF)*1000); - - attemptToSync(); + songChanged = true; + begin(); } @Override @@ -140,14 +141,13 @@ public class PCMObtainer implements Observer, PCMSystem { synchronized (this) { System.arraycopy(PCM, 1, frequencyBins, 0, frequencyBins.length); } - + updated = false; } } else { for (int freqID = 0; freqID < frequencyBins.length; freqID++) { frequencyBins[freqID] = 0; } } - updated = false; return frequencyBins; } @@ -226,14 +226,14 @@ public class PCMObtainer implements Observer, PCMSystem { public void update(Observable o, Object arg) { if (o == mc) { if (arg == mc.states.LOADED) { - updateMusic(); + loadMusic(); } else if (arg == mc.states.PLAYING) { - attemptToSync(); + begin(); } } } - private void attemptToSync() { + private void begin() { if (mc.isPlaying()) { if (sourceID == -1) { try { @@ -252,4 +252,14 @@ public class PCMObtainer implements Observer, PCMSystem { public void dispose() { streamReadThread.stop(); } + + @Override + public boolean hasAudioChanged() { + if (songChanged) { + songChanged = false; + return true; + } else { + return false; + } + } } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java index 3590ad6..7082460 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainPage.java @@ -5,8 +5,12 @@ import java.util.Observer; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetManager; +import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.ParticleEffect; +import com.badlogic.gdx.graphics.g2d.ParticleEffectPool; +import com.badlogic.gdx.graphics.g2d.ParticleEffectPool.PooledEffect; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.ui.Image; @@ -16,6 +20,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.Array; import zero1hd.rhythmbullet.RhythmBullet; import zero1hd.rhythmbullet.audio.MusicController; @@ -24,7 +29,7 @@ import zero1hd.rhythmbullet.audio.visualizer.DoubleHorizontalVisualizer; import zero1hd.rhythmbullet.desktop.audio.PCMObtainer; import zero1hd.rhythmbullet.graphics.ui.Page; import zero1hd.rhythmbullet.graphics.ui.components.MusicControls; -import zero1hd.rhythmbullet.graphics.ui.components.ScrollText; +import zero1hd.rhythmbullet.graphics.ui.components.ScrollingText; import zero1hd.rhythmbullet.util.ScreenConfiguration; public class MainPage extends Page implements Observer { @@ -40,10 +45,15 @@ public class MainPage extends Page implements Observer { private TextButton quitButton; private MusicControls musicControls; - private ScrollText scrollText; + private ScrollingText scrollText; private DoubleHorizontalVisualizer dhv; + private boolean playParticles = true; + private ParticleEffectPool particlePool; + private Array particles; + private float particleScale; + public MainPage(MusicController musicController, AudioMetadataController mmc, AssetManager assetManager, Skin skin, ScreenConfiguration screenConfiguration, ChangeListener playButtonListener, ChangeListener optionsButtonListener) { super(0, 0); this.mc = musicController; @@ -97,7 +107,7 @@ public class MainPage extends Page implements Observer { musicControls.setPosition((getWidth()-musicControls.getWidth() - 15f), 15f); addActor(musicControls); - scrollText = new ScrollText("...", "...", skin, false, true); + scrollText = new ScrollingText("...", "...", skin, false, true, 1f/screenConfiguration.getTargetFramesPerSecond()); scrollText.setWidth(0.5f*getWidth()); scrollText.setPosition(15, getHeight() - scrollText.getHeight()-30f); addActor(scrollText); @@ -107,6 +117,11 @@ public class MainPage extends Page implements Observer { dhv.updateMusic(); } + particles = new Array<>(); + ParticleEffect particle = assetManager.get("beateffect.p", ParticleEffect.class); + particleScale = getWidth()/particle.findEmitter("main").getSpawnWidth().getHighMax(); + particlePool = new ParticleEffectPool(assetManager.get("beateffect.p", ParticleEffect.class), 0, 16); + } @Override @@ -118,9 +133,40 @@ public class MainPage extends Page implements Observer { @Override public void draw(Batch batch, float parentAlpha) { dhv.draw(batch, parentAlpha); + updateParticles(batch); super.draw(batch, parentAlpha); } - + + private void updateParticles(Batch batch) { + if (mc.isPlaying() && playParticles) { + if (particles.size < 16 && dhv.isSignificantBeat()) { + PooledEffect particle = particlePool.obtain(); + particle.scaleEffect(particleScale); + particles.add(particle); + } + if (particles.size > 0) { + for (int i = particles.size - 1; i >= 0; i--) { + PooledEffect particle = particles.get(i); + particle.draw(batch, Gdx.graphics.getDeltaTime()); + if (particle.isComplete()) { + particles.removeIndex(i); + particle.free(); + } + } + } + } + + batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); + } + + public void stopParticles() { + playParticles = false; + for (int i = particles.size - 1; i >= 0 && particles.size > 0; i--) { + particles.get(i).free(); + particles.clear(); + } + } + @Override public void dispose() { dhv.dispose(); @@ -131,6 +177,7 @@ public class MainPage extends Page implements Observer { @Override public void setCameraPositionToPage(Vector3 cameraPosition) { getStage().setScrollFocus(null); + playParticles = true; super.setCameraPositionToPage(cameraPosition); } diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java index cb7b433..54e5294 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MainScreen.java @@ -128,7 +128,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { creditsPage = new CreditsPage(rhythmBullet.getSkin()); stage.addActor(creditsPage); - musicSelectionPage = new MusicSelectionPage(rhythmBullet.getAssetManager(), rhythmBullet.getSkin(), musicController, musicMetadataController, listeners.returnToMainPageListener, listeners.analysisPageButtonListener); + musicSelectionPage = new MusicSelectionPage(rhythmBullet.getAssetManager(), rhythmBullet.getSkin(), musicController, musicMetadataController, rhythmBullet.getScreenConfiguration(), listeners.returnToMainPageListener, listeners.analysisPageButtonListener); stage.addActor(musicSelectionPage); analysisPage = new AnalysisPage(musicController, musicMetadataController, rhythmBullet.getSkin(), listeners.musicSelectionPageButtonListener, listeners.confirmedSongListener); @@ -182,6 +182,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { @Override public void changed(ChangeEvent event, Actor actor) { setDisplayedPage(musicSelectionPage); + mainPage.stopParticles(); } }; @@ -203,6 +204,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { @Override public void changed(ChangeEvent event, Actor actor) { setDisplayedPage(optionsPage); + mainPage.stopParticles(); } }; @@ -224,6 +226,7 @@ public class MainScreen extends ScreenAdapter implements ResizeReadyScreen { @Override public void changed(ChangeEvent event, Actor actor) { setDisplayedPage(creditsPage); + mainPage.stopParticles(); } }; diff --git a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java index 5445b69..6ea3f70 100755 --- a/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java +++ b/desktop/src/zero1hd/rhythmbullet/desktop/screens/main/MusicSelectionPage.java @@ -37,7 +37,8 @@ import zero1hd.rhythmbullet.audio.AudioMetadataController; import zero1hd.rhythmbullet.audio.metadata.AudioMetadata; import zero1hd.rhythmbullet.audio.MusicController; import zero1hd.rhythmbullet.graphics.ui.Page; -import zero1hd.rhythmbullet.graphics.ui.components.ScrollText; +import zero1hd.rhythmbullet.graphics.ui.components.ScrollingText; +import zero1hd.rhythmbullet.util.ScreenConfiguration; public class MusicSelectionPage extends Page implements Observer { Preferences musicFileAnnotation; @@ -48,6 +49,7 @@ public class MusicSelectionPage extends Page implements Observer { private VerticalGroup vGroup; private TextButton back; private ScrollPane scrollPane; + private ScreenConfiguration screenConfig; private musicSelectionLoaderThread selectionLoaderThread; @@ -65,7 +67,7 @@ public class MusicSelectionPage extends Page implements Observer { private float scrollTimer, scrollDelay = 0.2f, scrollDelMod, songSelectionTimer; private float musicSelectDelay; - public MusicSelectionPage(AssetManager assetManager, Skin skin, MusicController musicController, AudioMetadataController musicMetadataController, ChangeListener backButtonListener, ChangeListener beginButtonListener) { + public MusicSelectionPage(AssetManager assetManager, Skin skin, MusicController musicController, AudioMetadataController musicMetadataController, ScreenConfiguration screenConfiguration, ChangeListener backButtonListener, ChangeListener beginButtonListener) { super(1, 0); this.assets = assetManager; this.mc = musicController; @@ -76,7 +78,7 @@ public class MusicSelectionPage extends Page implements Observer { selectables = new MusicSelectableButtonGroup(); selectables.setMinCheckCount(0); musicFileAnnotation = Gdx.app.getPreferences("music_file_annotation"); - + this.screenConfig = screenConfiguration; scrollPane = new ScrollPane(vGroup, skin); scrollPane.setSize(0.45f*getWidth(), getHeight()); scrollPane.setOverscroll(false, false); @@ -510,7 +512,7 @@ public class MusicSelectionPage extends Page implements Observer { } private class InformationTable extends Table { - private ScrollText songTitle; + private ScrollingText songTitle; private Label author; private Label musicDuration; private Label previousTop; @@ -525,7 +527,7 @@ public class MusicSelectionPage extends Page implements Observer { setSize(width, height); subInformation = new Table(skin); albumCover = new Image(assets.get("defaultCover.png", Texture.class)); - songTitle = new ScrollText("", null, skin, true, true); + songTitle = new ScrollingText("", null, skin, true, true, 1f/screenConfig.getTargetFramesPerSecond()); author = new Label(null, skin, "sub-font", skin.getColor("default")); musicDuration = new Label(null, skin, "sub-font", skin.getColor("default")); previousTop = new Label(null, skin, "sub-font", skin.getColor("default"));