渲染器如何传递给 类 的其余部分?

How did a Renderer pass to the rest of the classes?

我有一个游戏 class,它通过其构造函数初始化 window 和 SDL 渲染器。我从目前阅读的内容(不多)了解到每个 window.

应该只有一个渲染器

然后我有一个播放器class,我想通过构造函数加载带有图像的纹理,为此我需要渲染器,因此,我将渲染器作为构造函数参数并以这种方式我被迫从 Game 的构造函数将渲染器传递给 Player 构造函数(因为它在 The Game class 中实例化了 Player class)。

事实是渲染器在创建之前就被传递了,我不知道是否有另一种方法可以从游戏中调用播放器的构造函数,因为它迫使我那样说。我留下代码给你看:

游戏class:

#pragma once
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "player.hpp"
//#include "helpers.hpp"
using namespace std;

#define WINDOW_WIDHT 640
#define WINDOW_HEIGTH 480

class Game
{
public:
  Game();
  ~Game();

  void loop();
  void update() {}
  void input();
  void render();
  void draw() {}

private:
  SDL_Window *window;
  SDL_Renderer *renderer = nullptr;
  SDL_Event event;
  SDL_Texture *gTexture;
  bool running;

  Player player;
};

Game::Game() : player(renderer)
{
  SDL_Init(0);
  SDL_CreateWindowAndRenderer(WINDOW_WIDHT, WINDOW_HEIGTH, 0, &window, &renderer);
  SDL_SetWindowTitle(window, "Intento...");

  //inicializa la carga de pngs
  int imgFlags = IMG_INIT_PNG;
  if (!IMG_Init(imgFlags) & imgFlags)
  {
    cout << "No se puede inicializar SDL_Img" << endl;
  }
  
  running = true;
  loop();
}

Game::~Game()
{
  SDL_DestroyRenderer(renderer);
  SDL_DestroyWindow(window);
  IMG_Quit();
  SDL_Quit();
}

void Game::loop()
{
  while (running)
  {
    input();
    render();
    update();
  }
}

void Game::render()
{
  SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
  SDL_Rect rect;
  rect.x = rect.y = 0;
  rect.w = WINDOW_WIDHT;
  rect.h = WINDOW_HEIGTH;

  SDL_RenderFillRect(renderer, &rect);

  SDL_RenderPresent(renderer);
}

void Game::input()
{
  while (SDL_PollEvent(&event) > 0)
  {
    switch (event.type)
    {
    case SDL_QUIT:
      running = false;
      break;
    }
  }
}

Clase 玩家:

#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
//#include "helpers.hpp"
using namespace std;

class Player
{
  public:
    Player(SDL_Renderer *renderer);
    ~Player() = default;
    const SDL_Rect getDest() { return dest; }
    const SDL_Rect getSrc() { return src; }
    void setDest(int x, int y, int w, int h);
    void setSrc(int x, int y, int w, int h);
    SDL_Texture *loadTexture(std::string path, SDL_Renderer *renderer);

  private:
    SDL_Rect dest;
    SDL_Rect src;
};

Player::Player(SDL_Renderer *renderer){
  loadTexture("mario.png", renderer);
  /* setSrc(48, 48, 48, 48);
  setDest(100, 100, 48, 48);
  SDL_Rect playerRectSrc = getSrc();
  SDL_Rect playerRectDest = getDest(); */
}

void Player::setDest(int x, int y, int w, int h){
  dest.x = x;
  dest.y = y;
  dest.w = w;
  dest.h = h;
}
void Player::setSrc(int x, int y, int w, int h){
  src.x = x;
  src.y = y;
  src.w = w;
  src.h = h;
}

SDL_Texture* Player::loadTexture(std::string path, SDL_Renderer *renderer)
{
  SDL_Texture *newTexture = NULL;
  SDL_Surface *loadedSurface = IMG_Load(path.c_str());
  if (loadedSurface == NULL)
  {
    cout << "No se pudo cargar la imagen" << endl;
  }
  else
  {
    newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
    if (newTexture == NULL)
    {
      cout << "No se pudo generar la textura para player" << endl;
    }
    SDL_FreeSurface(loadedSurface);
  }
  return newTexture;
}

问题是创建渲染器后如何调用Player构造函数?我唯一能想到的不是通过构造函数创建纹理,而是通过函数创建纹理,但这不是正确的做法,对吧?这就是构造函数的用途

The only thing I can think of is not to create the texture through the constructor, but through a function, but it wouldn't be the right thing to do, right? that's what the constructor is for

没错。但是,SDL 是一个 C 库,因此它不使用负责 construction/destruction 的 C++ RAII。首先,我们在成员初始化列表 (: foo{}, bar{foo}) 中调用构造函数,它为我们提供了完全构造的成员对象。然后,我们在构造函数体中做任何我们想做的事({ use(foo); })。但是,在您的情况下,在成员初始化 (: player{renderer}).

之前需要构造函数主体 ({ SDL_CreateWindowAndRenderer(...); } )

The question is how to call the Player constructor after the renderer has been created?

在构造 Player:

之前构造 所有 SDL_ 东西
class RenderWindow {
    SDL_Window* window;
    SDL_Renderer* renderer;
public:
    RenderWindow() {
        SDL_Init(0);
        SDL_CreateWindowAndRenderer(WINDOW_WIDHT, WINDOW_HEIGTH, 0, &window, &renderer);
        SDL_SetWindowTitle(window, "Intento...");
    }
    // an easy double-free protection, you can use anything else
    RenderWindow(RenderWindow&&) = delete;
    ~RenderWindow() {
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Quit();
    }
    // lvalue-only to forbid dangling like RenderWindow{}.raw_renderer()
    auto raw_renderer() & { return renderer; }
};

现在,您的 PlayerGame 可以以自然的 RAII 构造然后使用的方式实现:

class Player {
public:
    Player(SDL_Renderer*); // TODO implement
};

class Game {
    // note the declaration order: construction goes from top to bottom
    RenderWindow render_window;
    Player player;
public:
    // SDL_* happens in render_window{},
    // so player{render_window.raw_renderer()} gets an initialized SDL_Renderer
    Game(): /*render_window{},*/ player{render_window.raw_renderer()} {}
};