Advertisement
Zenn_

MapCollider

Aug 21st, 2019
244
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.67 KB | None | 0 0
  1. #include <iostream>
  2. #include <math.h>
  3. #include <SFML/Graphics.hpp>
  4. #include "Map.h"
  5. #include "MapCollider.h"
  6. #include "MapInfo.h"
  7. #include "MapData.h"
  8. #include "GameData.h"
  9. #include "AssetManager.h"
  10. #include "Tile.h"
  11. #include "CONFIG.h"
  12.  
  13. #include "ECS.h"
  14. #include "PositionSystem.h"
  15. #include "VelocitySystem.h"
  16. #include "BoundsSystem.h"
  17. #include "CollisionSystem.h"
  18.  
  19. #define ONLYFORONE if(col->getCompID() == 0 && MAPCOLLIDER_EXCESSIVEDEBUG_PRINTGROUNDED)
  20.  
  21. namespace station {
  22.     MapCollider::MapCollider(Map* MAP) : _map(MAP) {
  23.     }
  24.  
  25.     bool MapCollider::isOOB(PositionComponent* pos, BoundsComponent* bounds) {
  26.         int width = _map->activeMap->mapWidth;
  27.         if (pos->position.x + (bounds->bounds.x / 2) < 0) return true;
  28.         if (pos->position.x - (bounds->bounds.x / 2) > width * TILE_WIDTH) return true;
  29.         if (pos->position.y + (bounds->bounds.y / 2) < 0) return true;
  30.         if (pos->position.y - (bounds->bounds.y / 2) > width * TILE_HEIGHT) return true;
  31.         return false;
  32.     }
  33.  
  34.     std::pair<sf::Vector2i, sf::Vector2i> MapCollider::getCollisionBounds(CollisionComponent* col) {
  35.         PositionComponent* pos = _map->_data->ecs->positionSystem->getComp(col->getCompID());
  36.         BoundsComponent* bounds = _map->_data->ecs->boundsSystem->getComp(col->getCompID());
  37.  
  38.         //get the edges of the tile area the collider potentially intersects with
  39.         //-1 since tile 0,0 is at 1,1
  40.         int left = TILE_WIDTH * floor(pos->position.x - (bounds->bounds.x / 2) - MAPCOLLIDER_TILEAREA_ADDEDAREA);
  41.         int right = TILE_WIDTH * ceil(pos->position.x + (bounds->bounds.x / 2) - 1 + MAPCOLLIDER_TILEAREA_ADDEDAREA);
  42.         int top = TILE_HEIGHT * floor(pos->position.y - (bounds->bounds.y / 2) - MAPCOLLIDER_TILEAREA_ADDEDAREA);
  43.         int bottom = TILE_HEIGHT * ceil(pos->position.y + (bounds->bounds.y / 2) - 1 + MAPCOLLIDER_TILEAREA_ADDEDAREA);
  44.  
  45.         int mapWidth = _map->activeMap->mapWidth;
  46.         int mapHeight = _map->activeMap->mapHeight;
  47.  
  48.         //push edges into map bounds
  49.         left = clip(left, 0, mapWidth - 1);
  50.         right = clip(right, 0, mapWidth - 1);
  51.         top = clip(top, 0, mapHeight - 1);
  52.         bottom = clip(bottom, 0, mapHeight - 1);
  53.  
  54.         return TileArea(sf::Vector2i(left, top), sf::Vector2i(right, bottom));
  55.     }
  56.  
  57.     /*the map class's update function sets every colliders grounded flag to
  58.     false, then calls this function for each collider and potentially sets
  59.     grounded to true again*/
  60.     /*ive tried using the entity-entity collision resolve algorithm for this too,
  61.     but it led to unwanted behaviour and was generally too intuitive so i couldnt
  62.     add new mechanics to it effectively, this will be enough anyway*/
  63.     void MapCollider::resolveCollisionWithMap(CollisionComponent* col) {
  64.         PositionComponent* pos = _map->_data->ecs->positionSystem->getComp(col->getCompID());
  65.         BoundsComponent* bounds = _map->_data->ecs->boundsSystem->getComp(col->getCompID());
  66.         VelocityComponent* vel = _map->_data->ecs->velocitySystem->getComp(col->getCompID());
  67.  
  68.         //check if entity is OOB, in that case dont bother with collision
  69.         //dont remove this, this will lead to unwanted collision behaviour too
  70.         if (isOOB(pos, bounds)) return;
  71.  
  72.         int currentTile;
  73.         sf::FloatRect currentTileBounds;
  74.  
  75.         TileArea tileArea = getCollisionBounds(col); //first = topleft, second = bottomright
  76.         bool onGround = false;
  77.  
  78.         for (int y = tileArea.first.y; y <= tileArea.second.y; y++) {
  79.             if (MAPCOLLIDER_EXCESSIVEDEBUG_TILEAREA) std::cout << "Tile Area: ";
  80.             for (int x = tileArea.first.x; x <= tileArea.second.x; x++) {
  81.                 currentTile = y * _map->activeMap->mapWidth + x;
  82.                 if (isSolid(currentTile)) {
  83.                     //tiles surrounding this tile
  84.                     int leftTile = currentTile >= 0 ? currentTile - 1 : -1;
  85.                     int rightTile = currentTile <= _map->activeMap->mapWidth ? currentTile + 1 : -1;
  86.                     int aboveTile = currentTile >= _map->activeMap->mapWidth - 1 ? currentTile - _map->activeMap->mapWidth : -1;
  87.                     int belowTile = currentTile <= _map->activeMap->mapWidth - _map->activeMap->mapWidth ? currentTile + _map->activeMap->mapWidth : -1;
  88.                     //are they solid? if so, dont push out into that direction
  89.                     bool dontPushOutLeft = leftTile != -1 ? isSolid(leftTile) : false;
  90.                     bool dontPushOutRight = rightTile != -1 ? isSolid(rightTile) : false;
  91.                     bool dontPushOutUp = aboveTile != -1 ? isSolid(aboveTile) : false;
  92.                     bool dontPushOutDown = belowTile != -1 ? isSolid(belowTile) : false;
  93.                     const sf::FloatRect tilebounds = getBoundsOfTile(currentTile);
  94.                    
  95.                     //if (col->getCompID() == 0) std::cout << "current tile (" << currentTile << ") is solid" << std::endl;
  96.  
  97.                     sf::Vector2f dir(pos->position.x - (x + 0.5f), pos->position.y - (y + 0.5f));
  98.                     //overlap between the collider and the tile in the x axis
  99.                     float x_overlap = (bounds->bounds.x * 0.5f) + (TILE_WIDTH * 0.5f) - abs(dir.x);
  100.                     //overlap between the collider and the tile in the y axis
  101.                     float y_overlap = (bounds->bounds.y * 0.5f) + (TILE_HEIGHT * 0.5f) - abs(dir.y);
  102.  
  103.                     //grounded?
  104.                     sf::Vector2f vectorLeftVertex(
  105.                         pos->position.y + (bounds->bounds.y * 0.5f) - (y + 0.5f),
  106.                         pos->position.x - (bounds->bounds.x * 0.5f) - (x + 0.5f)
  107.                         );
  108.                     sf::Vector2f vectorRightVertex(
  109.                         pos->position.y + (bounds->bounds.y * 0.5f) - (y + 0.5f),
  110.                         pos->position.x + (bounds->bounds.x * 0.5f) - (x + 0.5f)
  111.                     );
  112.                     float angleLeftVertex = atan2(
  113.                         vectorLeftVertex.y,
  114.                         vectorLeftVertex.x) * 180.0f / PI;
  115.                     float angleRightVertex = atan2(
  116.                         vectorRightVertex.y,
  117.                         vectorRightVertex.x) * 180.0f / PI;
  118.  
  119.                     ONLYFORONE std::cout << "angleLeftVertex: " << angleLeftVertex << '\n'
  120.                         << "angleRightVertex: " << angleRightVertex << '\n';
  121.  
  122.                     //if either one of the two is inside the angle range or they are at opposite sides of the
  123.                     //angle range, the entity is "above" the tile
  124.                     bool angleRangeFulfilled = false;
  125.                     if (isInAngleRange(angleLeftVertex) || isInAngleRange(angleRightVertex)) {
  126.                         angleRangeFulfilled = true;
  127.                         ONLYFORONE std::cout << "one of angles in range" << std::endl;
  128.                     }
  129.                     else if (isLeftToAngleRange(angleLeftVertex) && isRightToAngleRange(angleRightVertex)) {
  130.                         ONLYFORONE std::cout << "angles at other side of range" << std::endl;
  131.                         angleRangeFulfilled = true;
  132.                     }
  133.                     //angle < -45.0f && angle > -135.0f
  134.                     if (x_overlap > 0 && y_overlap > -0.01f && dir.y <= 0 && !dontPushOutUp && angleRangeFulfilled) {
  135.                         if (MAPCOLLIDER_EXCESSIVEDEBUG_PRINTGROUNDED) std::cout << "dir.y = " << dir.y << "\n";
  136.                         onGround = true;;
  137.                     }
  138.  
  139.                     //DEBUG: set vertices for debug lines and push them to vector for draw
  140.                     //nothing gets drawn here, the vertex arrays just get set up
  141.                     if (DEBUG_MAPCOLLIDER_DRAWGROUNDEDNESS) {
  142.                         //sf::Vertex lines[6];
  143.                         col->lines[0].position = sf::Vector2f(
  144.                             pos->position.x - (bounds->bounds.x * 0.5f),
  145.                             pos->position.y + (bounds->bounds.y * 0.5f)
  146.                         );
  147.                         col->lines[1].position = sf::Vector2f((x + 0.5f), (y + 0.5f));
  148.                         col->lines[2].position = sf::Vector2f(
  149.                             pos->position.x + (bounds->bounds.x * 0.5f),
  150.                             pos->position.y + (bounds->bounds.y * 0.5f)
  151.                         );
  152.                         col->lines[3].position = sf::Vector2f((x + 0.5f), (y + 0.5f));
  153.                         col->lines[4].position = sf::Vector2f(
  154.                             pos->position.x - (bounds->bounds.x * 0.5f),
  155.                             pos->position.y + (bounds->bounds.y * 0.5f) + 0.1f
  156.                         );
  157.                         col->lines[5].position = sf::Vector2f(
  158.                             pos->position.x + (bounds->bounds.x * 0.5f),
  159.                             pos->position.y + (bounds->bounds.y * 0.5f) + 0.1f
  160.                         );
  161.  
  162.                         for (int i = 0; i < 2; i++) {
  163.                             col->lines[i].color = isInAngleRange(angleLeftVertex) ? sf::Color::Green : sf::Color::Red;
  164.                         }
  165.                         for (int i = 2; i < 4; i++) {
  166.                             col->lines[i].color = isInAngleRange(angleRightVertex) ? sf::Color::Green : sf::Color::Red;
  167.                         }
  168.                         for (int i = 4; i < 6; i++) {
  169.                             col->lines[i].color = col->groundedOnMap ? sf::Color::Green : sf::Color::Red;
  170.                         }
  171.  
  172.                         groundedCheckThisUpdate.emplace_back(col);
  173.                     }
  174.  
  175.                     //intersection?
  176.                     if (!(x_overlap < 0 || y_overlap < 0)) {
  177.                         //prioritize y over x due to stepping if y is small enough
  178.                         if (col->groundedOnEntity || col->groundedOnMap) {
  179.                             if (y_overlap < MAPCOLLIDER_STEPHEIGHT && dir.y < 0) {
  180.                                 y_overlap = -1000.0f;
  181.                                 dir.y = -1000.0f;
  182.                             }
  183.                         }
  184.                         else {
  185.                             if (y_overlap < MAPCOLLIDER_STEPHEIGHT_AIRBORNE && dir.y < 0) {
  186.                                 y_overlap = -1000.0f;
  187.                                 dir.y = -1000.0f;
  188.                             }
  189.                         }
  190.  
  191.                         if (x_overlap < y_overlap) {
  192.                             //when you smash into a horizontal wall, adjust y velocity
  193.                             if (MAPCOLLIDER_IMPULSEABSORBTION_Y && !(col->groundedOnEntity || col->groundedOnMap)) {
  194.                                 float velx = abs(vel->velocity.x); //dir doesnt matter //TODO runtime crash here apparently
  195.                                 if (vel) vel->velocity.y -= abs(velx * col->onWallHitXToYConversion);
  196.                             }
  197.                             if (dir.x < 0) {
  198.                                 if (!dontPushOutLeft) {
  199.                                     pos->setPosition(
  200.                                         tilebounds.left - (bounds->bounds.x / 2) - MAPCOLLIDER_GIVE_X,
  201.                                         pos->position.y
  202.                                     );
  203.                                     if (vel) vel->velocity.x = std::min(vel->velocity.x, 0.0f);
  204.                                 }
  205.                             }
  206.                             else {
  207.                                 if (!dontPushOutRight) {
  208.                                     pos->setPosition(
  209.                                         (tilebounds.left + tilebounds.width) + (bounds->bounds.x / 2) + MAPCOLLIDER_GIVE_X,
  210.                                         pos->position.y
  211.                                     );
  212.                                     if (vel) vel->velocity.x = std::max(vel->velocity.x, 0.0f);
  213.                                 }
  214.                             }
  215.                         }
  216.                         else {
  217.                             if (dir.y < 0) {
  218.                                 if (!dontPushOutUp) {
  219.                                     pos->setPosition(
  220.                                         pos->position.x,
  221.                                         tilebounds.top - (bounds->bounds.y / 2) - MAPCOLLIDER_GIVE_Y
  222.                                     );
  223.                                     if (vel) vel->velocity.y = std::min(vel->velocity.y, 0.0f);
  224.                                 }
  225.                             }
  226.                             else {
  227.                                 if (!dontPushOutDown) {
  228.                                     pos->setPosition(
  229.                                         pos->position.x,
  230.                                         (tilebounds.top + tilebounds.height) + (bounds->bounds.y / 2) + MAPCOLLIDER_GIVE_Y
  231.                                     );
  232.                                     if(vel) vel->velocity.y = std::max(vel->velocity.y, 0.0f);
  233.                                 }
  234.                             }
  235.                         }
  236.                     }
  237.                     if (x_overlap > -MAPCOLLIDER_ONSIDEOFTILE_ZONE && y_overlap > 0.05f) {
  238.                         if(dir.x < 0) col->onSideOfTile = CollisionComponent::OnSideOfTile::RIGHT;
  239.                         if (dir.x > 0) col->onSideOfTile = CollisionComponent::OnSideOfTile::LEFT;
  240.                     }
  241.                 }
  242.                 if (MAPCOLLIDER_EXCESSIVEDEBUG_TILEAREA) std::cout << std::to_string(currentTile) << " ";
  243.             }
  244.             if (MAPCOLLIDER_EXCESSIVEDEBUG_TILEAREA) std::cout << '\n';
  245.         }
  246.         if (MAPCOLLIDER_EXCESSIVEDEBUG_TILEAREA) std::cout << '\n';
  247.         col->groundedOnMap = onGround;
  248.     }
  249.  
  250.     sf::FloatRect MapCollider::getBoundsOfTile(int index) {
  251.         return sf::FloatRect(
  252.             sf::Vector2f((index % _map->activeMap->mapWidth) * TILE_WIDTH, (index / _map->activeMap->mapWidth) * TILE_HEIGHT),
  253.             sf::Vector2f(TILE_WIDTH, TILE_HEIGHT)
  254.         );
  255.     }
  256.     sf::FloatRect MapCollider::getBoundsOfTile(int x, int y) {
  257.         return getBoundsOfTile((y * _map->activeMap->mapWidth) + x);
  258.     }
  259.  
  260.     int MapCollider::clip(int n, int lower, int upper) {
  261.         return std::max(lower, std::min(n, upper));
  262.     }
  263.  
  264.     bool MapCollider::isSolid(int INDEX) {
  265.         return _map->_mapInfo->tiles[_map->getTileID(INDEX)]->hasFlag(Tile::TileFlags::solid);
  266.     }
  267.  
  268.  
  269.     bool MapCollider::isLeftToAngleRange(float ANGLE) {
  270.         return ANGLE > -180.0f + MAPCOLLIDER_ANGLERANGEBOUND && ANGLE <= 0.0f;
  271.     }
  272.     bool MapCollider::isRightToAngleRange(float ANGLE) {
  273.         return ANGLE < 180.0f - MAPCOLLIDER_ANGLERANGEBOUND && ANGLE >= 0.0f;
  274.     }
  275.     bool MapCollider::isInAngleRange(float ANGLE) {
  276.         return !isLeftToAngleRange(ANGLE) && !isRightToAngleRange(ANGLE);
  277.     }
  278.  
  279.     void MapCollider::drawToGameDataRenderWindow() {
  280.         for(CollisionComponent* col : groundedCheckThisUpdate)
  281.             _map->_data->renderWindow->draw(col->lines, (sizeof(col->lines) / sizeof(*col->lines)), sf::Lines);
  282.     }
  283. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement