0
\$\begingroup\$

I am making an Asteroids clone in SDL2. I think the kind of world asteroids takes place in is called toroidal map. I'm refering to the fact that the edges of the world are connected so when you exit from one side, you appear at the opposite.

My first approach was with SDL_DrawRect(), but I don't know how to "break" the rectangle in order for it to appear in two (or more) sides at once. I faked the effect by creating another rectangle, like a shadow, when the original rectangle was touching an edge. It worked somehow, but I wanted the real deal: render the diferent parts of the rectangle in different edges.

My second approach is creating the rectangle by SDL_FPoints in a std::vector, like this:

struct SpaceObject {
  std::vector<SDL_FPoint> shape_;
  // more members
  void generateSquareShape(const SDL_FPoint& where);
  void move(float delta_time, const SDL_Point& screen_size);
  void render(SDL_Renderer& renderer) const;
  void warpCoordinates(SDL_FPoint& point, const SDL_Point& screen_size);
  // more functions
};

The shape is generated once with generateSquareShape(). It just sets the appropiate coordinates to each SDL_FPoint. The movement function is just applying deltas to each point:

void SpaceObject::move(float delta_time, const SDL_Point& screen_size) {
  for (auto& point: shape_) {
    point.x += delta_.x * delta_time;
    point.y += delta_.y * delta_time;
    warpCoordinates(point, screen_size);
  }
}

Render is more of the same, another vector travesal:

void SpaceObject::render(SDL_Renderer& renderer) const {
  for (const auto& point: shape_) {
    SDL_RenderDrawPointF(&renderer, point.x, point.y);
  }
}

The warpCoordinates() is called at every move to check if a point must go to another side:

void SpaceObject::warpCoordinates(SDL_FPoint& point, const SDL_Point& screen_size) {
  if (point.x < 0) {
    point.x = screen_size.x + point.x;
  } else if (point.x > screen_size.x - 1u) {
    point.x = point.x - screen_size.x;
  }

  if (point.y < 0) {
    point.y = screen_size.y + point.y;
  } else if (point.y > screen_size.y - 1u) {
    point.y = point.y - screen_size.y;
  }
}

All in all it accomplishes with the effect nicely, but I think it performs poorly (at least in my laptop i5 2,6ghz, 6gb RAM, win10) and it hasn't collision detection yet. One optimization I think is moving and rendering on the same function so it only loops through the points vector once, but I think that breaks the one function, one job, doesn't it?

So, is there anything I'm missing to improve performance? A faster algorithm to implement this toroidal world? Maybe a most suitable SDL class to work with? Or some C++ technique I'm not using and I should? Thanks in advance, any help appreciated.

Using win10, SDL2.0.12, and the latest visualC++ compiler.

\$\endgroup\$
  • \$\begingroup\$ "I faked the effect by creating another rectangle, like a shadow, when the original rectangle was touching an edge" — this is exactly how many games with wraparound worlds implement this effect. It might sound "fake," but that's what games are: illusions and approximations. As long as it gives the player the target experience you're aiming for, any route you take to get there is valid. \$\endgroup\$ – DMGregory May 30 at 17:22
  • 1
    \$\begingroup\$ Thank you @DMGregory for your advice. Appreciated and surprised that this cloning was in fact used. Also thanks for this "warparound" term which I didn't know; now I can get more results on the site which I'm currently reading. \$\endgroup\$ – Alex CB May 31 at 14:34
  • \$\begingroup\$ The Asteroids-accurate, easiest, implementation is to not wrap the object until it completely leaves the screen. But since you don't want to be old-school-accurate, your approach works fine. The only issue I see is player-perceived collision. A circle would be best but not if the player is in two locations at once. That's an ellipse and collision is much more difficult. \$\endgroup\$ – Casey Jun 21 at 3:58
1
\$\begingroup\$

Just use modulo operation on edges. It works in both positive and negative values.

Your update() function would call something like:

entity->posX = (entity->posX + entity->velocity * dt ) % screen_width
entity->posY = (entity->posY + entity->velocity * dt ) % screen_height

Creating a warp_object() function like you did, is the path to misery.

| improve this answer | |
\$\endgroup\$

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.