Implement scaling.

We render pack man at twice the size that we want on screen,
which is the native resolution of the asset file.
The maze is upscaled.
We then project everything onto a view which is applies
the scale factor.

This patches also dectect the appropriate scale on OSX.
This required a bit of objective C (scaling.mm),
but students should not look at this file and there
is a comment in that direction.
Scalling.cpp provides the default implementation
for other platforms.
This commit is contained in:
Corentin Jabot 2021-07-07 11:33:22 +02:00 committed by cor3ntin
parent 98c223933f
commit cbd19df739
5 changed files with 79 additions and 40 deletions

View file

@ -15,3 +15,9 @@ target_link_libraries(libpacman PUBLIC fmt::fmt sfml-graphics)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(libpacman PUBLIC OpenGL::OpenGL OpenGL::GLX)
endif ()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
target_sources(libpacman PRIVATE Scaling.mm)
else()
target_sources(libpacman PRIVATE Scaling.cpp)
endif ()

View file

@ -4,14 +4,30 @@
#include <string>
#include <vector>
double scaling_factor_for_window(sf::WindowHandle);
namespace pacman {
Canvas::Canvas()
: window(sf::VideoMode(windowDimensions().width, windowDimensions().height),
: window(sf::VideoMode((viewDimensions().width/2.0), (viewDimensions().height/2.0)),
"Pacman",
sf::Style::Titlebar | sf::Style::Close) {
sf::Style::Titlebar | sf::Style::Close)
, view(sf::FloatRect(0, 0, viewDimensions().width, viewDimensions().height))
{
window.setView(view);
window.setFramerateLimit(60);
window.setVerticalSyncEnabled(true);
// We render the game in view at twice the native resolution,
// Then project it on a scaled window - on some mac we get the
// scaling factor of the window to adjust the resolution
const auto scale = scaling_factor_for_window(window.getSystemHandle());
const auto width = viewDimensions().width/2.0 * scale;
const auto height = viewDimensions().height/2.0 * scale;
window.setSize(sf::Vector2u(width, height));
maze_texture = loadTexture("maze.png");
sprites_texture = loadTexture("sprites32.png");
game_font = loadFont("retro_font.ttf");
@ -55,10 +71,10 @@ void Canvas::renderMaze() {
maze.setTextureRect(sf::IntRect{
0,
0,
DEFAULT_MAZE_WIDTH,
DEFAULT_MAZE_HEIGHT });
maze.setScale(scale(DEFAULT_MAZE_SCALE_UP), scale(DEFAULT_MAZE_SCALE_UP));
maze.setPosition(scale(LEFT_MARGIN), scale(TOP_MARGIN));
MAZE_WIDTH,
MAZE_HEIGHT });
maze.setScale(MAZE_SCALE_UP, MAZE_SCALE_UP);
maze.setPosition(LEFT_MARGIN, TOP_MARGIN);
window.draw(maze);
}
@ -91,47 +107,52 @@ void Canvas::renderGhost(const Ghost & ghost) {
}
void Canvas::renderScore(int score) {
const int x = (LEFT_MARGIN + TARGET_MAZE_WIDTH + LEFT_MARGIN);
const int y = (TOP_MARGIN * 2) ;
sf::Text text;
text.setPosition(scale(RIGHT_PANEL_X), scale(TOP_MARGIN * 2));
text.setPosition(x, y);
text.setFont(game_font);
text.setString(fmt::format("SCORE\n{}", score));
text.setCharacterSize(int(scale(40)));
text.setCharacterSize(40);
text.setFillColor(sf::Color::White);
window.draw(text);
}
void Canvas::renderLives(int lives) {
constexpr GridPosition liveSprite = Atlas::pacman_left_narrow;
const auto x = scale(RIGHT_PANEL_X);
const auto y = scale(DEFAULT_TARGET_MAZE_HEIGHT);
const size_t x = (LEFT_MARGIN + TARGET_MAZE_WIDTH + LEFT_MARGIN);
const size_t y = TARGET_MAZE_HEIGHT;
Sprite pacmanSprite = getSprite(liveSprite);
for (int i = 0; i < lives - 1; i++) {
auto life_position = scale(i * DEFAULT_SPRITE_WIDTH);
pacmanSprite.setPosition(x + life_position, y);
size_t life_position = i * SPRITE_WIDTH * 1.5;
GridPosition pos{ x + life_position, y };
pacmanSprite.setPosition(pos.x, pos.y);
window.draw(pacmanSprite);
}
}
Rect Canvas::windowDimensions() {
return { 0, 0, int(scale(WINDOW_WIDTH)), int(scale(WINDOW_HEIGHT)) };
Rect Canvas::viewDimensions() {
const double width = (LEFT_MARGIN + TARGET_MAZE_WIDTH + SCORE_WIDTH);
const double height = (TOP_MARGIN + TARGET_MAZE_HEIGHT + BOTTOM_MARGIN);
return { 0, 0, int(width), int(height) };
}
Sprite Canvas::getSprite(GridPosition coordinate) const {
sf::Sprite sprite;
sprite.setTexture(sprites_texture);
sprite.setTextureRect(sf::IntRect{ int(coordinate.x * DEFAULT_SPRITE_WIDTH),
int(coordinate.y * DEFAULT_SPRITE_HEIGHT),
DEFAULT_SPRITE_WIDTH,
DEFAULT_SPRITE_HEIGHT });
sprite.scale(SCALE_FACTOR, SCALE_FACTOR);
sprite.setTextureRect(sf::IntRect{ int(coordinate.x * SPRITE_WIDTH),
int(coordinate.y * SPRITE_HEIGHT),
SPRITE_WIDTH,
SPRITE_HEIGHT });
return sprite;
}
void Canvas::renderSprite(Sprite sprite, Position pos) {
const auto x = scale(LEFT_MARGIN + int(pos.x * DEFAULT_SPRITE_WIDTH));
const auto y = scale(TOP_MARGIN + int(pos.y * DEFAULT_SPRITE_HEIGHT));
sprite.setPosition(x, y);
pos.x = LEFT_MARGIN + (pos.x * SPRITE_WIDTH);
pos.y = TOP_MARGIN + (pos.y * SPRITE_HEIGHT);
sprite.setPosition(pos.x, pos.y);
window.draw(sprite);
}
@ -156,8 +177,4 @@ sf::Font Canvas::loadFont(std::string_view path) {
return font;
}
float_t Canvas::scale(int value) {
return float_t(value) * SCALE_FACTOR;
}
} // namespace pacman

5
lib/Scaling.cpp Normal file
View file

@ -0,0 +1,5 @@
#include <SFML/Graphics.hpp>
double scaling_factor_for_window(sf::WindowHandle) {
return 1.0;
}

14
lib/Scaling.mm Normal file
View file

@ -0,0 +1,14 @@
/*
This is an Objective C file to detect the window's
resolution scale on Apple platforms.
It is not a C++ file and is not part of this course!
*/
#import "AppKit/NSWindow.h"
#include <cassert>
double scaling_factor_for_window(void* ptr) {
NSWindow* window = static_cast<NSWindow*>(ptr);
assert(window);
double d = [window backingScaleFactor];
return d;
}

View file

@ -19,21 +19,17 @@ public:
std::optional<sf::Event> pollEvent();
private:
static constexpr float_t SCALE_FACTOR = 0.5;
static constexpr uint16_t LEFT_MARGIN = 40 * 2;
static constexpr uint16_t TOP_MARGIN = 40 * 2;
static constexpr uint16_t BOTTOM_MARGIN = 40 * 2;
static constexpr uint16_t DEFAULT_MAZE_WIDTH = 448;
static constexpr uint16_t DEFAULT_MAZE_HEIGHT = 496;
static constexpr uint16_t DEFAULT_MAZE_SCALE_UP = 2;
static constexpr uint16_t DEFAULT_TARGET_MAZE_WIDTH = DEFAULT_MAZE_WIDTH * DEFAULT_MAZE_SCALE_UP;
static constexpr uint16_t DEFAULT_TARGET_MAZE_HEIGHT = DEFAULT_MAZE_HEIGHT * DEFAULT_MAZE_SCALE_UP;
static constexpr uint16_t MAZE_WIDTH = 448;
static constexpr uint16_t MAZE_HEIGHT = 496;
static constexpr uint16_t MAZE_SCALE_UP = 2;
static constexpr uint16_t TARGET_MAZE_WIDTH = 448 * MAZE_SCALE_UP;
static constexpr uint16_t TARGET_MAZE_HEIGHT = 496 * MAZE_SCALE_UP;
static constexpr uint16_t SCORE_WIDTH = 200 * 2;
static constexpr uint16_t DEFAULT_SPRITE_WIDTH = 32;
static constexpr uint16_t DEFAULT_SPRITE_HEIGHT = 32;
static constexpr uint16_t RIGHT_PANEL_X = LEFT_MARGIN + DEFAULT_TARGET_MAZE_WIDTH + LEFT_MARGIN;
static constexpr uint16_t WINDOW_WIDTH = LEFT_MARGIN + DEFAULT_TARGET_MAZE_WIDTH + SCORE_WIDTH;
static constexpr uint16_t WINDOW_HEIGHT = TOP_MARGIN + DEFAULT_TARGET_MAZE_HEIGHT + BOTTOM_MARGIN;
static constexpr uint16_t SPRITE_WIDTH = 32;
static constexpr uint16_t SPRITE_HEIGHT = 32;
void clear();
void render();
@ -47,17 +43,18 @@ private:
void renderScore(int score);
void renderLives(int lives);
static Rect windowDimensions();
static Rect viewDimensions();
static sf::Texture loadTexture(std::string_view path);
static sf::Font loadFont(std::string_view path);
static float_t scale(int value);
Sprite getSprite(GridPosition rect) const;
sf::RenderWindow window;
sf::View view;
sf::Texture maze_texture;
sf::Texture sprites_texture;
sf::Font game_font;
};
} // namespace pacman