I'm creating two textures and put them in the same TextureAtlas as shown in the code:
public void create () {
pix = new Pixmap(100, 100, Pixmap.Format.RGBA8888);
textureAtlas = new TextureAtlas();
pix.setColor(Color.WHITE);
pix.fill();
textureAtlas.addRegion("white", new TextureRegion(new Texture(pix)));
pix.setColor(Color.RED);
pix.fill();
textureAtlas.addRegion("red", new TextureRegion(new Texture(pix)));
tr_list = textureAtlas.getRegions();
pix.dispose()
}
and then when rendering:
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
for (int i = 0; i < 200; ++i) {
batch.draw(tr_list.get(i % tr_list.size), (int)(Math.random()* 500), (int)(Math.random()* 500));
}
font.draw(batch, "FPS: " + Integer.toString(Gdx.graphics.getFramesPerSecond()), 0, Gdx.graphics.getHeight() - 20);
font.draw(batch, "Render Calls: " + Integer.toString(batch.renderCalls), 0, Gdx.graphics.getHeight() - 60);
batch.end();
}
I was expecting batch.renderCalls to be equal to 1, since the textures are in the same TextureAtlas, but instead it's equal to 200. What am I doing wrong?
In order to draw your TextureRegions in a single render call, each of them must be a part of the same Texture, not TextureAtlas.
TextureAtlas can contain TextureRegions of different Textures, which actually happens in your case. Even though you use the same Pixmap, you create two different Textures from it.
You are drawing them in loop 200 times.
for (int i = 0; i < 200; ++i) {
// this will be called 200 times
batch.draw(tr_list.get(i % tr_list.size), (int)(Math.random()* 500), (int)(Math.random()* 500));
}
And also TextureAtlas class doesn't create pig picture of textures that you add to it. It is just container of TextureRegions how #Arctic23 noticed.
So main problem for you is to draw 2 textures in one render call. I wouldn't recommend you doing this. It is possible but it will be hard to work with them.
Calling batch.draw(..) every time you draw a texture is not a big performance problem. So i don't know why you detach this.
Related
I am working on a project in Android that builds Abelian Sandpiles (a type of 2D cellular Automaton). I have a grid of cells (that starts out small but later grows) and I'm drawing square (or circles) on the grid to show the state of each cell. At each step, I update usually less than 30% of the cells.
My basic approach is taken from this post: Android: How to get a custom view to redraw partially?
I draw all the shapes to the canvas and cache it as a bitmap, then at each step I update only the cells that need updating, then cache the result and repeat. This works well enough when the grid is fairly small (less than 50 x 50), but becomes increasingly unsatisfactory as the grid size increase. The problems are that
1) it is too slow when the number of updates becomes high
2) even when it runs smoothly, the drawing doesn't look clean - e.g., a line of small rectangles looks quite choppy and inconsistent (see image).
I am sure that there must be a better way to approach this problem. With a larger grid (e.g., 150 x 150), I'm drawing rects or circles with a width of ~2.33, and this can't be optimal. Any advice for improving performance and/or image quality?
Simplified drawing code is here:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (ready) {
if (needsCompleteRedraw) {
this.cachedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
cacheCanvas = new Canvas(this.cachedBitmap);
doInitialDrawing(cacheCanvas);
canvas.drawBitmap(this.cachedBitmap, 0, 0, null);
needsCompleteRedraw = false;
} else {
canvas.drawBitmap(this.cachedBitmap, 0, 0, null);
doPartialRedraws(cacheCanvas);
}
}
}
private void doInitialDrawing(Canvas clean) {
for (int i = 0; i < pile.gridHeight; i++) {
for (int j = 0; j < pile.gridWidth; j++) {
int state = pile.getGridValueAtPoint(new Point(j, i ));
clean.drawRect(j * cellSize, i * cellSize, (j + 1 )* cellSize, (i + 1) * cellSize, paints[state]);
}
}
}
private void doPartialRedraws(Canvas cached) {
for (Point p : pile.needsUpdateSet) {
int state = pile.getGridValueAtPoint(p);
cached.drawRect(p.x * cellSize, p.y * cellSize, (p.x + 1 )* cellSize, (p.y + 1) * cellSize, paints[state]);
}
pile.needsUpdateSet = new HashSet<Point>();
}
and my paint objects are set with antialias as true, and I've tried setting the style to both FILL and FILL_AND_STROKE.
Any suggestions would be much appreciated.
Ok. Several problems here.
1)Don't ever create a canvas in onDraw. If you think you need to, you haven't architected your drawing code correctly.
2)The point of doing draws to a cache bitmap is NOT to draw the cache in onDraw, but to do it on its own thread- or at least not at draw time.
You should have a second thread that draws to the cached bitmap on demand, then calls postInvalidate() on the view. The onDraw function should only be a call to drawBitmap, drawing the cached bitmap to the screen.
I am still getting to this problem, and i am feeling like my head is going to blow. Lets say, that i have this code for drawing a texture i created.
public MainMenuScreen(TheSpring game) {
this.game = game;
//camera for interface
guiCam = new OrthographicCamera(1020, 640);
guiCam.position.set(1020 / 2, 640/2, 0);
//buttons
play = new Button(10, 120, 400, 300, AssetLoader.play);
highscores = new Button(460, 10, 82, 102, AssetLoader.highscores);
sound = new Button(10, 10, 72, 72, AssetLoader.soundEnabled);
sound.AddTexture(AssetLoader.soundDisabled);
if (!Settings.soundEnabled)
sound.SwitchTexture();
help = new Button(120, 160, 80, 15, "Help", Color.WHITE);
//enable buttons
play.Enable(true);
highscores.Enable(true);
sound.Enable(true);
help.Enable(true);
//touchpoint for unproject
touchpoint = new Vector3();
}`
The texture i created for play button is also 400x300.
The problem is, that when i try to run this "app" my textures are blurred.
The Button is my class, and I am using its drawing method.
public void Draw(SpriteBatch batcher) {
if (enabled){
if (text != null) {
AssetLoader.ComicFont.setColor(clr);
AssetLoader.ComicFont.setScale(ScaleWidth, ScaleHeight);
AssetLoader.ComicFont.draw(batcher, text, x, y + height);
}
if (texture != null) {
batcher.draw(texture, x, y, width, height);
}
}
}
And the result is this (please don't judge me for my drawing skills, its only testing version)
For example, on the right side of button, there should be also black line, and lines should be straight. Can you please help me? :)
one possible solution can be setting your texture filters to Linear
By default they are not linear.
try this out
texture.setFilter(TextureFilter.Linear,TextureFilter.Linear);
I am using the libgdx framework and trying to repeat a background of spikes forever. The issue I am having is that the texture is moving extremely fast forever, but I want to slow it down. I was looking at a tutorial from Google Code and another user had this same issue. The "supposed" solution was this:
if(scrollTimer>1.0f)
scrollTimer = 0.0f;
scrollTimer = 3f;
This was to make it scroll 3 times faster, so I attempted this and substituted 0.3f instead of 3f, but this makes the background at a standstill.
Here is some relevant code that might help in solving this issue:
public class PlayScreen implements Screen{
final Rectangle upSpikeBounds = new Rectangle(0,0,Gdx.graphics.getWidth() * 2, 25);
final Rectangle downSpikeBounds = new Rectangle(0,Gdx.graphics.getHeight() - 25,Gdx.graphics.getWidth() * 2, 25);
Rectangle blockBounds;
float total_time = 0f;
Sprite spikeUpSprite, spikeDownSprite, spriteBlock;
public PlayScreen(Game game){
batch = new SpriteBatch();
player = new Player(new Vector2(Gdx.graphics.getWidth()/10, Gdx.graphics.getHeight()/2), batch);
stage = new Stage();
this.game = game;
Texture spikeUp = new Texture(Gdx.files.internal("spikes.png"));
Texture spikeDown = new Texture(Gdx.files.internal("spikes.png"));
spikeUp.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
spikeDown.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
spikeUpSprite = new Sprite(spikeUp, 0, 0, Gdx.graphics.getWidth(), 25);
spikeDownSprite = new Sprite(spikeDown, 0, 25, Gdx.graphics.getWidth(), -25);
spikeDownSprite.setPosition(0, Gdx.graphics.getHeight() - 20);
spikeUpSprite.setSize(1200, 25);
spikeDownSprite.setSize(1200, 25);
}
public void render(float deltaTime) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
label.setText("Score: " + player.score);
cam.position.set(player.getPosition().x + Gdx.graphics.getWidth()/2,Gdx.graphics.getHeight () / 2,0);
batch.setProjectionMatrix(cam.combined);
cam.update();
total_time += Gdx.graphics.getDeltaTime();
if (total_time > 1.0f){
total_time = 0.0f;
}
spikeUpSprite.setU(total_time);
spikeUpSprite.setU2((total_time+1));
spikeDownSprite.setU(total_time);
spikeDownSprite.setU2(total_time+1);
stage.act();
batch.begin();
spikeUpSprite.draw(batch);
spikeDownSprite.draw(batch);
batch.end();
stage.draw();
}
}
Can anyone help me?
You should look up the tween engine, it will enable you to use 'tween animation', which is an animation of values, who with a duration. For instance if you animate from zero to 700 with a 700ms duration, the value will start from zero and increase by 1 every ms
While micnubinub's answer is certainly interesting and worth checking out, I have figured out a quick fix for my issue. The problem lies in this line:
total_time += Gdx.graphics.getDeltaTime();
This is where the speed of the background lies. Because delta time is so fast (probably to the microsecond), the background scrolls by too fast. If I change it to a manual number, such as 0.0093f, I get a background that scrolls at the speed I desire.
I can't seem to get TiledSprite to work as I expect. There are no manuals, and the examples in AndEngineExamples is of no help.
Consider this code:
#Override
public void onLoadResources() {
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
mBlockAtlas = new BitmapTextureAtlas(512, 512, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
mBlockTexture = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(mBlockAtlas, getApplicationContext(), "num_plasma.png", 0, 0, 3, 3);
getTextureManager().loadTexture(mBlockAtlas);
}
This loads a 512x512 texture that I've - manually - divided into 3x3 tiles just for testing purposes. Now, doing this:
public Scene onLoadScene() {
mScene = new Scene();
mScene.setBackground(new ColorBackground(0.5f, 0.1f, 0.6f));
TiledSprite foo, foo2;
foo = new TiledSprite(0, 0, mBlockTexture);
foo.setCurrentTileIndex(1);
foo2 = new TiledSprite(0, 200, mBlockTexture);
foo2.setCurrentTileIndex(5);
mScene.attachChild(foo);
mScene.attachChild(foo2);
return mScene;
}
This puts up two sprites on the screen (as expected), but they both shows the same tile (5)!
How are one supposed to do if you have a bunch of sprites using the same texture but showing different tiles?
I think you need to deepCopy() the Texture when you are creating the Sprites, like
foo2 = new TiledSprite(0, 200, mBlockTexture.deepCopy());
Following this : Best approach for oldschool 2D zelda-like game
I got a simple 2D tiles generator working, im reading an int map[100][100] filled with either 1's or 0's and draw tiles according to their tile id, 0 is water, 1 grass.
Im using some basic Numpad control handler, using a camIncr (32.0f), i set the camera position according to the movement :
case KeyEvent.KEYCODE_DPAD_RIGHT:
cameraPosX = (float)(cameraPosX + camIncr);
break;
In my draw loop, im just drawing enough tiles to fit on my screen, and track the top left tile using cameraOffsetX and cameraOffsetY (its the camera position / tile size )
Im using a GLU.gluOrtho2D for my projection.
Here is the draw loop inside my custom renderer :
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode( GL10.GL_PROJECTION );
gl.glLoadIdentity( );
GLU.gluOrtho2D(gl, 0, scrWidth, scrHeight, 0);
repere.draw(gl, 100.0f); // this is just a helper, draw 2 lines at the origin
//Call the drawing methods
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
tiledBackground.draw(gl, filtering);
my tiledBackground draw function :
int cols = (569 / 32) + 2; // how many columns can fit on the screen
int rows = (320 / 32) + 1; // haw many rows can fit on the screen
int cameraPosX = (int) Open2DRenderer.getCameraPosX();
int cameraPosY = (int) Open2DRenderer.getCameraPosY();
tileOffsetX = (int) (cameraPosX / 32);
tileOffsetY = (int) (cameraPosY / -32);
gl.glPushMatrix();
for (int y = 0; y < rows; y++) {
for (int x = 0; x < cols; x++) {
try {
tile = map[y + tileOffsetY][x + tileOffsetX];
} catch (Exception e) {
e.printStackTrace(); //when out of array
tile = 0;
}
gl.glPushMatrix();
if (tile==0){
waterTile.draw(gl, filter);
}
if (tile==4) {
grassTile.draw(gl, filter);
}
gl.glTranslatef(32.0f, 0.0f, 0.0f);
}//
gl.glPopMatrix();
gl.glTranslatef(0.0f, 32.0f, 0.0f);
}
gl.glPopMatrix();
}
the waterTile and grassTile .draw function draw a 32x32 textured tile, might post the code if relevant.
Everything is fine, i can move using numpad arrows, and my map 'moves' with me, since im only drawing what i can see, its fast (see android OpenGL ES simple Tile generator performance problem where Aleks pointed me to a simple 'culling' idea)
I would like my engine to 'smooth scroll' now. I've tried tweaking the camIncr variable, the GLU.gluOrtho2D etc, nothing worked.
Any ideas ? :)
I finally found out.
i added a glTranslatef method right before entering the loop :
gl.glPushMatrix();
gl.glTranslatef(-cameraPosX%32, -cameraPosY%32, 0);
for (int y = 0; y < rows; y++) {
...
First, i was unsuccessfully trying to translate the scene using a brute cameraPosX / TILE_HEIGHT division, didn't work.
We have to translate the offset by which the tile extends beyond the screen, not the total cameraPosX offset, so we're using the Mod (%) operator instead of division.
Sorry for my bad english ^^