Обнаружение столкновений/удаление объекта из ArrayList

В настоящее время я пытаюсь проверить столкновение между падающим объектом и коробкой. Я понимаю базовое обнаружение столкновений, но моя проблема здесь в том, что я должен проверить его на неопределенное количество падающих объектов. Когда эти объекты (цветы) создаются, они сохраняются в ArrayList. ArrayList обрабатывает рисование объекта на холсте (используя for each для обновления позиции). Моя проблема возникает, когда цветок «пойман» в коробку. Как сделать так, чтобы он исчез с экрана/удален из списка массивов без нулевой ссылки? Я могу показать вам свою логику... пожалуйста, дайте мне знать, что вы думаете. Я ДЕЙСТВИТЕЛЬНО застрял, но чувствую, что я так близок к тому, чтобы понять это.

Блоссом Класс

public class Blossom{
private Bitmap blossom;
private int blossom_x = 0;
private int blossom_y = 0;
private int blossomWidth = 0;
private int blossomHeight = 0;
private boolean hit = false;

private Random generator = new Random();
RectF blossomRect;

private static final String TAG = "Debug";

public Blossom(Bitmap bitmap)
{
    blossom = bitmap;
    blossomWidth = blossom.getWidth();
    blossomHeight = blossom.getHeight();
    blossom_x = generator.nextInt(320-blossom.getWidth());
    blossomRect = new RectF(blossom_x,blossom_y, blossom_x + blossomWidth, blossom_y + blossomHeight);
}

public Bitmap getBitmap()
{
    return blossom;
}

public Boolean hit(int boxLeft, int boxTop, int boxRight,int boxBottom)
{
    if(blossom_x > boxLeft & blossom_y > boxTop
            & blossom_x + blossom.getWidth() < boxRight
            & blossom_y + blossom.getHeight() < boxBottom)
    {
        Log.v(TAG, "Collision Detected");
        return true;
    }
    else
    {
        return false;
    }
}

public float getBlossomX()
{
    return blossom_x;
}

public float getBlossomY()
{
    return blossom_y;
}

public float getBlossomWidth()
{
    return blossomWidth;
}

public float getBlossomHeight()
{
    return blossomHeight;
}

public void Draw(Canvas canvas)
{
    //draws the flower falling
    if (hit == false)
    {
        canvas.drawBitmap(blossom, blossom_x,
            blossom_y = blossom_y+5 , null);
    }

}

public void UpdatePosition()
{
    blossomRect.set(blossom_x, blossom_y, blossom_x + 25, blossom_y + 25);
}

}

BoardView

public class BoardView extends SurfaceView implements SurfaceHolder.Callback{
Context mContext;

Bitmap box = 
    (BitmapFactory.decodeResource
            (getResources(), R.drawable.box));

private BoardThread thread;
private int box_x = 140;
private int box_y = 378;
private int boxWidth = box.getWidth();
private int boxHeight = box.getHeight();
private ArrayList<Blossom> blossomArrayList = new ArrayList<Blossom>();
private int blossomNum = 0;
private String score;
private int currentScore = 0;
Thread timer;

boolean mode = false;
boolean game = false;

private static final String TAG = "Debug";
final Paint scorePaint = new Paint();

public BoardView(Context context){
    super(context);

    scorePaint.setColor(Color.BLACK);
    scorePaint.setTextSize(12);
    scorePaint.setTypeface(Typeface.MONOSPACE);


    //surfaceHolder provides canvas that we draw on
    getHolder().addCallback(this);

    // controls drawings
    thread = new BoardThread(getHolder(),this, blossomArrayList, box_x, box_y, 
            boxWidth, boxHeight);

    timer = new Thread(){
        public void run(){
            //makes sure the player still has 3 lives left
            while(game == false){
                uiCallback.sendEmptyMessage(0);
                try {
                    Thread.sleep(2000); // wait two seconds before drawing the next flower
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } //sleep for 2 seconds
            }
        }
    };
    timer.start();

    //intercepts touch events
    setFocusable(true);

}

@Override

public void onDraw(Canvas canvas){
    canvas.drawColor(Color.WHITE);
    score = "SCORE: " + currentScore;

    //note: pay attention to order you draw things
    //don't change order or else blossoms will fall
    //on top of box, not "into" it.

    //display the scoreboard
    canvas.drawText(score,240,420,scorePaint);
    //draw box and set start location

    for(Blossom blossom: blossomArrayList)   // getting errors here
    {
            blossom.Draw(canvas);
            blossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight);

    }

