Vấn đề phát hiện va chạm bản đồ dựa trên lát gạch


7

Tôi đang làm việc trên một bản sao Mario dựa trên gạch.

Điều này hoạt động tốt khi đi bộ và rơi. Nhưng khi người chơi nhảy gần một bức tường và đi về phía bên phải trong không trung, thì người chơi bị mắc kẹt trên tường. Sprite của người chơi lại rơi khi người chơi nhả phím.

Việc thiết lập khá dễ dàng và tôi không thể tìm thấy vấn đề. Bản đồ được xây dựng dưới dạng một mảng 2D với các khối bản đồ. Một khối có thể rắn hoặc không. Người chơi không thể di chuyển các vật thể rắn, duh ..

Trong vòng lặp trò chơi:

  • vị trí của người chơi được cập nhật (trọng lực, chuyển động ect.)
  • kiểm tra bản đồ về va chạm. Khi tìm thấy xung đột trong Y, thì vị trí của người chơi sẽ được cập nhật để nó ở trên hoặc bên dưới khối (tùy theo hướng của người chơi), sau đó hộp va chạm sẽ được cập nhật với vị trí mới. Sau đó, quá trình tương tự cho X.
  • Hộp va chạm được cập nhật đến vị trí mới (một vị trí miễn phí). Hộp được điều chỉnh sao cho cao hơn một chút để đưa khối bên dưới người chơi kiểm tra xem anh ta đã hạ cánh chưa. Điều này là để thay đổi trạng thái của người chơi, từ bay sprite sang nhàn rỗi.

Tôi cũng đã thử chuyển đổi kiểm tra X và Y để người chơi được di chuyển trên dòng X. Sau đó khi người chơi di chuyển, chuyển động sẽ rất chậm. Khi tôi ấn và nhả nút để di chuyển, thì người chơi sẽ di chuyển nhanh hơn nhưng ở trong mâm cặp. Rất nhanh

Có ai nhìn thấy lỗi hoặc có thể cho tôi một thuật toán va chạm tốt hơn cho điều này?

CẬP NHẬT (không cập nhật mã)

Tôi đã thay đổi phương thức kiểm tra x và y và thực hiện biến isonland. Vì vậy, khi đi bộ và nhảy vào tường hoạt động hoàn hảo. Chỉ bây giờ, khi người chơi nhảy Mario được đặt trở lại khi hạ cánh. Điều này là do phương pháp kiểm tra X đi trước và điều chỉnh vị trí của Mario.

Làm thế nào tôi có thể giải quyết điều đó?

Phương pháp cập nhật lớp bản đồ:

public void update(int timeElapsed) {
        //update entities
        for(Entity entity : _mapEntities) {
            entity.update(timeElapsed);
        }

        //update objects
        for(MapObject mapObt : _mapObjects) {
            mapObt.update(timeElapsed);
        }

        //check for collisions
        checkMapCollision();
    }

Phương thức cập nhật thực thể (trừu tượng):

public void update(int timeElapsed) {
        _velocity = new Vector2d(0.0F, 0.0F);

        //add gravity
        _velocity.y = Map._GRAVITY_PER_SEC * timeElapsed;
    }

Mario (mở rộng thực thể) cập nhật methos:

@Override
    public void update(int timeElapsed) {
        super.update(timeElapsed);

        if(_state == STATES.IDLE) {

        } else if(_isMoving) {
            _marioSmallWalk.update(timeElapsed);
        }

        if(_state == STATES.JUMPING) {
            setVelocityY(getVelocity().y + _jumpSpeed);

            _jumpSpeed += _JUMP_DECREASE * timeElapsed;

            //falling?
            if(getVelocity().y > 0) {
                setState(STATES.FALLING);
            }
        } 

        if(_isMoving) {
            double walkSpd = (_WALK_SPEED_SEC * timeElapsed);

            if(getFacing() == FACING.LEFT) {
                walkSpd = -walkSpd;
            }

            setVelocityX(getVelocity().x + walkSpd);
        }

        //falling?
        if(getVelocity().y > (Map._GRAVITY_PER_SEC * timeElapsed) + 1.0F) {
            setState(STATES.FALLING);
        }

        setPosition((int)(getX() + getVelocity().x), (int)(getY() + getVelocity().y));
    }

