Copy over pacman game
This commit is contained in:
parent
8062861808
commit
ef20432d9d
25 changed files with 682 additions and 29 deletions
|
@ -1 +0,0 @@
|
||||||
add_executable(01_main main.cpp)
|
|
|
@ -1,4 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
int main() {
|
|
||||||
std::cout << "Hello World\n";
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
add_executable(02_main main.cpp)
|
|
|
@ -1,4 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
int main() {
|
|
||||||
std::cout << "Hello World\n";
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
add_executable(11_main main.cpp)
|
|
|
@ -1,4 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
int main() {
|
|
||||||
std::cout << "Hello World\n";
|
|
||||||
}
|
|
|
@ -1,16 +1,14 @@
|
||||||
if (NOT APPLE AND NOT WIN32)
|
cmake_minimum_required(VERSION 3.17)
|
||||||
set(CMAKE_TOOLCHAIN_FILE toolchains/linux_clang_11.cmake)
|
project(pacman)
|
||||||
endif ()
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
project(modern_cpp C CXX)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
set(CMAKE_CXX_EXTENSIONS NO)
|
|
||||||
|
|
||||||
include_directories(include)
|
# Download automatically, you can also just copy the conan.cmake file
|
||||||
|
if (NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||||
|
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
|
||||||
|
file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.15/conan.cmake"
|
||||||
|
"${CMAKE_BINARY_DIR}/conan.cmake")
|
||||||
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(01_foundation)
|
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/src)
|
||||||
add_subdirectory(02_foundation)
|
add_subdirectory(src)
|
||||||
add_subdirectory(11_intermediate)
|
|
||||||
|
|
45
README.md
45
README.md
|
@ -1,2 +1,43 @@
|
||||||
# modern_cpp_exercises
|
# Mod(C++) - Pac-Man Exercise
|
||||||
Exercises for the TurtleSec training "Mod(C++)" - Foundation and Intermediate
|
|
||||||
|
## Ghosts
|
||||||
|
This will probably become relevant https://en.wikipedia.org/wiki/Ghosts_(Pac-Man)
|
||||||
|
This as well https://youtu.be/ataGotQ7ir8
|
||||||
|
https://gameinternals.com/understanding-pac-man-ghost-behavior
|
||||||
|
https://www.gamasutra.com/view/feature/3938/the_pacman_dossier.php?print=1
|
||||||
|
|
||||||
|
## Windows Toolchain
|
||||||
|
|
||||||
|
Visual Studio Community 2019 - latest (MSVC 19.28.29334.0)
|
||||||
|
|
||||||
|
## Get Clion
|
||||||
|
|
||||||
|
https://www.jetbrains.com/clion/download/download-thanks.html
|
||||||
|
|
||||||
|
## Get Python 3
|
||||||
|
|
||||||
|
https://docs.python.org/3/using/windows.html#the-full-installer
|
||||||
|
|
||||||
|
## Get Conan
|
||||||
|
|
||||||
|
pip3 install conan --upgrade
|
||||||
|
|
||||||
|
## Conan on Linux
|
||||||
|
|
||||||
|
Set this environment variable and Conan will tell you which packages you need to
|
||||||
|
intall on Linux.
|
||||||
|
CONAN_SYSREQUIRES_MODE=verify
|
||||||
|
|
||||||
|
In case of link errors around sndio, try to uninstall this package and rebuild the conan cache:
|
||||||
|
~~~
|
||||||
|
sudo apt remove libsndio-dev*
|
||||||
|
~~~
|
||||||
|
https://bugzilla.libsdl.org/show_bug.cgi?id=5105
|
||||||
|
|
||||||
|
## Currently does not work on windows - Get Clang
|
||||||
|
|
||||||
|
Clang 10 works seamlessly with Conan (version 1.31.4) - with 11 you have to update your
|
||||||
|
<userhome>/.conan/settings.yml file, add "11" to the list at "clang: version:"
|
||||||
|
|
||||||
|
https://releases.llvm.org/download.html
|
||||||
|
https://www.jetbrains.com/help/clion/quick-tutorial-on-configuring-clion-on-windows.html#clang-cl
|
||||||
|
|
BIN
assets/maze.png
Normal file
BIN
assets/maze.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 870 KiB |
BIN
assets/sprites32.png
Normal file
BIN
assets/sprites32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
19
conanfile.py
Normal file
19
conanfile.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
from conans import ConanFile, CMake
|
||||||
|
|
||||||
|
class ConanDependencies(ConanFile):
|
||||||
|
|
||||||
|
settings = "os", "compiler", "build_type", "arch"
|
||||||
|
generators = "cmake", "cmake_find_package"
|
||||||
|
default_options = {
|
||||||
|
"sdl2_image:jpg": "libjpeg"
|
||||||
|
}
|
||||||
|
|
||||||
|
def requirements(self):
|
||||||
|
self.requires("sdl2/2.0.9@bincrafters/stable")
|
||||||
|
self.requires("sdl2_image/2.0.4@bincrafters/stable")
|
||||||
|
|
||||||
|
def imports(self):
|
||||||
|
self.copy("*.dll", dst="bin", src="bin")
|
||||||
|
self.copy("*.dylib*", dst="bin", src="lib")
|
||||||
|
self.copy('*.so*', dst='lib', src='lib')
|
||||||
|
self.copy("license*", dst="licenses", folder=True, ignore_case=True)
|
103
src/Board.cpp
Normal file
103
src/Board.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include "Board.h"
|
||||||
|
|
||||||
|
// Legend
|
||||||
|
// 0 - wall
|
||||||
|
// 1 - pellet
|
||||||
|
// 2 - nothing
|
||||||
|
// 3 - door
|
||||||
|
// 4 - superpower
|
||||||
|
|
||||||
|
// 16 pixels per square
|
||||||
|
// Maze in pixels - width: 448 - height - 496
|
||||||
|
|
||||||
|
static const uint8_t board[ROWS][COLUMNS] = {
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7
|
||||||
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 0
|
||||||
|
{0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0}, // 1
|
||||||
|
{0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0}, // 2
|
||||||
|
{0,4,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,4,0}, // 3
|
||||||
|
{0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0}, // 4
|
||||||
|
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, // 5
|
||||||
|
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0}, // 6
|
||||||
|
{0,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0}, // 7
|
||||||
|
{0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0}, // 8
|
||||||
|
{0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0}, // 9
|
||||||
|
{0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0}, // 10
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,2,2,2,2,2,2,2,2,2,0,0,1,0,0,0,0,0,0}, // 11
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,0,0,0,3,3,0,0,0,2,0,0,1,0,0,0,0,0,0}, // 12
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0}, // 13
|
||||||
|
{3,2,2,2,2,2,1,2,2,2,0,0,0,0,0,0,0,0,2,2,2,1,2,2,2,2,2,3}, // 14
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0}, // 15
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0}, // 16
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,2,2,2,2,2,2,2,2,2,0,0,1,0,0,0,0,0,0}, // 17
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0}, // 18
|
||||||
|
{0,0,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0}, // 19
|
||||||
|
{0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0}, // 20
|
||||||
|
{0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0}, // 21
|
||||||
|
{0,1,0,0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,1,0}, // 22
|
||||||
|
{0,4,1,1,0,0,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,0,0,1,1,4,0}, // 23
|
||||||
|
{0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0}, // 24
|
||||||
|
{0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0}, // 25
|
||||||
|
{0,1,1,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,1,1,0}, // 26
|
||||||
|
{0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0}, // 27
|
||||||
|
{0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0}, // 28
|
||||||
|
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}, // 29
|
||||||
|
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // 30
|
||||||
|
};
|
||||||
|
|
||||||
|
Board::Board() {
|
||||||
|
resetBoardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Board::resetBoardState() {
|
||||||
|
for (uint8_t row = 0; row < ROWS; row++)
|
||||||
|
for (uint8_t column = 0; column < COLUMNS; column++)
|
||||||
|
board_state[row][column] = board[row][column];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Board::isWalkable(Position point, float_t position_delta, Direction direction) const {
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::LEFT:
|
||||||
|
return board_state[int(point.y)][int(point.x - position_delta)] != 0;
|
||||||
|
case Direction::RIGHT:
|
||||||
|
return board_state[int(point.y)][int(point.x) + 1] != 0;
|
||||||
|
case Direction::UP:
|
||||||
|
return board_state[int(point.y - position_delta)][int(point.x)] != 0;
|
||||||
|
case Direction::DOWN:
|
||||||
|
return board_state[int(point.y) + 1][int(point.x)] != 0;
|
||||||
|
case Direction::NONE:
|
||||||
|
default: return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect Board::pelletSprite() {
|
||||||
|
return pellet;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect Board::superPelletSprite() {
|
||||||
|
return super_pellet;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SDL_Point> Board::pelletPositions() {
|
||||||
|
std::vector<SDL_Point> positions;
|
||||||
|
for (uint8_t row = 0; row < ROWS; row++) {
|
||||||
|
for (uint8_t column = 0; column < COLUMNS; column++) {
|
||||||
|
if (board_state[row][column] == 1)
|
||||||
|
positions.push_back({column, row});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SDL_Point> Board::superPelletPositions() {
|
||||||
|
// Hard coded is probably better than this
|
||||||
|
std::vector<SDL_Point> positions;
|
||||||
|
for (uint8_t row = 0; row < ROWS; row++) {
|
||||||
|
for (uint8_t column = 0; column < COLUMNS; column++) {
|
||||||
|
if (board_state[row][column] == 4)
|
||||||
|
positions.push_back({column, row});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return positions;
|
||||||
|
}
|
35
src/Board.h
Normal file
35
src/Board.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef PACMAN_BOARD_H
|
||||||
|
#define PACMAN_BOARD_H
|
||||||
|
|
||||||
|
#include "Direction.h"
|
||||||
|
#include "Position.h"
|
||||||
|
#include <SDL2/SDL_rect.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
const uint8_t ROWS = 31;
|
||||||
|
const uint8_t COLUMNS = 28;
|
||||||
|
|
||||||
|
class Board {
|
||||||
|
public:
|
||||||
|
Board();
|
||||||
|
|
||||||
|
[[nodiscard]] bool isWalkable(Position point, float_t d, Direction direction) const;
|
||||||
|
|
||||||
|
SDL_Rect pelletSprite();
|
||||||
|
|
||||||
|
SDL_Rect superPelletSprite();
|
||||||
|
|
||||||
|
std::vector<SDL_Point> pelletPositions();
|
||||||
|
|
||||||
|
std::vector<SDL_Point> superPelletPositions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t board_state[ROWS][COLUMNS];
|
||||||
|
const SDL_Rect super_pellet = {0*32, 9*32, 32, 32};
|
||||||
|
const SDL_Rect pellet = {1*32, 9*32, 32, 32};
|
||||||
|
|
||||||
|
void resetBoardState();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_BOARD_H
|
12
src/CMakeLists.txt
Normal file
12
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
include(${CMAKE_BINARY_DIR}/conan.cmake)
|
||||||
|
conan_add_remote(NAME bincrafters INDEX 1 URL https://api.bintray.com/conan/bincrafters/public-conan)
|
||||||
|
|
||||||
|
conan_cmake_run(CONANFILE ../conanfile.py BASIC_SETUP CMAKE_TARGETS BUILD missing)
|
||||||
|
|
||||||
|
find_package(sdl2 REQUIRED)
|
||||||
|
find_package(sdl2_image REQUIRED)
|
||||||
|
include_directories(${sdl2_INCLUDE_DIRS} ${sdl2_image_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.cpp")
|
||||||
|
add_executable(${PROJECT_NAME} ${sources})
|
||||||
|
target_link_libraries(${PROJECT_NAME} sdl2::sdl2 sdl2_image::sdl2_image)
|
12
src/Direction.h
Normal file
12
src/Direction.h
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef PACMAN_DIRECTION_H
|
||||||
|
#define PACMAN_DIRECTION_H
|
||||||
|
|
||||||
|
enum class Direction {
|
||||||
|
NONE,
|
||||||
|
LEFT,
|
||||||
|
RIGHT,
|
||||||
|
UP,
|
||||||
|
DOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_DIRECTION_H
|
60
src/Game.cpp
Normal file
60
src/Game.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
Game::Game()
|
||||||
|
: window(448*2, 496*2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Game::now() {
|
||||||
|
return std::chrono::system_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::run() {
|
||||||
|
InputState inputState;
|
||||||
|
auto current_time = now();
|
||||||
|
while (!inputState.close) {
|
||||||
|
processEvents(inputState);
|
||||||
|
auto time_delta = now() - current_time;
|
||||||
|
auto milli_delta = std::chrono::duration_cast<std::chrono::milliseconds>(time_delta);
|
||||||
|
pacMan.update(milli_delta, inputState, board);
|
||||||
|
current_time += time_delta;
|
||||||
|
window.update(pacMan, board);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::processEvents(InputState & inputState) {
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
switch (event.type) {
|
||||||
|
case SDL_QUIT:
|
||||||
|
inputState.close = true;
|
||||||
|
break;
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
keyToggle(event, inputState, true);
|
||||||
|
break;
|
||||||
|
case SDL_KEYUP:
|
||||||
|
keyToggle(event, inputState, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::keyToggle(const SDL_Event & event, InputState & inputState, bool on) {
|
||||||
|
switch (event.key.keysym.sym) {
|
||||||
|
case SDLK_UP:
|
||||||
|
inputState.up = on;
|
||||||
|
break;
|
||||||
|
case SDLK_DOWN:
|
||||||
|
inputState.down = on;
|
||||||
|
break;
|
||||||
|
case SDLK_LEFT:
|
||||||
|
inputState.left = on;
|
||||||
|
break;
|
||||||
|
case SDLK_RIGHT:
|
||||||
|
inputState.right = on;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
24
src/Game.h
Normal file
24
src/Game.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef PACMAN_GAME_H
|
||||||
|
#define PACMAN_GAME_H
|
||||||
|
|
||||||
|
#include "Board.h"
|
||||||
|
#include "GameWindow.h"
|
||||||
|
#include "InputState.h"
|
||||||
|
#include "PacMan.h"
|
||||||
|
|
||||||
|
class Game {
|
||||||
|
public:
|
||||||
|
Game();
|
||||||
|
void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
GameWindow window;
|
||||||
|
PacMan pacMan;
|
||||||
|
Board board;
|
||||||
|
|
||||||
|
static void processEvents(InputState & inputState) ;
|
||||||
|
static void keyToggle(const SDL_Event & event, InputState & inputState, bool on);
|
||||||
|
[[nodiscard]] static auto now() ;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_GAME_H
|
149
src/GameWindow.cpp
Normal file
149
src/GameWindow.cpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
#include "GameWindow.h"
|
||||||
|
#include "PacMan.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_image.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
GameWindow::GameWindow(int width, int height) {
|
||||||
|
initSDL();
|
||||||
|
initSDLImage();
|
||||||
|
auto sdl_window = createWindow(width, height);
|
||||||
|
auto sdl_renderer = createRenderer(sdl_window);
|
||||||
|
createWindowSurface(sdl_window);
|
||||||
|
setDrawColor(sdl_renderer);
|
||||||
|
maze_texture = loadTexture(sdl_renderer, "../../../assets/maze.png");
|
||||||
|
sprite_texture = loadTexture(sdl_renderer, "../../../assets/sprites32.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::update(const PacMan & pacMan, Board board) {
|
||||||
|
SDL_RenderClear(renderer.get());
|
||||||
|
|
||||||
|
renderMaze();
|
||||||
|
renderBoard(board);
|
||||||
|
renderPacMan(pacMan);
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::renderMaze() const {
|
||||||
|
renderTexture(maze_texture.get(), nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::renderBoard(Board board) {
|
||||||
|
renderPellets(board);
|
||||||
|
renderSuperPellets(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::renderSuperPellets(Board & board) const {
|
||||||
|
SDL_Rect sprite_rect = board.superPelletSprite();
|
||||||
|
std::vector<SDL_Point> superPelletPositions = board.superPelletPositions();
|
||||||
|
for (const auto & pos : superPelletPositions) {
|
||||||
|
SDL_Rect maze_rect = targetRect({ float_t(pos.x), float_t(pos.y) }, 16);
|
||||||
|
renderTexture(sprite_texture.get(), &sprite_rect, &maze_rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::renderPellets(Board & board) const {
|
||||||
|
SDL_Rect sprite_rect = board.pelletSprite();
|
||||||
|
std::vector<SDL_Point> pelletPositions = board.pelletPositions();
|
||||||
|
for (const auto & pos : pelletPositions) {
|
||||||
|
SDL_Rect maze_rect = targetRect({ float_t(pos.x), float_t(pos.y) }, 16);
|
||||||
|
renderTexture(sprite_texture.get(), &sprite_rect, &maze_rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::renderPacMan(const PacMan & pac_man) const {
|
||||||
|
Position maze_position = pac_man.currentPosition();
|
||||||
|
SDL_Rect maze_rect = targetRect(maze_position, 16);
|
||||||
|
SDL_Rect sprite_rect = pac_man.currentSprite();
|
||||||
|
renderTexture(sprite_texture.get(), &sprite_rect, &maze_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect GameWindow::targetRect(const Position & position, int pixel_increase) {
|
||||||
|
int pixels = 32;
|
||||||
|
int displacement = pixel_increase / 2;
|
||||||
|
return {
|
||||||
|
int(pixels * position.x) - displacement,
|
||||||
|
int(pixels * position.y) - displacement,
|
||||||
|
(pixels + pixel_increase),
|
||||||
|
(pixels + pixel_increase)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::renderTexture(SDL_Texture * texture, SDL_Rect * texture_rect, SDL_Rect * target_rect) const {
|
||||||
|
if (SDL_RenderCopy(renderer.get(), texture, texture_rect, target_rect) < 0)
|
||||||
|
exitFailure("Failed to copy texture to renderer");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::initSDL() {
|
||||||
|
if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
|
||||||
|
exitFailure("Failed to initialize the SDL2 library");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::initSDLImage() {
|
||||||
|
int img_flags = IMG_INIT_PNG;
|
||||||
|
if (IMG_Init(img_flags) != img_flags)
|
||||||
|
exitImgFailure("Failed to init SDL_Image with png");
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Window * GameWindow::createWindow(int width, int height) {
|
||||||
|
window = std::unique_ptr<SDL_Window, SDL_Window_Deleter>(SDL_CreateWindow(
|
||||||
|
"Pacman",
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
SDL_WINDOWPOS_CENTERED,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
SDL_WINDOW_OPENGL));
|
||||||
|
|
||||||
|
if (!window)
|
||||||
|
exitFailure("Failed to create window");
|
||||||
|
|
||||||
|
return window.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Renderer * GameWindow::createRenderer(SDL_Window * sdl_window) {
|
||||||
|
renderer = std::unique_ptr<SDL_Renderer, SDL_Renderer_Deleter>(SDL_CreateRenderer(
|
||||||
|
sdl_window,
|
||||||
|
-1,
|
||||||
|
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC));
|
||||||
|
|
||||||
|
if (!renderer)
|
||||||
|
exitFailure("Failed to create renderer");
|
||||||
|
|
||||||
|
return renderer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::createWindowSurface(SDL_Window * sdl_window) {
|
||||||
|
window_surface = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(SDL_GetWindowSurface(sdl_window));
|
||||||
|
if (!window_surface)
|
||||||
|
exitFailure("Failed to get the surface from the window");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::setDrawColor(SDL_Renderer * sdl_renderer) {
|
||||||
|
if (SDL_SetRenderDrawColor(sdl_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE) < 0)
|
||||||
|
exitFailure("Failed to set renderer color");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SDL_Texture, SDL_Texture_Deleter> GameWindow::loadTexture(SDL_Renderer * sdl_renderer, const std::string& path) {
|
||||||
|
auto surface = std::unique_ptr<SDL_Surface, SDL_Surface_Deleter>(IMG_Load(path.c_str()));
|
||||||
|
if (!surface)
|
||||||
|
exitImgFailure("Failed to load image");
|
||||||
|
|
||||||
|
auto texture = std::unique_ptr<SDL_Texture, SDL_Texture_Deleter>(SDL_CreateTextureFromSurface(sdl_renderer, surface.get()));
|
||||||
|
if (!texture)
|
||||||
|
exitFailure("Failed to create texture from surface");
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::exitFailure(const std::string& message) {
|
||||||
|
std::cerr << message << "\n";
|
||||||
|
std::cerr << "SDL2 Error: " << SDL_GetError() << "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWindow::exitImgFailure(const std::string& message) {
|
||||||
|
std::cerr << message << "\n";
|
||||||
|
std::cerr << "SDL2_Image Error: " << IMG_GetError() << "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
65
src/GameWindow.h
Normal file
65
src/GameWindow.h
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#ifndef PACMAN_GAMEWINDOW_H
|
||||||
|
#define PACMAN_GAMEWINDOW_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "PacMan.h"
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
struct SDL_Window_Deleter {
|
||||||
|
void operator()(SDL_Window * window) {
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SDL_Renderer_Deleter {
|
||||||
|
void operator()(SDL_Renderer * renderer) {
|
||||||
|
SDL_DestroyRenderer(renderer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SDL_Surface_Deleter {
|
||||||
|
void operator()(SDL_Surface * surface) {
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SDL_Texture_Deleter {
|
||||||
|
void operator()(SDL_Texture * texture) {
|
||||||
|
SDL_DestroyTexture(texture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PacMan;
|
||||||
|
|
||||||
|
class GameWindow {
|
||||||
|
public:
|
||||||
|
explicit GameWindow(int width, int height);
|
||||||
|
void update(const PacMan & pacMan, Board board);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<SDL_Window, SDL_Window_Deleter> window;
|
||||||
|
std::unique_ptr<SDL_Renderer, SDL_Renderer_Deleter> renderer;
|
||||||
|
std::unique_ptr<SDL_Surface, SDL_Surface_Deleter> window_surface;
|
||||||
|
std::unique_ptr<SDL_Texture, SDL_Texture_Deleter> maze_texture;
|
||||||
|
std::unique_ptr<SDL_Texture, SDL_Texture_Deleter> sprite_texture;
|
||||||
|
SDL_Window * createWindow(int width, int height);
|
||||||
|
SDL_Renderer * createRenderer(SDL_Window * window);
|
||||||
|
void createWindowSurface(SDL_Window * sdl_window);
|
||||||
|
static void initSDL();
|
||||||
|
static void initSDLImage();
|
||||||
|
static void setDrawColor(SDL_Renderer * sdl_renderer);
|
||||||
|
static void exitFailure(const std::string& message);
|
||||||
|
static void exitImgFailure(const std::string& message);
|
||||||
|
static std::unique_ptr<SDL_Texture, SDL_Texture_Deleter> loadTexture(SDL_Renderer * sdl_renderer, const std::string& path);
|
||||||
|
void renderMaze() const;
|
||||||
|
void renderPacMan(const PacMan & pac_man) const;
|
||||||
|
void renderBoard(Board board);
|
||||||
|
void renderPellets(Board & board) const;
|
||||||
|
void renderSuperPellets(Board & board) const;
|
||||||
|
static SDL_Rect targetRect(const Position & position, int pixel_increase);
|
||||||
|
void renderTexture(SDL_Texture * texture, SDL_Rect * texture_rect, SDL_Rect * target_rect) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_GAMEWINDOW_H
|
1
src/InputState.cpp
Normal file
1
src/InputState.cpp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "InputState.h"
|
13
src/InputState.h
Normal file
13
src/InputState.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef PACMAN_INPUTSTATE_H
|
||||||
|
#define PACMAN_INPUTSTATE_H
|
||||||
|
|
||||||
|
class InputState {
|
||||||
|
public:
|
||||||
|
bool close = false;
|
||||||
|
bool up = false;
|
||||||
|
bool down = false;
|
||||||
|
bool left = false;
|
||||||
|
bool right = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_INPUTSTATE_H
|
73
src/PacMan.cpp
Normal file
73
src/PacMan.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#include "PacMan.h"
|
||||||
|
|
||||||
|
PacMan::PacMan() :
|
||||||
|
right_animation{right_wide, right_narrow, closed, right_narrow},
|
||||||
|
left_animation{left_wide, left_narrow, closed, left_narrow},
|
||||||
|
up_animation{up_wide, up_narrow, closed, up_narrow},
|
||||||
|
down_animation{down_wide, down_narrow, closed, down_narrow} {
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Rect PacMan::currentSprite() const {
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::NONE: return closed;
|
||||||
|
case Direction::LEFT: return left_animation[animation_position];
|
||||||
|
case Direction::RIGHT: return right_animation[animation_position];
|
||||||
|
case Direction::UP: return up_animation[animation_position];
|
||||||
|
case Direction::DOWN: return down_animation[animation_position];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Position PacMan::currentPosition() const {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacMan::update(std::chrono::milliseconds time_delta, InputState state, const Board & board) {
|
||||||
|
setDirection(state);
|
||||||
|
updateAnimationPosition(time_delta);
|
||||||
|
updateMazePosition(time_delta, board);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacMan::setDirection(const InputState & state) {
|
||||||
|
if (state.left)
|
||||||
|
direction = Direction::LEFT;
|
||||||
|
else if (state.right)
|
||||||
|
direction = Direction::RIGHT;
|
||||||
|
else if (state.up)
|
||||||
|
direction = Direction::UP;
|
||||||
|
else if (state.down)
|
||||||
|
direction = Direction::DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacMan::updateAnimationPosition(std::chrono::milliseconds time_delta) {
|
||||||
|
animation_position_delta += (time_delta.count() / 100.0);
|
||||||
|
animation_position = int(animation_position + animation_position_delta) % 4;
|
||||||
|
animation_position_delta = (animation_position_delta < 1) ? animation_position_delta : (animation_position_delta - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PacMan::updateMazePosition(std::chrono::milliseconds time_delta, const Board & board) {
|
||||||
|
float_t position_delta = (time_delta.count() / 128.0);
|
||||||
|
|
||||||
|
if (board.isWalkable(pos, position_delta, direction)) {
|
||||||
|
switch (direction) {
|
||||||
|
case Direction::NONE:
|
||||||
|
break;
|
||||||
|
case Direction::LEFT:
|
||||||
|
pos.x -= position_delta;
|
||||||
|
pos.y = floor(pos.y);
|
||||||
|
break;
|
||||||
|
case Direction::RIGHT:
|
||||||
|
pos.x += position_delta;
|
||||||
|
pos.y = floor(pos.y);
|
||||||
|
break;
|
||||||
|
case Direction::UP:
|
||||||
|
pos.x = floor(pos.x);
|
||||||
|
pos.y -= position_delta;
|
||||||
|
break;
|
||||||
|
case Direction::DOWN:
|
||||||
|
pos.x = floor(pos.x);
|
||||||
|
pos.y += position_delta;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
src/PacMan.h
Normal file
45
src/PacMan.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef PACMAN_PACMAN_H
|
||||||
|
#define PACMAN_PACMAN_H
|
||||||
|
|
||||||
|
#include "Board.h"
|
||||||
|
#include "Direction.h"
|
||||||
|
#include "InputState.h"
|
||||||
|
#include "Position.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_rect.h>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
class PacMan {
|
||||||
|
public:
|
||||||
|
PacMan();
|
||||||
|
[[nodiscard]] SDL_Rect currentSprite() const;
|
||||||
|
[[nodiscard]] Position currentPosition() const;
|
||||||
|
|
||||||
|
void update(std::chrono::milliseconds time_delta, InputState state, const Board & board);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Direction direction = Direction::NONE;
|
||||||
|
Position pos = {14, 23};
|
||||||
|
const SDL_Rect right_wide = {0*32, 0, 32, 32};
|
||||||
|
const SDL_Rect right_narrow = {1*32, 0, 32, 32};
|
||||||
|
const SDL_Rect closed = {2*32, 0, 32, 32};
|
||||||
|
const SDL_Rect left_narrow = {3*32, 0, 32, 32};
|
||||||
|
const SDL_Rect left_wide = {4*32, 0, 32, 32};
|
||||||
|
const SDL_Rect up_wide = {5*32, 0, 32, 32};
|
||||||
|
const SDL_Rect up_narrow = {6*32, 0, 32, 32};
|
||||||
|
const SDL_Rect down_wide = {7*32, 0, 32, 32};
|
||||||
|
const SDL_Rect down_narrow = {8*32, 0, 32, 32};
|
||||||
|
const SDL_Rect right_animation[4];
|
||||||
|
const SDL_Rect left_animation[4];
|
||||||
|
const SDL_Rect up_animation[4];
|
||||||
|
const SDL_Rect down_animation[4];
|
||||||
|
uint8_t animation_position = 0;
|
||||||
|
float_t animation_position_delta = 0.0;
|
||||||
|
|
||||||
|
void setDirection(const InputState & state);
|
||||||
|
void updateAnimationPosition(std::chrono::milliseconds time_delta);
|
||||||
|
void updateMazePosition(std::chrono::milliseconds time_delta, const Board & board);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_PACMAN_H
|
11
src/Position.h
Normal file
11
src/Position.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef PACMAN_POSITION_H
|
||||||
|
#define PACMAN_POSITION_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
struct Position {
|
||||||
|
float_t x;
|
||||||
|
float_t y;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PACMAN_POSITION_H
|
7
src/main.cpp
Normal file
7
src/main.cpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include "Game.h"
|
||||||
|
|
||||||
|
extern "C" int main([[maybe_unused]] int argc, [[maybe_unused]] char * argv[]) {
|
||||||
|
Game game;
|
||||||
|
game.run();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue