How to draw a circle with Bitmap pixels? - java

In my app I blend 2 Bitmaps, one Bitmap is on the screen and when i Swipe with my finger it reveals the matching pixels from the second Bitmap. The way I have now works but when I touch the screen it only reveals squares from the other Bitmap since pixels are squares, and I'm revealing a group of them by touching the screen.
How do I group pixels together to create a circle?
This is my current code:
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (!surfaceReady)
return false;
// Check if the touch pointer is the one you want
if (event.getPointerId(event.getActionIndex()) == 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// User touched screen...
case MotionEvent.ACTION_MOVE:
// User dragged his finger...
blend((int) event.getX(), (int) event.getY());
}
// Update the blending effect bitmap here and trigger a frame
// redraw,
// if you don't already have an animation thread to do it for you.
drawOverlays();
return true;
}
return false;
}
And the blending code:
// In order to make the effect more "visible" I've created a larger brush
public int BS = 50;
public int HBS = BS >> 1;
int[] pixels = new int[BS * BS];
private void blend(int x, int y) {
if (x >= HBS && y >= HBS && x < bmp2.getWidth() - HBS && x < operation.getWidth() - HBS && y < bmp2.getHeight() - HBS && y < operation.getHeight() - HBS) {
bmp2.getPixels(pixels, 0, BS, x - HBS, y - HBS, BS, BS);
operation.setPixels(pixels, 0, BS, x - HBS, y - HBS, BS, BS);
}
}

Related

Java - Pixel-Perfect Collision - gap between player and wall

I'm currently working on a Top-Down-Shooter and having some issues with collision.
My world is made of tiles (64x64). The tiles and the entities are rectangles. The player moves with a speed of e.g 2.74 (and not in pixels for smoother movement). But when it comes to the collision between the player (an entity) and a wall i have some issues. To check if there is a collision i take the current position of my player and his movement speed to calculate where his next position would be and if there is any collision. But i check every pixel on the way, so i cant skip an obstacle even if the movement speed is very high. Let's just say the players current position is X:200 Y:200 and he moves 2.74 Pixels a tick in the x direction. My game now checks if there is any collision at X:201 Y:200, X:202 Y:200 or X:202.74 Y:200 and if not moves the player to that position. If I now try to move the player further in the x direction and there is a wall 0.26 Pixels away the player wont move and leave a tiny gap. I tried to calculate the distance between player and wall and add this amount to the players position but for that I need to know which side of the wall the player hits. Also I want the player to be able to move up and down when the wall he hits is in front of him and the other way around.
Here is my collision method (in Java):
public static boolean collision(float ex, float ey, int width, int height) { // ex, ey would be the next position of the player
if (ex < 0 || ex + width > worldWidth || ey < 0 || ey + height > worldHeight) return true; // checks if this position is in the world
int firstTileX = (int) (ex / Tile.TILE_SIZE); // calculates tiles he could possible collide width
int firstTileY = (int) (ey / Tile.TILE_SIZE);
int lastTileX = (int) ((ex + width - 1) / Tile.TILE_SIZE);
int lastTileY = (int) ((ey + height - 1) / Tile.TILE_SIZE);
for (int y = firstTileY; y <= lastTileY; y++) {
if (y < 0) continue; // checks for out of bounds
if (y >= worldTileHeight) break;
for (int x = firstTileX; x <= lastTileX; x++) {
if (x < 0) continue;
if (x >= worldTileWidth) break;
if (tiles[y][x].solid) return true; // if the tile is solid -> collision found
}
}
return false; // no collision found
}
And my movement method:
public void move(float xa, float ya) {
float nx, ny;
while (xa != 0 || ya != 0) {
nx = x;
ny = y;
if (xa != 0) {
if (Math.abs(xa) > 1) { // if the x-speed is greater than 1
nx = x + MathUtil.abs(xa); // returns -1 for negative numbers and 1 for positiv
xa -= MathUtil.abs(xa);
} else { // less than 1
nx = x + xa;
xa = 0;
}
}
if (ya != 0) { // same here
if (Math.abs(ya) > 1) {
ny = y + MathUtil.abs(ya);
ya -= MathUtil.abs(ya);
} else {
ny = y + ya;
ya = 0;
}
}
if (!Level.collision(nx, ny, width, height)) setPosition(nx, ny); // checks if there is an collision and sets the new position if not
else if (!Level.collision(nx, y, width, height)) x = nx; // if there was a collision check if the player can walk in x direction
else if (!Level.collision(x, ny, width, height)) y = ny; // or in y direction
}
}
My problem is the pretty much the same as CoderMusgrove's problem in his post (Pixel-perfect collision and doubles):
Summary & Question
I have a problem where if the speed of an entity isgreater thanthe distance from the tile it is going into, it will leave at least a pixel in between itself and the tile, and I really don't like this. What kind of algorithm could I use that will find the tiniest difference between the entity and the tile?
If you need any additional information, I will be glad to add it.
Thanks for your help!
Easily resolvable by changing your interpretation.
You are retaining a fractional position for the purpose of fine grained speed. Ignore the fraction for the purpose of collision detection and display (if you were to do sub-pixel rendering, do the collision on the subpixel rendering accurarcy level).
int screenX = (int) Math.round(objX);
int screenY = (int) Math.round(objY);
// rendering and collision detection based on rounded position