Phương pháp lớp MapMapCollision:

public void checkMapCollision() {
        //enteties move so check it
        for(Entity entity : _mapEntities) {
            //get the corners
            Rectangle bounds = entity.getBounds();
            Block[] corners = getCornerBlocks(bounds);
            Vector2d dir = entity.getDirection();

            //moving down
            if(dir.y > 0) {
                if(corners[2].isSolid() || corners[3].isSolid()) {
                    Rectangle blkBounds = null;

                    if(corners[2].isSolid()) {
                        blkBounds = corners[2].getBounds();
                    } else {
                        blkBounds = corners[3].getBounds();
                    }

                    entity.setPositionY(blkBounds.y);
                }
            } else {
                if(corners[0].isSolid() || corners[1].isSolid()) {
                    Rectangle blkBounds = null;

                    if(corners[0].isSolid()) {
                        blkBounds = corners[0].getBounds();
                    } else {
                        blkBounds = corners[1].getBounds();
                    }

                    entity.setPositionY(blkBounds.y + blkBounds.height + bounds.height);
                }
            }

            bounds = entity.getBounds();
            corners = getCornerBlocks(bounds);

            //moving to the right
            if(dir.x > 0) {
                if(corners[1].isSolid() || corners[3].isSolid()) {
                    Rectangle blkBounds;

                    if(corners[1].isSolid()) {
                        blkBounds = corners[1].getBounds();
                    } else {
                        blkBounds = corners[3].getBounds();
                    }

                    entity.setPositionX(blkBounds.x - (bounds.width-entity.getCurrentSprite().getOffsetX())-1);
                }
            } else {
                if(corners[0].isSolid() || corners[2].isSolid()) {
                    Rectangle blkBounds;

                    if(corners[0].isSolid()) {
                        blkBounds = corners[0].getBounds();
                    } else {
                        blkBounds = corners[2].getBounds();
                    }

                    entity.setPositionX(blkBounds.x + blkBounds.width + (bounds.width/2));
                }
            }

            bounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height+1);
            corners = getCornerBlocks(bounds);

            //moving down
            if(dir.y > 0) {
                if(corners[2].isSolid() || corners[3].isSolid()) {
                    Rectangle blkBounds = null;

                    if(corners[2].isSolid()) {
                        blkBounds = corners[2].getBounds();
                    } else {
                        blkBounds = corners[3].getBounds();
                    }

                    entity.landed();
                    System.out.println("landed");
                }
            }
        }
    }

Bạn có chắc chắn sau khi bạn phát hiện một vụ va chạm bạn đang phản ứng với nó? Nghe có vẻ như nhân vật không bị đẩy ra khỏi đối tượng và thay vào đó bị dừng lại ở nơi họ đang ở (nhưng tôi đã không đọc mã một cách xuyên suốt).
James

Vâng, tôi chắc chắn về điều đó. Các entity.setPositionX()hoặc entity.setPositionY()được gọi sau khi kiểm tra va chạm. Khi tôi đi dựa vào tường mà không nhảy thì người chơi bị đẩy lùi chính xác.
Sven van Zoelen

Câu trả lời:


8

Nhân vật sprite của bạn bị chặn trên các giao điểm giữa hai ô. Xem sơ đồ sau, mô tả tình huống khi bắt đầu thói quen va chạm của bạn.

Va chạm với một bức tường