    canvas.drawBitmap(box, box_x, box_y, null);

}

@Override
public boolean onTouchEvent(MotionEvent event){
    //handles movement of box
    if(event.getAction() == MotionEvent.ACTION_DOWN){
        if(event.getX() > box_x & event.getY() > box_y & 
                event.getX() < box_x + boxWidth & event.getY() < box_y + boxHeight)
        {
            mode = true;
        }
    }

    if(event.getAction() == MotionEvent.ACTION_MOVE) {
        if(event.getX() > box_x & event.getY() > box_y & 
                event.getX() < box_x + boxWidth & event.getY() < box_y + boxHeight)
        {
            mode = true;
        }
        if(mode == true){
            box_x = (int)event.getX();
        }   

    }

    if(event.getAction() == MotionEvent.ACTION_UP){
        mode = false;
    }

    invalidate();
    return true;
}

@Override
public void surfaceChanged(SurfaceHolder holder, 
        int format, int width, int height ){
    Log.v(TAG, "Surface Changed");
    //somehow these don't seem to be working
}

@Override
public void surfaceCreated(SurfaceHolder holder){
    thread.startRunning(true);
    thread.start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder){
    Log.v(TAG, "Surface Destroyed");
    //somehow these don't seem to be working
    thread.startRunning(false);
    thread.stop();
    timer.interrupt();
    timer.stop();
}

private Handler uiCallback = new Handler(){
    public void handleMessage(Message msg){
        //add a new blossom to the blossom ArrayList!!
        blossomArrayList.add(new Blossom( 
            (BitmapFactory.decodeResource
                    (getResources(), R.drawable.blossom))));
        blossomNum++;

        //remove neccesary blossoms from list
        Log.v(TAG, "Number of Blossoms =" + blossomNum);
    }
};

}

ДоскаНить

public class BoardThread extends Thread {

private SurfaceHolder surfaceHolder;
private BoardView boardView;

private ArrayList<Blossom> blossomArrayList;
private int boxX;
private int boxY;
private int boxWidth;
private int boxHeight;
private boolean mrun =false;

public BoardThread(SurfaceHolder holder, BoardView boardView2, 
        ArrayList<Blossom> blossomArrayList1,
        int box_x, int box_y, int boxW, int boxH) {

    surfaceHolder = holder;
    boardView=boardView2;

    blossomArrayList = blossomArrayList1;
    boxX = box_x;
    boxY = box_y;
    boxW = boxWidth;
    boxH = boxHeight;
}

public void startRunning(boolean run) {

    mrun=run;
}

@Override
public void run() {

    super.run();
     Canvas canvas;
     while (mrun) {
        canvas=null;
         try {
             canvas = surfaceHolder.lockCanvas(null);
              synchronized (surfaceHolder) {
                 //test for collision
                 Collision(blossomArrayList, boxX, boxY, boxWidth, boxHeight);
                 // draw flowers
                 boardView.onDraw(canvas);   // and getting errors here - concurrent 
             }
         } finally {
                 if (canvas != null) {
                 surfaceHolder.unlockCanvasAndPost(canvas);
             }
         }
     }
  }

public void Collision(ArrayList<Blossom> blossomArrayList, int box_x, int box_y, 
        int boxWidth, int boxHeight)
{
    for(Blossom blossom: blossomArrayList)
    {
        blossom.UpdatePosition();
        if(blossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight) == true)
        {
            ///if flower is caught, add to score
            //currentScore += 100;
        }

    }
}

}

