Android Libgdx: draw a lot of sprites - java

I want to draw a lot of sprites so I wrote this code in render method:
for(int i=0;i<100;i++)
{
spriteBlocchi[i] = new Sprite(blocco);
batch.draw(spriteBlocchi[i],xBlocco,50,100,200);
xBlocco = xBlocco + 0.1f;
}
spriteBlocchiis a Sprite array. I wrote this because in this way all 100 sprites are drawn by the render method, but it doesn't work. It draws only a sprite and I don't know why, in fact I created an array of sprites. This cycle is always called in render, so all the blocks should be drawn...what's the problem?

Related

Java - What's a good and efficient way to construct an Image out of several smaller images?

I'm making a 2d topdown view game where the camera follows the player so that the player is always in the center of the screen. I'm using Swing for all of my Graphics at the moment. I currently have a set of tiles that I use to create Sprites which all contain an Image. I can display all of the Images in the Sprites, but player movement always jerks the camera around, and I thought of combining all of my smaller Images into a large one, several times the screen, which I can take neat sub-tile precision slices of for smoother camera movement. When trying to do this I couldn't quite figure out how a Raster is constructed so I thought I might as well ask if better methods are available. My current attempt for creating the big Image looks something like this:
private Image prepareBackground() {
Raster raster = background.getData();
int[] rgb = new int[background.getColorModel().getNumComponents()];
for( int i=0;i< everySprite.length;++i){
for(int j=0;j<everySprite[0].length;++j){
Raster tileImage = ((BufferedImage)everySprite[i][j].getImage()).getData();
for(int k=0;k< tileImage.getHeight();++k){
for(int l =0;l<tileImage.getWidth();++l){
int[] values = tileImage.getPixel(k,l, rgb);
//how do I set the new pixel?
}
}
}
}
}
When I got this far I realized there are no setters for a Raster's single pixel, just getters.

About performance and drawing Bitmaps

At the beginning I have to say that I'm learning java on my own and may make a stupidest mistake ever...
I'm facing a problem with FPS drop while drawing Bitmaps in my 2d game.
My game's map consists of Tiles 100x100px, and every tile has some surface graphic and may have something else (like tree, rock, or whatever).
I have 2 main drawing methods. Both looks similar. First is drawing map and second everything that is on it (trees etc.).
Here is a code. I'm limiting what is to be drawn in this method.
public static void drawMap(Canvas canvas, Player player, ArrayList<MapField> map)
{
int a = player.getShiftX()/Constants.TILE_SIZE;
int b = player.getShiftY()/Constants.TILE_SIZE;
for (int x = a-Constants.VISIBILITY_X; x<=a+Constants.VISIBILITY_X; x++)
{
if (x>=0&&x<=99)
{
for (int y = b-Constants.VISIBILITY_Y; y<=b+Constants.VISIBILITY_Y*2-1; y++)
{
if (y>=0&&y<=99)
{
map.get(x+y*100).update();
map.get(x+y*100).draw(canvas);
}
}
}
}
}
Then I'm calling:
public void draw(Canvas canvas)
{
canvas.drawBitmap(Graphics.TILES_BITMAP[tileId], null, rect, p);
}
When I limit "vision" to 3 squares each direction (drawing about 60 tiles) FPS is 60. As soon as I get rid of that limit (whole screen is to be drew - about 250 tiles) FPS are dropping to 27-30 which makes game unplayable.
It is normal behaviour? Is Java that limited?
Or just maybe I made a mistake here?
Full code to be seen here (if anyone care to check):
gitlink
Someone told me that with that amount of graphics i should already use some OpenGL and suggested me to learn LibGDX for example. But as for me, pure java is more elegant :)
Ok, i've managed to solve this issue. It would only for for API26+ though.
Instead of just:
canvas = this.surfaceHolder.lockCanvas();
i've put:
canvas = this.surfaceHolder.lockHardwareCanvas();
And now again i have 60FPS+ :)

Trying to draw a circle in LibGDX

