First happy new year to all here!
I have a question about drawing background in code. I have a code for simple Android game and all assests is in png format, expect background. I`m not a programmer (but newbie in this and I learn with live examples).
I think this code draw background clouds on the screen:
//draw cloud layer 1
background_shader.setColor(getResources().getColor(R.color.blue_dark));
int radius = DrawBackgroundCloud(canvas, (ScreenHeight() / 2), 7);
canvas.drawRect(0, (float) ((ScreenHeight() / 2.2) + radius * 1.5), ScreenWidth(), ScreenHeight(), background_shader);
//draw cloud layer 2
background_shader.setColor(getResources().getColor(R.color.blue_darkest));
radius = DrawBackgroundCloud(canvas, (int) (ScreenHeight() / 1.5), 4);
canvas.drawRect(0, (float) ((ScreenHeight() / 1.7) + radius * 1.5), ScreenWidth(), ScreenHeight(), background_shader);
This draw some random circles as clouds but I want to change this to draw something like hills or mountains. Here is a picture of current background and what I`m looking for.
http://prntscr.com/5nqa25
Can anyone help me with this? I will be really thankfuly
Responding to the further question in the comment:
you cant really do that with canvas.drawColor, but you can use a proper Paint object and use canvas.drawPaint (or other canvas method that uses Paint object - if you want to for example draw shape with gradient).
The key part of creating your gradient Paint object is calling its setShader(...) method. For example like so:
mGradientPaint = new Paint();
mGradientPaint.setStyle(Paint.Style.FILL);
mGradientPaint.setShader(new LinearGradient(0, 0, 0, getHeight(), Color.TRANSPARENT, Color.GREEN, Shader.TileMode.MIRROR));
Related
hi guys I am trying to implement a box2d world. I have read that box2d uses meters. and You need to convert it from pixels to meters.
I tried to draw an image but do I have to scale down also the image? I think that is a bad I idea to draw the image, the image are very huge and can't figure what to do to make it work with the box2d pixel per meter
public class TestScreen extends ScreenAdapter {
private final Body body;
private int V_WIDTH = 320;
private int V_HEIGHT = 480;
private int PPM = 100;
private SpriteBatch batch;
private OrthographicCamera camera;
private World world;
private Sprite sprite;
Box2DDebugRenderer box2DDebugRenderer;
public TestScreen(){
batch = new SpriteBatch();
camera = new OrthographicCamera();
camera.setToOrtho(false, V_WIDTH / PPM, V_HEIGHT / PPM);
camera.position.set(0,0,0);
world = new World(new Vector2(0,0) , true);
sprite = new Sprite(new Texture("test/player.png"));
box2DDebugRenderer = new Box2DDebugRenderer();
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.KinematicBody;
body = world.createBody(bodyDef);
FixtureDef fixtureDef = new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox(sprite.getWidth()/2 / PPM, sprite.getHeight()/2 / PPM);
fixtureDef.shape = shape;
body.createFixture(fixtureDef);
sprite.setPosition(body.getPosition().x - sprite.getWidth() /2 ,body.getPosition().y - sprite.getHeight() / 2 );
}
#Override
public void render(float delta) {
super.render(delta);
camera.position.set( body.getPosition().x, body.getPosition().y , 0);
camera.update();
world.step(1/60.0f, 6, 2);
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
box2DDebugRenderer.render(world, camera.combined);
}
}
with out ppm
with PPm
should I scale down the image? what is the best way to draw the image
You don't need to convert from pixel to meter. As a matter of fact you should forget about pixels. They exist only on your screen and you game logic should not know anything about your screen. That is what a camera or viewport is for, you specify how much of the world to show and if the display should be stretched or blackboxed or whatever. So no pixels, period. They are evil and give you wrong ideas.
Now if you create your own game you can say that a single unit represents 1mm, 34cm or a couple of lightyears. You tell the object responsible for displaying your game how much of these units to display. However you are using Box2D, and Box2D has already filled in the unit for you 1 unit == 1m. It is probably possible to change this or at least create a wrapper class that converts you units to the Box2D unit.
The reason why it is important to keep true to the Box2D unit is the following. If you drop a marble on the ground it seems to be moving faster then the sun in the sky. But believe me, the sun is moving a lot faster but since it is a lot further away it seems to move slowly. Since Box2D is all about movement you should keep true to the unit or things will start to act strange.
Let's just use 1 unit == 1m for now and suddenly everything should become a lot simpler by asking a view questions.
how much of your game world do you want to show in meters?
float width = 20; // 20 meters
//You can calculate on your chosen width or height to maintain aspect ratio
float height = (Gdx.graphics.getHeight() / Gdx.graphics.getWidth()) * width;
camera = new OrthographicCamera(width, height);
//Now the center of the camera is on 0,0 in the game world. It's often more desired and practical to have it's bottom left corner start out on 0,0
//All we need to do is translate it by half it's width and height since that is the offset from it's center point (and that is currently set to 0,0.
camera.translate(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
How large is our object? Keep in mind that mass, weight and size are completely different things.
Sprite mySprite = new Sprite(myTexture);
//position it somewhere within the bounds of the camera, in the below case the center
//This sprite also gets a size of 1m by 1m
mySprite.setBounds(width / 2, height / 2, 1, 1);
How do we want the SpriteBatch to draw to the screen?
//We tell the SpriteBatch to use out camera settings to draw
spriteBatch.setProjectionMatrix(camera.combined);
//And draw the sprite using this SpriteBatch
mySprite.draw(spriteBatch);
Same counts for the Box2dDebugRenderer implemenation. If you want shapes to show you need to use that combined matrix from your camera again to draw it.
box2DDebugRenderer.render(world, camera.combined);
Of course, when things move around you need to update your sprite position accordingly. You can get this information from the box2d.Body object. But this is beyond the scope of your question.
To finally show you what is going wrong:
camera.setToOrtho(false, V_WIDTH / PPM, V_HEIGHT / PPM);
Your camera shows 320/100 == 3.2f x 480/100 == 4.8f of your game world. Your sprite might be 64x64 pixels. You are not telling anywhere at what size to draw your sprite so it will assume 1 pixel = 1 unit and you set your camera to show 3.2f units in width. We can and should leave pixels out of the equation and just ask what size you want your object to be. Then set the Sprite to that size. Here you see that thinking in pixels just gives you problems.
For a space game where you fly a ship of 100x20 meters in 3th person you probably want your camera viewport to be very large. But for a ant game where your ants are real size you want a very small camera viewport. Do think about physics in real life. Galileo Galilei discovered that objects fall at the same speed, disregarding resistance. So if that ant would drop a sand grain it would look like it would fall very fast because your screen represents much less meters.
For a implementation of a dropping soccer ball look at my answer here. It creates a box2D body and attaches a image to it. I keep the functionality of the ball encapsulated within the Ball() class. (disclaimer: I have just played around a bit with Box2D and I don't know the exact physical behaviors of a soccer ball so I am not stating this is a correct implementation, but it does show how to setup your scene and have a image represent your Box2D body).
I'm making a game with a mouse cursor, and I'd like to represent the health by overlaying the cursor with a green version of the image, but only a geometric sector of it corresponding to the health percentage. Solutions from posts like these: Drawing slices of a circle in java? & How to draw portions of circles based on percentages in Graphics2D? are pretty much what I want to do, but with a BufferedImage as opposed to a solid color fill.
//Unfortunately all this does is cause nothing to draw, but commenting this out allows the overlay image to draw
Arc2D.Double clip = new Arc2D.Double(Arc2D.PIE);
double healthAngle = Math.toRadians((((Double)data.get("health")).doubleValue() * 360.0 / 100.0) - 270.0);
clip.setAngles(0, -1, Math.cos(healthAngle), Math.sin(healthAngle));
System.out.println(Math.cos(healthAngle) + " " + Math.sin(healthAngle));
g.setClip(clip);
In short, how do I draw a sector of a BufferedImage given any angle?
If you read the API docs for setClip(Shape) you'll see that the only shape that is guaranteed to work, is a rectangle. So, setting the clip probably won't work.
However, there are other options. The most obvious is probably to use a TexturePaint to fill your arc with the BufferedImage. Something like:
TexturePaint healthTexture = new TexturePaint(healthImg, new Rectangle(x, y, w, h));
g.setPaint(healthTexture);
g.fill(arc); // "arc" is same as you used for "clip" above
Another option is to first draw the arc in solid color, over a transparent background, then paint the image over that, using the SRC_IN Porter-Duff mode. Something like:
g.setPaint(Color.WHITE);
g.fill(arc); // arc is same as your clip
g.setComposite(AlphaComposite.SrcIn); // (default is SrcOver)
g.drawImage(x, y, healthImg, null);
I'm trying to make a game where the player sometimes have to rotate, but I can't figure out a way to make it rotate more each time, here's what I've got:
Graphics2D g2d = (Graphics2D) g;
AffineTransform at = new AffineTransform();
at.translate(300, Main.height - 115);
if(rotateright) {
at.rotate(Math.PI / 15);
} else if(rotateleft) {
at.rotate(Math.PI / 15);
}
at.scale(0.125, 0.125);
at.translate(-texture.getWidth() / 2, -texture.getHeight() / 2);
g2d.drawImage(texture, at, null);
This method makes it rotate just fine, but I can't find a way to make it rotate more each time, so this method pretty much just rotates it, but only once, and then it just stops. Is there anyway to add to the rotation or something?
you're always starting of with the image in the normal rotation (0 deg/RAD rotated), and then radiating it by PI/15 RAD. if you want it to rotate over time, you will need to multiply PI/15 with a value that will be changing over the duration of your animation.
I pretty much finished my LibGDX project and now I'm just adding user-friendliness.
I have a Texture (also placed in a sprite) that I would like to fade in and fade out repeatedly (NOT fast blinking). It's just rectangular funky-text that says "Touch to Start".
I considered making an animation of 6 or so pictures with varying opacity and just keep changing slides. Is this the best way to go?
I'm also looking for a libGDX effect that controls the transparency to avoid all the overhead and not make my animation choppy.
Can't think of any relevant code to add, Thanks for your help
EDIT
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(touchToStartImage, screenWidth / 2 - touchToStartImage.getWidth() / 2, screenHeight / 2 - touchToStartImage.getHeight() / 2);
elapsed += Gdx.graphics.getDeltaTime();
blinkFontCache.setAlphas(Interpolation.fade.apply((elapsed / 0.01f) % 1f));
blinkFontCache.draw(batch);
blinkFontCache.translate(2f, 2f);
batch.end();
I also defined blinkFontCache = new BitmapFontCache(numberPrinter); where numberPrinter is bitmapfont that is supposed to draw text. I've read the API guide for Interpolation and blinkFontCache, but unfortunately with the above I do not notice any change in the screen I have. Thanks
SOLUTION
EDIT with INTERPOLATION
elapsed += Gdx.graphics.getDeltaTime();
touchToStartSprite.setAlpha(Interpolation.fade.apply((elapsed / FADE_TIME) % 1f));
blinker.begin();
touchToStartSprite.draw(batch);
blinker.end();
EDIT with ACTIONS
definitions
text = new Image(highScoreImage);
text.addAction(Actions.alpha(0));
text.act(0);
text.addAction(Actions.forever(Actions.sequence(Actions.fadeIn(FADE_TIME), Actions.fadeOut(FADE_TIME))));
render()
blinker.begin();
text.act(Gdx.graphics.getDeltaTime());
text.draw(blinker, 1);
blinker.end();
You could use the Image class from scene2d, which is an actor that can take a texture region and gives you several methods that can be useful. Here's an implementation.
Image text = new Image(clickToStartRegion);
Float fadeTime = 1f;
//...
text.addAction(Actions.alpha(0)); //make the text transparent.
text.act(0); //update the text once
text.addAction(Actions.sequence(Actions.fadeIn(fadeTime), Actions.fadeOut(fadeTime));
//...
text.act(deltaTime);
//...
text.draw(batch, 1);
You can use the Interpolation class for the alpha. Assuming you're using a Sprite to draw this:
private float elapsed;
private static final float FADE_TIME = 1f; //time between blinks
//...
elapsed += deltaTime;
sprite.setAlpha(Interpolation.fade.apply((elapsed / FADE_TIME) % 1f));
//...
spriteBatch.begin();
sprite.draw(spriteBatch);
spriteBatch.end();
Bitmap newBm = ...
Canvas canvas = new Canvas(newBm);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setTextSize((int) (44 * scale));
Rect bounds = new Rect();
paint.getTextBounds(gText, 0, gText.length(), bounds);
canvas.drawText(gText, x, y, paint);
I drew text on the Bitmap like so. How could I get a grey background that is the same height as the text but covers the whole screen??
You could use a Rect. Before drawing the text draw the Rect to the screen:
int screenWidth = getApplicationContext().getResources().getDisplayMetrics().widthPixels;
Rect greyBack = new Rect(0,top,screenWidth,bottom);
Paint paint = new Paint();
paint.setARGB(128, 100, 100, 100); //added alpha because Snapchat has translucent //grey background
canvas.drawRect(greyBack, paint);
top and bottom need to be coordinates above and below the text. You could use y's value and take away a bit for top and add a bit for bottom. How much you add/subtract is up to you and changes the height of the greyBack background.
The best way to see and learn how these sort of things are done with well written code is to look at the android source code itself. For example here is the onDraw method for a TextView it includes additional stuff you won't probably need like compoundPadding, but you can follow it through and get the basic concept of how it's done.