Camera handling in 2D game

Hi everyone, I’m happy to join the community! :slight_smile:

I want to create my first 2d game in Panda3d, also it’s a good oportunity to practise my C++ skills, so that’s why I decided to Panda.

I’ve met some first issues, that I probably don’t understand…
I’ve created a 2d map using the CardMaker, it’s a png file with 2048x2048pixels map.
In the same way I’ve created a hero.
Now I want to create a camera which will follow the hero and will show some limited part of map (let’s say 1/3 of map), but I can’t achive that.
Can you help me with understanding what I’m doing wrong? :slight_smile:

Code:
Map initialization.

void
InitMap(PandaFramework& framework, WindowFramework* window)
{
  PT(Texture)
  mapTexture = TexturePool::load_texture(MAP_PATH);
  if (!mapTexture) {
    std::cerr << "Failed to load map texture!" << std::endl;
    return;
  }

  CardMaker cm("map-card");
  cm.set_frame(-1.0f, 1.0f, -1.0f, 1.0f);
  mapNode = window->get_render_2d().attach_new_node(cm.generate());
  mapNode.set_texture(mapTexture);
  mapNode.set_scale(1.0f, 1.0f, 1.0f);
}

Hero

static NodePath player;

static void
SetPlayerTexture(WindowFramework* window)
{
  PT(Texture)
  heroTexture = TexturePool::load_texture(PLAYER_PATH);
  if (!heroTexture) {
    /** @todo: Error handling. */
    std::cerr << "Failed to load hero texture!" << std::endl;
    return;
  }

  CardMaker cmHero("hero-card");
  static constexpr float HERO_SIZE{ 128.0f / 2048.0f };
  cmHero.set_frame(-HERO_SIZE, HERO_SIZE, -HERO_SIZE, HERO_SIZE);
  player = window->get_render_2d().attach_new_node(cmHero.generate());
  player.set_texture(heroTexture);
  player.set_scale(1, 1, 1);
  player.set_transparency(TransparencyAttrib::M_alpha);
}

static void
SetupPlayerCamera(WindowFramework* window)
{
  Camera* camera = window->get_camera(0);
  PT(OrthographicLens) lens = new OrthographicLens();
  lens->set_film_size(0.3f, 0.3f);
  lens->set_near_far(-1000, 1000);
  camera->set_lens(lens);

  NodePath cameraNP = window->get_camera_group();
  cameraNP.reparent_to(player);
  cameraNP.set_pos(player.get_pos().get_x(), 0, player.get_pos().get_z());

  AsyncTaskManager::get_global_ptr()->add(
    new GenericAsyncTask("Update Camera Task", UpdateCamera, window));
}

AsyncTask::DoneStatus
UpdateCamera(GenericAsyncTask* task, void* data)
{
  WindowFramework* window = static_cast<WindowFramework*>(data);
  LPoint3f hero_pos = player.get_pos();

  NodePath cameraNP = window->get_camera_group();
  cameraNP.set_pos(hero_pos.get_x(), 0, hero_pos.get_z());

  return AsyncTask::DS_cont;
}

void
InitPlayer(PandaFramework& framework, WindowFramework* window)
{
  SetPlayerTexture(window);
  RegisterKeys(framework);

  framework.get_task_mgr().add(
    new GenericAsyncTask("UpdatePlayerTask", UpdatePlayerTask, (void*)window));

  SetupPlayerCamera(window);
}

Main

int
main(int argc, char* argv[])
{
  PandaFramework framework;
  framework.open_framework(argc, argv);
  framework.set_window_title("Panda Game");
  WindowFramework* window = framework.open_window();

  window->enable_keyboard();
  InitMap(framework, window);
  InitPlayer(framework, window);

  framework.main_loop();
  framework.close_framework();

  return 0;
}

Greetings, and welcome to the forum! I hope that you find your time here to be positive! :slight_smile:

As to your question: Well, you don’t specify quite what’s not working, so I’m somewhat guessing.

However, I did notice that, when you update the camera, you set it’s position–having already reparented it to to player.

As a result, you’re essentially setting its position twice: once by virtue of reparenting it (which causes it to inherit its position from its parent–here the player), and a second time by explicitly calling “set_pos”.

Or, put more accurately, reparenting the camera to the player causes its position to be relative to the player. As a result, setting its position to that of the player causes it to end up as far from the player as the player is from the origin.

