For my android game I use Libgdx and I detect the collision between Bob (Omino) and Plant (Pianta) with this code that works fine :
Assets.class
pianta = new Animation(0.5f,new TextureRegion(items, 160, 384, 64, 96),
new TextureRegion(items, 224, 384, 64, 96));
Pianta.class
public class Pianta extends GameObject {
public static final float PIANTA_WIDTH = 2;
public static final float PIANTA_HEIGHT = 3;
public static float stateTime;
public Pianta(float x, float y) {
super(x, y, PIANTA_WIDTH, PIANTA_HEIGHT);
stateTime = 0;
}
public void update(float deltaTime) {
stateTime += deltaTime;
}
}
World.class
Pianta pianta1_0 = new Pianta(x+10,2.2f);
piante.add(pianta1_0);
private void collisionPiante(){
int len = piante.size();
for(int i=0;i<len;i++){
if(OverlapTester.overlapRectangles(piante.get(i).bounds,omino.bounds)){
omino.ominoMorto();
}
}
}
WorldRender.class
private void renderPiante() {
TextureRegion keyFrame;
int len = world.piante.size();
for(int i = 0; i < len; i++) {
Pianta pianta = world.piante.get(i);
keyFrame = Assets.pianta.getKeyFrame(Pianta.stateTime, Animation.ANIMATION_LOOPING);
batcher.draw(keyFrame,pianta.position.x, pianta.position.y, 2, 3);
}
}
but if you watch the image 2 below, you can see that Bob hit but there isn't collision with stone (Pietra) !!
This is the code :
Assets.class
pietra1 = new TextureRegion(items,288,416,128,64);
Pietra.class
public class Pietra extends GameObject {
public static float PIETRA_WIDTH = 4;
public static float PIETRA_HEIGHT = 2;
public Pietra(float x, float y) {
super(x, y, PIETRA_WIDTH, PIETRA_HEIGHT);
}
}
World.class
Pietra pietra1_0 = new Pietra(x+25,2.2f);
pietre.add(pietra1_0);
private void collisionPietre(){
int len2 = pietre.size();
for(int l=0;l<len2;l++){
if(OverlapTester.overlapRectangles(pietre.get(l).bounds,omino.bounds)){
omino.ominoMorto();
}
}
}
WorldRender.class
private void renderPietre() {
int len = world.pietre.size();
for(int i = 0; i < len; i++) {
Pietra pietra = world.pietre.get(i);
batcher.draw(Assets.pietra1,pietra.position.x, pietra.position.y, 4, 2);
}
}
OverlapTester
public class OverlapTester {
public static boolean overlapRectangles (Rectangle r1, Rectangle r2) {
if (r1.x < r2.x + r2.width && r1.x + r1.width > r2.x && r1.y < r2.y + r2.height && r1.y + r1.height > r2.y)
return true;
else
return false;
}
Someone can tell me why the collision with the plant works fine and with stone Bob hit even if there is no collision? as you can see the code is the same, the only difference is that the plant is an animated object while the stone isn't.
Check your OverlapTester. This is how Libgdx does it in the Rectangle.java class:
/** #param rectangle the other {#link Rectangle}
* #return whether this rectangle overlaps the other rectangle. */
public boolean overlaps (Rectangle rectangle) {
return !(x > rectangle.x + rectangle.width || x + width < rectangle.x || y > rectangle.y + rectangle.height || y + height < rectangle.y);
}
If I understood right overlapRectangles checks the case if rectangle is totally inside. It is not probably thing you want.
LibGDX has special functionality for collision checking. Please, check http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Intersector.html
You may wish to replace your OverlapTester with the Rectangle's helper function contains. For instance:
World Class
if(OverlapTester.overlapRectangles(piante.get(i).bounds,omino.bounds)){
omino.ominoMorto();
}
Can be:
if (piante.get(i).bounds.contains(omino.bounds)) {
omino.ominoMorto();
}
Related
I have a little problem with a simple path following AI.
If i use setRotation() the sprite has a sudden rotation but I need to rotate it in a gradual and natural way.
I have very low knowledge of trigonometry so i ask here.
public class AISprite extends Sprite {
private Vector2 velocity = new Vector2();
private float speed = 100;
private Array<Vector2> path; // it contains viewpoints
private int waypoint = 0; // index of actual waypoint
public AISprite(Sprite sprite, Array<Vector2> path){
super(sprite);
this.path = path;
}
#Override
public void draw(SpriteBatch spriteBatch){
update(Gdx.graphics.getDeltaTime());
super.draw(spriteBatch);
}
public void update(float delta){
float angle = (float) Math.atan2(path.get(waypoint).y - getY(), path.get(waypoint).x - getX());
velocity.set((float) Math.cos(angle) * speed,(float) Math.sin(angle)*speed);
rotation(angle);
// if the last waypoint is reached, start again
if(isWaypointReached()){
if(waypoint + 1 >= path.size)
waypoint = 0;
else
waypoint++;
}
}
private void rotazione(float angle){
setRotation(angle*MathUtils.radiansToDegrees);
}
private boolean isWaypointReached() {
return Math.abs(path.get(waypoint).x - getX()) <= speed * Gdx.graphics.getDeltaTime()
&&
Math.abs(path.get(waypoint).y - getY()) <= speed * Gdx.graphics.getDeltaTime();
}
public Array<Vector2> getPath(){
return path;
}
public int getWaypoint(){
return waypoint;
}
As this seems to be a recurring topic on 'the stack', I am going to reinforce my problem as something not covered. What has been covered is 2D tile collision for platform games etc., but with the way I have made my game, there are no tiles. I am also using no extra libraries, everything is written by my own hand.
What I have is bounding Rect's for every object in the game. So far there are only two object classes in use, Platform and Entity. Entity contains all the stuff for player movement etc. while Platform is for a solid non-moving platform.
Platform.java:
package com.toongames.game.objects;
import android.graphics.Color;
import android.graphics.Rect;
import com.toongames.framework.Graphics;
public class Platform {
private int x, y;
private int width, height;
public Platform(int par1, int par2, int par3, int par4) {
x = par1;
y = par2;
width = par3;
height = par4;
}
public Rect getBounds() {
return new Rect(x, y, x + width, y + height);
}
}
Entity.java:
package com.toongames.game.entity;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import com.toongames.framework.Graphics;
import com.toongames.framework.Image;
public class Entity {
public final float GRAVITY = 0.1F;
private String entityID;
private Point pos;
private int dx;
private float vel;
public Point desiredPos;
public boolean onGround;
public Entity(String par0String, int par1, int par2) {
entityID = par0String;
pos = new Point(par1, par2);
desiredPos = pos;
dx = 0;
vel = 0;
}
public void update(float deltaTime) {
vel = vel + (GRAVITY * deltaTime);
pos.y += (vel * deltaTime);
pos.x += dx;
}
public void setDx(int par1) {
dx = par1;
}
public int getDx() {
return dx;
}
public void setVelocity(int par1) {
vel = par1;
}
public float getVelocity() {
return vel;
}
public void setPos() {
pos = desiredPos;
}
public Rect getBounds() {
return new Rect(desiredPos.x, desiredPos.y, desiredPos.x + 80, desiredPos.y + 80);
}
}
I have successfully made the player collide with things both up and down, but I cannot for the life of me manage to make the player collide right and left. Whenever I collide with a platform while moving left or right, I just jump to the top of the platform I collided with.
I know it has something to do with my logic, but I cannot figure out the correct logic to use.
ScreenGame.java:
package com.toongames.game.screen;
// Imports here...
public class ScreenGame extends Screen {
private Entity player;
private Button left, right, jump;
private Platform floor, p, p2, p3;
private ArrayList<Platform> platforms;
public ScreenGame(Game game) {
super(game);
player = new Entity("PLAYER", 300, 100, Assets.charRight);
left = new Button(Assets.move_left, 10, 790 - Assets.move_left.getHeight(), Assets.move_left.getWidth(), Assets.move_left.getHeight());
right = new Button(Assets.move_right, 20 + Assets.move_left.getWidth(), 790 - Assets.move_right.getHeight(), Assets.move_right.getWidth(), Assets.move_right.getHeight());
jump = new Button(Assets.jump, 1270 - Assets.jump.getWidth(), 790 - Assets.jump.getHeight(), Assets.jump.getWidth(), Assets.jump.getHeight());
floor = new Platform(0, 790, 1280, 80);
p = new Platform(1280 - 500, 500, 400, 80);
p2 = new Platform(0, 200, 400, 80);
p3 = new Platform(400, 120, 200, 80);
platforms = new ArrayList<Platform>();
platforms.add(floor);
platforms.add(p);
platforms.add(p2);
platforms.add(p3);
}
// An update method calls these
public void updateMovement(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_DOWN) {
if (inBounds(left.getBounds(), event)) {
player.setDx((int) -(deltaTime * 1.5F));
} else if (inBounds(right.getBounds(), event)) {
player.setDx((int) deltaTime * 2);
} else if (inBounds(jump.getBounds(), event)) {
if (player.onGround) {
player.setVelocity(-8);
}
}
} else if (event.type == TouchEvent.TOUCH_DRAGGED) {
if (inBounds(left.getBounds(), event)) {
player.setDx((int) -deltaTime * 2);
} else if (inBounds(right.getBounds(), event)) {
player.setDx((int) deltaTime * 2);
} else if (inBounds(jump.getBounds(), event)) {
if (player.onGround) {
player.setVelocity(-8);
}
} else {
player.setDx(0);
player.jumpCounter = 0;
}
} else if (event.type == TouchEvent.TOUCH_UP) {
player.setDx(0);
player.jumpCounter = 0;
}
}
}
// An update method calls these
public void updateGameObjects(float deltaTime) {
for (Platform p : platforms)
p.update();
player.update(deltaTime);
}
// An update method calls these
public void checkCollisions() {
Rect playerRect = player.getBounds();
for (Platform p : platforms) {
Rect pRect = p.getBounds();
if (Rect.intersects(playerRect, pRect)) {
Rect intersection = playerRect;
intersection.intersect(pRect);
if (player.getVelocity() != player.GRAVITY) {
int resolutionHeight;
if (player.getVelocity() < player.GRAVITY)
resolutionHeight = intersection.height();
else {
resolutionHeight = -intersection.height();
player.onGround = true;
}
player.setVelocity(0);
player.desiredPos = new Point(player.desiredPos.x, player.desiredPos.y + resolutionHeight);
}
}
}
player.setPos();
}
}
As an extra note, I have cut out some of the unnecessary code to do with images for the entity and entity health etc.. Also I have cut out empty methods and stuff like that that have no relevance what so ever.
[EDIT] Cut out most of the drawing code and imports. All the absolutely necessary stuff is there now.
player.desiredPos = new Point(player.desiredPos.x, player.desiredPos.y + resolutionHeight);
isn't this "move above, never right/left?"
I think your Rect.intersects method should return one of { NONE, LEFT, RIGHT, UP, DOWN } indicating in which direction the collision occured. So you can react to the collision in the right direction...
I've just started with Android programming using eclipse and recently came across this problem. I have a bunch of sprites assigned to an arraylist. Now, I want to make it so that collision is detected automatically between sprites but the template I'm currently using can only detect collision between the surface's borders and the moving sprites. Each sprite's position and speed is generated randomly.
How can I change the update() function in my Sprite() class to detect collision between the moving sprites themselves and at the same changing/bouncing to the opposite direction?
Here's my Sprite class template:
package com.gameproject.cai_test;
import java.util.Random;
public Sprite(GameView gameView, Bitmap bmp) {
this.width = bmp.getWidth() / BMP_COLUMNS;
this.height = bmp.getHeight() / BMP_ROWS;
this.gameView = gameView;
this.bmp = bmp;
Random rnd = new Random();
x = rnd.nextInt(gameView.getWidth() - width);
y = rnd.nextInt(gameView.getHeight() - height);
xSpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;
ySpeed = rnd.nextInt(MAX_SPEED * 2) - MAX_SPEED;
}
private void update() {
if (x >= gameView.getWidth() - width - xSpeed || x + xSpeed <= 0) {
xSpeed = -xSpeed;
}
x = x + xSpeed;
if (y >= gameView.getHeight() - height - ySpeed || y + ySpeed <= 0) {
ySpeed = -ySpeed;
}
y = y + ySpeed;
currentFrame = ++currentFrame % BMP_COLUMNS;
}
public void onDraw(Canvas canvas) {
update();
int srcX = currentFrame * width;
int srcY = getAnimationRow() * height;
Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
Rect dst = new Rect(x, y, x + width, y + height);
canvas.drawBitmap(bmp, src, dst, null);
}
private int getAnimationRow() {
double dirDouble = (Math.atan2(xSpeed, ySpeed) / (Math.PI / 2) + 2);
int direction = (int) Math.round(dirDouble) % BMP_ROWS;
return DIRECTION_TO_ANIMATION_MAP[direction];
}
//gameplay operations
//only values from 0 to 9 will be picked; each assigned its own sprite in the list
public int randomValue(){
Random rnd = new Random();
RandomValue = rnd.nextInt(10);
return RandomValue;
}
//sequence operation from addition, subtraction, multiplication, and division
public int produceSum(){
int addOne = 0;
int addTwo = 0;
Sum = addOne + addTwo;
return Sum;
}
public int produceDiff(){
int deductOne = 0;
int deductTwo = 0;
Difference = deductOne - deductTwo;
return Difference;
}
public int produceProduct(){
int multiOne = 0;
int multiTwo = 0;
Product = multiOne * multiTwo;
return Product;
}
public int produceQuotient(){
int divideOne = 0;
int divideTwo = 0;
Quotient = divideOne / divideTwo;
return Quotient;
}
//each time this returns true, the game is reset with new operation
//compares the value of the bubble picked to the random number being compared through operations
public boolean compareBubbleValue(int randomBubble, int bubbleValue){
if (randomBubble == bubbleValue){
return true;
}
return false;
}
}
As you can see, the update() method only checks the collision between the moving sprites and the borders.
Okey, so lets name your array of sprites spriteArray and loop through it twice
public Rect getBounds(){ //put this in your sprite and enemy-class.
return new Rect(x, y, x+width, y+height);
}
Public void checkCollision(){
for (int i = 0; i<spriteArray.size(); i++){
Rect mySprite = spriteArray.get(i).getBounds(); //create rect for every sprite in array
for (int j = 0; j<spriteArray.size(); i++){
Rect myOtherSprite = spriteArray.get(i).getBounds();
if(mySprite.intersect(myOtherSprite)){ //check if they touch
//Todo code here
}
}
}
}
then you just put this method in your update-method.
Here's the coding for the GameView class (pardon the mess, it's a jumble of codes and comments):
package com.gameproject.cai_test;
public class GameView extends SurfaceView {
private Bitmap bmp;
private Bitmap background;
private Bitmap backgroundImage;
private Bitmap pop;
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Sprite> sprites = new ArrayList<Sprite>();
private List<TempSprite> temps = new ArrayList<TempSprite>();
private int[] bubbleValue = {0,1,2,3,4,5,6,7,8,9,10};
private long lastClick;
private SpriteObject timer;
private SpriteObject morebanner;
private SpriteObject formulaBox;
private SpriteObject levelbanner1;
private SurfaceHolder surfaceHolder;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
timer = new SpriteObject (BitmapFactory.decodeResource(getResources(), R.drawable.hourglass), 1200, 100);
morebanner = new SpriteObject(BitmapFactory.decodeResource(getResources(), R.drawable.morebanner), 650, 300);
formulaBox = new SpriteObject(BitmapFactory.decodeResource(getResources(), R.drawable.formulabox), 650, 600);
background = BitmapFactory.decodeResource(getResources(), R.drawable.background);
backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.background);
pop = BitmapFactory.decodeResource(getResources(), R.drawable.pop);
final Toast toast1 = Toast.makeText(context, "LEVEL 1 start", Toast.LENGTH_LONG);
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
//setSurfaceSize(getWidth(), getHeight());
toast1.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast1.show();
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
//banner();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
}
private void createSprites() {
sprites.add(createSprite(R.drawable.bubble1));
sprites.add(createSprite(R.drawable.bubble2));
sprites.add(createSprite(R.drawable.bubble3));
sprites.add(createSprite(R.drawable.bubble4));
sprites.add(createSprite(R.drawable.bubble5));
sprites.add(createSprite(R.drawable.bubble6));
sprites.add(createSprite(R.drawable.bubble7));
sprites.add(createSprite(R.drawable.bubble8));
sprites.add(createSprite(R.drawable.bubble9));
sprites.add(createSprite(R.drawable.bubble10));
for (int i = 0; i <= 10; i++){
bubbleValue[i] = sprites.indexOf(i);
}
}
private Sprite createSprite(int resource) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
return new Sprite(this, bmp);
}
public void setSurfaceSize(int width, int height)
{
synchronized (surfaceHolder)
{
int canvasWidth = width;
int canvasHeight = height;
backgroundImage = Bitmap.createScaledBitmap(backgroundImage, width, height, true);
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE);
//levelbanner1.draw(canvas);//causes error when applied;for reference only
for (int i = temps.size() - 1; i >= 0; i--) {
temps.get(i).onDraw(canvas);
}
for (Sprite sprite : sprites) {
timer.draw(canvas);
//formulaBox.draw(canvas);
sprite.onDraw(canvas);
}
if (sprites.size() == 0){
morebanner.draw(canvas);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 300) {
lastClick = System.currentTimeMillis();
float x = event.getX();
float y = event.getY();
synchronized (getHolder()) {
for (int i = sprites.size() - 1; i >= 0; i--) {
Sprite sprite = sprites.get(i);
if (sprite.isCollition(x, y)) {
sprites.remove(sprite);
temps.add(new TempSprite(temps, this, x, y, pop));
break;
}
}
}
}
return true;
}
public void update(){
//for possible check of collision bet. sprites
//
}
}
I've tried assigning a Rect for the sprites here and checking collision on the bottom update() function but result gets awry and produces a runtime error. Probably would be better if its automated in the Sprite class update() function, just as it does for border collision.
If you want to have nice and proper collisions between your sprites, maybe looking at a physics engine like http://www.jbox2d.org/ would help.
It will handle a lot of the complex specific cases for you (tunnelling, time of impact, broadphase for early discard...)
I want to move background with the bob(Android Game Character) moving for that I make a Dynamic object name Background
public static int BACKGROUND_MOVE=0;
public static int BACKGROUND_FALL=1;
public static final float BACKGROUND_MOVE_VELOCITY =3.5f;
public static float BOB_WIDTH =10.0f;
public static float BOB_HEIGHT =15.0f;
public static long startTime = 0;
public int state;
public float stateTime;
public BackGround(float x, float y)
{
super(x, y, BOB_WIDTH, BOB_HEIGHT);
state = BACKGROUND_MOVE;
stateTime = 0;
accel.set(0,-2);
velocity.y=3.5f;//55
}
public void update (float deltaTime)
{
//velocity.add(World.gravity.x, World.gravity.y * deltaTime);
velocity.add(accel.x * deltaTime,accel.y*deltaTime);
position.add(velocity.x * deltaTime, velocity.y * deltaTime);
if (velocity.y > 0 && state==Bob.BOB_STATE_HIT)//BOB_STATE_HIT is bob running //condition
{
if (state != BACKGROUND_MOVE)
{
state = BACKGROUND_MOVE;
stateTime = 0;
}
}
if (velocity.y > 0 && state != Bob.BOB_STATE_HIT)
{
if (state != BACKGROUND_FALL)
{
state = BACKGROUND_FALL;
stateTime = 0;
}
}
// if (velocity.y < 0 && state == BOB_STATE_HIT)
// {
// if (state != BOB_STATE_JUMP) {
// state = BOB_STATE_JUMP;
// stateTime = 0;
// }
//}
//if (position.y < 0) position.x = World.WORLD_WIDTH;
//if (position.x > World.WORLD_WIDTH) position.x = 0;
stateTime += deltaTime;
}
public void move()
{
if(state==BACKGROUND_MOVE)
{
startTime=System.nanoTime()/1000000000;
// state = BACKGROUND_MOVE;
velocity.y = BACKGROUND_MOVE_VELOCITY;
stateTime = 0;
}
}
similar to bob I amke object in World class and make an ArrayList Add Backgroung into that arraylist and at time of drawing get from arraylist and draw it...but no any effect show simply screen show and cross the rangeand red screen shown...please anyone help..
/*---------------background cam------*/
this.bcam = new OrthographicCamera(FRUSTUM_WIDTH,FRUSTUM_HEIGHT);
this.bcam.position.set(FRUSTUM_WIDTH,FRUSTUM_HEIGHT, 0);
this.batch = batch;
public void renderBackground()
{
GLCommon gl = Gdx.gl;
gl.glClearColor(1, 0, 0, 1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
bcam.update();
batch.setProjectionMatrix(bcam.combined);
batch.disableBlending();
batch.begin();
if (world.objbackground.state ==BackGround.BACKGROUND_MOVE)
batch.draw(Assets.mainbackgroundRegion, cam.position.x - FRUSTUM_WIDTH / 2, cam.position.y - FRUSTUM_HEIGHT / 2, FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
// else
// {
// batch.draw(Assets.touchbackgroundRegion, cam.position.x -
// FRUSTUM_WIDTH / 2, cam.position.y - FRUSTUM_HEIGHT / 2,
// FRUSTUM_WIDTH, FRUSTUM_HEIGHT);
// if (elapsed == 5)
// changebackground = 0;
// }
batch.end();
}
Either you want to move the background with Bob (1) or you want to have a fixed background (2):
(1): Set background's position to Bob's (probably with an offset, you can do that in the update function of the world), render using batch.draw(......, background.position.x, background.position.y).
(2): Throw away your complicated update methods in the BackGround object and use the render method as you do now.
But honestly, your intentions and explanations are not very clear, try to improve on that!
i am able to get my bitmap set of points (as an array) using this link
now my question is how can i bound these points as shape/region. Means when user touched on area of my bounded points, i want to move objects(shape) according to that. Above link return points of colored bitmap (it remove transparent part), only colored part points are return as an array.
This is what my code :
1) CustomSahpe.java
public class CustomShape {
private final Context context;
Bitmap bitmap;
int width, height;
int[] pixels;
private final ArrayList<Point> points = new ArrayList<Point>();
public CustomShape(Context context) {
// TODO Auto-generated constructor stub
// super(context);
this.context = context;
bitmap = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_menu_balloon);
width = bitmap.getWidth();
height = bitmap.getHeight();
pixels = new int[width * height];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
getActualBitmap();
}
public ArrayList<Point> getPoints(){
return points;
}
public void getActualBitmap() {
for (int x = 0; x < width; x+=2) {
int firstY = -1, lastY = -1;
for (int y = 0; y < height; y+=2) {
boolean transparent = (pixels[y * width + x] == Color.TRANSPARENT);
if (!transparent) {
if (firstY == -1) {
firstY = y;
}
lastY = y;
}
}
if (firstY != -1) {
points.add(new Point(x, firstY));
points.add(new Point(x, lastY));
}
}
}
}
2) MyShapre.java
class MyShape{
CustomShape customShape ;
Point points[];
private int x, y;
Path path = new Path();
public MyShape(Context context) {
customShape = new CustomShape(ScaleTestActivity.this);
points = new Point[customShape.getPoints().size()];
for(int i=0;i<customShape.getPoints().size();i++){
points[i] = new Point();
points[i] = customShape.getPoints().get(i);
}
}
public Path getPath(){
return path;
}
public void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
Paint paint = new Paint();
paint.setColor(Color.WHITE);
for(int i =0 ;i<points.length;i++){
Point point = new Point(points[i].x + getX(), points[i].y + getY());
path.lineTo(points[i].x, points[i].y);
canvas.drawPoint(point.x,point.y,paint);
}
}
public void setX(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setY(int y) {
this.y = y;
}
public int getY() {
return y;
}
}
}
3) MainPanel.java
class MainPanel extends View{
Context context;
MyShape myShape;
boolean flag = false;
public MainPanel(Context context) {
super(context);
this.context = context;
myShape = new MyShape(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.RED);
myShape.onDraw(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int x,y;
x = (int)event.getX();
y = (int)event.getY();
Point point = new Point(x, y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
myShape.setX(x);
myShape.setY(y);
RectF rectF = new RectF();
Path path = myShape.getPath();
path.computeBounds(rectF, true);
Region region = new Region();
region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));
if(region.contains(x,y)){
flag = true;
Log.i("System out","onDown");
}
break;
case MotionEvent.ACTION_MOVE:
Log.i("System out","onMove : "+flag);
if(flag){
myShape.setX(x);
myShape.setY(y);
Log.i("System out","onMove");
}
break;
case MotionEvent.ACTION_UP:
// myShape.setX(x);
// myShape.setY(y);
flag = false;
Log.i("System out","onUp");
break;
default:
break;
}
invalidate();
return true;
}
}
4) ScaleTestActivity.java
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MainPanel(this));
}
I use a Polygon class to detect touches on rotated bitmaps. It's based mostly on information and code from this site http://alienryderflex.com/polygon/. This should work with your code.
public class Polygon {
// Polygon coodinates.
private final int[] polyY, polyX;
// Number of sides in the polygon.
private final int polySides;
/**
* Default constructor.
* #param px Polygon y coods.
* #param py Polygon x coods.
* #param ps Polygon sides count.
*/
public Polygon( final int[] px, final int[] py, final int ps ) {
polyX = px;
polyY = py;
polySides = ps;
}
/**
* Checks if the Polygon contains a point.
* #see "http://alienryderflex.com/polygon/"
* #param x Point horizontal pos.
* #param y Point vertical pos.
* #return Point is in Poly flag.
*/
public boolean contains( final float x, final float y ) {
boolean oddTransitions = false;
for( int i = 0, j = polySides -1; i < polySides; j = i++ ) {
if( ( polyY[ i ] < y && polyY[ j ] >= y ) || ( polyY[ j ] < y && polyY[ i ] >= y ) ) {
if( polyX[ i ] + ( y - polyY[ i ] ) / ( polyY[ j ] - polyY[ i ] ) * ( polyX[ j ] - polyX[ i ] ) < x ) {
oddTransitions = !oddTransitions;
}
}
}
return oddTransitions;
}
}
You could add this constructor to help you convert a Point array to a Polygon object.
public Polygon(Point[] points){
polySides = points.length;
polyY = new int[polySides];
polyX = new int[polySides];
for(int i = 0; i < polySides; i++){
polyY[i] = points[i].y;
polyX[i] = points[i].x;
}
}
You might be able to use it in your MyShape class with this method.
public boolean isTouched(final float X, final float Y){
final Polygon p = new Polygon(points);
return p.contains(X, Y);
}
Now if you have an odd shape you should be able to detect exactly if the use touches it. I have used this method many times.
Are you looking for a way to tell whether a touch event falls on the non-transparent portion of your drawn bitmap? If so, why don't you just map the touch coordinate to the proper pixel on the bitmap and test the color?
And if that's the case, then you can skip all the path clipping stuff, since the link you posted was only doing that to overcome emulator inefficiencies.
It's a bit complicated so I am not going to provide the full source but I will give you an idea.
You need to transfer your shape in to a triangles collection, then on touch find the nearest point of your shape and check if your are inside this point triangle.
For searching and sorting points you can use red-black-red tree structure.
The search algorithm eventually should be at O(log(N)) and creating shape structure should be O(N*Log(N))