person Hani Honey    schedule 20.04.2011    source источник


Ответы (2)


Установка флага видимости - это один из способов, однако я бы рекомендовал против этого, поскольку вы добавляете неопределенное количество растровых изображений в ArrayList... вы довольно быстро обнаружите, что вам не хватает памяти. Измените итератор обнаружения столкновений с foreach на рукописный цикл, это позволит избежать проблем с параллелизмом, с которыми вы можете столкнуться в коде, который вы указали выше.

for (int i = 0; i < blossomArrayList.size(); i++)
            {
                if(blossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight)) {
                    blossomArrayList.remove(i);
                }
            }

Кроме того, я бы порекомендовал изменить все ваши итераторы ArrayList foreach на циклы for, написанные вручную, поскольку итерация ArrayList (но не любого другого объекта) выполняется относительно медленно на Android и может привести к неожиданным проблемам параллелизма.

В-третьих, кажется, что вам нужно запустить метод Collision() только один раз после завершения цикла UpdatePositions, поскольку вы уже проверяете каждый Blossom в своем методе Collision().

person Joe    schedule 21.04.2011
comment
Проблема в том, что, поскольку я добавляю новый цветок в ArrayList каждые определенное количество секунд, он иногда падает, если я использую обычный цикл for :/ - person Hani Honey; 21.04.2011
comment
Интересный. Не могли бы вы предоставить трассировку стека вашего сбоя, чтобы лучше диагностировать проблему? - person Joe; 21.04.2011
comment
Я не совсем уверен, как это реализовать. Но, насколько я помню, все, что я сделал, это заменил циклы for на циклы for each, и они сработали. и состояние было именно то, что вы перечислили выше. - person Hani Honey; 21.04.2011
comment
Я бы порекомендовал выполнить обнаружение столкновений до выполнения вашего SurfaceView onDraw() (//тестовое столкновение; boardView.onDraw(canvas);) Как и сейчас, ваши столкновения обновляются в отдельном потоке, чем ваш onDraw, что означает любой из ваших Blossoms может быть изменен любым потоком в любое время (что приводит к исключению параллелизма - оба потока читают/записывают один и тот же объект). Это гораздо более потокобезопасный способ справиться с этим. - person Joe; 21.04.2011
comment
Ура, спасибо, что посмотрели. Я посмотрю, что я могу сделать завтра! - person Hani Honey; 21.04.2011
comment
Я отредактировал свой код сейчас. Я разместил свое обнаружение столкновений там, где вы мне сказали. однако после нескольких обнаружений столкновений я столкнулся с некоторыми ошибками. Он сообщает мне, что поток завершается с необработанным исключением (группа = 0x4001d800). Затем он выдает мне исключение параллельной модификации, упоминая итератор arraylist, мой метод boardview.ondraw и место, из которого я его вызываю в BoardThread. Я не понимаю, почему это происходит, если только это не связано с тем, что мой метод обнаружения столкновений изменяет список массивов одновременно с методом ondraw. Это можно как-то исправить? - person Hani Honey; 23.04.2011

Один из способов сделать это — иметь поле на Blossom, указывающее, активно оно или нет, а затем рисовать его только в том случае, если оно активно. Если он неактивен, другой Blossom может заменить его в списке.

person Feanor    schedule 20.04.2011
comment
о, я вижу .... так что мне никогда не придется удалять его из списка массивов ... хм, я собираюсь попробовать. мне нравится звук этого. ну... я полагаю, в конце концов было бы хорошо очистить этот список, потому что он стал бы очень большим... но пока мне это нравится. это может по крайней мере помочь мне перейти к следующему шагу. - person Hani Honey; 21.04.2011
comment
однако я все еще думаю, что что-то действительно не так с моим методом обнаружения столкновений. или хотя бы откуда я звоню... - person Hani Honey; 21.04.2011