Search code examples
javalibgdxscene2d

Making a "pressing" effect on a TextButton with a shadow


I'm currently generating the font for my TextButton with a shadow, using FreeType. I would like to do something so that when I click on the textbutton, the text is moved a bit down and to the right, simulating a press. The shadow shouldn't move. Here's and example of what I mean:

Example

Notice how the shadow doesn't move. How can I accomplish something like this? I suspect it has something to do with drawables, but I'm not sure. Help is very much appreciated!


Solution

  • I think I figured it out. My idea is to override the TextButton class with the one that will handle touchingDown and touchingUp itself.

    The class is creating two types of fonts (you will need the .ttf file but of course you can change it and pass BitmapFont objects or something) - and generally good idea is to choose thin and bold font (because of shadow).

    Here is the code - I made comments with description how it works and what am I actually doing, because Ive found it more convenient:

        package put.here.your.package;
    
    
        import com.badlogic.gdx.Gdx;
        import com.badlogic.gdx.graphics.Texture.TextureFilter;
        import com.badlogic.gdx.graphics.g2d.BitmapFont;
        import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
        import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
        import com.badlogic.gdx.scenes.scene2d.InputEvent;
        import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
        import com.badlogic.gdx.scenes.scene2d.ui.Skin;
        import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
        import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
    
        public class MyTextButton extends TextButton
        {
            BitmapFont upFont, downFont;
            LabelStyle upStyle, downStyle;
            float upLeftPad, upTopPad, downLeftPad, downTopPad;
    
            public MyTextButton(String text, Skin skin, String styleName) //you can create more constructors just call in them super then init!
            {
                super(text, skin, styleName);
    
                init();
            }
    
            private void init()
            {   
                //see the comments near the method below
                this.createFonts();
    
                //this is important! it will cleared effect of skin up/down settings what is good for me but should not be for you - then remove the following line
                this.clearListeners();
    
                //creating styles basing on current style and fonts we have generated
                upStyle = new LabelStyle( this.getLabel().getStyle() );
                upStyle.font = upFont;
    
                downStyle = new LabelStyle( this.getLabel().getStyle() );
                downStyle.font = downFont;
    
                //setting upStyle for a start - on a start the button is not pushed
                this.getLabel().setStyle(upStyle);
    
                //setting the listener that will change the style due to is button clicked or not right now - see its definition on the bottom of class
                this.addListener(clickListener);
    
                //this is calculating current 
                this.upLeftPad = getLabelCell().getPadLeft();
                this.upTopPad = getLabelCell().getPadTop();
    
                this.downLeftPad = getLabelCell().getPadLeft() + 4; //the value (4) should be changed if you will change shadowOffset of fonts
                this.downTopPad = getLabelCell().getPadTop() + 4; //because we want to make shadow small and move label which make us an illusion of pushing font
    
                //here one note! the funny thing is that imho above value should be shadowOffsetY of upFont - shadowOffsetY of downFont
                //but it is not actually - I mean it is not ok, right bottom corner is not at the same position which is weird so just choose your value
            }
    
            //this method create two fonts - up version and pushed version - the pushed version should have smaller shadow
            private void createFonts()
            {
                //firstly we need a generator (integrated with LibGDX) to generate from .ttf file - so you will need .ttf!
                FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("font.ttf")); //path to yout .ttf file
                FreeTypeFontParameter parameter = new FreeTypeFontParameter();
    
                parameter.size = 42;
    
                parameter.magFilter = TextureFilter.Linear; //it will make
                parameter.minFilter = TextureFilter.Linear; //the font render better
    
                //generating font for nonclicked (up) button version
                parameter.shadowOffsetX = 6; //defining of 
                parameter.shadowOffsetY = 6; //shadow offset ~= size
    
                upFont = generator.generateFont(parameter);
    
                //generating font for nonclicked (up) button version
                parameter.shadowOffsetX = 3; //as above
                parameter.shadowOffsetY = 3; //it should be smaller!
    
                downFont = generator.generateFont(parameter);
    
                //fonts are generated
    
                generator.dispose();
            }
    
            //setting style and label offset for pushed button
            private void setDown()
            {
                getLabel().setStyle(downStyle);
    
                getLabelCell().padTop( downTopPad );
                getLabelCell().padLeft( downLeftPad );
            }
    
            //setting style and label offset for non pushed button
            private void setUp()
            {
                getLabel().setStyle(upStyle);
    
                getLabelCell().padTop( upTopPad );
                getLabelCell().padLeft( upLeftPad );
            }
    
            //the listener that will change style due to what action is actually being performed on the button
            ClickListener clickListener = new ClickListener() 
            {
                @Override
                public boolean touchDown (InputEvent event, float x, float y, int pointer, int button)
                {
                    setDown();
                    return true;
                }
    
                @Override
                public void touchDragged (InputEvent event, float x, float y, int pointer)
                {
                    setUp();
                }
    
                @Override
                public void touchUp (InputEvent event, float x, float y, int pointer, int button)
                {
                    setUp();
                }
            };
    
        }
    

    and you are using it just like:

        MyTextButton start = new MyTextButton("START", game.skin, "button");
        stage.addActor(start);
    

    Notes:

    • You should change the package from first line to yours
    • You can manipulate the MyTextButton settings changing shadow offsets and font offset and you will have to when you will change font size - for now it is 42
    • You can change class name to TextButton and use it as simple TextButton (just import proper one)
    • You are creating normal skin and the properties for up will be kept while properties for down will be ignored (however you can cancel ignoring down by deleting clearListeners() line )
    • The class handle both touchUp and touchDrag situations (so if you click on the button then move mouse it will behave as when touchUp)
    • You can add your own listeners of course to use the button to changing screens for example

    The effect:

    enter image description here