I'm improving a simple Android game called Arkanoid, where I'm having a small problem drawing a RectF on a Canvas.
I implemented a power-up option where, if you take this power-up, the Paddle streches from a 200x Paddle to a 500x Paddle.
private RectF r;
private Paint paint = new Paint();
private Bitmap flipperBit;
// Draw Paddle
flipperBit = BitmapFactory.decodeResource(getResources(), R.drawable.flipper);
paint.setColor(Color.WHITE);
if (powerUpTaken) {
r = new RectF(flipper.getX(), flipper.getY(), flipper.getX() + 500, flipper.getY() + 40);
} else {
r = new RectF(flipper.getX(), flipper.getY(), flipper.getX() + 200, flipper.getY() + 40);
}
canvas.drawBitmap(flipperBit, null, r, paint);
The main problem is that when I hit the powerup, the Paddle successfully streches from 200 to 500, but goes out of border as in the gif below, thinking it's still a 200x Paddle.
Please note that all of this , it's written inside the onDraw overrided-function.
I know, I need to implement a way to stretch the Paddle for x seconds, but as of now I want to make it work first.
Any suggestion would be really appreciated. :)
//set to ZERO if not needed
final cMargin = 10;
int cPaddingWidth;
final int cPaddingHeight = 40;
final RectF cRectF = new RectF();
if (powerUpTaken) cPaddingWidth = 500;
else cPaddingWidth = 200;
cRectF.left = Math.max(cMargin, flipper.getX());
cRectF.right = cRect.left + cPaddingWidth;
if (cRectF.right > (this.getWidth() - cMargin)) {
cRectF.left = (this.getWidth() - cMargin) - cPaddingWidth;
cRectF.right = cRectF.left + cPaddingWidth;
}
cRectF.top = Math.max(0, flipper.getY());
cRectF.bottom = cRect.top + cPaddingHeight;
r.set(cRectF);
Related
There are balls in my app that just fly through display. They draws as I want. But now I want to draw the trail behind them.
All I could make is just drawing by canvas.drawPath something like following picture:
But it is not what I want. It should have pointed tail and gradient color like this:
I have no idea how to make it. Tried BitmapShader - couldn't make something right. Help, please.
Code:
First of all, there is Point class for position on display:
class Point {
float x, y;
...
}
And trail is stored as queue of Point:
private ConcurrentLinkedQueue<Point> trail;
It doesn't matter how it fills, just know it has size limit:
trail.add(position);
if(trail.size() > TRAIL_MAX_COUNT) {
trail.remove();
}
And drawing happened in DrawTrail method:
private void DrawTrail(Canvas canvas) {
trailPath.reset();
boolean isFirst = true;
for(Point p : trail) {
if(isFirst) {
trailPath.moveTo(p.x, p.y);
isFirst = false;
} else {
trailPath.lineTo(p.x, p.y);
}
}
canvas.drawPath(trailPath, trailPaint);
}
By the way, trailPaint is just really fat paint :)
trailPaint = new Paint();
trailPaint.setStyle(Paint.Style.STROKE);
trailPaint.setColor(color);
trailPaint.setStrokeWidth(radius * 2);
trailPaint.setAlpha(150);
I see you want to see a gradient on the ball path, you could use something like this
int x1 = 0, y1 = 0, x2 = 0, y2 = 40;
Shader shader = new LinearGradient(0, 0, 0, 40, Color.WHITE, Color.BLACK, TileMode.CLAMP);
trailPaint = new Paint();
trailPaint.setShader(shader);
This is what you should change your trailPaint to and see if it works.
provided from here.
I found solution. But still think it is not the best one.
First of all there are my class fields used for that task.
static final int TRAIL_MAX_COUNT = 50; //maximum trail array size
static final int TRAIL_DRAW_POINT = 30; //number of points to split the trail for draw
private ConcurrentLinkedQueue<Point> trail;
private Paint[] trailPaints;
private float[][] trailPoss, trailTans;
private Path trailPath;
Additionally to trailPath object I used PathMeasure object to split path to multiple equal parts.
After filling trail array object added call of trail calculating function.
lastTrailAdd = now;
trail.add(pos.Copy());
if (trail.size() > TRAIL_MAX_COUNT) {
trail.remove();
}
FillTrail();
Then my FillTrail function.
private void FillTrail() {
trailPath.reset();
boolean isFirst = true;
for(Point p : trail) {
if(isFirst) {
trailPath.moveTo(p.x, p.y);
trailPoss[0][0] = p.x;
trailPoss[0][1] = p.y;
isFirst = false;
} else {
trailPath.lineTo(p.x, p.y);
}
}
PathMeasure path = new PathMeasure(trailPath, false);
float step = path.getLength() / TRAIL_DRAW_POINT;
for(int i=0; i<TRAIL_DRAW_POINT; i++) {
path.getPosTan(step * i, trailPoss[i], trailTans[i]);
}
}
It separated from drawing thread. Next code is drawing function.
private void DrawTrail(Canvas canvas) {
if(trail.size() > 1) {
float prevWidthHalfX = 0f, prevWidthHalfY = 0f, prevX = 0f, prevY = 0f;
Path trailStepRect = new Path();
boolean isFirst = true;
for (int i = 0; i < TRAIL_DRAW_POINT; i++) {
float currWidthHalf = (float) (radius) * i / TRAIL_DRAW_POINT / 2f,
currWidthHalfX = currWidthHalf * trailTans[i][1],
currWidthHalfY = currWidthHalf * trailTans[i][0],
currX = trailPoss[i][0], currY = trailPoss[i][1];
if (!isFirst) {
trailStepRect.reset();
trailStepRect.moveTo(prevX - prevWidthHalfX, prevY + prevWidthHalfY);
trailStepRect.lineTo(prevX + prevWidthHalfX, prevY - prevWidthHalfY);
trailStepRect.lineTo(currX + currWidthHalfX, currY - currWidthHalfY);
trailStepRect.lineTo(currX - currWidthHalfX, currY + currWidthHalfY);
canvas.drawPath(trailStepRect, trailPaints[i]);
} else {
isFirst = false;
}
prevX = currX;
prevY = currY;
prevWidthHalfX = currWidthHalfX;
prevWidthHalfY = currWidthHalfY;
}
}
}
Main point of this is drawing trail by parts with different paints. Closer to ball - wider the trail. I think I will optimise it, but it is allready work.
If you want to watch how it looks just install my app from google play.
I have made a simple game where you click on a object and it should dissapear. It works fine on desktop with the dimensions 240 x 480 but on my phone the dimensions are wider like 1920 x 1080 so the touch down coordinates are different so on desktop it might log the touch 100 x 50 but if i tapped the same place on phone it would be 400 x 200 so i just want to scale them or use the same.
private double WidthScale = (272 / Gdx.graphics.getWidth());
private double HeightScale = (408 / Gdx.graphics.getHeight());
private Array<Rectangle> rockets;
private long lastDropTime = 0;
private float tap_X = 0;
private float tap_Y = 0;
public GameRenderer() {
cam = new OrthographicCamera();
cam.setToOrtho(false, 408, 272);
batch = new SpriteBatch();
rockets = new Array<Rectangle>();
spawnRocket();
}
public void render(){
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
batch.setProjectionMatrix(cam.combined);
batch.begin();
for(Rectangle rocket : rockets) {
batch.draw(AssetLoader.rocket ,rocket.x, rocket.y,
rocket.width, rocket.height);
}
batch.end();
if (Gdx.input.justTouched()){
tap_X = (int) (Gdx.input.getX() * HeightScale);
tap_Y = (int) ((Gdx.graphics.getHeight()-Gdx.input.getY()) * WidthScale);
Gdx.app.log("MyTag", String.valueOf(tap_X));
Gdx.app.log("MyTag", String.valueOf(tap_Y));
Gdx.app.log("MyTag", String.valueOf(HeightScale));
Gdx.app.log("MyTag", String.valueOf(WidthScale));
}
Iterator<Rectangle> iter = rockets.iterator();
while(iter.hasNext()) {
Rectangle rocket = iter.next();
rocket.y -= 70 * Gdx.graphics.getDeltaTime();
if(rocket.y + 32 < 0) iter.remove();
if (rocket.x < tap_X && tap_X < rocket.x + rocket.width) {
Gdx.app.log("MyTag", "getRekt");
if (tap_Y > rocket.y && tap_Y < rocket.y + rocket.height) {
Gdx.app.log("MyTag", "poo");
iter.remove();
}
}
}
if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRocket();
}
private void spawnRocket() {
Gdx.app.log("MyTag", "Rocket Spawned");
Rectangle rocket = new Rectangle();
rocket.x = MathUtils.random(0 , 272 - 16);
rocket.y = 408 + rocket.height;
rocket.height = 32;
rocket.width = 16;
rockets.add(rocket);
lastDropTime = TimeUtils.nanoTime();
}
In order to work with different screen sizes, you have to deal with viewports, there's an entirely section in libGDX wiki that shows how to work with it. A fast example could be the following (however, I suggest to read the wiki and try others viewports by yourself):
// Declare a viewport object
private Viewport v;
public GameRenderer() {
// initialize after your camera initialization
v = new FitViewport(408, 272, cam); // <- I have use a FitViewport
// but you can use others
// I guess you could remove the setToOrtho() but not too sure...
// ... rest of code
}
You have to properly dispose and update this object *
A thing you should consider is, when you use something like Gdx.input.getX() you are getting a screen current position, you have to transform this coordinate system to your world coordinate system. Wherever you use something like that you have to do the following:
Vector3 v = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
v = cam.unproject(v);
// now v has your coordinate world system and you can properly make use of it
// you can do now something like:
tap_x = (int) v.x;
tap_y = (int) v.y;
Hope you find this useful.
So ive been trying to make a game app that either displays a red button with text or a green button with text randomly on the android screen. If anyone can help me with this i would appreciate it. also on a side note i want to slowly generate faster cool upside if anyone knows how to do that. Thanks!
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas){
String str = "Joke of the day";
super.onDraw(canvas);
paint = new Paint();
Random random = new Random();
Random randomTwo = new Random();
//Rect ourRect = new Rect();
Rect topRect = new Rect();
Rect backGround = new Rect();
paint.setColor(Color.BLACK);
backGround.set(0,0,canvas.getWidth(),canvas.getHeight());
canvas.drawRect(backGround, paint);
for(int i = 0; i <= 900; i++;){
}
if(blank == time){
paint.setColor(Color.RED);
canvas.drawCircle(random, randomTwo, 230, paint);
}else {
paint.setColor(Color.GREEN);
canvas.drawCircle(random, randomTwo, 230, paint);
}
}
You only need one Random instance.
Declare private long lastUpdated = 0; and private int lastColor = Color.BLACK; outside of the onDraw.
Update the bottom portion to:
final float radius = 230f;
if(System.currentTimeMillis() > lastUpdated + 1000){
lastColor = random.nextInt(2) == 1 ? Color.RED : Color.GREEN;
lastUpdated = System.currentTimeMillis();
}
paint.setColor(lastColor);
canvas.drawCircle(random.nextInt(canvas.getWidth()-radius/2) + radius/2f, random.nextInt(canvas.getHeight()-radius/2) + radius/2f, radius, paint);
This will draw a circle of either red or green at random location every second.
You need the radius/2 because the coordinates are from the center of the circle.
As for your second part of your question, also on a side note i want to slowly generate faster cool upside. You'd have to clarify what you mean.
Edit:
Provided a more complete (and correct) sample here:
https://gist.github.com/mshi/8287fd3956c9a917440d
I have a game that generates bodies forever until the game ends. For some reason, the game will be nice and smooth at first, but then it will start to slow down and become choppy as you keep going while playing. Then, after you die and restart, the game repeats this process. I dispose all I can. Here is what pushes the player body:
//in main game class
private Vector2 movement = new Vector2();
sSpeed = 200000;
switch(button)
{
case Buttons.LEFT:
movement.y = sSpeed * 1.2f;
movement.x = sSpeed * 1.5f;
table.clear();
}
return false;
}
//in Level Generator class
public LevelGenerator(BodyDef bDef, float topEdge, float bottomEdge, float minGap, float maxGap, float w, float h, Sprite s, World world) {
this.bDef = bDef;
this.topEdge = topEdge;
this.bottomEdge = bottomEdge;
this.minGap = minGap;
this.maxGap = maxGap;
width = w;
height = h;
this.s = s;
this.world = world;
}
public void generate(float rightEdge) {
if(x + MathUtils.random(minGap, maxGap) > rightEdge) {
return;
}
x = rightEdge;
float y = MathUtils.random(topEdge - height * 2f, bottomEdge + height * 2f);
bDef = new BodyDef();
bDef.type = BodyType.DynamicBody;
PolygonShape ast = new PolygonShape();
ast.setAsBox(width, height, new Vector2(x + width, y + height), 0);
item = world.createBody(bDef);
Fixture fix = item.createFixture(ast, 0);
}
//in main game class
generator.generate(camera.position.x + camera.viewportWidth / 2 + 10);
generator = new LevelGenerator(ballD3, 120, -125, 58, 63, 12.5f, 12.5f, aSprite1 , world);
Sadly the best way to avoid this kind of memory leak is to track the objects yourself.
I've gotten around this problem by creating an array called bodies that holds all the bodies. Then you run a for loop something like this (sorry, JavaScript:)
_.each(bodies,function(body){
var pos = body.GetPosition();
if (pos.y > 500) world.destroyBody(body);
});
I'm new to Android programming and now I'm trying to make a simple Sea Battle game for one person. Ships are places, player hits the field and see whether the shot hit or not.
Basically, the field looks like this:
The code is:
public void onDraw(Canvas canvas) {
if (getWidth() > getHeight()) {
rebro = getHeight();
} else {
rebro = getWidth(); // the smaller size of screen is "rebro"
}
rebro_piece = rebro / 10; // divide the screen by 10 (to get 10x10 field)
Paint background = new Paint();
background.setColor(getResources().getColor(R.color.game_background));
canvas.drawRect(0, 0, rebro, rebro, background); // draw background
Paint divider = new Paint();
divider.setColor(getResources().getColor(R.color.divider_black));
// drawing divider lines
for (int i=0; i<11; i++) {
canvas.drawLine(0, i*rebro_piece, rebro, i*rebro_piece, divider); // horizontal
canvas.drawLine(i*rebro_piece, 0, i*rebro_piece, rebro, divider); // vertical
}
canvas.drawLine(rebro-1, 0, rebro-1, rebro, divider);
}
That's how I make the "field."
In another class I have a method that collects numbers x and y of a 10×10 array that represents where the ships are placed. For debugging, I need to draw them on my field. Ship coordinates are retrieved in a cycle.
So I wrote a drawShip(int x, int y) method.
On Stack Overflow I've founded a question about "Why I can't paint outside onDraw()?" and I've changed my method to this:
public void drawShip(int x, int y) {
myX = x; //global
myY = y; //global
needToPaintShip = true; //boolean
invalidate(); // refreshing?
needToPaintShip = false;
}
Here needToPaintShip decides whether the redrawing of canvas is needed or not.
Also I've edited the onDraw(Canvas canvas) method:
if(needToPaintShip == true) {
Paint ship = new Paint();
ship.setColor(getResources().getColor(R.color.ship_color));
Log.d(TAG, "onDraw(): rebro_piece = " + rebro_piece + " , myX = "+ myX + " , myY = " + myY); // I only get the last coordinates!
Rect r = new Rect(myX*(rebro_piece),myY*rebro_piece, myX*(rebro_piece+1), myY*(rebro_piece+1));
canvas.drawRect(r, ship);
}
but the result is awful:
Guys, I'm desperate. How can I fix this and make "ships" be drawn on the field?
Why do you set needToPaintShip = false; after calling invalidate()? Don't you need to draw the ship again in subsequent frames?
Also, it seems like this item:
Rect r = new Rect(myX*(rebro_piece),myY*rebro_piece, myX*(rebro_piece+1), myY*(rebro_piece+1));
should probably be:
Rect r = new Rect(myX*(rebro_piece),myY*rebro_piece, (myX+1)*rebro_piece, (myY+1)*rebro_piece));
As for why the ship always appears in the bottom right corner, that depends on what you pass to drawShip(x,y), which isn't shown. Is it possible that you are passing pixel coordinates instead of something in the range [0-10)?