That is, if the player is at (2, 0, 3), then the camera ends up at (2 + 2, 0 + 0, 3 + 3) = (4, 0, 3).

There is one other thing, however: Unless I’m missing it in your code, you don’t seem to be setting the camera’s position on the y-axis to be different to that of the player or the background. As a result, the camera will presumably be unable to see either of them–they’re too close, and are likely being culled away.

I’d suggest pulling the camera back a bit–maybe setting its y-coordinate to something like -10 as a start, and adjusting from there.

Thanks for your answer @Thaumaturge
Yes… I didn’t specify what’s wrong… sorry.

So the result of my code is that the camera is not working at all. I see whole map and my character moving on it but camera is not working.
My expectation would be to have camera zoomed on about 1/3 of map and player in the center always while moving.

I will try to follow your advices and update my code.

1 Like

Ah, wait! I’ve just spotted something else!

You seem to be attaching your map and player to the 2D render scene-graph (“render_2d”)–but I think that the camera that you’re controlling is the one that renders the 3D scene-graph!

If I’m right, then moving the camera isn’t affecting what’s rendered: there’s nothing in the 3D scene-graph to be rendered by the camera that’s being moved, and the camera that renders the 2D scene-graph isn’t being moved.

I’m honestly not sure of how to get the 2D camera from the C++ side, I’m afraid–I’m more familiar with the Python side of the engine.

I’d suggest experimenting with the code that you have–perhaps with the number provided to “window->get_camera”–and otherwise waiting for someone more familiar with the C++ side of the engine to comment.

So… maybe using 3d scene but only with x and z axis will be easier?

I tried but I stuck at beginning… My textures aren’t displayed right now…
My map is a .png 2048x2048 pixels, my player is 64x64 pixels .png file.
I’m trying to set camera to display only some part of my map.
What I’m doing wrong here?

Code:

static NodePath player;
static NodePath mapNode;

void
InitMap(PandaFramework& framework, WindowFramework* window)
{
  mapNode = window->load_model(framework.get_models(), MAP_PATH);
  mapNode.reparent_to(window->get_render());
  mapNode.set_scale(1, 1, 1);
  mapNode.set_pos(1024, 0, 1024);
}

void
InitPlayer(PandaFramework& framework, WindowFramework* window)
{
player = window->load_model(framework.get_models(), PLAYER_PATH);
player.reparent_to(window->get_render());
player.set_scale(1, 1, 1);
player.set_pos(1024, 0, 1024);
player.set_transparency(TransparencyAttrib::M_alpha);

window->get_camera_group().set_pos(player.get_x() - 512, -1000, player.get_z() + 512);
window->get_camera_group().look_at(player);
}

int
main(int argc, char* argv[])
{
  PandaFramework framework;
  framework.open_framework(argc, argv);
  framework.set_window_title("Panda Game");
  WindowFramework* window = framework.open_window();

  window->enable_keyboard();
  InitMap(framework, window);
  InitPlayer(framework, window);

  framework.main_loop();
  framework.close_framework();

  return 0;
}

Not necessarily–if you can just get the 2D camera (did you try my suggestion for that?) then your original approach seems like it should largely work.

Right now, the main thing that I see in your code is that the numbers that you’re using–1024, etc.–are huge for the 3D screen-graph.

I’m guessing that you’re using the sizes of your textures for reference… but the thing is, the 3D scene-graph doesn’t work that way. The size of the textures isn’t important–the size of the geometry is.

That is, if you have a 3D model that’s 1 unit by 1 unit in size, it doesn’t matter whether the texture that you apply to it is 16x16 pixels or 2048x2048 pixels–the model will still have the same size.

This issue is affecting pretty much all of your positions, by the looks of it: your map and player are being placed way over on the positive x- and z- axes, while your camera is so far back now that the objects may be too small to see.

(Note: I say that they’re “huge for the 3D scene graph”–but they would also be huge for the standard 2D scene graph.

It’s not impossible to set up the 2D scene graph to be scaled to work as you’re expecting–but that will likely complicate things. Thus I’d recommend that for now you stick with the standard scene-graphs, and not attempt to use pixel-based coordinates.)

Unfortunately I’m only able to call get_camera(0)
also window->get_num_cameras() returns only ‘1’.

Ah, fair enough!

In that case, I’d suggest going with what I said about the numbers that you’re using for the 3D scene-graph. If you can get the size of those numbers into keeping with the geometry that you’re using, then you should, I would expect, be able to see your objects.