I want to make a button that directly extends Actor. The reason I want to do this is because I don't want to go through the trouble of making a text button that has skins and such. I just want to be able to load my png file on the screen and make it clickable. Anyone have ideas or should I just stick to the Textbutton?
How about an ImageTextButton?
Or you use a Button, and add a Stack (which holds multiple items, that you can place on them self)
I don't recommend you, to do your own button. You'll reinvent the wheel.
I suggest: try ImageTextButton.
If that does not help --> Button + Stack
public class TextureActor extends Actor {
private Texture texture;
public TextureActor(Texture texture) {
this.texture = texture;
}
#Override
public void draw(Batch batch, float parentAlpha) {
batch.draw(texture, getX(), getY(), getWidth(), getHeight());
}
}
You can certainly replace Texture with anything else which can be drawn by the batch. Or use another draw method with more detailed parameters.
Buttons don't actually need a skin, so it would be far easier to just make a factory method to simplify it, rather than code all the logic of a proper button:
public static Button makeButton (TextureRegion upImage, TextureRegion downImage, Actor contentActor) {
Button.ButtonStyle style = new Button.ButtonStyle();
style.up = new TextureRegionDrawable(upImage);
style.down = new TextureRegionDrawable(downImage);
return contentActor == null ? new Button(style) : new Button(contentActor, style);
}
Related
Is there a way to check if my sprite is visible? For instance I have a specific sprite I want to appear only IF this other sprite isn't visible. Example "You Win" will only appear if you win the game and the "You Lose" sprite isn't already on the screen. Thanks!
Visibility of sprite can be maintained by extending Sprite class. Add data member in your extended Sprite Class and at run-time check your sprite is visible of not.
you can also fulfill your requirement by this :
private Sprite win,loose;
private SpriteBatch spriteBatch;
private DialogType dialogType;
#Override
public void create () {
dialogType=DialogType.NONE;
spriteBatch=new SpriteBatch();
win=new Sprite(...);
loose=new Sprite(...);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.begin();
...
...
if(dialogType==WIN){
win.draw(spriteBatch);
}else if(dialogType==LOOSE){
loose.draw(spriteBatch);
}
spriteBatch.end();
}
public enum DialogType {
NONE,WIN,LOOSE
}
Seems like an overwhelm to do what you are trying to do, like I said in the comment with a simple boolean win,loose; you pretty much cover your example, or like other comment said you should not track the game state according with some sprite visibility make a enum and track the game state instead, like the other answer, BUT ima give you a solution
Use Image instead...
Image image = new Image(texture);
image.setBounds(0,0,100,100); // set x,y,width,height
stage.add(image); //add to stage
image.setVisible(false); //make it invisible
image.setVisible(true); //make it visible
image.isVisible(); //check if is visible
I was able to draw my buttons, but nothing happens when I click/"touch" them, and I have no idea what I'm doing wrong. Can anyone please help? Shouldn't the textButtonStyle change to "ToothButton" when it's touched? Am I not supposed to use an InputListener for an Android app?
MainMenuButtons.java
public class MainMenuButtons extends Stage {
Stage buttons;
MMButton startButton, optionButton;
public MainMenuButtons(Viewport viewport) {
startButton = new MMButton(634,550, "Start");
optionButton = new MMButton(634,450, "Options");
buttons = new Stage(viewport);
buttons.addActor(startButton);
buttons.addActor(optionButton);
Gdx.input.setInputProcessor(this);
}
MMButton.java (Main Menu Button)
public class MMButton extends Actor{
TextButton button;
TextButton.TextButtonStyle textButtonStyle;
BitmapFont font;
Skin skin;
TextureAtlas buttonAtlas;
public MMButton(int x, int y, String name) {
font = new BitmapFont();
skin = new Skin();
buttonAtlas = new TextureAtlas(Gdx.files.internal("menuButton.atlas"));
skin.addRegions(buttonAtlas);
textButtonStyle = new TextButton.TextButtonStyle();
textButtonStyle.font = font;
textButtonStyle.up = skin.newDrawable("ToothButtonUp");
textButtonStyle.down = skin.newDrawable("ToothButton");
button = new TextButton(name, textButtonStyle);
button.setBounds(x, y, 246, 90);
button.setTouchable(Touchable.enabled);
button.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
System.out.println("down");
return true;
}
public void touchUp(InputEvent event, float x, float y, int pointer, int button ) {
super.touchUp( event, x, y, pointer, button );
}
});
}
MainMenuScreen.java (I'm sorry for all the code I really just want this problem solved!) :(
OrthoCamera is a class I got online that simplified the use of a camera.
public class MainMenuScreen extends Screen{
private OrthoCamera camera;
MainMenuButtons buttons;
#Override
public void create() {
camera = new OrthoCamera();
buttons = new MainMenuButtons(new ScreenViewport());
}
#Override
public void update() {
camera.update();
buttons.draw();
}
#Override
public void render(SpriteBatch batch) {
batch.setProjectionMatrix(camera.combined);
buttons.draw();
}
#Override
public void resize(int width, int height) {
camera.resize();
buttons.getViewport().update(width,height,true);
}
Your problem is with the MainMenuButtons class. It has two stages. One (buttons) has the button actors added to it, and the other (this) is set as the input processor. That's no good.
There are two solutions.
Either...
Replace this line...
Gdx.input.setInputProcessor(this);
With this...
Gdx.input.setInputProcessor(buttons);
And don't bother extending stage.
Or (if you really want to extend stage)...
Get rid of the buttons variable altogether, and replace these lines...
buttons.addActor(startButton);
buttons.addActor(optionButton);
With this...
addActor(startButton);
addActor(optionButton);
I see a few issues.
You have a stage within a stage. The inner stage has your buttons in it, but it's not set as the input processor. Remove the inner stage and use the outer one directly. Or if you're trying to organize your code here, make your MainMenuButtons class not extend Stage (and give it a getStage() method so the game class can call act and draw on it).
You never call act on your stage, so it won't respond to or act on anything.
Your MMButton class contains an inner Button instance that is never added to the Stage, so the inner Button will never receive touch events, even though it has a listener. You could make the MMButton class extend Group instead of Actor, and add the inner Button to it via addActor(). But I think you are reinventing the wheel here. You don't need an MMButton class when you can use the existing TextButton class directly--it's already an Actor itself. From what I can see, MMButton is a redundant wrapper.
Sidenote: It is inadvisable to use separate texture atlases and fonts for each button. A texture atlas is meant to be shared by many objects to take advantage of sprite batching.
I want to achieve something similar to the following image:
Problem: how can we achieve red coloured, unread counter? am I going to design some psd and then reuse it in the app? but then I have to duplicate alot of .png's for each number (let's say my limit is 99 for that). but that would be redundancy.
What is the best practice to achieve this effect ?
You could create a custom View and override the onDraw() method to draw the numbers. What you would probably want to do is to have an icon fully prepared like above except for the number missing in the red circle. Then, in the custom View, you first draw that icon and then you draw the number (you will have to work a little to figure our the precise position in pixels where to draw it, and how to draw it, i.e. text-size, font, color).
Modulo a method getSomeBitmapFromResources() that imports a bitmap from resources (see e.g. here), your custom View could look something like this:
public class MyView extends View {
//Fields:
private Paint paint; //Need a Paint object for colors, fonts, etc.
private RectF rect;
private int numberToPaint;
//Constructors:
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
//Choose the text properties that work for you here:
paint.setColor(Color.WHITE);
paint.setTypeface(Typeface.create("sans-serif", Typeface.BOLD));
paint.setTextSize(12);
}
public MyView(Context context) {
this(context, null);
}
//Most importantly: override onDraw for rendering of the view:
#Override
protected void onDraw(Canvas canvas) {
rect.set(0, 0, getWidth(), getHeight()); //But: make sure your View
//will have the same size of the Bitmap you use! Set the size in XML!
canvas.drawBitmap(getSomeBitmapFromResources(), null, rect, paint);
//Here you will have to carefully choose the position of the text.
//Also consider that depending on numberToPaint the x-coordinate may have to
//be modified. Likely you want to use the Paint.getTextBounds method determine the size.
canvas.drawText("" + numberToPaint, 60, 30, paint);
}
public void chooseNumberAndDraw(int n) {
numberToPaint = n;
postInvalidate(); //Force redraw
}
}
In XML, you want to add your custom View using a tag like
<com.mysite.myproject.MyView
android:layout_width="64dp"
android:layout_height="64dp"
/>
of course replacing width and height by the actual bitmap dimensions.
Use public TabLayout.Tab setCustomView (int layoutResId)
Create a Layout with TextView and Button use this in Custom view. you may use textView for showing counter.
For reference
setCustomView
Following is the complete Example:
Example
You can also use this library.
For organization's sake, I use multiple scenes for my game and rather than having each scene have a constructor that receives a Viewport (my game is scalable), I would like to set each stage's viewport separate of the constructor, then after the viewport is set, add the actors. In the main class, it would happen like this:
public void setStage(Stage s)
{
if(currentStage != null)
currentStage.dispose();
currentStage = s;
currentStage.setViewport(view);
}
To make this go fluidly, each stage has an init method that is called within an overriden setViewport:
#Override
public void setViewport(Viewport v)
{
super.setViewport(v);
init();
}
However, all this gives me is a black screen... I have tried updating the camera and viewport, but no avail (note that the actors are having their render methods called).
Why am I getting this black screen and how do I fix it? If it's not possible I'll just revert to using the constructor.
If I understood correctly you want to do this:
Stage stage1 = new Stage();
stage1.getViewport().update(width, height);
rather than this:
Stage stage1 = new Stage (new StretchViewport(width, height)); // It doesn't have to be StretchViewport
In the first case (what you are trying to do) a ScalingViewport will be costructed automatically for you with dimensions of the Gdx.graphics and an orthographic camera and acts like a StretchViewport. Why not using the second case directly where you pass the viewport you want. You can always alter your viewport whenever you want by calling stage1.getViewport().update(width, height);
or by calling stage1.setViewport(width, height, false); in older Libgdx versions.
Viewport has changed recently so if you can extend Viewport class to Override the update method maybe you can achieve what you want:
public class ViewportExtendClass extends StretchViewport{
public ViewportExtendClass(float worldWidth, float worldHeight) {
super(worldWidth, worldHeight);
}
#Override
public void update (int screenWidth, int screenHeight, boolean centerCamera) {
super.update(screenWidth, screenHeight, centerCamera);
// DO YOUR INITIALIZATION HERE
}
}
From your main class you create new stage :
Stage stage1 = new Stage (new ViewportExtendClass (width, height));
and then you call :
stage1.getViewport().update(width, height);
Like this you can alter stage viewport and re initialize your assets.
#Override
public void setViewport(Viewport v)
{
super.setViewport(v);
this.getViewport().update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
Camera c = this.getViewport().getCamera();
c.position.set(c.viewportWidth/2, c.viewportHeight/2, 0);
init();
}
This works, but you should also be able to update the Viewport like that at the begin of your application, if you continue to use the same one. I set the position like that instead of centering because some of my Stages will be larger than the screen.
I have to write a simple Java app which can load pictures, show it in a GUI form, allow the user to apply some transformation, and show the transformed picture.
My solution is working fine, but the UI is flickering a bit, because the repaint method called too often (for example when the user scaling the image with a JSlider)
My code looks like this:
public class ImageCanvas extends Canvas
{
private BufferedImage image;
// ...
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if(image != null)
{
// I draw out the image...
}
}
public void setImage(BufferedImage image)
{
this.image = image;
this.repaint();
}
public void setRotation(double rotation)
{
this.rotation = rotation;
this.repaint();
}
public void setScale(double scaleX, double scaleY)
{
//set the scaling field, then repaint ....
}
// and so on...
}
And, of course, I have an ImageCanvas control on my main UI, and I simply call the public methods (see for example the "setRotation" method above) which repaint the canvas area. I know it's a simple question, but I don't even find a DoubleBuffered property on the Canvas...
Any help appreciated.
Double buffering is built-in for Swing (i.e. JComponent derived) classes.
If you want built-in double-buffering, you should extend JPanel rather than Canvas, and override paintComponent, not paint.
If you can use JPanel than go for it. Please make sure you are not overriding the JPanel.paint method, override JPanel.paintComponent instead.
See this link for details.
Usually graphic lags in these applications can be caused by setting a empty variable at the top of the script, then changing its value, then waiting for the repaint to update it. You could try changing the:
setRotation(double rotation);
so that it rotates the image in that method.
Just a general thing I happen to see while dealing with graphics.