Libgdx: Drawing lots of particles - java

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

Related

[Java][Libgdx] Isometric Tilemap Scaling and Coordinate Conversion

I have a 15x15 Isometric Tilemap consisting of 128 x 64 pixel tiles. Using an ExtendViewport and OrthogonalCamera I am able to render the map. However, it does not scale properly when resizing the window as the aspect ratio of the tiles becomes distorted and occasionally the map will move over to the right side of the window. The map does look fine if I force fullscreen resolution, but not if it's manually scaled or in windowed mode. When I say that the aspect ratio becomes distorted I mean that the tiles will appear stretched or jagged resulting in poor aesthetics.
Is there a simple way to render a perfectly squared (e.g. 10x10, 15x15) sized Isometric Tilemap, display it in the centre of the screen and have it scale properly without distorting the aspect-ratio as the screen size increases?
Then there is the issue of conversion between cartesian and isometric coordinates.
http://clintbellanger.net/articles/isometric_math/ The following post explains how to convert between cartesian and isometric coordinates, but I am not able to make it work. The code below is the closest thing I've come to a working solution so far, but it becomes increasingly more off as you move towards the right of the screen, not by terribly much, but it's definitely noticeable.
public static Vector2 cartesianToIsometric(Camera camera, GameMap map, Vector3 point) {
camera.unproject(point);
float tileWidth = (float)map.getMapWidth() * unitScale(map);
float tileHeight = (float)map.getMapHeight() * unitScale(map);
point.x /= tileWidth;
point.y = (point.y - tileHeight / 2) / tileHeight + point.x;
point.x -= point.y - point.x;
return new Vector2((int)point.x, (int)point.y);
}
Here is a snippet of the relevant code from my GameRenderer Class.
public GameRenderer(GameState world) {
this.map = new Map01();
this.world = world;
this.camera = new OrthographicCamera();
this.viewport = new ExtendViewport(0, 3072, camera);
centerCamera(map);
batchRenderer = new SpriteBatch();
}
public void render() {
Gdx.gl.glClearColor(0.11f, 0.6f, 0.89f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT | (Gdx.graphics.getBufferFormat().coverageSampling?GL20.GL_COVERAGE_BUFFER_BIT_NV:0));
renderer.setView(camera);
renderer.render();
map.render();
batchRenderer.begin();
AssetLoader.font.getData().setScale(8);
AssetLoader.font.draw(batchRenderer, map.getSelectedTile().getName(), -400, 1500);
batchRenderer.end();
}
public void resize(int width, int height){
viewport.update(width, height);
renderer = new IsometricTiledMapRenderer(map.getTiledMap(), MapUtils.unitScale(map));
batchRenderer.setProjectionMatrix(camera.combined);
centerCamera(map);
}
private void centerCamera(GameMap map){
camera.position.set(MapUtils.getMapCenter(map));
}
I could almost certainly make it work with what I have, but it seems like I'm missing something painfully simple. It's probably a good idea to have a solid foundation and functional coordinate system, before I start implementing the actual gameplay.
Here is a picture of the map in question if that is of any help. Thanks in advance for any advice.

Sprite image appears stretched and quality is poor, can't get in correct position GDXLib

