So I created a game where u can move your character and u need to pass lines with that character, but I have 1 problem. When the first line reaches bottom of my screen only then the score starts counting. I want to make so that when my character passes the lines gap the score would +1. What should I do?
EDIT: Also the score counts when last line reaches bottom!
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
score++;
}
}
public void draw(Canvas canvas){
for(Obstacle ob : obstacles)
ob.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(100);
paint.setColor(Color.GREEN);
canvas.drawText("" + score, 50, 100 + paint.descent()- paint.descent(), paint);
EDIT:
int currY = -5*Constants.SCREEN_HEIGHT/4, rect;
while(currY < 0){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(new Obstacle(obstacleHeight, color, xStart, currY, playerGap));
currY += obstacleHeight + obstacleGap;
}
if (obstacles.get(obstacles.size() - 1).getRectangle().top >= currY) {
score++;
}
EDIT2:
private void populateObatacles(){
int currY = -5*Constants.SCREEN_HEIGHT/4;
while(currY < 0){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(new Obstacle(obstacleHeight, color, xStart, currY, playerGap));
currY += obstacleHeight + obstacleGap;
}
}
EDIT3:
if (obstacles.get(lastLineScored-1).getRectangle().top >= currY) {
score++;
lastLineScored--;
}
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
lastLineScored++;
}
}
}
public void draw(Canvas canvas){
for(Obstacle ob : obstacles)
ob.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(100);
paint.setColor(Color.GREEN);
canvas.drawText("" + score , 50, 100 + paint.descent()- paint.descent (), paint);
EDIT4:
Obstacle manager http://pastebin.com/6E77QtHj
Obstacle http://pastebin.com/p33mPrat
Change your test. Instead of checking whether the top of the line is greater than SCREEN_HEIGHT, check whether it is greater than the character's Y coordinate. Also, you need to check against each
if (obstacles.get(obstacles.size()-1).getRectangle().top >= /** character Y coordinate */) {
score++;
}
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
}
EDIT: You need to keep track of which lines are already past the character and only up the score when a new line goes past. You'll need to keep an index of which lines have already been scored. So create a field to keep that index; let's call it lastScoredLine. It should be initialized to obstacles.size(). You'll have to adjust that field whenever a line falls off the screen.
if (obstacles.get(lastLineScored-1).getRectangle().top >= /** character Y coordinate */) {
score++;
lastLineScored--;
}
if (obstacles.get(obstacles.size()-1).getRectangle().top >= Constants.SCREEN_HEIGHT ){
int xStart = (int) (Math.random()*(Constants.SCREEN_WIDTH- playerGap));
obstacles.add(0, new Obstacle(obstacleHeight, color, xStart, obstacles.get(0).getRectangle().top - obstacleHeight - obstacleGap, playerGap));
obstacles.remove(obstacles.size()-1);
lastLineScored++;
}
Related
I learned about how to create an OTP from drawable using canvas. OTP screen
But I want to have a box instead of a plane lines at the bottom, and I want to have the text in the middle of the box, instead of 8 dp above the line. I made a few modification like adding a rectf and drawing using canvas. It was easy. But It will not be responsive on all the screens.
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
int availableHeight= getHeight()-getPaddingTop()-getPaddingBottom();
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
int startX = getPaddingLeft();
int bottom = getHeight() - getPaddingBottom();
//Text Width
Editable text = getText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(getText(), 0, textLength, textWidths);
Paint mPaint = new Paint();
RectF rectF;
int cornerRadius = 15;
for (int i = 0; i < mNumChars; i++) {
mPaint.setColor(getResources().getColor(R.color.colorViews));
mPaint.setAlpha(70);
mPaint.setStyle(Paint.Style.FILL);
rectF = new RectF(startX, availableHeight, startX + (int) mCharSize, bottom);
canvas.drawRoundRect(rectF,cornerRadius,cornerRadius,mPaint);
// canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
//y= bottom-mLineSpacing
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom-mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
}
And my xml file looks something like this
<com.example.android.EditTextOTP
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:cursorVisible="false"
android:digits="1234567890"
android:inputType="number"
android:maxLength="4"
android:textIsSelectable="false"
android:textSize="20sp"
android:background="#null"
android:paddingStart="40dp"
android:paddingEnd="40dp"
android:paddingTop="60dp"
app:layout_constraintTop_toBottomOf="#id/tv_otp_text"
app:layout_constraintStart_toStartOf="#id/gl_inside_bg"
app:layout_constraintEnd_toEndOf="#id/gl_vertical_end"
android:id="#+id/et_otp_box"
>
</com.example.android.EditTextOTP>
The above way works fine but I want to place the number as I type in the center of the BOX. not roughly on the top of the BOX. How can I improve this?
A while back I found this great color picker from Piotr Adams which I can not find on Git anymore but it's still on this page: https://www.programcreek.com/java-api-examples/index.php?source_dir=Random-Penis-master/app/src/main/java/com/osacky/penis/picker/ColorPicker.java
The main reason I use this color picker in my app is because I want to be able to place a pointer on the RadialGradient based on a color. This library calculates the position for a certain color, this means placing a picker on the correct location is very fast and easy.
The problem is I don't quite understand how it works. I now want to generate a RadialGradient with different colors. But the logic it uses does not work when I generate a RadialGradient with different colors.
Here is the code that generates the RadialGradient:
private Bitmap createColorWheelBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
int colorCount = 12;
int colorAngleStep = 360 / 12;
int colors[] = new int[colorCount + 1];
float hsv[] = new float[]{0f, 1f, 1f};
for (int i = 0; i < colors.length; i++) {
hsv[0] = (i * colorAngleStep + 180) % 360;
colors[i] = Color.HSVToColor(hsv);
}
colors[colorCount] = colors[0];
SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null);
RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorWheelRadius, 0xFFFFFFFF, 0x00FFFFFF, TileMode.CLAMP);
ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER);
colorWheelPaint.setShader(composeShader);
Canvas canvas = new Canvas(bitmap);
canvas.drawCircle(width / 2, height / 2, colorWheelRadius, colorWheelPaint);
return bitmap;
}
The code for listening to changes of the picker, so this calculates the color based on a position:
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
int x = (int) event.getX();
int y = (int) event.getY();
int cx = x - getWidth() / 2;
int cy = y - getHeight() / 2;
double d = Math.sqrt(cx * cx + cy * cy);
if (d <= colorWheelRadius) {
colorHSV[0] = (float) (Math.toDegrees(Math.atan2(cy, cx)) + 180f);
colorHSV[1] = Math.max(0f, Math.min(1f, (float) (d / colorWheelRadius)));
selectedPointer.setColor(Color.HSVToColor(colorHSV));
notifyListeners();
invalidate();
}
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
}
return super.onTouchEvent(event);
}
Finally the code that calculates the position based on a color:
// drawing color wheel pointer
float hueAngle = (float) Math.toRadians(colorHSV[0]);
int colorPointX = (int) (-Math.cos(hueAngle) * colorHSV[1] * colorWheelRadius) + centerX;
int colorPointY = (int) (-Math.sin(hueAngle) * colorHSV[1] * colorWheelRadius) + centerY;
float pointerRadius = 0.075f * colorWheelRadius;
int pointerX = (int) (colorPointX - pointerRadius / 2);
int pointerY = (int) (colorPointY - pointerRadius / 2);
colorPointerCoords.set(pointerX, pointerY, pointerX + pointerRadius, pointerY + pointerRadius);
canvas.drawOval(colorPointerCoords, colorPointerPaint);
So my question is how can I for example change the RadialGradient to only include 2 colors, without breaking the calculations of getting the color? Even an explanation on how this works would be great!
There is great tutorial here: http://tekeye.uk/android/examples/ui/android-color-picker-tutorial (not mine). I don't know much about the theory behind it either but you can use this code to calculate color based on position.
// Calculate channel based on 2 surrounding colors and p angle.
private int ave(int s, int d, float p) {
return s + java.lang.Math.round(p * (d - s));
}
// Calculate color based on drawn colors and angle based on x and y position.
private int interpColor(int colors[], float unit) {
if (unit <= 0) {
return colors[0];
}
if (unit >= 1) {
return colors[colors.length - 1];
}
// Adjust the angle (unit) based on how many colors there are in the list.
float p = unit * (colors.length - 1);
// Get starting color position in the array.
int i = (int)p;
p -= i;
// Now p is just the fractional part [0...1) and i is the index.
// Get two composite colors for calculations.
int c0 = colors[i];
int c1 = colors[i+1];
// Calculate color channels.
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
int r = ave(Color.red(c0), Color.red(c1), p);
int g = ave(Color.green(c0), Color.green(c1), p);
int b = ave(Color.blue(c0), Color.blue(c1), p);
// And finally create the color from the channels.
return Color.argb(a, r, g, b);
}
You can call the interpreting function like this for example.
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX() - CENTER_X;
float y = event.getY() - CENTER_Y;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
// Calculate the angle based on x and y positions clicked.
float angle = (float)java.lang.Math.atan2(y, x);
// need to turn angle [-PI ... PI] into unit [0....1]
float unit = angle/(2*PI);
if (unit < 0) {
unit += 1;
}
// mColors is your list with colors so int[].
int color = interpColor(mColors, unit);
break;
}
}
I already tried it in my project and it works like a charm. So hope it helps you too. :)
EDIT:
Oh so my colors are set up like this.
mColors = intArrayOf(-0x10000, -0xff01, -0xffff01, -0xff0001, -0xff0100, -0x100, -0x10000)
So you can add/remove as many colors as you want and since the interpret functions calculates based on size of this array it should work.
Im developing an android app with the help of dlib, i have a code that with the help of the 68 face landmarks i draw a path for the outer part of the lips and another one for the inner path of the lips is there any way that with 2 different paths i can fill the gap in between them?
i've seen methods like, fill the outer one and fill the inside one with white so it looks like you're only painting the outside part but i cant do that since im painting it over the an image and i just want to color the lips, is there any way to accomplish this?
This is the code i used to paint the lips:
for (final VisionDetRet ret : results) {
// Draw landmark
int x = 0;
ArrayList<android.graphics.Point> landmarks = ret.getFaceLandmarks();
int ppX = 0, ppY = 0;
Paint mFaceLandmardkPaint = new Paint();
mFaceLandmardkPaint.setColor(Color.GREEN);
mFaceLandmardkPaint.setStrokeWidth(2);
mFaceLandmardkPaint.setStyle(Paint.Style.STROKE);
Path pth = new Path();
pth.setFillType(Path.FillType.EVEN_ODD);
Path pth2 = new Path();
pth2.setFillType(Path.FillType.EVEN_ODD);
for (android.graphics.Point point : landmarks) {
if (x >= 48 && x <= 60) {
int pointX = (int) (point.x);
int pointY = (int) (point.y);
if (x != 48) {
//canvas.drawLine(pointX, pointY, ppX, ppY, mFaceLandmardkPaint);
pth.lineTo(pointX, pointY);
} else {
pth.moveTo(pointX, pointY);
}
ppX = pointX;
ppY = pointY;
//canvas.drawCircle(pointX, pointY, 1, mFaceLandmardkPaint);
} else if (x > 60) {
pth.close();
}
if (x >= 61) {
int pointX = (int) (point.x);
int pointY = (int) (point.y);
if (x != 61) {
//canvas.drawLine(pointX, pointY, ppX, ppY, mFaceLandmardkPaint);
pth2.lineTo(pointX, pointY);
} else {
pth2.moveTo(pointX, pointY);
}
ppX = pointX;
ppY = pointY;
//canvas.drawCircle(pointX, pointY, 1, mFaceLandmardkPaint);
}
x++;
}
pth2.close();
Paint red = new Paint();
red.setColor(android.graphics.Color.RED);
red.setStyle(Paint.Style.FILL);
c.drawPath(pth2, mFaceLandmardkPaint);
c.drawPath(pth, red);
}
This code generates a full painted mouth.
Is there any way to accomplish this with native canvas, paint and paths?
How can I draw Path with fading (opacity or thicknes) line? Something like this.
I know there is LinearGradient shader for Paint, but it won't bend along the Path.
One possible solution might be to get points along the Path and just draw it by myself through the segments`. But I coouldn't find any method for that either.
I came up with the following code. The mos important thing is PathMeasure's getPosTan() method.
if (getGesturePath() != null) {
final short steps = 150;
final byte stepDistance = 5;
final byte maxTrailRadius = 15;
pathMeasure.setPath(getGesturePath(), false);
final float pathLength = pathMeasure.getLength();
for (short i = 1; i <= steps; i++) {
final float distance = pathLength - i * stepDistance;
if (distance >= 0) {
final float trailRadius = maxTrailRadius * (1 - (float) i / steps);
pathMeasure.getPosTan(distance, pathPos, null);
final float x = pathPos[0] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
final float y = pathPos[1] + RandomUtils.nextFloat(0, 2 * trailRadius) - trailRadius;
paint.setShader(new RadialGradient(
x,
y,
trailRadius > 0 ? trailRadius : Float.MIN_VALUE,
ColorUtils.setAlphaComponent(Color.GREEN, random.nextInt(0xff)),
Color.TRANSPARENT,
Shader.TileMode.CLAMP
));
canvas.drawCircle(x, y, trailRadius, paint);
}
}
}
I have canvas which draws pie chart for me. After I change radio button to disagree position, my pie chart has to show different values. I am doing this with Listener (setted up on radio group). It is working but for updating the canvas (not adding and overdrawing, I have to clear it first and redraw with new values) I use this:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
Under the piechart I want to write number of percent. It is working without that line what is on top. But after I execute the line, only piechart is redrawn (with new values) but text with percentage is not shown (even if I set text to that again):
if (checkedId == R.id.agreeRadio) {
values[0] += 1;
values[1] -= 1;
setupPieChart();
setupPercentageValueToGraph();
}
Method calculating percentage and writing it:
public void setupPercentageValueToGraph() {
float[] degrees = calculateData(values);
// get percentage number from values
float percentage = (degrees[0] / (degrees[0] + degrees[1])) * 100;
// setup color of the number shown red/green
if (percentage >= 50) {
percentageTV.setTextColor(Color.parseColor("#47B243"));
} else {
percentageTV.setTextColor(Color.parseColor("#DB262A"));
}
// set the text
percentageTV.setText((int) percentage + "%");
}
Method to set piechart to linearLayout:
public void setupPieChart() {
float[] degrees = calculateData(values);
graphLayout.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
graphLayout.addView(new MyGraphView(this, degrees, size));
}
Class which is drawing the piechart where that clear is used:
public class MyGraphView extends View {
public static final int PADDING = 4;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float[] value_degree;
int strokeWidth;
private int[] COLORS = { Color.parseColor("#47B243"),
Color.parseColor("#DB262A") };
// size of bigger half circle
RectF rectf = new RectF(2, 2, 62, 62);
// size of smaller half circle
RectF rectf2 = new RectF(9, 9, 55, 55);
// size of the smallest half circle
RectF rectf3 = new RectF(16, 16, 48, 48);
int temp = 0;
public MyGraphView(Context context, float[] values, int size) {
super(context);
// setting up size of pie chart dynamically
int difference = size / 9;
rectf.set(PADDING, PADDING, size + PADDING, size + PADDING);
rectf2.set(difference + PADDING, difference + PADDING, size
- difference + PADDING, size - difference + PADDING);
rectf3.set(difference * 2 + PADDING, difference * 2 + PADDING, size
- difference * 2 + PADDING, size - difference * 2 +
PADDING);
// setting up brush size
strokeWidth = size / 15;
// assign degrees of agree and disagree to array
value_degree = new float[values.length];
for (int i = 0; i < values.length; i++) {
value_degree[i] = values[i];
}
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
//canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (int i = 0; i < value_degree.length; i++) {
// set type of "brush"
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Paint.Style.STROKE);
// set shadow
paint.setShadowLayer(2, 1, 1, Color.BLACK);
// setLayerType(LAYER_TYPE_SOFTWARE, paint);
// agree
if (i == 0) {
final Path path = new Path();
paint.setColor(COLORS[i]);
// draw 3 paths to show 3 curves
path.addArc(rectf, 180, value_degree[i] - 4);
path.addArc(rectf2, 180, value_degree[i] - 5);
path.addArc(rectf3, 180, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
// disagree
} else {
temp += (int) value_degree[i - 1];
paint.setColor(COLORS[i]);
final Path path = new Path();
path.addArc(rectf, temp + 180 + 4, value_degree[i] - 4);
path.addArc(rectf2, temp + 180 + 5, value_degree[i] - 5);
path.addArc(rectf3, temp + 180 + 6, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
}
}
}
}
Any idea why text is not written? Or any other way how to update piechart after radiobutton is changed?
Finally I found a way how to do it. To draw the text on the same canvas as graph was drawn:
// draw the number of percent under piechart
// setup brush
Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
// set shadow
paint2.setShadowLayer(3, 2, 2, Color.parseColor("#404040"));
// fill it not stroke
paint2.setStyle(Style.FILL);
// set text family + make it bold
paint2.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
// convert dp to pixels and setup the size of text
int MY_DIP_VALUE = 23; // 5dp
int pixel = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, MY_DIP_VALUE,
getResources()
.getDisplayMetrics());
// setup color of the number shown red/green
if (percent >= 50) {
paint2.setColor(COLORS[0]);
} else {
paint2.setColor(COLORS[1]);
}
// draw the text
paint2.setTextSize(pixel);
float textWidth = paint2.measureText(percent + "%");
canvas.drawText(percent + "%", size / 2 - textWidth / 2 + PADDING,
size, paint2);
I have removed TextView and did few little changes, but the key was to draw it. Still dont understand why I was not able to write text on it but had to draw but it is working!