LibGDX drawing circles scales somehow - java

I try to draw 2 circles using textures in Java with LibGDX.
My classes are pretty straightforward, GameObject has a circle (for logic) and a draw method.
Here is my draw method:
#Override
public void draw(Batch batch, float alpha){
batch.draw(sprite, circle.x-circle.radius, circle.y-circle.radius, circle.x+circle.radius, circle.y+circle.radius);
}
This is the code I execute:
GameObject go = new GameObjectBuilder().createNew()
.withPosition(32, 32)
.withRadius(32)
.withTexture(new Texture(Gdx.files.internal("ball.png")))
.withId(0)
.build();
GameObject go2 = new GameObjectBuilder().createNew()
.withPosition(64, 64)
.withRadius(32)
.withTexture(new Texture(Gdx.files.internal("ball.png")))
.withId(1)
.build();
stage.addActor(go);
stage.addActor(go2);
And this is the result:
Somehow using circles with the same size, scales them when placed further away from the origin. Currently my main class extends ApplicationAdapter and that's it.
Here is the stack of calls I make in my builder.
circle.setPosition(x, y); // LibGDX.Circle class call
circle.setRadius(radius); // LibGDX.Circle class call
sprite = new Sprite(texture);
Using a ShapeRenderer, calling the same objects and drawing a filled circle yields these results:
shapeRenderer.setColor(Color.BLACK);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
for(GameObject goo : objects.values()) {
shapeRenderer.circle(goo.getX(), goo.getY(), goo.getRadius());
}
shapeRenderer.end();
Final edit:
batch.draw(sprite, circle.x-circle.radius, circle.y-circle.radius, circle.radius*2, circle.radius*2);
Did the trick!

Been a while since I used libGDX and I can tell some things have changed. However...
void draw(TextureRegion region,
float x,
float y,
float width,
float height)
Draws a rectangle with the bottom left corner at x,y and stretching the region to cover the given width and height.
Batch.draw draws a rectangle from x,y to x + width, y + height. What you have done is to scale the shape based on the objects internal position.
#Override
public void draw(Batch batch, float alpha){
batch.draw(sprite, circle.x-circle.radius, circle.y-circle.radius, circle.x+circle.radius, circle.y+circle.radius);
}
for circle 1 we have
batch.draw(sprite, 32-32, 32-32, 32+32, 32+32);
This draws the sprite from 0,0 to 64,64
for circle 2 we have
batch.draw(sprite, 64-32, 64-32, 64+64, 64+64);
This draws the sprite from 32,32 to 128,128
This should fix your code:
#Override
public void draw(Batch batch, float alpha){
batch.draw(sprite, circle.x, circle.y, circle.radius, circle.radius);
}
This will scale the objects base on the circle's radius.
Reference: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/Batch.html#draw-com.badlogic.gdx.graphics.Texture-float-float-float-float-

Related

Libgdx set ortho camera