Mario đã được chuyển sang bên phải do điều khiển của người chơi. Điều này sẽ đặt sprite trong ba gạch. Sau đó, bạn thực hiện phát hiện va chạm từ trên xuống Y. Lưu ý điểm trong vòng tròn màu xanh: điều này sẽ phát hiện xung đột với khối thứ hai và cố gắng giải quyết bằng cách đặt tọa độ Y ở giới hạn trên của khối đó, tạm dừng Mario trong giữa không trung.

Ngay khi người chơi nhả chìa khóa để di chuyển Mario sang phải, sprite sẽ không di chuyển vào tường và điểm mà trước đó sẽ phát hiện ra một vụ va chạm, sẽ không đăng ký một cú đánh. Điều này sẽ cho phép Mario rơi xuống đất.

Việc giải quyết nhanh và bẩn cho vấn đề này là giải quyết va chạm X trước tiên và chỉ áp dụng trọng lực khi người chơi không ở trên mặt đất. Nếu bạn giữ trọng lực, vấn đề tương tự sẽ xảy ra khi đi bộ xung quanh (đây là hành vi 'trippy' mà bạn đã thấy sau khi hoán đổi kiểm tra X và Y;)).


Cảm ơn! Tôi đã hoán đổi kiểm tra x và y và thực hiện kiểm tra isOnLand. Vì vậy, người chơi có thể nhảy và lướt vào tường. Bây giờ một vấn đề khác đã xuất hiện, khi người chơi đứng cạnh tường và nhảy, sau đó khi anh ta hạ cánh x được điều chỉnh (từ sự va chạm của sàn nhà) và người chơi được đặt vào tường ..
Sven van Zoelen

Tôi thấy khó có thể tưởng tượng cấu trúc mã có thể dựa trên nhận xét của bạn. Tại sao X cần phải điều chỉnh khi Mario va chạm với sàn sau khi nhảy? Tôi nghĩ rằng điều này sẽ chỉ cần một sự điều chỉnh trong Y, khiến X không bị ảnh hưởng.
ma

1

Tôi đã tìm ra những gì tôi đã làm sai.

Chìa khóa nằm ở khi bạn cập nhật vị trí. Điều tôi đã làm sai là tôi đã thực hiện phương pháp 'thay đổi vị trí trước rồi kiểm tra và điều chỉnh' và tôi phải thực hiện theo thứ tự, kiểm tra và sau đó cập nhật.

Mã trước của tôi đã làm như sau:

  • cập nhật vị trí thực thể với vận tốc.
  • kiểm tra va chạm và cập nhật vị trí thực thể.

Điều này dẫn đến việc sau khi người chơi nhảy lên, vị trí của Mario đã được thay đổi trong phương thức cập nhật để hộp va chạm sẽ nằm trong các ô đất. Sau đó, phương thức va chạm đã thay đổi vị trí X thành x của góc dưới bên trái và sau đó y sẽ di chuyển nó lên trên mặt đất. Điều này có nghĩa là Mario bị dịch chuyển trở lại một vài pixel mỗi khi người chơi hạ cánh từ một cú nhảy.

Tôi đã cố gắng trao đổi các phương thức kiểm tra X và Y, nhưng sau đó, vấn đề chỉ thay đổi sang một thứ khác (cái ôm trên tường).

Bây giờ giải pháp, tôi đã thay đổi nó như sau:

  • cập nhật vận tốc thực thể.
  • phương pháp va chạm
    • cố gắng đặt thực thể lên X với vận tốc, nếu thất bại, thay đổi nó tốt nhất có thể.
    • cố gắng đặt thực thể lên Y với vận tốc, nếu thất bại, thay đổi nó tốt nhất có thể.

Bây giờ X được kiểm tra / thay đổi trước và sau đó hộp va chạm được cập nhật với vị trí thực thể mới. Sau đó, Y được kiểm tra / thay đổi.

Công việc này như một cái duyên vậy :)


Bạn có thể cho một số ví dụ? Thật khó để hiểu những gì bạn đã làm
gyozo kudor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.