1
\$\begingroup\$

I m writing a game engine using mainly SDL2 and box2D, I implemented TiledMap class that loads a tmx map and everything is fine it even support animation and objects that later will generate Box2D bodies for collision

Everything is fine till Camera came in I implemented camera class and its working fine for the sprites and the map but something is annoying me the bodies that get generated will stay at their position when the camera moves!

To illustrate the problem see these screenshots:

The bodies and the texture are not in same position while moving the camera

Without moving the camera the result is normal:

The texture and the bodies fit!

I tried to come up with some solution one of them is to translate all of the map bodies backward. The solution honestly worked but have some side effects that made me not satisfied at all with the result

  • Expensive (specially if your setting the camera every frame)
  • Results in a non physical behavior !

Finally I will show some code so the reader will understand exactly what Im doing:

Camera Class:

void Camera::addObjectToCamera(TiledMap* map)
{
    m_CameraAttachedObjects.emplace_back(map);
}


void Camera::moveCamera(Vec2i p_Offset)
{
    m_CamPos += p_Offset;
    for (auto& obj : m_CameraAttachedObjects)
    {
        p_Offset.x = -p_Offset.x;
        obj->translateMap(p_Offset);
    }
}

void Camera::setCameraPosition(Vec2i p_Pos)
{
    m_CamPos = p_Pos;
    for (auto& obj : m_CameraAttachedObjects)
    {
        obj->setPosition(p_Pos);
    }
}

TiledMap class:

void TiledMap::render(float dt) 
{
    SDL_RenderCopy(r, m_MapTexture, &m_MapSrc, NULL);
}

void TiledMap::setPosition(Vec2i pos)
{
    static Vec2i LastPositionTranslated = Vec2i(0, 0);
    if (Utility::IsInBox(Vec2i(pos.x, pos.y), Vec2i(0,0), Vec2i(m_MapDst.w, m_MapDst.h))) {
        m_Position = pos;
        m_MapSrc.x = pos.x;
        m_MapSrc.y = pos.y;
        for (auto& itr : m_cachedAnimatiedTiles) {
            auto current_rect = itr->tiledLayerData->DestDraw;
            itr->tiledLayerData->DestDraw->x = current_rect->x - (pos.x - LastPositionTranslated.x);
            itr->tiledLayerData->DestDraw->y = current_rect->y - (pos.y - LastPositionTranslated.y);
        }
        for (auto& phyObj : m_allMapBodies) {
            Vec2f old_transform = phyObj->GetTransform().p;
            phyObj->SetTransform
            (
                Vec2f(old_transform.x-float(pos.x - LastPositionTranslated.x), old_transform.y-float(pos.y - LastPositionTranslated.y))
                ,0.f
            );
        }
        LastPositionTranslated = pos;
    }
}

void TiledMap::translateMap(Vec2i pos)
{
    if (Utility::IsInBox(Vec2i(m_MapSrc.x - pos.x, m_MapSrc.y - pos.y), Vec2i(0, 0), Vec2i(m_MapDst.w, m_MapDst.h))) {
        m_Position += pos;
        m_MapSrc.x -= pos.x;
        m_MapSrc.y -= pos.y;
        for (auto& itr : m_cachedAnimatiedTiles) {
            auto current_rect = itr->tiledLayerData->DestDraw;
            itr->tiledLayerData->DestDraw->x = current_rect->x + pos.x;
            itr->tiledLayerData->DestDraw->y = current_rect->y + pos.y;
        }
        for (auto& phyObj : m_allMapBodies) {
            Vec2f old_transform = phyObj->GetTransform().p;
            phyObj->SetTransform(old_transform + Vec2f((float)(pos.x), (float)(pos.y)), 0.f);
        }
    }
}

Game class (game manager):

            body->SetLinearVelocity(Vec2f(20.f, body->GetLinearVelocity().y));
        cam->moveCamera(Vec2i(10, 0));

the current code is translating the bodies too which is i consider a bad solution of my initial problem anyone have better solution to scroll in box2d? (See another part of the box2d world)

I aiming later (after fixing this problem) to position the camera to follow my player.

Note: I know my camera is kinda fake and its just change which part of the map texture to draw but there is no other way to do it .

\$\endgroup\$
1
\$\begingroup\$

Looking at your code, it looks like your camera actually moves the physics objects in the world. This is not a common approach. It's hard to judge what you do with the visual objects, but it seems like you only move the position where they are rendered, and not their logical world position, is that correct?

In any case, the pattern I use when creating cameras, is keeping the camera offset in the camera, and only applying it to the sprite objects when rendering the level. This way the world simulation keeps running just fine, and the rendering code is also nicely decoupled from the movement code.

So, you would move your camera just like you currently are, but don't apply the offset to the objects in your world. Instead, in your rendering function for the level, do something like the following:

void Level::Render()
{
    auto cameraOffset = m_Camera->GetOffset();

    for (auto& object : m_Objects)
    {
        Sprite* sprite = object->GetSprite();
        sprite->SetPosition(object->GetWorldPosition() + cameraOffset);

        Renderer::RenderSprite(sprite);
    }

    if (Globals::DoDebugRendering)
    {
        for (auto& geometry : m_Geometry)
        {
            Renderer:RenderShapeAt(geometry, geometry->GetOrigin() + cameraOffset);
        }
    }
}

So just to clarify, the goal is to keep the position changes caused by the camera totally visual, and unrelated to the objects' positions in the game world.

\$\endgroup\$
  • \$\begingroup\$ Thanks for replying, Changing the rendering location of the shapes didn't help me, \$\endgroup\$ – Omarito Oct 28 '18 at 15:43
  • \$\begingroup\$ If you don't move anything logically (in world space), then there's absolutely nothing that can go wrong with this approach, so you must be still moving something in world space. \$\endgroup\$ – Tyyppi_77 Oct 28 '18 at 15:49
  • \$\begingroup\$ Thanks for replying, Changing the rendering location of the shapes didn't help me. The way I implemented this for the map itself that i combine all tiles in one big texture and then render it using SDL_RenderCopy and using the 3rd argument of the function I can define what part of the map to render on my screen. Since the bodies are in different coordinates (Box2D coordinates) they stay where they are even while changing their coordinates in debug draw. so my camera work only on SDL2 textures cant get it to work with box2d bodies. \$\endgroup\$ – Omarito Oct 28 '18 at 15:51
  • \$\begingroup\$ The way I edited my code after your post is that i get all map bodies vertices and combine them in the map texture that will be rendered later when the map is loaded. \$\endgroup\$ – Omarito Oct 28 '18 at 15:52
  • 1
    \$\begingroup\$ Solved i finally understand what you mean! Thanks very much \$\endgroup\$ – Omarito Oct 28 '18 at 23:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.