I'm having problems setting my orthographic camera to the bottom left part of my screen (0,0)
public GameScreen(Main game) {
this.game = game;
Width = 200;
Height = 300;
view = new ExtendViewport(Width,Height);
camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
camera.setToOrtho(false,Width/2,Height/2);
camera.position.set(Width,Height,0);
camera.update();
play.Player1();
staple = new Stage();
staple.addActor(play);
staple.addActor(pile);
Gdx.input.setInputProcessor(staple);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.graphics.getDeltaTime();
game.getBatch().begin();
game.getBatch().setProjectionMatrix(camera.combined);
game.getBatch().end();
staple.act();
staple.draw();
}
#Override
public void resize(int width, int height) {
view.update(width,height);
view.setScreenPosition(width,height);
}
I've set my viewport as extended viewport using my width and height values I have assigned but I'm struggling to move the camera to the bottom left
part of my screen (0,0) where it can focus on my images on my android device.
Here are a little example how to use camera and viewport:
First we must define how big is our world the camera shows:
private static final int WORLD_WIDTH = 300;
private static final int WORLD_HEIGHT = 250;
Our world is now 300 x 250 units (not Pixel!) big.
It's importent to think in units not in pixels!!
Now we need a OrthographicCamera, a Viewport and a SpriteBatch
OrthographicCamera camera;
Viewport viewport;
SpriteBatch batch;
#Override
public void create () {
camera = new OrthographicCamera(); // we create a OrthographicCamera
viewport = new ExtendViewport(WORLD_WIDTH, WORLD_HEIGHT, camera); // we create a new Viewport with our camera and we will display our world 300 x 250 units
batch = new SpriteBatch(); // we create a new SpriteBatch for draw our textures
}
In our render method we say the batch only to draw what we can see in our Viewport with the method setProjectionMatrix()
#Override
public void render (float delta) {
camera.update(); //update our camera every frame
batch.setProjectionMatrix(camera.combined); //say the batch to only draw what we see in our camera
batch.begin();
batch.draw(texture, 0,0); //draw our texture on point 0,0 bottom left corner
batch.end();
}
And in the resize method:
public void resize(int width, int height){
viewport.update(width, height); //update the viewport to recalculate
}
To understand why you have this issue:
In your code you never set the camera to the viewport: view = new ExtendViewport(Width,Height);
So your viewport never apply to the batch.
To render the correct way without Viewport you must know that the position of OrhographicCamera is in the center.
So when you set a Camera to position 0,0 and size 50,50 you see the world from -25 to 25 in each direction;
To use OrthographicCamera without Viewport:
public void create () {
camera = new OrthographicCamera(WORLD_WIDTH, WORLD_HEIGHT); // we create a OrthographicCamera and we will display our world 300 x 250 units
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0); //we set position of camera, our world point 0,0 is now the bottom left corner in the camera
batch = new SpriteBatch(); // we create a new SpriteBatch for draw our textures
texture = new Texture("badlogic.jpg");
}
public void render () {
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(texture, 0,0);
batch.end();
}
The important point is in the resize method:
public void resize(int width, int height){
camera.viewportWidth = WORLD_WIDTH;
camera.viewportHeight = WORLD_HEIGHT * height / width;
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
}
With this calculation you always see a World of 300 width and 250 * ratio of width, and height.
And exactly this calculation does the viewport for you. Depending on which Vieport (FitViewport, ScreenViewport, ExtendViewport) you use this calculation will be different, try it out.
I hope this helps you to understand how camera, viewport and Spritebatch works together.
Here are useful links to the libgdx wiki which descript the Viewport and Camera:
https://github.com/libgdx/libgdx/wiki/Viewports
https://github.com/libgdx/libgdx/wiki/Orthographic-camera
Use camera.position.set(Width, Height, 1); instead of 0
First you set the Camara width and height equal to the amount of pixels of the window.
camera = new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
Then you center it at 100, 150. And move it again to 200, 300
camera.setToOrtho(false,Width/2,Height/2);
camera.position.set(Width,Height,0);
You are also using a Viewport but never make use of it.
I would recommend just using a Viewport of choice. A Viewport can take a camera so if you insist using your own camera you can create it but then also pass it to the Viewport when you construct it.
EDIT
Following is a tested minimal example.
public class TestScreen extends ScreenAdapter {
private Viewport viewport;
private ShapeRenderer sr;
public TestScreen() {
// Note that extend viewport extends it's camera so you end up with smaller or larger view of your world depending on the aspect ratio of the physical screen.
viewport = new ExtendViewport(200, 300);
viewport.apply();
System.out.println(viewport.getWorldWidth());
// Just for testing in the resize method.
viewport.getCamera().translate(0, 0, 0);
viewport.getCamera().update();
// ShapeRenderer for testing
sr = new ShapeRenderer();
sr.setAutoShapeType(true);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(.04f, .06f, .1f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Draw circle at 0.0 in the world
sr.setProjectionMatrix(viewport.getCamera().combined);
sr.begin(ShapeRenderer.ShapeType.Line);
sr.circle(0, 0, 100);
sr.end();
}
#Override
public void resize(int width, int height) {
// Will center the camera in the world at half it's width and half it's height so left bottom is 0.0 as long as the camera did.
// By using true here you cannot move the camera since you order it to center on the screen and thus the circle we are drawing
// remains in the bottom left.
//viewport.update(width, height, true);
// This will just update the viewport, we moved the camera slightly to the left so the circle appears slight right from the middle.
viewport.update(width, height, false);
// So you want to start your camera centered on something but still want to move it you need to specify that center in the camera
// by either changing it's position or translating it like I did in the constructor. Unfortunately you only get to know the size
// of the world that is being displayed once this resize method did it's job so certain parts might get cut off or your world does
// not fill the screen.
}
}

Libgdx: Drawing lots of particles

What is the best way to draw lots of particles (circles) moving in the background in LibGDX?
200 particles running in the background is what I can get out of my app. Anything above will get my app to stutter. Ive actually tested an App where it's possible to run up to 200.000 particles in the background without having to sacrifice fps. This is my Game class in short:
public Array<Particles> particlesArray;
SpriteBatch batch;
OrthographicCamera camera;
Texture sParticlesTexture;
public void create(){
camera = new OrthographicCamera();
camera.setToOrtho(false, 1080, 1920);
batch = new SpriteBatch;
Pixmap pixmap = new Pixmap(Particles.MAX_PARTICLE_RADIUS*2, Particles.MAX_PARTICLE_RADIUS*2, Pixmap.Format.RGBA4444);
pixmap.setColor(Color.WHITE);
pixmap.fillCircle(pixmap.getWidth() / 2, pixmap.getHeight() / 2, Particles.MAX_PARTICLE_RADIUS);
sParticlesTexture = new Texture(pixmap);
pixmap.dispose();
size = random(2, Particles.MAX_PARTICLE_RADIUS+1);
for(int i=0; i<200; i++){
particlesArray.add(new Particles(random(size, width-size),
random(0, height),
0,
random(0.15f*height, 0.25f*height)*0.15f*size,
size));
}
public void render(float deltaTime){
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//update camera and draw in camera
camera.update();
batch.setProjectionMatrix(camera.combined);
batch.begin();
drawFallingObjects(particlesArray, batch);
batch.end()
moveParticles(particlesArray, deltaTime);
}
public <T extends Objects> void drawFallingObjects(Array<T> objects, SpriteBatch batch){
for(T item: objects){
item.draw(batch);
}
}
public void moveParticles(Array<Particles> particlesArray, float deltaTime){
for(Particles item: particlesArray){
size = random(2, Particles.MAX_PARTICLE_RADIUS+1);
item.move(deltaTime);
//creating particles if out of scale
if(item.y+item.mDiameter<0){
item.x = random(size, width-size);
item.y = height+20;
item.vy = random(0.15f*height, 0.25f*height)*0.15f*size;
item.mDiameter = size;
}
}
}
And this my Particles class:
import com....sParticlesTexture;
public class Particles{
public static int MAX_PARTICLE_RADIUS = 4;
public Particles(float x, float y, float vx, float vy, float mDiameter){
super(x, y, vx, vy, mDiameter);
radius = mDiameter/2;
}
#Override
public void draw(SpriteBatch batch){
batch.draw(sParticlesTexture, x-radius, y-radius, mDiameter, mDiameter);
}
#Override
public void move(float deltaTime){
y -= ceil(vy*deltaTime);
x += ceil(vx*deltaTime);
}
public void dispose() {
sParticlesTexture.dispose();
}
All Particles objects use one and the same texture. This improves a lot instead of creating hundred different textures. So what can be done now? I've googled a lot. What would help in my case? A Framebuffer, shader? And how should I implement these in my game? What about CPUSpriteBatch?
I also came across the particle system from LibGDX but it doesn't work differently than what I do.
First of all have a look at Particle effect which is much more efficient. https://github.com/libgdx/libgdx/wiki/2D-ParticleEffects
If you are not trying to get that kind of effect and want to use a lot of particles, you may not want to perform such large number of calculations in the Java. Rather use NDK and calculate the values from C/C++.
As Nabin said, libgdx has a particle system in place already which is already tuned to be efficient. Libgdx also has a tool called the 2D Particle editor which allows you to view and edit particles before you add them to your application. A guide on the Editor can be found on the libgdx site and gamedevelopment.blog.
From the code samples you provided, I think you could also possibly use a shader to create the same effect. The bonus to this is its all done on the GPU. Some example shaders can be found on Shadertoy and guide on shaders from GamesFromScratch or GLSL Shader Tutorial for Libgdx

libgdx drawing an image at touch location

I'm just trying to get libgdx to create a picture wherever I touch the screen.
here's what i have that isn't doing anything
SpriteBatch batch;
Texture img;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
public class MyInputProcessor implements InputProcessor {
public boolean touchDown (int x, int y, int pointer, int button) {
batch.begin();
batch.draw(img,Gdx.input.getX(),Gdx.input.getY());
batch.end();
return true;
}
... (the rest of the input methods)
if you can't tell, I don't really know what I'm doing yet, I think it has to do with the batch.draw() being in the touchDown method instead of the render area but I can't figure out from research how to do it a different way either
or maybe this is all wrong too, point is I'm doing this to learn so hopefully the correct answer will help me understand some important things about java in general
LibGDX, like basically all game engines, re-renders the entire scene every time render() is called. render() is called repeatedly at the frame rate of the game (typically 60fps if you don't have a complex and unoptimized game). The first drawing-related thing you usually do in the render() method is to clear the screen, which you have already done with Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);. Then you re-draw the whole scene with whatever changes there might be since the last frame.
You are trying to draw something with the batch outside of the render method. In this case, you are doing it when there is a touch down. But since you are doing this only when there is a touch down, the object will appear and disappear on the next call to render(), so it will only be on screen for 1/60th of a second. If you want to do this with an input processor, you need to set some variable to true to indicate the render method should draw it, and other variables to indicate where to draw it. Then in the render() method, you draw the stuff if the variable is true.
Secondly, the x and y that an input processor gets do not necessarily (and usually don't) correspond with the x and y in OpenGL. This is because OpenGL has it's own coordinate system that is not necessarily sized exactly the same as the screen's coordinate system. The screen has (0,0) in the top left with the Y axis going down, and the width and height of the screen matching the number of actual pixels on the screen. OpenGL has (0,0) in the center of the screen with the Y axis going up, and the width and height of the screen being 2 regardless of the actual screen pixels.
But the OpenGL coordinate system is modified with projection matrices. The LibGDX camera classes make this simpler. For 2D drawing, you need an OrthographicCamera. You set the width and size of the OpenGL world using the camera, and can also position the camera. Then you pass the camera's calculated matrices to the SpriteBatch for it to position the scene in OpenGL space.
So to get an input coordinate into your scene's coordinates, you need to use that camera to convert the coordinates.
Finally, LibGDX cannot magically know that it should pass input commands to any old input processor you have created. You have to tell it which InputProcessor it should use with a call to Gdx.input.setInputProcessor().
So to fix up your class:
SpriteBatch batch;
Texture img;
boolean isTouchDown;
final Vector3 touchPosition = new Vector3();
OrthographicCamera camera;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
camera = new OrthographicCamera();
Gdx.input.setInputProcessor(new MyInputProcessor()); // Tell LibGDX what to pass input to
}
#Override
void resize (int width, int height) {
// Set the camera's size in relation to screen or window size
// In a real game you would do something more sophisticated or
// use a Viewport class to manage the camera's size to make your
// game resolution-independent.
camera.viewportWidth = width;
camera.viewportHeight = height;
camera.update(); // re-calculate the camera's matrices
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined); // pass camera's matrices to batch
batch.begin();
if (isTouchDown) { // Only draw this while the screen is touched.
batch.draw(img, touchPosition.x, touchPosition.y);
}
batch.end();
}
public class MyInputProcessor implements InputProcessor {
public boolean touchDown (int x, int y, int pointer, int button) {
isTouchDown = true;
touchPosition.set(x, y, 0); // Put screen touch coordinates into vector
camera.unproject(touchPosition); // Convert the screen coordinates to world coordinates
return true;
}
public boolean touchUp (int screenX, int screenY, int pointer, int button){
isTouchDown = false;
return true;
}
//... (the rest of the input methods)
}

How to get a region of rotated Actor in libgdx

In a small game I am developing using libgdx, I have spears as game objects.
I implemented them using Scene2D, as a subclass of Actor. The spears can be rotated by multitudes of 90° . I want to have just the "speartip" part of my image as a part that "hurts" the Player, the shaft shall be harmless. So when I construct my Spear object, I also construct 2 rectangles that cover the tip and the shaft of the spear. But when my actor gets rotated using setRotation() , the rectangles obviously don't, cause they aren't "attached" to the spear object.
Do you have some suggestions on how to handle this kind of stuff? Code below.
public TrapEntity(Texture texture, float pos_x, float pos_y, float width, float height, Vector2 center,
float rotation, Rectangle hurtZone, Rectangle specialZone) {
super(texture, pos_x, pos_y, width, height, center, rotation);
this.hurtZone = new Rectangle(hurtZone.getX(), hurtZone.getY(), hurtZone.getWidth(), hurtZone.getHeight());
this.specialZone = new Rectangle(specialZone.getX(), specialZone.getY(), specialZone.getWidth(), specialZone.getHeight());
}
And the render method in the same class. I use it to render the bounds of the "hurtZone" rectangle:
#Override
public void draw(Batch batch, float alpha){
super.draw(batch, alpha);
batch.end();
sr.setProjectionMatrix(batch.getProjectionMatrix());
sr.setTransformMatrix(batch.getTransformMatrix());
sr.begin(ShapeRenderer.ShapeType.Line);
sr.setColor(Color.RED);
sr.rect(hurtZone.getX(), hurtZone.getY(), hurtZone.getWidth(), hurtZone.getHeight());
sr.end();
batch.begin();
}
Rectangles can't be rotated, I had to do a similar thing and struggled with finding a solution. The best solution I have found yet is to use a Polygon. You would do something like this;
//Polygon for a rect is set by vertices in this order
Polygon polygon = new Polygon(new float[]{0, 0, width, 0, width, height, 0, height});
//properties to update each frame
polygon.setPosition(item.getX(), item.getY());
polygon.setOrigin(item.getOriginX(), item.getOriginY());
polygon.setRotation(item.getRotation());
Then, to check if a point is within the rotated Polygon use;
polygon.contains(x, y);

Achieve parallax effect in libGDX game

I'm working in a game with some friends in which we have a large horizontal world and a OrthographicCamera that shows only 1/3 of it. This camera it's moved when the horizontal position of the player change so the camera only move to the left and to the right.
Some of the objects showed in the game are near the player point-of-view but others are far away (for example, islands). With this in consideration, we cannot set fixed positions for elements and move only the camera. We need to achieve a parallax effect taking in consideration the distance of the elements.
Here is a simple image to explain it better:
The viewport to the left shows 3 objects of the game. The green one is near the player, the red ellipse is far and the yellow one is in the middle. In the viewport to the right the camera has been moved to the right so all the objects disappear to the left. The thing is that the relative movement of the green rectangle is greater than the movement of the yellow. In the same way, movement of yellow object is greater than red object movement.
I created all my assets scaled taking in consideration how far they are but now, how can I simulate this perspective using libGDX? Is there any class to do it? If I have to set elements position in each iteration, how could I calculate the right position?
Note that the example below is not tested as I am just recalling how I did it. The idea is simple - create layers with an extra layer for each with initial positions and velocity and move them. If a layer goes off the edge, put another one (that is why we create an extra layer) at the opposite edge.
Say you have a parallax object that takes initial positions, size, and velocity-
public class Parallax extends DynamicGameObject {
public float width, height; // Use setter/getter if you prefer
public Parallax(float x, float y, float width, float height, float velocityX, float velocityY) {
super(x, y, width, height);
velocity.set(velocityX, velocityY);
this.width = width;
this.height = height;
}
public void update(float deltaTime) {
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
}
public void setPosition(float x, float y) {
position.set(x, y);
}
}
DynamicGameObject is taken from SuperJumper demo-
public class DynamicGameObject extends GameObject {
public final Vector2 velocity;
public final Vector2 accel;
public DynamicGameObject(float x, float y, float width, float height) {
super(x, y, width, height);
velocity = new Vector2();
accel = new Vector2();
}
}
GameObject as well-
public class GameObject {
public final Vector2 position;
public final Rectangle bounds;
public GameObject(float x, float y, float width, float height) {
this.position = new Vector2(x,y);
this.bounds = new Rectangle(x - width/2f, y - height/2f, width, height);
}
}
Say we have two layers - one in front and the other goes at back. We have one texture for each. Each texture fills the entire screen. We create two instances for each layer so that when one texture starts going off the screen, the other shows up at the edge to fill the gap. If you have smaller textures, you need to determine first how many textures you need to fill the screen and then create layers with one extra to fill the gap in between.
We can create an array of parallax layers during world creation-
Array<Parallax> parallaxList = new Array<Parallax>(4);
We can create the layers like this-
// Back
/* First parallax for back layer is at 0 x-axis. If you want to move the texture from right to left, the value of BACK_VELOCITY_X should be negative. You can experiment with velocity value for desire pace of movement. We do not want our layer to move on y-axis. Hence, it is set to 0. */
parallaxList.add(new Parallax(0, BACK_TEXTURE_HEIGHT, BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, BACK_VELOCITY_X, 0));
/* This one is also for back layer but it is positioned at the right edge of the layer above*/
parallaxList.add(new Parallax(BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, BACK_TEXTURE_WIDTH, BACK_TEXTURE_HEIGHT, SOME_VELOCITY_X, 0));
// Front
parallaxList.add(new Parallax(0, 0, FRONT_TEXTURE_WIDTH, FRONT_TEXTURE_HEIGHT, FRONT_VELOCITY_X, 0));
parallaxList.add(new Parallax(FRONT_TEXTURE_WIDTH, 0, FRONT_TEXTURE_WIDTH, FRONT_TEXTURE_HEIGHT, FRONT_VELOCITY_X, 0));
We update the layers on an update call in each frame-
// In our example, TOTAL_LAYERS is 4
for (int i = 0; i < TOTAL_LAYERS; i++) {
int tmpInt;
Parallax parallax = parallaxList.get(i);
parallax.update(deltaTime);
// If one layer is off the edge, put it at the right of the next one
// In this example, layers are moving from right to left
if (parallax.position.x <= -parallax.width) {
// We know that parallaxList's indexes 0 and 1 hold the back layers
// and indexes 2 and 3 have the front layers. You can add additional
// parameters in Parallax class to indicate a group so that you do not
// have to determine the group in dirty way like this
if(i == 0){
tmpInt = 1;
} else if(i == 1) {
tmpInt = 0;
} else if(i == 2) {
tmpInt = 3;
} else {
tmpInt = 2;
}
parallax.setPosition(parallaxList.get(tmpInt).position.x + parallax.width, parallax.position.y);
}
}
You can use an OrthographicCamera and a SpriteBatch to draw the parallax layers. You can actually use the game camera you have but I think using a separate camera is much cleaner. Anyways, parallax textures are usually big enough to be batched in a separate call so using the game camera most probably will not save you a draw call.

Categories

Resources