W jaki sposób naprawić detekcję kolizji i reakcje na nią?

0

Dzień dobry! Robię grę 2d typu RPG. Kolizję jaką chciałbym zaimplementować to kolizja typu AABB. Problem polega na tym, że kolizja jest wykrywana nawet jak jej nie ma

screenshot-20180127120807.png

Kolorem czarnym zaznaczony jest blok
czerwony - gracz, który ma kolizje
zielony - gracz, który jest troche wyżej / dalej i już nie ma kolizji.

sposób w który sprawdzam tą kolizję jest następujący

bool Collision(const glm::vec4& object1, const glm::vec4& object2)
{
	bool collisionX = object1.x <= object2.w && object1.w >= object2.x;
	bool collisionY = object1.y <= object2.z && object1.z >= objkect2.y;
	retrun collisinX && collisionY;
}
void DoCollision()
{
	glm::vec4 playerObject = glm::vec4(
		player.getX(),
		player.getY(),
		player.getX() + player.getW(),
		player.getY() + player.getH()
	);

	glm::vec4 boxObject = glm::vec4(
		box.getX(),
		box.getY(),
		box.getX() + box.getW(),
		box.getY() + box.getH()
	);

	if(Collision(playerObject, boxObject){
		if(playerObject.x <= boxObject.w) {
			std::cout<<"Player kolizja X"<<std::endl;
		} else if(playerObject.w >= boxObject.x) {
			std::cout<<"Player kolizja W"<<std::endl;
		}

		if(playerObject.y <= boxObject.z) {
			std::cout<<"Player kolizja Y"<<std::endl;
		} else if(playerObject.z >= boxObject y) { 
			std::cout<<"Player kolizja H"<<std::endl;
		}
	}
}

Przy czym box jest zaprogramowany jako mapa klocków w tablicy.

W jaki sposób to naprawić? Może sprawdzać kolizje w inny sposób?

0

Co to sa vec4 obiekty? AABB sprawdza(prosta matematyka) czy prostokaty I kwadraty (rownolegle do osi ukladu)na siebie nachodza.

0

To jest mój kawałek wykrywania kolizji

bool CollisionChecker::checkAABB(std::shared_ptr<Entity> &left, std::shared_ptr<Entity> &right) {
    if (left->positions.x < right->positions.x + right->positions.width &&
        left->positions.x + left->positions.width > right->positions.x &&
        left->positions.y < right->positions.y + right->positions.height &&
        left->positions.height + left->positions.y > right->positions.y) {
        return true;
    } else {
        return false;
    }
}

na kolizję reaguję tak że gdy jej nie ma to oldPosition jest ustawiane na aktualną pozycję i position aktualizuję, jeśli została wykryta position przyjmuje wartość oldPosition

void CollisionChecker::checkEntityInhArray(std::vector<std::shared_ptr<Entity>> &mapEntity) {
    for (int i = 0; i < mapEntity.size(); i++) {
        for (int j = 0; j < mapEntity.size(); j++) {
            if (i != j) {
                if (mapEntity[i]->isCollisionAble() && (mapEntity[j]->isCollisionAble())) {
                    if (checkAABB(mapEntity[i], mapEntity[j])) {
                        mapEntity[i]->setCollisionStatus(IS_COLLIDET);
                        break;
                    } else {
                        mapEntity[i]->setCollisionStatus(NO_COLLISION);
                    }
                }
            }
        }
    }
}

reakcja na kolizję w konkretnym obiekcie

void Player::update(const float dt) {
    if (getCollsionStatus() == IsCollidet::NO_COLLISION) {
        positions = oldPositions;
    } else {
        oldPositions = positions;
        sprite.setPosition(positions.x, positions.y);
    }

    switch (getStatus()) {
        case MOVING:
            spriteData.currentTime += dt;
            if (spriteData.currentTime >= spriteData.timeToNextFrame) {
                spriteData.currentTime = 0;
                if (spriteData.currentFrame >= spriteData.numbersOfFrame) {
                    spriteData.currentFrame = 0;
                } else {
                    spriteData.currentFrame++;
                }
            }

            switch (getDirectory()) {
                case UP:
                    setStatus(MOVING);
                    idleTime = 0;
                    moveUp(dt);
                    break;
                case DOWN:
                    setStatus(MOVING);
                    idleTime = 0;
                    moveDown(dt);
                    break;
                case LEFT:
                    setStatus(MOVING);
                    idleTime = 0;
                    moveLeft(dt);
                    break;
                case RIGHT:
                    setStatus(MOVING);
                    idleTime = 0;
                    moveRight(dt);
                    break;
            };
            break;
        case IDLE:
            std::cout << "PLAYER: IDLE STATUS " << std::endl;

            break;
        case STOP:

            break;
    };

    if (getStatus() != IDLE) {
        idleTime += dt;
    }
}
0

w głównej pętli gry - główna metoda update

void PlayState::update(const float dt)
{
    system("CLS");

    ch.checkEntityInhArray(mapEntity);
//ch - jest argumentem klasy PlayState
    //...
}

https://gitlab.com/electric.team/game-2d/blob/Krecik/src/GameStates/PlayState.cpp

0

Dzięki, sprawdzam sobie powoli ten kodzik.

Wracając do mojego kodu, to zrobiłem taki upgrade, że dodałem do klasy gracza zmienną prevPosition i w sprawdzaniu kolizji sprawdzam też poprzednią pozycję tzn.

if (box.getBlockState() == b.COLLIDABLE) {
	if (checkAABB(player, box)) {
		if (player.getPosX() <= box.getPosX() + box.getWidth() && 
		    player.getPrevPosX() >= box.getPosX() + box.getWidth()) {

			player.setPosX(box.getPosX() + box.getWidth());
		} else if (player.getPosX() + player.getWidth() >= box.getPosX() && 
                              player.getPrevPosX() + player.getWidth() <= box.getPosX()) {

			player.setPosX(box.getPosX() - player.getWidth());
		} else if (player.getPosY() <= box.getPosY() + box.getHeight() && 
                              player.getPrevPosY() >= box.getPosY() + box.getHeight()) {

			player.setPosY(box.getPosY() + box.getHeight());
		} else if (player.getPosY() + player.getHeight() >= box.getPosY() &&
                              player.getPrevPosY() + player.getHeight() <= box.getPosY()) {

			player.setPosY(box.getPosY() - player.getHeight());
		}
}	

I o ile nie ma już tak dużych problemów jak w pierwszym przypadku to pojawiły się inne, mniejsze

*1. Pojawia się taka lekka linia, która blokuje ruch w lewo albo prawo

screenshot-20180127212746.png

Kolorem zielonym zaznaczone są te linię(oczywiście w dość dużym powiększeniu, bo dzieje się to tylko i wyłącznie, kiedy gracz koliduje na osi Y i X jednocześnie).
W jaki sposób pozbyć się tego błędu?
Również chciałbym się zapytać. Czy jest to wydajny sposób na sprawdzanie kolizji? Sprawdzam ifem czy box jest podatny na kolizję więc kolizja powinna być sprwdzana tylko dla tych boków, w innym przypadku kod nawet się nie wykonuje, więc wydaje mi się, że jest ok.

1 użytkowników online, w tym zalogowanych: 0, gości: 1