Android- How to drag a picture on a canvas

I need to drag an image like in a videogame. I have a few lines drawed and I need to drag it and see the lines drawed before "out" of the window. I give you two images trying to help to understand it.
Image before drag:
Then I touch the screen with my finger and I make a move to the top of the screen, so this three lines have to be upper than the initial image, like this:
I don't know how to do this "drag efect", can someone help me? This is not an image as a .jpg file, is three lines drawed with "drawLine" on a canvas into an imageView.
My onTouchEvent Code and my drawRectangle code:
public boolean onTouch(View view, MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_UP:
{
x = event.getX();
y = event.getY();
getFirstFoot();
return true;
}
case MotionEvent.ACTION_MOVE:
{
ammountX = event.getX() - startX;
ammountY = event.getY() - startY;
canvas.save();
canvas.translate(ammountX, ammountY);
//canvas.restore();
imageView.invalidate();
return true;
}
case MotionEvent.ACTION_DOWN:
{
startX = event.getX();
startY = event.getY();
}
}
return true;
}
drawRectagle code:
private void drawRectangle(boolean erase)
{
buttonRed.setVisibility(View.VISIBLE);
buttonGree.setVisibility(View.VISIBLE);
buttonYellow.setVisibility(View.VISIBLE);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.ascensorsininfo);
canvas.drawBitmap(bitmap2, x - (bitmap2.getWidth() / 2), y - bitmap2.getHeight(), new Paint());
drawedPoints.add(String.valueOf(x - (bitmap2.getWidth() / 2)));
drawedPoints.add(String.valueOf(y - bitmap2.getHeight()));
imageView.invalidate();
y -= bitmap2.getHeight();
}
Any solution would be apreciated. Thanks!
Check out Canvas.translate
It will "move" everything you draw afterwards the ammount you say in x and y axis.
Don't forget to save and restore it, like:
canvas.save();
canvas.translate(ammountX, ammountY);
// [draw your stuff here]
canvas.restore();
If your question is more towards how to detect the dragging effect, you have to Override your view's onTouchEvent method. Something like:
#Override
public boolean onTouchEvent (MotionEvent event){
if(event.getActionMasked() == MotionEvent.DOWN){
startX = event.getX();
startY = event.getY();
return true;
}else if(event.getActionMasked() == MotionEvent.MOVE){
ammountX = event.getX() - startX;
ammountY = event.getY() - startY;
return true;
}
return super.onTouchEvent(event);
}

Collision detection while using rectangles

