I'm new to LibGDX and was trying to implement parallax background.
Everything went good until I faced such issue: I get some stripes when scrolling background. You can see it in attached image:
So I looked deeper into an issue and figured out that this some sort of texture bleeding. But the case is that my textures already have [Linear, Nearest] filter set and TexturePacker uses duplicatePadding. Actually, I don't know any other methods to solve this issue. Please help!
Here's some of my code:
TexturePacker
TexturePacker.Settings settings = new TexturePacker.Settings();
settings.minWidth = 256;
settings.minHeight = 256;
settings.duplicatePadding = true;
TexturePacker.process(settings, "../../design", "./", "textures");
AssetLoader
textureAtlas = new TextureAtlas(Gdx.files.internal("textures.atlas"));
for (int i = 0; i < 2; i++) {
Background.skies.add(textureAtlas.findRegion("background/sky", i));
Background.skies.get(i).getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Nearest);
}
for (int i = 0; i < 2; i++) {
Background.clouds.add(textureAtlas.findRegion("background/cloud", i));
Background.clouds.get(i).getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Nearest);
}
for (int i = 0; i < 8; i++) {
Background.cities.add(textureAtlas.findRegion("background/city", i));
Background.cities.get(i).getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Nearest);
}
Background.moon = textureAtlas.findRegion("background/moon");
Background.forest = textureAtlas.findRegion("background/forest");
Background.road = textureAtlas.findRegion("background/road");
Background.moon.getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Nearest);
Background.forest.getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Nearest);
Background.road.getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Nearest);
BackgroundDrawer
private void drawParallaxTextureList(Batch batch, List<TextureAtlas.AtlasRegion> list,
float moveX, float posY) {
for (int i = 0; i < list.size(); i++) {
boolean needDraw = false;
float shift = GameScreen.VIEWPORT_WIDTH * i;
float drawX = 0.0f;
if (shift - moveX <= -(GameScreen.VIEWPORT_WIDTH)) { // If it's behind the screen
if (i == 0) { // If it's first element
if (moveX >= GameScreen.VIEWPORT_WIDTH * (list.size() - 1)) { // We need to show first after last
needDraw = true;
drawX = (GameScreen.VIEWPORT_WIDTH) - (moveX - ((GameScreen
.VIEWPORT_WIDTH) * (list.size() - 1)));
}
}
} else if (shift - moveX < (GameScreen.VIEWPORT_WIDTH - 1)) {
needDraw = true;
drawX = shift - moveX;
}
if (needDraw) {
batch.draw(list.get(i), (int) drawX, (int) posY);
}
}
}
NOTE: I don't use any camera for drawing right now. I only use FitViewport with size of 1920x1280. Also, bleeding sometimes appears even in FullHD resolution.
UPDATE: Setting both Nearest filters for minification and magification with increasing paddingX and disabling antialiasing solved issue, but final image become too ugly! Is there way to avoid disabling antialiasing? Because without it, downscale look awful.
Try to set both min and mag filters as Nearest
.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
In GUI TexturePacker there is an option to extrude graphics - it means repeating every of border pixel of texture. Then you can set both filters to Linear
.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
but unfortunately I cannot see this option in the TexturePacker.Settings object you are using. You can try to set Linear to both but I'm pretty sure it won't be working (Linear filter takes nearest 4 texels to generate the one so it will probably still generate issues).
Try to use GUI Texturepacker then with extrude option maybe
A few possible reasons for this artifact:
Maybe the padding is not big enough when the sprite resolution is shrunk down. Try changing your texture packer's filterMin to MipMapLinearNearest. And also try increasing the size of paddingX and paddingY.
Maybe you're seeing dim or brightened pixels at the edge of your sprite because you're not using pre-multiplied alpha and your texture's background color (where its alpha is zero) is white. Try setting premultiplyAlpha: true. If you do this, you need to also change the SpriteBatch's blend function to (GL20.GL_ONE, GL20.GL_ONE_MINUS_SRC_ALPHA) to render properly.
You seem to be rounding your sprite positions and sizes to integers when you draw them. This would work in a pixel perfect game, where you're sure the sprites are being rendered exactly at 1:1 resolution to the screen. But once the screen size does not match exactly, your rounding might produce gaps that are less than 1 pixel wide, which will look like semi-transparent pixels.
Related
I am quite new to opengl es 2.0 on android. I am working on a project which draws a few plane indicators on screen(like altimeter, compass etc). After doing the tutorial from the official google dev site here http://developer.android.com/training/graphics/opengl/index.html I just continued along this path, drawing circles, triangles, squares etc (only 2d stuff). I can make the drawn objects move using rotation and translation matrices, but the only way I know how to do this(except for how they did it in the tutorial) is like this in the onDrawFrame() method of my renderer class:
//set values for all Indicators
try {
Thread.sleep(1);
// for roll + pitch:
if(roll < 90) {
roll += 1.5f;
} else roll = 0;
if(pitch < 90) {
pitch += 0.5f;
} else pitch = 0;
// for compass:
if(compassDeg > 360) compassDeg = 0;
else compassDeg += 1;
//for altimeter
if(realAltitude >= 20000) realAltitude = 0;
else realAltitude += 12;
//for speedometer:
if(realSpeed >= 161) realSpeed = 0;
else realSpeed += 3;
} catch (InterruptedException e) {
e.printStackTrace();
}
roll, pitch, compassDeg, speed etc are the parameters the indicators receive and I designed them to move accordingly (if compassDeg = 0 for example, the compass will point north and so on). These parameters will eventually be received via bluetooth but for now I'm modifying them from the code directly because I don't have a bluetooth implementation yet.
I am pretty sure this is not the best way to do it, sometimes the drawn objects stutter and seem to go back a few frames, then back again and I don't think pausing the drawing method is a good idea in general.
I've seen that in the tutorial I mentioned in the beginning they use something like this:
//Use the following code to generate constant rotation.
//Leave this code out when using TouchEvents.
long time = SystemClock.uptimeMillis() %4000L ;
float contAngle = -0.090f * ((int) time);
Matrix.setRotateM(contRotationMatrix, 0, contAngle, 0, 0, -1.0f);
Matrix.multiplyMM(contMVPMatrix, 0, mMVPMatrix4, 0, contRotationMatrix, 0);
which is still kinda weird I think, there has to be a more straightforward way in which to specify how to draw each frame, to rotate and translate objects frame by frame.
So my question is how do I make everything move frame by frame or something like that, or at least how do I find out when one frame has been drawn?
I want to moove two objects smoothely at Touching.
Here is my Code:
for(int i = 0; i <96; i++){
Asstest.rect_pipe_down.y--);
}
This should move the rect 96 pixels down (SMOOTH)
But it just close without smoothed...
What did I wrong?
If you Touch, the pipes should close, but not hard, smooth should they close.
But with following code they just close hard...
Here is the full touched code:
if(Gdx.input.isTouched()){
Assets.rect_pipe_down.y = 512 - 320/2;
Assets.rect_pipe_up.y = -320 + 320/2;
for (int i = 0; i < 96; i++){
smoothTime = TimeUtils.millis();
if(TimeUtils.millis() - smoothTime > 10) {
Assets.rect_pipe_down.y--;
Assets.rect_pipe_up.y++;
batch.begin();
batch.draw(Assets.region_pipe_down, Assets.rect_pipe_down.x, Assets.rect_pipe_down.y);
batch.draw(Assets.region_pipe_up, Assets.rect_pipe_up.x, Assets.rect_pipe_up.y);
batch.end();
}
}
closed = true;
}
You cannot do rendering multiple times in one render() call, one call is for drawing exactly one frame. In your current code, the later images simply overwrite the previous ones.
What you could do is have a variable which persists between frames which stores whether or not the pipes are currently closing, a constant for the speed and some condition to tell when they can stop - maybe when they are some given distance from each other, not sure what you would want here. Anyway, that's what I'll use in my example.
Then in the render() method, before drawing anything, you can do this:
if (closing) {
Assets.rect_pipe_down.y -= CLOSE_SPEED * delta;
Assets.rect_pipe_up.y += CLOSE_SPEED * delta;
if (Assets.rect_pipe_down.y - Assets.rect_pipe_up.y < TARGET_DIST) {
Assets.rect_pipe_down.y = Assets.rect_pipe_up.y + TARGET_DIST;
closing = false;
}
}
Here, closing is a variable you set to true when you want them to start closing, the others are constants. You could add some more variables/constants if you want to make sure they end up at a specific height independent on framerate.
I have a game that I converted to my Android phone. I tried both renders, and found out that CPU mode is a better option than GPU for my type of game. In GPU mode, the game has a lot of lag and the graphics are not sharp as it is in CPU mode. The purpose of the game is to kill enemies coming from the top of the screen before they hit the bottom. When the game starts, it's not laggy at all, but then it starts to lag when there are a few enemies on the screen and they start coming down faster. This is the code of spawning the enemies:
function makeEnemies():void
{
var chance:Number = Math.floor(Math.random() * 150);
if (chance <= level && enemies.length < 4)
{
tempEnemy = new Enemy();
tempEnemy.x = Math.round(Math.random() * 480);
tempEnemy.cacheAsBitmap = true;
addChild(tempEnemy);
tempEnemy.scaleX = 1.5;
tempEnemy.scaleY = 1.5;
enemies.push(tempEnemy);
tempEnemy.speed = enemyBaseSpeed + ((level - 1) * speedLevelInc);
if (tempEnemy.speed > MAX_SPEED)
{
tempEnemy.speed = MAX_SPEED;
}
}
}
function moveEnemies():void
{
var tempEnemy:MovieClip;
for (var i:int =enemies.length-1; i>=0; i--)
{
tempEnemy = enemies[i];
if (tempEnemy.dead)
{
score++;
score++;
roachLevel.score_txt.text = String(score);
enemies.splice(i,1);
}
else
{
tempEnemy.rotation += (Math.round(Math.random()*.4));
tempEnemy.x -= (Math.sin((Math.PI/180)*tempEnemy.rotation))*tempEnemy.speed;
tempEnemy.y += (Math.cos((Math.PI/180)*tempEnemy.rotation))*tempEnemy.speed;
if (tempEnemy.x < 10)
{
tempEnemy.x = 11;
}
if (tempEnemy.x > stage.stageWidth - offset)
{
tempEnemy.x = stage.stageWidth - offset;
}
if (tempEnemy.y > stage.stageHeight)
{
removeEnemy(i);
lives--;
roachLevel.lives_txt.text = String(lives);
}
}
}
}
This code is always looped to spawn multiple enemies. All my images have been converted to bitmaps. Is there any other way for me to make my game with no lag? The faster they get, the more lag they have and the user can't kill them anymore. Please help!
There are certain optimizations you can make (whether or not it's enough I can't tell):
you don't need cacheAsBitmap if you've converted everything to bitmaps, so comment this out
don't set scale! this will definitely slow things down. Since scale always seems to be 1.5, just resize your graphics
don't set tempEnemy.rotation on each iteration, if possible. Can you make tempEnemy a MovieClip that "pre-rotates" each frame in the timeline? This will be way more efficient for Flash. The rotation can be set as a Number property in the MovieClip class if you still need it for calculating tempEnemy.x and y.
(There are several minor things you can optimize, like setting var K = (Math.PI/180)*tempEnemy.rotation) in the enemies loop-- but this won't gain you a lot)
I'm writing a scratch card like app, and I use a SurfaceView for that.
I fill it with some kind of color and I draw some Path on it with PorterDuff.Mode.CLEAR PorterDuffXfermode. I have to identify when the user fully scratched it (the SurfaceView's canvas is fully transparent). Can anybody give me some advice, how to identify it?
I tried it with saving the coordinates of the paths, but because of the drawing stroke width I can't calculate the covered area well.
I tried to get a Bitmap from the SurfaceView's getDrawingCache method and iterate on its pixels and use the getPixel method. It doesn't work and i think it would be not an efficient way to examine the canvas.
Assuming the canvas will not be large or scalable to an arbitrary size I think looping over the pixels would be effective.
Given a canvas of large or arbitrary size I would create an array representation of the canvas and mark pixels as you go, keeping a count of how many the user has hit at least once. Then test that number against a threshold value that determines how much of the ticket must be scratched for it to be considered "scratched off". Incoming pseudo-code
const int count = size_x * size_y; // pixel count
const int threshhold = 0.8 * count // user must hit 80% of the pixels to uncover
const int finger_radius = 2; // the radias of our finger in pixels
int scratched_pixels = 0;
bit [size_x][size_y] pixels_hit; // array of pixels all initialized to 0
void OnMouseDown(int pos_x, int pos_y)
{
// calculates the mouse position in the canvas
int canvas_pos_x, canvas_pos_y = MousePosToCanvasPos(pos_x, pos_y);
for(int x = canvas_pos_x - finger_rad; x < canvas_pos_x + brush_rad; ++x)
{
for(int y = canvas_pos_y - finger_rad; y < canvas_pos_y + brush_rad; ++y)
{
int dist_x = x - canvas_pos_x;
int dist_y = y - canvas_pos_y;
if((dist_x * dist_x + dist_y * dist_y) <= brush_rad * brush_rad
&& pixels_hit[x][y] == 0)
{
++scratched_pixels;
pixels_hit[x][y] = 1;
}
}
}
}
bool IsScratched()
{
if(scratched_pixels > threshhold)
return true;
else
return false;
}
I have a requirement to display somewhat big images on an Android app.
Right now I'm using an ImageView with a source Bitmap.
I understand openGL has a certain device-independent limitation as to
how big the image dimensions can be in order for it to process it.
Is there ANY way to display these images (with fixed width, without cropping) regardless of this limit,
other than splitting the image into multiple ImageView elements?
Thank you.
UPDATE 01 Apr 2013
Still no luck so far all suggestions were to reduce image quality. One suggested it might be possible to bypass this limitation by using the CPU to do the processing instead of using the GPU (though might take more time to process).
I don't understand, is there really no way to display long images with a fixed width without reducing image quality? I bet there is, I'd love it if anyone would at least point me to the right direction.
Thanks everyone.
You can use BitmapRegionDecoder to break apart larger bitmaps (requires API level 10). I've wrote a method that will utilize this class and return a single Drawable that can be placed inside an ImageView:
private static final int MAX_SIZE = 1024;
private Drawable createLargeDrawable(int resId) throws IOException {
InputStream is = getResources().openRawResource(resId);
BitmapRegionDecoder brd = BitmapRegionDecoder.newInstance(is, true);
try {
if (brd.getWidth() <= MAX_SIZE && brd.getHeight() <= MAX_SIZE) {
return new BitmapDrawable(getResources(), is);
}
int rowCount = (int) Math.ceil((float) brd.getHeight() / (float) MAX_SIZE);
int colCount = (int) Math.ceil((float) brd.getWidth() / (float) MAX_SIZE);
BitmapDrawable[] drawables = new BitmapDrawable[rowCount * colCount];
for (int i = 0; i < rowCount; i++) {
int top = MAX_SIZE * i;
int bottom = i == rowCount - 1 ? brd.getHeight() : top + MAX_SIZE;
for (int j = 0; j < colCount; j++) {
int left = MAX_SIZE * j;
int right = j == colCount - 1 ? brd.getWidth() : left + MAX_SIZE;
Bitmap b = brd.decodeRegion(new Rect(left, top, right, bottom), null);
BitmapDrawable bd = new BitmapDrawable(getResources(), b);
bd.setGravity(Gravity.TOP | Gravity.LEFT);
drawables[i * colCount + j] = bd;
}
}
LayerDrawable ld = new LayerDrawable(drawables);
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < colCount; j++) {
ld.setLayerInset(i * colCount + j, MAX_SIZE * j, MAX_SIZE * i, 0, 0);
}
}
return ld;
}
finally {
brd.recycle();
}
}
The method will check to see if the drawable resource is smaller than MAX_SIZE (1024) in both axes. If it is, it just returns the drawable. If it's not, it will break the image apart and decode chunks of the image and place them in a LayerDrawable.
I chose 1024 because I believe most available phones will support images at least that large. If you want to find the actual texture size limit for a phone, you have to do some funky stuff through OpenGL, and it's not something I wanted to dive into.
I wasn't sure how you were accessing your images, so I assumed they were in your drawable folder. If that's not the case, it should be fairly easy to refactor the method to take in whatever parameter you need.
You can use BitmapFactoryOptions to reduce size of picture.You can use somthing like that :
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3; //reduce size 3 times
Have you seen how your maps working? I had made a renderer for maps once. You can use same trick to display your image.
Divide your image into square tiles (e.g. of 128x128 pixels). Create custom imageView supporting rendering from tiles. Your imageView knows which part of bitmap it should show now and displays only required tiles loading them from your sd card. Using such tile map you can display endless images.
It would help if you gave us the dimensions of your bitmap.
Please understand that OpenGL runs against natural mathematical limits.
For instance, there is a very good reason a texture in OpenGL must be 2 to the power of x. This is really the only way the math of any downscaling can be done cleanly without any remainder.
So if you give us the exact dimensions of the smallest bitmap that's giving you trouble, some of us may be able to tell you what kind of actual limit you're running up against.