I am quite new to programming so bear with me here...
I am making a 2d basic game just to practice programming in android studio and can't get my sprite to the correct position on the screen. Also when I draw the sprite it appears stretched and the quality is very poor. Any help is appreciated!
public class MyGdxGame extends ApplicationAdapter {
SpriteBatch batch;
Texture background;
Texture ball;
#Override
public void create () {
batch = new SpriteBatch();
background = new Texture("gamebackground.png");
ball = new Texture("ball2.png");
}
#Override
public void render () {
batch.begin();
batch.draw(background, 0,0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
batch.draw(ball, 0,0, Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
batch.end();
}
You need to keep the original width/height ratio:
instead of scaling it to half the screen size, define your scaling like that:
float scaleFactor = 2.0f;
batch.draw(ball, 0,0, ball.getWidth()*scaleFactor, ball.getHeight*scaleFactor);
If your image is "blurry", and you want the individual pixels to stay crisp, try loading the texture like that:
ball = new Texture("ball2.png");
ball.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
This prevents (default) linear interpolation when scaling the texture.

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)
}

Libgdx TiledMap bug in render

I do a Mario like game with the libgdx library.
All works fine but sometime (especially when the camera goes fast) my TileMap has a little bug during the render.
A picture worth thousand word, so here it is : http://postimg.org/image/4tudtwewn/
I have tried to increment FPS, but there is no change. I have no idea where that is come from.
Here is my code :
public void show() {
TmxMapLoader loader = new TmxMapLoader();
this.plan = loader.load("maps/level-"+this.world+"-"+this.level+".tmx");
this.renderer = new OrthogonalTiledMapRenderer(this.plan);
...
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
this.renderer.render();// rendu de la carte
Batch batch = this.renderer.getSpriteBatch();
...
This happens when your Camera's position is not perfectly aligned with screen-space coordinates (pixels).
This results in some sprites being rounded to the next pixel while some other (that were connected to those) being rounded to the previous one, resulting in visible ugly glitches.
The easiest fix I could come up with is making sure that the Camera position is always perfectly aligned with screen-space coordinates.
public class TileMapCamera extends OrthographicCamera {
// Map tile size to round to
private int tileSize;
/**
* Create a pixel-perfect camera for a map with the specified tile size
* #param tileSize
*/
public TileMapCamera(int tileSize){
this.tileSize = tileSize;
}
#Override
public void update(){
// Round position to avoid glitches
float prevx = position.x;
float prevy = position.y;
position.x = (int)(position.x * tileSize) / (float)tileSize;
position.y = (int)(position.y * tileSize) / (float)tileSize;
super.update();
position.set(prevx, prevy, 0);
}
}
This works for a tile-based coordinate viewport:
mapViewport = new FitViewport(16, 15, new TileMapCamera(map.getProperties().get("tilewidth", Integer.class)));
If you're working with pixel-based coordinate viewports, you should round the camera position to the nearest integer instead.
I think its about the filtering.
This will help:
TiledMapRenderer Artifact
If the problem you are referring to, is the spacing you can fix when you import the tileset as it says Tenfour04
add or change pixel padding.

Scaling to sceen using libgdx on Android

I'm creating a simple game for Android using libgdx. I've come to the issue of having different screen sizes for different devices yet haven't found any concreted documentation on how to deal with this problem.
I think I'm supposed to use an OrthographicCamera? An example of code I have so far is:
private OrthographicCamera camera;
public void create() {
batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("data/cube.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
camera = new OrthographicCamera(1280, 720);
sprite = new Sprite(texture);
sprite.setOrigin(0, 0);
sprite.setPosition(1280/2, 600);
}
public void render() {
Gdx.gl.glClearColor(0.204f, 0.255f, 0.255f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
}
Am I going along the right lines? I don't have any other devices to test on and my emulators are causing me issues.
In case you didn't already do so, you should upgrade your LibGDX version to the latest release which is 1.0.0. In this version the socalled Viewport has been introduced.
You can find some screenshots and code snippets and everything you need to know here.
Basically you will have to decide for a certain strategy (your question sounds like you are interesting in either ScreenViewport or StretchViewport) and then let that manage your camera.
What I use in my libGDX projects is to override the resize method and set the OrthographicCamera to the size of the screen as follows, using the built in method called setToOrtho(boolean yDown) which sets camera centered on the current size which a parameter if you want the y-Axis pointing down or not:
#Override
public void resize(int width, int height){
camera.setToOrtho(false);
}
This, however will not change the size of your textures if you want to rescale your textures as well then I would recommend rather than setting them to an absolute size e.g 15 pixels try setting them to a percentage of the screen size.
Or, another, sometimes more effective method is to work out the correct sizes at a certain size e.g 800 x 480 then work out the percentage increase in width and apply that to your sprites E.g:
#Override
public void resize(int width, int height){
super.resize(width, height);
camera.setToOrtho(false);
//The following is if the sprites are normally correct scaled
//for a screen size of 800 x 480, change to whatever you need
//They should both be floats and class variables
widthChange = width / 800;
heightChange = height / 480;
}
public void render() {
Gdx.gl.glClearColor(0.204f, 0.255f, 0.255f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Rather than using sprite.draw(batch), use:
batch.draw(sprite, sprite.x * widthChange, sprite.y * heightChange, sprite.width * widthChange, sprite.height * heightChange);
batch.end();
}

Categories

Resources