So I understand that I'm not coding this the best way possible at the moment; this is a sort of test run. What I'm trying to do is wall collisions using rectangles and the intersects property (sorry if I'm not using the correct terminology). So far I have 2 rectangles on screen. 1 the player controls and the other which the play is colliding with. When they collide the player stops moving. The problem is that if the player is trying to move into the rectangle while they are already colliding then the player can't move in any direction perpendicular to the movement ie if the player is holding the right arrow key moving into the rectangle, then they cannot move up or down. The game works on the premise that if your x or y coordinates aren't valid, then you will be moved back to the last valid coordinate recorded but I'm having trouble detecting the valid x and y coordinate separately. Here is the code:
public void Collision()
{
if(x < 0)
x = 0;
if(x > 400 - width)
x = 400 - width;
if(y < 0)
y = 0;
if(y > 300 - height)
y = 300 - height;
rect1 = new Rectangle(x, y, 16, 16);
rect2 = new Rectangle(sx, sy, wid, hei);
if(!rect1.intersects(rect2))
{
validX = true;
validY = true;
}
else
{
validX = false;
validY = false;
}
if(validX)
{
lastValidX = x;
}
if(validY)
{
lastValidY = y;
}
if(!validX)
{
x = lastValidX;
}
if(!validY)
{
y = lastValidY;
}
}
The Collision() method in the Guy class is where I'm having the trouble I believe. Yes my code is pretty messy right now but this is only a test.
Thanks, David.
You can implement what you're describing by doing extra logic around here (i.e. detecting cases when one is false and the other is true):
if(!rect1.intersects(rect2))
{
validX = true;
validY = true;
}
else
{
validX = false;
validY = false;
}
However, it seems like maybe you shouldn't be allowing the rectangles to ever be in a "colliding" state in the first place. For example, you can change the Move method to do something like
public void Move()
{
int oldX = x, oldY = y;
x += dx;
y += dy;
if (Collision()) {
x = oldX;
y = oldY;
}
}

how do i change specific rect color on a canvas in android?

ive been trying to change the color of specific rects in an array of rects when i touch the screen but it doesnt seem to be working, heres my code:
public Paint blue = new Paint();
RandomColorGen rc;
ArrayList<Integer> colors = RandomColorGen.ColorList(5);
Random rand = new Random();
int columns = 50;
int rows = 50;
Rect square[][] = new Rect[rows][columns];
public boolean isTouched;
public Canvas canvas;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
for (int x = 0; x < rows; x++) {
for (int y = 0; y < columns; y++) {
square[x][y] = new Rect();
blue.setColor(colors.get(rand.nextInt(colors.size())));
blue.setStyle(Paint.Style.FILL);
square[x][y].set(0, 0, (canvas.getWidth() - 10) / rows,
((canvas.getHeight() - 100) / columns));
square[x][y].offsetTo(x * ((canvas.getWidth() - 10) / rows), y
* ((canvas.getHeight() - 100) / columns));
canvas.drawRect(square[x][y], blue);
}
}
if(isTouched){
blue.setColor(colors.get(rand.nextInt(colors.size())));
blue.setStyle(Paint.Style.FILL);
canvas.save();
canvas.clipRect(square[1][1]);
canvas.drawRect(square[1][1], blue);
canvas.restore();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouched = true;
break;
}
return true;
}
the colors.get() thingy is an arraylist of colors. am i taking the wrong approach?
How you are calling the paint function after the onTouch action performed...
OnTouch action is the first action so how do you call Paint() function??
I just tested your code, it works but there are couple of notes worth mentioning:
Allocating objects during drawing is a very bad behavior ( specially when allocating 50*50 ) ! consider moving the allocation code to the constructor and change position of the rectangles at the onDraw() method if you want to achieve the same behavior you have now.
Your usage for onTouchEvent() is not complete, you need to have the isTouched true as long as the user didn't raise his hand, which can be done as the following :
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isTouched = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
isTouched = false;
break;
}
invalidate();
return true;
}
Also request a layout every time you receive a TouchEvent by using invalidate()

Joining shapes together and tracing their outline

