Witam! próbuje zrobić grę 2D użwająć open gl. Jak na razie zrobiłem parę klas do mapy i gracza.
class Block
{
public:
Block(const Rect& destRect, const Rect& uvRect, const GLuint& texture)
{
this->uvRect = uvRect;
this->texture = texture;
this->destRect = destRect;
}
~Block();
void Update(float deltaTime);
void Draw(Renderer& renderer)
{
renderer.Draw(destRect, uvRect, texture);
}
Vector2 getCenter() const
{
Vector2 result;
result.x = destRect.x + destRect.w / 2;
result.y = destRect.y + destRect.h / 2;
return result;
}
float getPosX() const;
float getPosY() const;
float getWidth() const;
float getHeight() const;
private:
Rect uvRect = Rect(0.0f, 0.0f, 0.0f, 0.0f);
Rect destRect = Rect(0.0f, 0.0f, 0.0f, 0.0f);
GLuint texture = 0;
};
class TileMap
{
public:
TileMap();
~TileMap();
void Initialize(const std::string& filePath)
{
LoadFromFile(filePath);
loadTextures();
for (int y = 0; y < map.size(); ++y) {
for (int x = 0; x < map[y].size(); ++x) {
destRect.w = 64.0f;
destRect.h = 64.0f;
destRect.x = destRect.w * (float)x;
destRect.y = destRect.h * (float)y;
switch(map[y][x]) {
case 1:
block.emplace_back(destRect, uvRect, textureID("Wooden"));
break;
}
}
}
}
void Draw(Renderer& renderer)
{
for (auto& b : block) {
b.Draw(renderer);
}
}
void Update(float deltaTime)
{
for (auto& b : block) {
b.Update(deltaTime);
}
}
std::vector<std::vector<int>> getMap();
private:
void LoadFromFile(const std::string& filePath)
{
std::ifstream openFile(filePath);
map.clear();
int tileCode;
std::string line;
if (openFile.is_open()) {
while (!openFile.eof()) {
while (std::getline(openFile, line)) {
std::stringstream str(line);
std::vector<int> tempV;
while (str >> tileCode) {
tempV.push_back(tileCode);
}
map.push_back(tempV);
}
}
}
openFile.clear();
openFile.close();
}
void loadTextures();
TextureManager texture;
Rect destRect = { 0.0f, 0.0f, 0.0f, 0.0f };
Rect uvRect = { 0.0f, 0.0f, 1.0f, 1.0f };
std::vector<Block> block;
std::vector<std::vector<int>> map;
std::map<std::string, GLuint> textureID;
};
Odnosząc się do pierwszego pytania w tytule, mapa musi wyglądać tak:
1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
żeby uzyskać taki efekt
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
Dlaczego tak się dzieje?
Co do drugiego pytania, tzn. kolizji między mapą a graczem, to mam taki kod:
bool Player::collideWithLevel(const std::vector<std::vector<int>>& levelData)
{
std::vector<Vector2> collideTilePositions;
//sprawdzam każdy róg
checkTilePosition(levelData, collideTilePositions, position.x, position.y); // position.x - pozycja gracza
checkTilePosition(levelData, collideTilePositions, position.x + hitBox.x, position.y);
checkTilePosition(levelData, collideTilePositions, position.x, position.y + hitBox.y);
checkTilePosition(levelData, collideTilePositions, position.x + hitBox.x, position.y + hitBox.y);
if (collideTilePositions.size() == 0) {
return false;
}
for (int i = 0; i < collideTilePositions.size(); i++) {
collideWithTile(collideTilePositions[i]);
}
return true;
}
void Player::checkTilePosition(const std::vector<std::vector<int>>& levelData, std::vector<Vector2>& collideTilePositions, float x, float y)
{
float TILE_WIDTH = 64; // HEIGHT jest takie samo
Vector2 gridPos = Vector2(x / TILE_WIDTH, y / TILE_WIDTH);
if (gridPos.x < 0 || gridPos.x >= levelData[0].size() ||
gridPos.y < 0 || gridPos.y >= levelData.size()) {
return;
}
if (levelData[gridPos.y][gridPos.x] != 0) {
collideTilePositions.push_back(gridPos * TILE_WIDTH + Vector2(TILE_WIDTH / 2.0f, TILE_WIDTH / 2.0f));
}
}
void Player::collideWithTile(Vector2 tilePos)
{
float TILE_WIDTH = 64;
const float TILE_RADIUS = TILE_WIDTH / 2.0f;
const float MIN_DISTANCE_X = hitBox.x / 2 + TILE_RADIUS; // hitboxX i hitBoxY - wysokość i szerokość gracza
const float MIN_DISTANCE_Y = hitBox.y / 2 + TILE_RADIUS;
Vector2 distVec = getCenter() - tilePos; // getCenter - pobiera środek gracza
float DepthX = MIN_DISTANCE_X - abs(distVec.x);
float DepthY = MIN_DISTANCE_Y - abs(distVec.y);
if (DepthX > 0 && DepthY > 0) {
if (std::max(DepthX, 0.0f) < std::max(DepthY, 0.0f)) {
if (distVec.x < 0) {
position.x -= DepthX;
} else {
position.x += DepthX;
}
} else {
if (distVec.y < 0) {
position.y -= DepthY;
} else {
position.y += DepthY;
}
}
}
}
void Player::Update(const std::vector<std::vector<int>>& map, flaot deltaTime)
{
//... input etc.
collideWithLevel(map)
}
I problem polega na tym, że gdy kolizja w ogóle nie czyta osi X, a dla osi Y w opaczny sposób, tzn. gdy dolna ścianka gracza uderza w górną ściankę bloku mapy zamiast wrócić do góry (tzn. na pozycję górnej ścianki bloku), teleportuje się do ścianki dolnej bloku.
Można by powiedzieć, że skoro tak się dzieje to kiedy jest sprawdzana kolizja na Y po prostu zmienić znaki.
Próbowałem, co się stało, to w momencie w którym dolna ścianka gracza uderza w górną bloku, to gracz odsuwa się o za duży dystans. Dodatkowo jest też błąd przy kolizji ścianka górna gracza - dolna bloku.
Wydaje mi się, że najlepiej pokaże to filmik:
Tutaj pierwszy przypadek:
Tutaj z odwróconym depthY tzn,
} else {
if (distVec.y < 0) {
position.y += DepthY;
} else {
position.y -= DepthY;
}
}
Co robię w tym źle?