this is really basic but I can't figure out what's going wrong. Basically I'm trying to draw a circle around a certain area of one of my objects. I've initialised a ShapeRenderer in the constructor (called srDebugCircle) and have this for loop in the render() method to draw every object.
for (GameObject object : levels.get(LEVEL_INDEX)) {
if (object.getType() == ObjectType.SWINGING_SPIKES) {
object.draw(batch);
srDebugCircle.begin(ShapeType.Filled);
srDebugCircle.circle(object.getxPos() + object.getWidth()/2, object.getyPos(), object.getWidth()/2);
srDebugCircle.setColor(Color.BLACK);
srDebugCircle.end();
}
if (object.getType() == ObjectType.COIN && (Coin) object).isVisible()) {
object.draw();
}
...
}
The problem is I only see like 4 out of 15 objects when I add the code for the circle. When I remove it / comment it it works as usual - however, in both cases, I can never see a black filled circle.
I'm specifically talking about this part:
srDebugCircle.begin(ShapeType.Filled);
srDebugCircle.circle(object.getxPos() + object.getWidth()/2, object.getyPos(), object.getWidth()/2);
srDebugCircle.setColor(Color.BLACK);
srDebugCircle.end();
Can anybody see why I'm having this problem?
An alternative to Springrbua’s answer is to draw using Pixmaps instead of ShapeRenderer. Switching between SpriteBatch and ShaperRenderer is an expensive operation and Pixmaps don’t require ending the SpriteBatch. Pixmap offers fewer draw methods than ShapeRenderer, but it does include drawing a filled circle.
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.BLACK);
pixmap.fillCircle(x, y, r);
Texture texture = new Texture(pixmap);
// render
batch.begin();
batch.draw(texture, x, y);
batch.end();
The problem is, that you have two Renderer/Batches running at the same time:
The SpriteBatch batch and the ShapeRenderer srDebugCircle.
This can result in strange behaivor.
To solve the problem, call end() for one Renderer/Batch before calling begin() for the other.
In your case it would look something like this:
object.draw(batch);
batch.end()
srDebugCircle.begin(ShapeType.Filled);
srDebugCircle.setColor(Color.BLACK); // Set Color before drawing
srDebugCircle.circle(object.getxPos() + object.getWidth()/2, object.getyPos(), object.getWidth()/2);
srDebugCircle.end();
Also note, that calling end() on a SpriteBatch calls flush() which should be called as rarely as possible. Therefore it might be a good idea to draw everything with the SpriteBatch and then draw all the ShapeRenderer things.

LibGDX problems rotating sprite

