I haven't worked with LibGdx in a couple of years, but I remember figuring this out at some point. I'm trying to create a button, and use ShapeRenderer to draw a Rectangle around the edge of the button (to outline it).
So far, I've attempted to create my own custom Actor and use ShapeRenderer in the draw method, but it won't update the position of the rectangle, opacity etc. In other words, it doesn't take advantage of any of the Actor class benefits. Is there any way to draw a line or shape using the Actor classes which will then get updated with applied Actions etc?
Here is an example of what I was doing in my custom Actor class:
public class Shape extends Actor {
ShapeRenderer sr;
float x, y, w, h;
public Shape (float x, float y, float w, float h) {
sr = new ShapeRenderer();
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
batch.end();
sr.begin(ShapeRenderer.ShapeType.Line);
sr.setColor(Color.BLACK);
sr.rect(x, y, w, h);
sr.end();
batch.begin();
}}
Any help would be greatly appreciated; thanks in advance!
EDIT 4/21 2:39PM EST
Made recommended changes, shape still doesn't move / rotate with actions. Any other suggestions?
public class Shape extends Actor {
ShapeRenderer sr;
public Shape (ShapeRenderer sr, float x, float y, float w, float h) {
this.sr = sr;
setX(x);
setY(y);
setWidth(w);
setHeight(h);
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
batch.end();
sr.begin(ShapeRenderer.ShapeType.Line);
sr.setColor(Color.BLACK);
sr.rect(getX(), getY(), getWidth(), getHeight());
sr.end();
batch.begin();
}}
EDIT 4/21 2:46PM EST
Here's how I'm implementing my custom actor, I add it to a parent group and set actions on the group...
//Generate buttons
ImageTextButton.ImageTextButtonStyle style = new ImageTextButton.ImageTextButtonStyle();
parameter.size = game.labelButtonSize;
style.font = generator.generateFont(parameter);
style.fontColor = Color.BLACK;
buttonYes = new ImageTextButton(Consts.CAMERA_STRING_BUTTON_YES, style);
buttonYes.align(Align.left);
buttonYes.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
Gdx.app.log("TAG", "Clicked Yes");
}
});
Group bY = new Group();
bY.addActor(buttonYes);
bY.addActor(new Shape(sr, buttonYes.getX(), buttonYes.getY(), buttonYes.getWidth(), buttonYes.getHeight()));
bY.addAction(Actions.rotateTo(90, 2));
Actor already has fields for x, y, width, height, and color. You have to use the getter methods to access them. By using your own, you're ignoring the built-in functionality.
Also, you probably want to have only one ShapeRenderer in the whole app because each ShapeRenderer instance has a big vertex data array and compiles a shader.
Note that you create a lot of extra draw calls because you keep having to flush the Batch and the ShapeRenderer for each actor that does this. If your performance begins to suffer, you may want to replace this rectangle with a NinePatch, which you can draw with the Batch.
Alternatively, if you are not using sprites at all, you might consider overriding drawDebug instead of draw so you can use SpriteBatch's built in ShapeRenderer and avoid all the extra flushing.
I have a similar scenario. What I noticed is that I had to take the TransformMatrix from the Batch and apply it to the ShapeRenderer. See code below.
#Override
public void draw(Batch batch, float parentAlpha) {
batch.end();
shapeRenderer.setTransformMatrix(batch.getTransformMatrix());
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
Color color = getColor();
shapeRenderer.setColor(color.r, color.g, color.b, color.a * parentAlpha);
shapeRenderer.rect(getX(), getY(), getWidth(), getHeight());
shapeRenderer.end();
batch.begin();
}
Related
I'm building a mobile Box2D body/joint editor in libGDX, but I'm getting inconsistent results between my desktop and android builds, the android build producing incorrect position Y-values. In summary, the editor consists of a HUD with its own FitViewport for consistent screen presence, and the actual build area grid with its own FitViewport that can be navigated and zoomed, much like any run-of-the-mill image editor. The user touches the screen, and a target appears above that position that can be dragged around to select points on the grid, connecting points that serve as the vertices of the shapes that will become Box2D bodies. This target-selector exists on the HUD viewport so that it can remain a static size if the user were to zoom in on the grid area. So, in order to select a point on this grid, I have to convert the target's position in the HUD viewport to screen coordinates, then convert it to that point on the build area grid. Next to the target, I have a BitmapFont that renders text of the targets current position.
So here's an example of my problem. Let's say the target is at the arbitrary point (0,0) on the grid. The text would say "0,0". In my desktop build it does this accurately. In my Android build, the Y-value is offset by varying values, based on camera zoom, even though that point is without a doubt (0, 0) in that space(The bottom-left of the grid is coded at 0,0). I don't understand how this could happen, since both builds share the core module where this code resides. I knew this would be hard to explain, so I took some screen shots.
These images show the desktop build, no issues, correct values:
desktop app image, default zoom, y-value is correct
desktop app image, camera zoomed in, y-value is still correct
These images show the android build, y-value is incorrect but becomes more correct as the camera zooms in:
android app image, default zoom, y-value is way off
android app image, zoomed in, y-value is closer to correct but still off
I really want to show some code, but I have no idea which parts are relevant to this issue. Essentially what is happening is that the user touches the screen, the target pops up 200px (HUD stage local) above that point, the targets position is converted to screen coordinates, then unprojected to build area grid coordinates. What gets me is that the Y-values are correct in the desktop build, so why would they be different in the android build. Here is my TargetSelector class where everything goes down:
public class TargetSelector extends Group {
private ShapeRenderer shapeRenderer;
private BitmapFont font;
private Vector2 position;
private float yOffset;
private boolean targetActive;
private Vector2 unprojectedPosition;
private OrthographicCamera linkedCamera;
private boolean hasLink = false;
public TargetSelector(){
initShapeRenderer();
initTargetFields();
initFont();
initInputListener();
}
private void initShapeRenderer(){
shapeRenderer = new ShapeRenderer();
shapeRenderer.setAutoShapeType(true);
shapeRenderer.setColor(Color.RED);
}
private void initTargetFields(){
unprojectedPosition = new Vector2();
position = new Vector2();
yOffset = 200;
targetActive = false;
}
private void initFont(){
font = new BitmapFont();
font.getData().scale(1.0f);
font.setColor(Color.WHITE);
}
private void initInputListener(){
setBounds(0, 0, Info.SCREEN_WIDTH, Info.SCREEN_HEIGHT);
addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
targetActive = true;
position.set(x, y).add(0, yOffset);
return true;
}
#Override
public void touchDragged(InputEvent event, float x, float y, int pointer) {
position.set(x, y).add(0, yOffset);
}
#Override
public void touchUp(InputEvent event, float x, float y, int pointer, int button) {
targetActive = false;
}
#Override
public void exit(InputEvent event, float x, float y, int pointer, Actor toActor) {
targetActive = false;
}
});
}
#Override
public void act(float delta) {
super.act(delta);
if(targetActive && hasLink){
updateUnprojectedPosition();
}
}
#Override
public void draw(Batch batch, float parentAlpha) {
if(targetActive){
batch.end();
Gdx.gl.glEnable(GL20.GL_BLEND);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
shapeRenderer.setProjectionMatrix(getStage().getCamera().combined);
shapeRenderer.begin();
// Render target
Gdx.gl20.glLineWidth(3);
shapeRenderer.set(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(Color.RED);
shapeRenderer.circle(position.x, position.y, 32);
shapeRenderer.circle(position.x, position.y, 1);
// Render position text background
Vector2 fontPosition = position.cpy().add(64, 64);
shapeRenderer.set(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(0, 0, 0, 0.5f);
shapeRenderer.rect(fontPosition.x - 8, fontPosition.y - 32, 172, 40);
shapeRenderer.end();
Gdx.gl.glDisable(GL20.GL_BLEND);
Gdx.gl20.glLineWidth(1);
batch.setProjectionMatrix(getStage().getCamera().combined);
batch.begin();
// Render position text
String text;
if(hasLink)
text = "(" + (int) unprojectedPosition.x + " , " + (int) unprojectedPosition.y + ")";
else
text = "No Link";
font.draw(batch, text, fontPosition.x, fontPosition.y, 100, 10, false);
}
super.draw(batch, parentAlpha);
}
public void updateUnprojectedPosition(){
Vector2 screenPosV2 = localToScreenCoordinates(position.cpy());
Vector3 posV3 = new Vector3(screenPosV2.x, screenPosV2.y, 0);
linkedCamera.unproject(posV3);
unprojectedPosition.set(MathUtils.round(posV3.x), MathUtils.round(posV3.y));
}
public void linkToCamera(OrthographicCamera camera){
if(camera != null){
linkedCamera = camera;
hasLink = true;
}
}
}
The linkedCamera is assigned in a manager class that handles the relationship between all HUD actions and the build area grid.
This is my first time ever asking a question here, I'm sorry if I'm being too verbose but the issue is complicated to explain, at least it was for me.
My goal is to create a game that is always displayed with an aspect ratio of 9:16 (basically 16:9, but upright) using FitViewport; it should be independet of a target device's resolution. In order to test this setup, I created the following minimal working example. A small green square indicates the origin of the coordinate system:
MyGame.java
public class MyGame extends ApplicationAdapter {
final int WORLD_WIDTH = 900;
final int WORLD_HEIGHT = 1600;
Stage stage;
Viewport vp;
public void create() {
stage = new Stage();
vp = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT, stage.getCamera());
stage.setViewport(vp);
stage.addActor(new MySquare());
}
public void render() {
stage.act();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.draw();
}
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
// dispose...
}
MySquare.java
public class MySquare extends Actor {
ShapeRenderer renderer = new ShapeRenderer();
#Override
public void draw(Batch batch, float alpha){
batch.end();
renderer.begin(ShapeRenderer.ShapeType.Filled);
renderer.setColor(Color.GREEN);
renderer.rect(0, 0, 50, 50);
renderer.end();
batch.begin();
}
}
Unfortunately, the result is not as expected: As you can see, the green square is actually not a square. This behavior is the same for both Windows and Android (in landscape mode):
However, when setting the size of the window programmatically and explicitly via LwjglApplicationConfiguration in DesktopLauncher.java to a valid 9:16 resolution, the green square is displayed correctly. Why is that and how can I avoid this workaround (which does not work for Android anyway)?
Your problem is that your shape renderer is ignoring the camera. Update it like this:
public void draw(Batch batch, float alpha){
batch.end();
renderer.setProjectionMatrix(batch.getProjectionMatrix()); // <<<<< Add this
renderer.begin(ShapeRenderer.ShapeType.Filled);
renderer.setColor(Color.GREEN);
renderer.rect(0, 0, 50, 50);
renderer.end();
batch.begin();
}
If you are planning to eventually use sprites, and you're simply wanting rectangle placeholders for your actors, you don't need a custom actor for this. You can use a generic Actor and call setDebug(true) on it, and Stage will automatically draw its outline using an internal ShapeRenderer. Of course, you must first set a size and position on the Actor.
I'm currently developing a game where you have to avoid Asteroids. To make the Game look same on every device I use the FitViewport. Unfortunately I somehow get White Bars on the top and on the Bottom instead of Black ones. My Game Background is also white, so it looks a bit weird.
GameScreen:
#Override
public void create()
{
float aspectRatio = (float)Gdx.graphics.getWidth() / (float)Gdx.graphics.getHeight();
cam = new OrthographicCamera();
viewport = new FitViewport(MyGdxGame.WIDTH * aspectRatio, MyGdxGame.HEIGHT, cam);
[...]
}
#Override
public void render(SpriteBatch batch)
{
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
em.render(batch); //render Ship and Asteroids
[...]
}
#Override
public void resize(int width, int height)
{
viewport.update(width, height);
cam.position.set(MyGdxGame.WIDTH / 2, MyGdxGame.HEIGHT /2, 0);
}
I dragged the Ship into the white Bar.
LibGDX provides viewports as a more convenient way of dealing with different aspect ratios. You don't have to multiply MyGdxGame.WIDTH with aspectRatio. Just initialize it with MyGdxGame.WIDTH and MyGdxGame.HEIGHT.
Also, in resize function, you can change the cam position using viewport values (instead of using constants):
cam.position.set(cam.viewportWidth / 2, cam.viewportHeight / 2, 0);
I found some issues in your code. for best practice while handling with the different screen ratio just try with the fill viewPort. Here is simply editing your code with the fill viewport . Just try it once.
#Override
public void create()
{
float aspectRatio = (float)Gdx.graphics.getWidth() / (float)Gdx.graphics.getHeight();
camera = new OrthographicCamera();
camera.position.set(0, 0, 0);
camera.update();
//1280 is the screen width and 800 is screen height
camera.setToOrtho(false, 1280, 800);
viewPort = new FillViewport(1280, 800, camera);
}
#Override
public void render(SpriteBatch batch)
{
batch.setProjectionMatrix(camera.combined);
batch.begin();
em.render(batch); //render Ship and Asteroids
[...]
}
#Override
public void resize(int width, int height)
{
viewPort.update(width, height);
}
just try with the above code . it will definitely work
I'm trying to make a Minesweeper game in android to practise using Canvas to draw images.
I'm currently using drawBitmap(image,x,y,antialiasPaint) but, when I resize the board and the tiles become so small, this occurs:
Image
You can see the black lines that appears sometimes between the tiles...
I'm drawing each Tile object using this code.
/**
* #param canvas Canvas directly from View's #Override draw(Canvas canvas)
* #param parentX Parent X position
* #param parentY Parent Y position
*/
public void draw(Canvas canvas, float parentX, float parentY){
canvas.drawBitmap(base,parentX+x,parentY+y, paint);
}
Using a bufferedBitmap like this solves that problem, but it slows a lot the performance rathen than boosting it...
Maybe I'm not doing it well? I'm doing it like this:
private Canvas c = new Canvas();
private Bitmap bufferedImage;
public void zoom(float z){
this.scale *= z;
this.scale = Math.max(0.3f, Math.min(scale, 1f));
this.vWidthScaled = vWidth/scale;
this.vHeightScaled = vHeight/scale;
bufferedImage = Bitmap.createBitmap((int)vWidthScaled, (int)vHeightScaled, Bitmap.Config.ARGB_8888);
}
#Override
public void draw(Canvas canvas){
c.setBitmap(bufferedImage);
grid.draw(c, vWidthScaled, vHeightScaled);
canvas.drawColor(TileStyle.getInstance().getBackgroundColor());
canvas.scale(scale, scale);
canvas.drawBitmap(bufferedImage,0,0,null);
canvas.scale(1/scale, 1/scale);
}
Is there any other solution I can use to solve this?
When I tried draw a Texture using SpriteBatch it was result flip like this:
Here what I do:
I create MyRect object which draw bounding rectangle and an image.
Here MyRect class preview:
public class MyRect {
private Vector2 position;
private int width;
private float height;
private Texture img;
private Sprite sprite;
public MyRect(int x, int y, int width, int height){
img = new Texture("badlogic.jpg");
sprite = new Sprite(img);
position = new Vector2(x,y);
this.width = width;
this.height = height;
}
public void draw(ShapeRenderer shapeRenderer, SpriteBatch batch){
// Gdx.gl.glClearColor(0, 0, 0, 1);
// Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
shapeRenderer.begin(ShapeType.Line);
// Draw Background color
shapeRenderer.setColor(55 / 255.0f, 80 / 255.0f, 100 / 255.0f, 1);
shapeRenderer.rect(position.x, position.y, width, height);
shapeRenderer.end();
batch.begin();
// batch.disableBlending();
batch.draw(img, position.x, position.y,
width, height);
batch.end();
}
}
Parameters ShapeRenderer and SpriteBatch pass by GameScreen class.
GameScreen preview:
public class GameScreen implements Screen{
private MyRect myRect;
private ShapeRenderer shapeRenderer;
private SpriteBatch batch;
private OrthographicCamera cam;
public GameScreen(){
myRect = new MyRect(10,10,50,50);
int gameHeight=100;
cam = new OrthographicCamera();
cam.setToOrtho(true, 136, gameHeight);
batch = new SpriteBatch();
batch.setProjectionMatrix(cam.combined);
shapeRenderer = new ShapeRenderer();
shapeRenderer.setProjectionMatrix(cam.combined);
}
#Override
public void render(float delta) {
// TODO Auto-generated method stub
myRect.draw(shapeRenderer, batch);
}
}
Why is this happen? Am I doing it wrong?
Your camera is upside down because you called setToOrtho(true, ...) on it instead of setToOrtho(false, ...)
It is valid to use an upside-down camera (which might be more comfortable if you've used Flash or some other Y-down system before), but then you need to flip all your TextureRegions (aka Sprites): sprite.flip(false, true). Alternatively, you can create a TextureAtlas using TexturePacker (look it up in libgdx documentation) and set the flipY option, so it flips them ahead of time for you. Eventually, for performance you will need to use a TextureAtlas anyway.
By the way, when you start drawing multiple instances of MyRect, you are going to need to move the spriteBatch.begin() and end() and shapeRenderer.begin() and end() out of the individual MyRect's draw method or you will run into a performance problem. And as such, there will need to be two draw methods (one for sprite batch and one for shape renderer).
apparently, the only thing I see different than how I would do, is try to change:
this meybe if it works"for test"
cam.setToOrtho(false, 136, gameHeight);
this is how I use the camera.
I do not know whether to use true, you have to do some different way, to draw the batch, in anyway. if the camera false, it looks good, I think it has to flip the image before to draw uv