EDIT: Found solution. I've added it after the question.
I'm designing a game for Android and I'm trying to come up with ways to reduce the calculations at the render stage. I've got a method at the moment which takes all the metal and rubber blocks in the level and stores a texture id into a int[][] grid so that the renderer just reads that instead of calculating every blocks tiled textures every frame.
This works fine but now I'm trying to create a list of corner and straight pieces for the level edges and the laser blocks in the level. The level bounds and laser blocks are drawn using a set of straight laser textures and corner textures. I'm not sure how best to tackle working out where not to render lasers where blocks overlap with other blocks and with the level edges. The pictures below shows what I mean:
drawing
ingame
Here you can see the level edge (the L shaped laser path extending beyond the pictures edges) and three/two internal laser blocks (respectively to picture order). As far as I can tell I should create a similar grid to above but with booleans so any square that kills the player on upon touching (highlighted in red) is true and the safe squares are false.
I then first thought of going through all the true (red) cells in the grid and work out what the laser outline would look like using their neighbouring grid cells but I realised this could very difficult so I'm certain now that I use the false squares to find it out. I'm sure I could get a rough solution working by starting at the lower left square of the level bounds and iterate through the grid until I find a false tile (unless the first square is false) and then travel through the grid going right until I reach a true cell to the right and so I would turn left and continue up through the grid until a true is found above to turn left OR a false is found on the right to turn right. I'd repeat this process until I reach my starting false cell.
I came up with this while writing this question out lol. It seems like the easiest way so I guess my question is is this a good way to do it and how would I work out the laser blocks which touch each other but not touch the level bounds as the above method would only trace the outer most laser path.
Thank you for taking the time to read through this. I hope I've explained it well enough and I look forward to any light that can be shed on this subject.
SOLUTION:
public static boolean[][] laserField = new boolean[(int) Static.WORLD_SIZE][(int)Static.WORLD_SIZE];
public static List<LaserData> laserData = new ArrayList<LaserData>();
public static void calcLaserBoundryAreas() {
laserField = new boolean[(int) Level.levelBounds.bounds.width+5][(int) Level.levelBounds.bounds.height+5];
for (int i=0;i<laserField.length;i++) {
for (int j=0;j<laserField[i].length;j++) {
if(i==0 || i==laserField.length-1 || j==0 || j==laserField[i].length-1)
laserField[i][j] = true;
else
laserField[i][j] = false;
}
}
for (LaserBlock lBlock : lBlocks) {
int cols = (int)lBlock.bounds.width;
int rows = (int)lBlock.bounds.height;
float startX = lBlock.position.x - (cols-1f)/2f;
float startY = lBlock.position.y - (rows-1f)/2f;
for (int i=0;i<cols;i++) {
for (int j=0;j<rows;j++) {
addLaserCell(startX+i, startY+j);
}
}
}
addLaserData();
}
private static void addLaserCell(float x, float y) {
int cellX = (int)(x- Level.levelBounds.bounds.lowerLeft.x+2);
int cellY = (int)(y- Level.levelBounds.bounds.lowerLeft.y+2);
if (cellX < 0 || cellX > laserField.length-1) return;
if (cellY < 0 || cellY > laserField[cellX].length-1) return;
laserField[cellX][cellY] = true;
}
private static void addLaserData() {
laserData = new ArrayList<LaserData>();
for (int i=1;i<laserField.length-1;i++) {
for (int j=1;j<laserField[i].length-1;j++) {
if (!laserField[i][j]) {
checkNeighbours(i,j);
}
}
}
optimiseLaserData();
}
private static void checkNeighbours(int x, int y) {
boolean u = laserField[x][y+1];
boolean ul = laserField[x-1][y+1];
boolean l = laserField[x-1][y];
boolean bl = laserField[x-1][y-1];
boolean b = laserField[x][y-1];
boolean br = laserField[x+1][y-1];
boolean r = laserField[x+1][y];
boolean ur = laserField[x+1][y+1];
/*
* TOP LEFT CORNER
*/
float posX, posY;
posX = Level.levelBounds.bounds.lowerLeft.x+x-2.5f;
posY = Level.levelBounds.bounds.lowerLeft.y+y-1.5f;
if(u && ul && l)
laserData.add(new LaserData(posX, posY, true, 0, 0));
else if(!u && ul && l)
laserData.add(new LaserData(posX, posY, false, 1, 0));
else if(!u && ul && !l)
laserData.add(new LaserData(posX, posY, true, 0, 2));
/*
* BOTTOM LEFT CORNER
*/
posX = Level.levelBounds.bounds.lowerLeft.x+x-2.5f;
posY = Level.levelBounds.bounds.lowerLeft.y+y-2.5f;
if(l && bl && b)
laserData.add(new LaserData(posX, posY, true, 0, 1));
else if(!l && bl && b)
laserData.add(new LaserData(posX, posY, false, 1, 1));
else if(!l && bl && !b)
laserData.add(new LaserData(posX, posY, true, 0, 3));
/*
* BOTTOM RIGHT CORNER
*/
posX = Level.levelBounds.bounds.lowerLeft.x+x-1.5f;
posY = Level.levelBounds.bounds.lowerLeft.y+y-2.5f;
if(b && br && r)
laserData.add(new LaserData(posX, posY, true, 0, 2));
else if(!b && br && r)
laserData.add(new LaserData(posX, posY, false, 1, 2));
else if(!b && br && !r)
laserData.add(new LaserData(posX, posY, true, 0, 0));
/*
* TOP RIGHT CORNER
*/
posX = Level.levelBounds.bounds.lowerLeft.x+x-1.5f;
posY = Level.levelBounds.bounds.lowerLeft.y+y-1.5f;
if(r && ur && u)
laserData.add(new LaserData(posX, posY, true, 0, 3));
else if(!r && ur && u)
laserData.add(new LaserData(posX, posY, false, 1, 3));
else if(!r && ur && !u)
laserData.add(new LaserData(posX, posY, true, 0, 1));
}
private static void optimiseLaserData() {
List<LaserData> optiLaserData = new ArrayList<LaserData>();
for(LaserData ld : laserData) {
if(ld.cornerPiece)
optiLaserData.add(ld);
else if(ld.dir == 0 || ld.dir == 2){
float x = ld.x;
float bottomY = ld.y;
float topY = ld.y;
float count = 1;
while (searchStraightLaserData(laserData, x, topY+1, ld.dir)) {
count++;
topY++;
}
while (searchStraightLaserData(laserData, x, bottomY-1, ld.dir)) {
count++;
bottomY--;
}
float centerY = bottomY + (topY-bottomY)/2;
if(!searchStraightLaserData(optiLaserData, x, centerY, ld.dir))
optiLaserData.add(new LaserData(x, centerY, false, count, ld.dir));
} else {
float y = ld.y;
float leftX = ld.x;
float rightX = ld.x;
float count = 1;
while (searchStraightLaserData(laserData, rightX+1, y, ld.dir)) {
count++;
rightX++;
}
while (searchStraightLaserData(laserData, leftX-1, y, ld.dir)) {
count++;
leftX--;
}
float centerX = leftX + (rightX-leftX)/2;
if(!searchStraightLaserData(optiLaserData, centerX, y, ld.dir))
optiLaserData.add(new LaserData(centerX, y, false, count, ld.dir));
}
}
laserData = optiLaserData;
}
private static boolean searchStraightLaserData(List<LaserData> data, float x, float y, int dir) {
for(LaserData ld : data)
if(ld.x == x && ld.y == y && ld.dir == dir && !ld.cornerPiece)
return true;
return false;
}
These methods first create a boolean grid that is the size of the level edge bounds with a 1 square extra edge on each side. This is initialised to false to represent safe areas and the extra edge is set to true so that we have a kind of hollow box. The extra edge helps later by eliminating the need to check for incorrect indices on the laserField.
After the level extents are mapped to a grid individual cells are changed to true where ever is covered by a laser block.
Once the boolean grid is fully mapped it then iterates through each grid cell and when it finds a cell which is false it passes it grid coordinates to the next method which looks at 12 different neighbour patterns to determine if any lasers should be rendered around this cell. The LaserData constructor takes the following args (float x, float y, boolean cornerPiece, float length, int direction)
The last section does a brute force search to check if any adjacent straight pieces can be replaced by a single longer straight piece to save rendering extra sprites.
The renderer can then just read through the laserData list each frame and it has all the information it needs to render the correct texture, its position, length etc...
NOTE: The width and height of the Level bounds are 3 units smaller than the actual playing area to account for width of player outside the boundry. That's where the levelBounds.lowerleft+5, +2 and + 1.5f etc... come from. It's a little hacky I know but it's old code and I daren't touch it xD

Categories