OK so I'm really confused I've rotated sprites before and had no problem such as rotating a boat as it moves through an ocean, but for some reason I'm having a really big problem this time. So I create a texture in an assets file, but not static textures. I load the texture using the following:
class Assets{
Texture img;
public Assets(){
img = new Texture(Gdx.files.internal("images/PNG.png")
And then I call the assets in the main class by calling:
Assets assets = new Assets()
And then I have a class that is an animator just for this main character because his animation is so different and varied from other characters.
class Animations{
Guy MYGUY;
Texture firstTexture;
ArrayList<Texture> running;
Sprite CurrentSprite;
public Animations(Texture standingStill, Guy myGuy){
MYGUY = myGuy;
firstTexture = standingStill;
running = new ArrayList<Texture>();
running.add(firstTexture);
CurrentSprite = new Sprite(firstTexture);
public void update (int direction, int state){
CurrentSprite.setPosition(MYGUY.X, MYGUY.Y)
// I have a switch here, but it does nothing yet because I haven't added in different actions for the character.
//However I do have a switch for direction, because that is important right now
switch(state){
case Guy.LEFT:
CurrentSprite.set rotation(180);
//yes there are more, but even rotating 180 won't work correctly
}
Then I have a renderer class to draw everything, i have the object MyGuy in an object for the world called myLand and I draw it with:
myLand.GUY.animation.CurrentSprite(batch);
So my problem arises on the rotation, whenever it rotates 180 degrees it seems to always rotate around the coordinates (0, 0) instead of the center of the sprite. So it usually ends up where I move like five to the right, but then if I try to go left it does double the distance backwards, but the camera position stays the same, and the guy usually disappears off the left or right side of the screen.
Try use rotate(...)method instead of setRotation(...).
With setOrigin(widthSprite\2, heightSprite\2)
That action rotate sprite itself.
Try
sprite.setOriginCenter();
This should help
Instead of rotating the sprite, just flip it with this single line:
CurrentSprite.flip(true, false);
the first boolean is X flip (that's what you want to set as true when going left) and the second is the Y flip.

LibGdx How to repeat background?

Few days ago I figured out how to do some scrolling in LibGdx. Now I'm triying to do something related. I want to repeat the background. My scrolling follows a ship (Is an s[ace ship game). In the background there is a space photo loaded as a Texture. When the ship reach the end of the backgorund, It keeps going and there's no background anymore. I have read about wrap but I don't really understand How It works. I did that:
px=new Pixmap(Gdx.files.internal("fondo.jpg"));
background=new Texture(px);
background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
And then, in my render method
spriteBatch.begin();
spriteBatch.draw(background,0,0,500,50);
drawShip();
spriteBatch.end();
Of course It doesn't work, It only draws the background once. I don't know how make this wrap method work. Any help?
SOLUTION
I figured It out. It's not a nice code but It works.
First I declare two Textures with the same image
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
Also I declare two variables like this to specify the X value of the position of each bck
int posXBck1=0,posXBck2=0;
Then I use that in Render()
public void calculoPosicionFondos(){
posXBck2=posXBck1+ANCHODEFONDO;
if(cam.position.x>=posXBck2+cam.viewportWidth/2){
posXBck1=posXBck2;
}
}
Where:
ANCHODEFONDO is the width of my background
Cam is an OtrhoCam.
So I said that if the cam is in bck2 (wich means that you can't see bck1 anymore) It change positions, giving bck1 de position of bck2 and, in the next render loop, recalculating bck2
Then just paint both bck in your render mode.
Like Teitus said, do not load your texture multiple times, ever! Anyway, you where on the right track with the wrapper:
texture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
Now you can just use the draw method with the source location. The source location is the area you choose to draw on the texture.
batch.draw(texture, x, y, srcX, srcY, srcWidth, srcHeight)
To scroll your texture from right to left all you have to do is increase srcX incrementally. So create a int that increments in the update/render method.
int sourceX = 0;
//render() method
//Increment the variable where to draw from on the image.
sourceX += 10;
//Simply draw it using that variable in the srcX.
batch.draw(YourTexture, 0, 0, sourceX, 0, screenWidth, screenHeight);
Because you are wrapping the texture it will wrap/loop and scroll indefinitely. There might be a issue with the sourceX int if the game runs for a very long time because a int can only hold 2147483647. It takes a while but you can fix it by subtracting the image width each time the number goes over the total image width.
Don't to this, please:
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
That will load your big background texture twice. That's a complete waste. If you want to keep your solution at least do:
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=bkg1;
Regarding the texture Wrapping. If your texture is 500px wide, and you draw a 500px sprite, you won't see any repetition. If you want it repeated 2 times, draw it 1000px wide with 0-2 texture coordinates.
I'm not sure how spriteBatch handles the call you posted, you could try that one, or may be use the overload that uses a texture region and set your region manually.
I see this is a pretty old question, but I think there is an easier way to accomplish background scrolling. Just use the Sprite class. Here is a snippet I use for layered background images that scroll from right to left.
public class LevelLayer
{
public float speedScalar = 1;
private List<Sprite> backgroundSprites = new ArrayList<Sprite>();
public LevelLayer()
{
}
public void addSpriteLayer(Texture texture, float startingPointX, float y, int repeats)
{
for (int k = 0; k < repeats; k++)
{
Sprite s = new Sprite(texture);
s.setX(startingPointX + (k*texture.getWidth()));
s.setY(y);
backgroundSprites.add(s);
}
}
public void render(SpriteBatch spriteBatch, float speed)
{
for (Sprite s : backgroundSprites)
{
float delta = s.getX() - (speed * speedScalar);
s.setX(delta);
s.draw(spriteBatch);
}
}
}
Then you can use the same texture or series of textures like so:
someLayer.addSpriteLayer(sideWalkTexture1, 0, 0, 15);
someLayer.addSpriteLayer(sideWalkTexture2, 15 * sideWalkTexture1.getWidth(), 0, 7);
I change background repeating sections randomly in code and make new ones or reset existing sets when they go off screen. All the layers go to a pool and get pulled randomly when a new one is needed.
SOLUTION
I figured It out. It's not a nice code but It works.
First I declare two Textures with the same image
bck1=new Texture(Gdx.files.internal("fondo.jpg"));
bck2=new Texture(Gdx.files.internal("fondo.jpg"));
Also I declare two variables like this to specify the X value of the position of each bck
int posXBck1=0,posXBck2=0;
Then I use that in Render()
public void calculoPosicionFondos(){
posXBck2=posXBck1+ANCHODEFONDO;
if(cam.position.x>=posXBck2+cam.viewportWidth/2){
posXBck1=posXBck2;
}
}
Where:
ANCHODEFONDO is the width of my background
Cam is an OtrhoCam.
So I said that if the cam is in bck2 (wich means that you can't see bck1 anymore) It change positions, giving bck1 de position of bck2 and, in the next render loop, recalculating bck2
Then just draw both bck in your render()

Categories

Resources