diff --git a/2dgk_7/2dgk_7.sln b/2dgk_7/2dgk_7.sln
new file mode 100644
index 0000000..2402553
--- /dev/null
+++ b/2dgk_7/2dgk_7.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31729.503
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{48610DB6-1D7A-4667-B1C7-5A023FAEB4DE}") = "2dgk_7", "2dgk_7\2dgk_7.vcxproj", "{14004A67-8ECA-43C5-94CF-4FD1653746B2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Debug|x64.ActiveCfg = Debug|x64
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Debug|x64.Build.0 = Debug|x64
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Debug|x86.ActiveCfg = Debug|Win32
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Debug|x86.Build.0 = Debug|Win32
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Release|x64.ActiveCfg = Release|x64
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Release|x64.Build.0 = Release|x64
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Release|x86.ActiveCfg = Release|Win32
+ {14004A67-8ECA-43C5-94CF-4FD1653746B2}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {958FDA17-FFCC-4AA3-932E-C8CF2B78EBD3}
+ EndGlobalSection
+EndGlobal
diff --git a/2dgk_7/2dgk_7/2dgk_7.vcxproj b/2dgk_7/2dgk_7/2dgk_7.vcxproj
new file mode 100644
index 0000000..065bd5f
--- /dev/null
+++ b/2dgk_7/2dgk_7/2dgk_7.vcxproj
@@ -0,0 +1,186 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {14004a67-8eca-43c5-94cf-4fd1653746b2}
+ My2dgk7
+ 10.0
+
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ vcpkg_installed\x86-windows\x86-windows\include\SDL2
+
+
+ Console
+ true
+ vcpkg_installed\x86-windows\x86-windows\debug\lib\manual-link;%(AdditionalLibraryDirectories)
+ SDL2maind.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ vcpkg_installed\x86-windows\x86-windows\include\SDL2
+
+
+ Console
+ true
+ true
+ true
+ vcpkg_installed\x86-windows\x86-windows\lib\manual-link;%(AdditionalLibraryDirectories)
+ SDL2main.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ vcpkg_installed\x64-windows\x64-windows\include\SDL2
+
+
+ Console
+ true
+ vcpkg_installed\x64-windows\x64-windows\debug\lib\manual-link;%(AdditionalLibraryDirectories)
+ SDL2maind.lib;%(AdditionalDependencies)
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ vcpkg_installed\x64-windows\x64-windows\include\SDL2
+
+
+ Console
+ true
+ true
+ true
+ vcpkg_installed\x64-windows\x64-windows\lib\manual-link;%(AdditionalLibraryDirectories)
+ SDL2main.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2dgk_7/2dgk_7/2dgk_7.vcxproj.filters b/2dgk_7/2dgk_7/2dgk_7.vcxproj.filters
new file mode 100644
index 0000000..637c52f
--- /dev/null
+++ b/2dgk_7/2dgk_7/2dgk_7.vcxproj.filters
@@ -0,0 +1,84 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/2dgk_7/2dgk_7/Constants.h b/2dgk_7/2dgk_7/Constants.h
new file mode 100644
index 0000000..3793f52
--- /dev/null
+++ b/2dgk_7/2dgk_7/Constants.h
@@ -0,0 +1,18 @@
+#pragma once
+namespace KapitanGame {
+ namespace Constants {
+ constexpr const char* WINDOW_TITLE = "2DGK - Zadanie 2 (Lab 7-8)";
+ //Analog joystick dead zone
+ constexpr int JOYSTICK_DEAD_ZONE = 8000;
+ //Screen dimension constants
+ constexpr int WINDOW_DEAD_ZONE = 100;
+ constexpr int SCREEN_WIDTH = 640;
+ constexpr int SCREEN_HEIGHT = 480;
+ constexpr float SCREEN_RATIO = static_cast(SCREEN_WIDTH) / SCREEN_HEIGHT;
+ constexpr int TILE_WIDTH = 32;
+ constexpr int TILE_HEIGHT = 32;
+ constexpr float SPEED = 0.16f;
+ constexpr float SMOOTH = 0.4f;
+ constexpr int CIRCLES_COUNT = 100;
+ }
+}
diff --git a/2dgk_7/2dgk_7/GamePad.h b/2dgk_7/2dgk_7/GamePad.h
new file mode 100644
index 0000000..4d8990f
--- /dev/null
+++ b/2dgk_7/2dgk_7/GamePad.h
@@ -0,0 +1,8 @@
+#pragma once
+#include
+namespace KapitanGame {
+ struct GamePad {
+ bool Buttons[SDL_CONTROLLER_BUTTON_MAX];
+ int Axis[SDL_CONTROLLER_AXIS_MAX];
+ };
+}
diff --git a/2dgk_7/2dgk_7/KCircle.cpp b/2dgk_7/2dgk_7/KCircle.cpp
new file mode 100644
index 0000000..df88d83
--- /dev/null
+++ b/2dgk_7/2dgk_7/KCircle.cpp
@@ -0,0 +1,75 @@
+#include "KCircle.h"
+
+#include "KRect.h"
+#include "Utils.h"
+
+namespace KapitanGame {
+
+
+ KCircle::KCircle(const KVector2D& position, const KVector2D& velocity, const float& radius, const KTexture& texture) : KShape(position, velocity, texture),
+ Radius(radius) {
+ }
+
+ bool KCircle::IsCollision(const KCircle& other) const {
+ return (Position - other.Position).Length() < Radius + other.Radius;
+ }
+
+ bool KCircle::IsCollision(const KRect& other) const {
+ const auto f = KVector2D(Utils::Clamp(Position.X, other.GetPosition().X - other.GetWidth() / 2, other.GetPosition().X + other.GetWidth() / 2),
+ Utils::Clamp(Position.Y, other.GetPosition().Y - other.GetHeight() / 2, other.GetPosition().Y + other.GetHeight() / 2));
+
+ return (Position - f).Length() < Radius;
+ }
+
+ KVector2D KCircle::GetSeparationVector(const KCircle& other) const {
+ const KVector2D centerDiff = Position - other.Position;
+ const float centerDiffLength = centerDiff.Length();
+ return centerDiff / centerDiffLength * (Radius + other.Radius - centerDiffLength);
+ }
+
+ KVector2D KCircle::GetSeparationVector(const KRect& other) const {
+ const float l = other.GetPosition().X - other.GetWidth() / 2;
+ const float r = other.GetPosition().X + other.GetWidth() / 2;
+ const float t = other.GetPosition().Y - other.GetHeight() / 2;
+ const float b = other.GetPosition().Y + other.GetHeight() / 2;
+ const auto f = KVector2D(Utils::Clamp(Position.X, l, r),
+ Utils::Clamp(Position.Y, t, b));
+
+ if (Position == f) {
+ const auto left = Position.X - l + Radius;
+ const auto right = r - Position.X + Radius;
+ const auto top = Position.Y - t + Radius;
+ const auto bottom = b - Position.Y + Radius;
+ return KRect::GetSeparationVector(left, right, top, bottom);
+ }
+ return (Position - f) / (Position - f).Length() * (Radius - (Position - f).Length());
+ }
+
+ void KCircle::CollisionDetectionWithMapStep(const SDL_Rect& map) {
+
+ if (Position.X - map.x - Radius < 0)
+ {
+ Position.X += -(Position.X - map.x - Radius);
+ Velocity = Velocity.Reflect({ -1.f, 0.f });
+ }
+ if (Position.X + Radius - map.w > 0)
+ {
+ Velocity = Velocity.Reflect({ 1.f, 0.f });
+ Position.X += -(Position.X - map.w + Radius);
+ }
+ if (Position.Y - Radius - map.y < 0) {
+ Position.Y += -(Position.Y - map.y - Radius);
+ Velocity = Velocity.Reflect({ 0.f, 1.f });
+ }
+ if (Position.Y + Radius - map.h > 0) {
+ Position.Y += -(Position.Y - map.h + Radius);
+ Velocity = Velocity.Reflect({ 0.f, -1.f });
+ }
+ }
+
+ float KCircle::GetRadius() const {
+ return Radius;
+ }
+
+}
+
diff --git a/2dgk_7/2dgk_7/KCircle.h b/2dgk_7/2dgk_7/KCircle.h
new file mode 100644
index 0000000..8816b59
--- /dev/null
+++ b/2dgk_7/2dgk_7/KCircle.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "KShape.h"
+#include "KTexture.h"
+#include "KVector2d.h"
+
+namespace KapitanGame {
+ class KCircle final : public KShape
+ {
+ public:
+ KCircle(const KVector2D& position, const KVector2D& velocity, const float& radius, const KTexture& texture);
+ void CollisionDetectionWithMapStep(const SDL_Rect& map) override;
+ float GetRadius() const;
+ protected:
+ KVector2D GetSeparationVector(const KCircle& other) const override;
+ KVector2D GetSeparationVector(const KRect& other) const override;
+ bool IsCollision(const KCircle& other) const override;
+ bool IsCollision(const KRect& other) const override;
+ private:
+ float Radius{ 1.f };
+ };
+}
+
+
diff --git a/2dgk_7/2dgk_7/KGame.cpp b/2dgk_7/2dgk_7/KGame.cpp
new file mode 100644
index 0000000..eaa2587
--- /dev/null
+++ b/2dgk_7/2dgk_7/KGame.cpp
@@ -0,0 +1,233 @@
+#include "KGame.h"
+#include "SDL.h"
+#include
+#include
+#include
+#include
+#include
+
+#include "Utils.h"
+#include "Constants.h"
+#include "KTexture.h"
+#include "KTile.h"
+#include "KVector2d.h"
+
+namespace KapitanGame {
+ KGame::KGame() : HasSeparation(true), HasCollision(true) {
+ //Initialize SDL
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ throw std::runtime_error(Utils::StringFormat("SDL could not initialize! SDL_Error: %s", SDL_GetError()));
+ }
+ //Set texture filtering to linear
+ if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
+ {
+ printf("Warning: Linear texture filtering not enabled!\n");
+ }
+
+ Input.Init();
+
+ //Create window
+ Window = SDL_CreateWindow(Constants::WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
+ if (Window == nullptr)
+ {
+ throw std::runtime_error(Utils::StringFormat("Window could not be created! SDL_Error: %s", SDL_GetError()));
+ }
+ //Create renderer for window
+ Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED);
+ if (Renderer == nullptr)
+ {
+ throw std::runtime_error(Utils::StringFormat("Renderer could not be created! SDL Error: %s", SDL_GetError()));
+ }
+ //Initialize renderer color
+ SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF);
+ }
+
+ KGame::~KGame() {
+ //Free loaded images
+
+ CircleTexture.Free();
+
+ Input.Free();
+
+ //Destroy window
+ SDL_DestroyRenderer(Renderer);
+ SDL_DestroyWindow(Window);
+ Window = nullptr;
+ Renderer = nullptr;
+
+ //Quit SDL subsystems
+ SDL_Quit();
+ }
+
+ bool KGame::LoadMedia()
+ {
+ //Loading success flag
+ bool success = true;
+
+ if (!PlayerTexture.LoadFromFile("textures/player.bmp", Renderer))
+ {
+ printf("Failed to load player texture image!\n");
+ success = false;
+ }
+ if (!PlayerTexture2.LoadFromFile("textures/player2.bmp", Renderer))
+ {
+ printf("Failed to load player texture image!\n");
+ success = false;
+ }
+
+ for (int i = static_cast(TileType::Default); i <= static_cast(TileType::Dot); ++i)
+ {
+ if (!TileTextures[i].LoadFromFile("textures/0" + std::to_string(i) + ".bmp", Renderer))
+ {
+ printf("Failed to load 0%d texture image!\n", i);
+ success = false;
+ }
+ }
+
+ Tiles.clear();
+ std::ifstream levelFile;
+ levelFile.open("level.txt");
+ if (levelFile.fail())
+ {
+ printf("Failed to load level.txt!\n");
+ success = false;
+ }
+ else
+ {
+ int y = 0;
+ std::string line;
+ while (std::getline(levelFile, line)) {
+ if (MapWidth < static_cast(line.length() * Constants::TILE_WIDTH))
+ MapWidth = static_cast(line.length()) * Constants::TILE_WIDTH;
+ for (auto i = 0ull; i < line.length(); ++i) {
+ auto type = TileType::Default;
+ switch (line[i])
+ {
+ case '|':
+ type = TileType::VerticalWall;
+ break;
+ case '-':
+ type = TileType::HorizontalWall;
+ break;
+ case '.':
+ type = TileType::Dot;
+ break;
+ default:;
+ }
+ Tiles.emplace_back(i * Constants::TILE_WIDTH, y * Constants::TILE_HEIGHT, type);
+ }
+ ++y;
+ }
+ MapHeight = y * Constants::TILE_HEIGHT;
+ levelFile.close();
+ }
+ return success;
+ }
+
+ SDL_Texture* KGame::LoadTexture(const std::string& path) const {
+ //The final optimized image
+ SDL_Texture* newTexture = nullptr;
+
+ //Load image at specified path
+ SDL_Surface* loadedSurface = SDL_LoadBMP(path.c_str());
+ if (loadedSurface == nullptr)
+ {
+ printf("Unable to load image %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
+ }
+ else
+ {
+ //Create texture from surface pixels
+ newTexture = SDL_CreateTextureFromSurface(Renderer, loadedSurface);
+ if (newTexture == nullptr)
+ {
+ printf("Unable to optimize image %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
+ }
+
+ //Get rid of old loaded surface
+ SDL_FreeSurface(loadedSurface);
+ }
+
+ return newTexture;
+ }
+
+ int KGame::Run(int argc, char* args[])
+ {
+ //Load media
+ if (!LoadMedia())
+ {
+ printf("Failed to load media!\n");
+ }
+ else
+ {
+ //Main loop flag
+ bool quit = false;
+
+ //Event handler
+ SDL_Event e;
+
+ SDL_Rect map{ 0,0,Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT };
+
+ for (int i = 0; i < Constants::CIRCLES_COUNT; ++i) {
+ KVector2D position(Utils::RandomNumber() * (Constants::SCREEN_WIDTH - 2 * CircleTexture.GetWidth()) + CircleTexture.GetWidth(), Utils::RandomNumber() * (Constants::SCREEN_HEIGHT - 2 * CircleTexture.GetHeight()) + CircleTexture.GetHeight());
+ Circles.emplace_back(position, KVector2D(100.f, 100.f), CircleTexture.GetWidth() / 2, CircleTexture);
+ }
+ uint32_t previousTime;
+ uint32_t time = previousTime = SDL_GetTicks();
+ printf("\n");
+
+ //While application is running
+ while (!quit)
+ {
+ previousTime = time;
+ time = SDL_GetTicks();
+
+ Input.HandleInputPreEvents();
+
+ //Handle events on queue
+ while (SDL_PollEvent(&e))
+ {
+ //User requests quit
+ if (e.type == SDL_QUIT)
+ {
+ quit = true;
+ continue;
+ }
+ Input.HandleEvent(e);
+ }
+ Input.HandleInputPostEvents();
+
+ if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_S) || Input.IsControllerButtonPressed(Controllers::Controller1, SDL_CONTROLLER_BUTTON_START))
+ {
+ HasSeparation = !HasSeparation;
+ }
+ if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_C) || Input.IsControllerButtonPressed(Controllers::Controller1, SDL_CONTROLLER_BUTTON_BACK))
+ {
+ HasCollision = !HasCollision;
+ }
+
+
+ for (auto& circle : Circles)
+ circle.CollisionDetectionStep(Circles, HasSeparation, HasCollision);
+
+ for (auto& circle : Circles)
+ circle.CollisionDetectionWithMapStep(map);
+
+ for (auto& circle : Circles)
+ circle.MovementStep(static_cast(time - previousTime) / 1000.f);
+
+ //Clear screen
+ SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF);
+ SDL_RenderClear(Renderer);
+
+ for (auto& circle : Circles)
+ circle.Render(Renderer);
+
+ //Update screen
+ SDL_RenderPresent(Renderer);
+ }
+ }
+
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/2dgk_7/2dgk_7/KGame.h b/2dgk_7/2dgk_7/KGame.h
new file mode 100644
index 0000000..23d0e8f
--- /dev/null
+++ b/2dgk_7/2dgk_7/KGame.h
@@ -0,0 +1,40 @@
+#pragma once
+#include "KTexture.h"
+#include
+#include
+
+#include "KCircle.h"
+#include "KInput.h"
+
+
+namespace KapitanGame {
+
+ class KGame
+ {
+ public:
+ KGame();
+ ~KGame();
+ KGame(const KGame& other) = delete;
+ KGame(KGame&& other) noexcept = delete;
+ KGame& operator=(const KGame& other) = delete;
+ KGame& operator=(KGame&& other) noexcept = delete;
+ int Run(int argc, char* args[]);
+ private:
+ SDL_Window* Window = nullptr;
+
+ SDL_Renderer* Renderer = nullptr;
+
+ KTexture CircleTexture;
+
+ std::vector Circles;
+ KInput Input;
+ bool HasSeparation;
+ bool HasCollision;
+ private:
+ bool LoadMedia();
+
+ SDL_Texture* LoadTexture(const std::string& path) const;
+ };
+}
+
+
diff --git a/2dgk_7/2dgk_7/KInput.cpp b/2dgk_7/2dgk_7/KInput.cpp
new file mode 100644
index 0000000..c071cfe
--- /dev/null
+++ b/2dgk_7/2dgk_7/KInput.cpp
@@ -0,0 +1,153 @@
+#include "KInput.h"
+#include "Constants.h"
+
+#include
+
+namespace KapitanGame {
+ KInput::KInput() {
+ Initialized = false;
+ }
+
+ void KInput::Init() {
+ if (Initialized) return;
+ if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) != 1)
+ SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
+
+ const int nJoysticks = SDL_NumJoysticks();
+ GamepadsCount = 0;
+
+ // Count how many controllers there are
+ for (int i = 0; i < nJoysticks; i++)
+ if (SDL_IsGameController(i))
+ GamepadsCount++;
+
+ // If we have some controllers attached
+ if (GamepadsCount > 0)
+ {
+ for (int i = 0; i < GamepadsCount; i++)
+ {
+ // Open the controller and add it to our list
+ SDL_GameController* pad = SDL_GameControllerOpen(i);
+ if (SDL_GameControllerGetAttached(pad) == 1)
+ ConnectedControllers.push_back(pad);
+ else
+ printf("SDL_GetError() = %s\n", SDL_GetError());
+ }
+ SDL_GameControllerEventState(SDL_ENABLE);
+ }
+
+ // Vectors are empty to begin with, this sets their size
+ ControllerInputs.resize(GamepadsCount);
+ LastControllerInputs.resize(GamepadsCount);
+
+ // Set the status of the controllers to "nothing is happening"
+ for (int i = 0; i < GamepadsCount; i++) {
+ for (int a = 0; a < SDL_CONTROLLER_AXIS_MAX; a++) {
+ ControllerInputs[i].Axis[a] = 0;
+ LastControllerInputs[i].Axis[a] = 0;
+ }
+ for (int b = 0; b < SDL_CONTROLLER_BUTTON_MAX; b++) {
+ ControllerInputs[i].Buttons[b] = false;
+ LastControllerInputs[i].Buttons[b] = false;
+ }
+ }
+ Initialized = true;
+ }
+
+ void KInput::Free() {
+ for (const auto& pad : ConnectedControllers) {
+ SDL_GameControllerClose(pad);
+ }
+ ConnectedControllers.clear();
+ }
+
+ void KInput::HandleInputPreEvents() {
+ for (int i = 0; i < GamepadsCount; i++) {
+ for (int a = 0; a < SDL_CONTROLLER_AXIS_MAX; a++) {
+ LastControllerInputs[i].Axis[a] = ControllerInputs[i].Axis[a];
+ }
+ for (int b = 0; b < SDL_CONTROLLER_BUTTON_MAX; b++) {
+ LastControllerInputs[i].Buttons[b] = ControllerInputs[i].Buttons[b];
+ }
+ }
+ LastKeyboardInputs = KeyboardInputs;
+ }
+ void KInput::HandleInputPostEvents() {
+ int size = 0;
+ const Uint8* currentKeyStates = SDL_GetKeyboardState(&size);
+ KeyboardInputs = std::vector(currentKeyStates, currentKeyStates + size);
+ }
+ void KInput::HandleEvent(const SDL_Event& event) {
+ switch (event.type) {
+ // This happens when a device is added
+ // A future improvement is to cope with new controllers being plugged in
+ // when the game is running
+ case SDL_CONTROLLERDEVICEADDED:
+ printf("DEVICEADDED cdevice.which = %d\n", event.cdevice.which);
+ break;
+
+ // If a controller button is pressed
+ case SDL_CONTROLLERBUTTONDOWN:
+ // Cycle through the controllers
+ for (int i = 0; i < GamepadsCount; i++) {
+ // Looking for the button that was pressed
+ if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) {
+ // So the relevant state can be updated
+ ControllerInputs[i].Buttons[event.cbutton.button] = true;
+ }
+ }
+ break;
+
+ // Do the same for releasing a button
+ case SDL_CONTROLLERBUTTONUP:
+ for (int i = 0; i < GamepadsCount; i++) {
+ if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) {
+ ControllerInputs[i].Buttons[event.cbutton.button] = false;
+ }
+ }
+ break;
+
+ // And something similar for axis motion
+ case SDL_CONTROLLERAXISMOTION:
+ for (int i = 0; i < GamepadsCount; i++) {
+ if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) {
+ ControllerInputs[i].Axis[event.caxis.axis] = event.caxis.value;
+ }
+ }
+ break;
+ }
+ }
+ bool KInput::IsControllerButtonPressed(const Controllers controllerId, const SDL_GameControllerButton button) {
+ if (controllerId < Controllers::Controller1 || controllerId > static_cast(GamepadsCount - 1)) return false;
+
+ return ControllerInputs[static_cast(controllerId)].Buttons[button] && !LastControllerInputs[static_cast(controllerId)].Buttons[button];
+ }
+
+ bool KInput::IsControllerButtonHeld(const Controllers controllerId, const SDL_GameControllerButton button) {
+ if (controllerId < Controllers::Controller1 || controllerId > static_cast(GamepadsCount - 1)) return false;
+
+ return ControllerInputs[static_cast(controllerId)].Buttons[button] && LastControllerInputs[static_cast(controllerId)].Buttons[button];
+ }
+
+ float KInput::GetControllerAxis(const Controllers controllerId, const SDL_GameControllerAxis axis) {
+ if (controllerId static_cast(GamepadsCount - 1)) return 0.0;
+
+ const int value = ControllerInputs[static_cast(controllerId)].Axis[axis];
+ if (std::abs(value) < Constants::JOYSTICK_DEAD_ZONE) return 0.0;
+
+ return static_cast(value) / 32768.0f;
+ }
+
+ bool KInput::IsKeyboardButtonPressed(const Uint8 scanCode) {
+ if (scanCode > KeyboardInputs.size() || scanCode > LastKeyboardInputs.size())
+ return false;
+ return KeyboardInputs[scanCode] && KeyboardInputs[scanCode] != LastKeyboardInputs[scanCode];
+ }
+
+ bool KInput::IsKeyboardButtonHeld(const Uint8 scanCode) {
+ if (scanCode > KeyboardInputs.size())
+ return false;
+ return KeyboardInputs[scanCode];
+ }
+}
+
diff --git a/2dgk_7/2dgk_7/KInput.h b/2dgk_7/2dgk_7/KInput.h
new file mode 100644
index 0000000..0542139
--- /dev/null
+++ b/2dgk_7/2dgk_7/KInput.h
@@ -0,0 +1,38 @@
+#pragma once
+#include
+#include
+
+#include "GamePad.h"
+
+namespace KapitanGame {
+ enum class Controllers : int {
+ Controller1,
+ Controller2,
+ Controller3,
+ Controller4
+ };
+
+ class KInput {
+ std::vector ConnectedControllers;
+ std::vector ControllerInputs;
+ std::vector LastControllerInputs;
+ std::vector KeyboardInputs;
+ std::vector LastKeyboardInputs;
+ int GamepadsCount;
+ bool Initialized;
+ public:
+ KInput();
+ void Init();
+ void Free();
+ void HandleInputPreEvents();
+ void HandleInputPostEvents();
+ void HandleEvent(const SDL_Event& event);
+ bool IsControllerButtonPressed(Controllers controllerId, SDL_GameControllerButton button);
+ bool IsControllerButtonHeld(Controllers controllerId, SDL_GameControllerButton button);
+ float GetControllerAxis(Controllers controllerId, SDL_GameControllerAxis axis);
+ bool IsKeyboardButtonHeld(Uint8 scanCode);
+ bool IsKeyboardButtonPressed(Uint8 scanCode);
+ };
+}
+
+
diff --git a/2dgk_7/2dgk_7/KPlayerController.cpp b/2dgk_7/2dgk_7/KPlayerController.cpp
new file mode 100644
index 0000000..19567bb
--- /dev/null
+++ b/2dgk_7/2dgk_7/KPlayerController.cpp
@@ -0,0 +1,5 @@
+#include "KPlayerController.h"
+
+namespace KapitanGame {
+
+}
diff --git a/2dgk_7/2dgk_7/KPlayerController.h b/2dgk_7/2dgk_7/KPlayerController.h
new file mode 100644
index 0000000..1f29a80
--- /dev/null
+++ b/2dgk_7/2dgk_7/KPlayerController.h
@@ -0,0 +1,14 @@
+#pragma once
+#include "KShape.h"
+#include "KVector2d.h"
+
+namespace KapitanGame {
+ class KPlayerController
+ {
+ private:
+ KVector2D Input;
+ KShape Shape;
+ };
+}
+
+
diff --git a/2dgk_7/2dgk_7/KRect.cpp b/2dgk_7/2dgk_7/KRect.cpp
new file mode 100644
index 0000000..9cc6079
--- /dev/null
+++ b/2dgk_7/2dgk_7/KRect.cpp
@@ -0,0 +1,73 @@
+#include "KRect.h"
+
+#include "KCircle.h"
+#include "Utils.h"
+
+namespace KapitanGame {
+ KRect::KRect(const KVector2D& position, const KVector2D& velocity, const KTexture& texture, const float width,
+ const float height) : KShape(position, velocity, texture), Width(width), Height(height) {
+ }
+
+ void KRect::CollisionDetectionWithMapStep(const SDL_Rect& map) {
+ }
+
+ float KRect::GetWidth() const {
+ return Width;
+ }
+
+ float KRect::GetHeight() const {
+ return Height;
+ }
+
+ KVector2D KRect::GetSeparationVector(const float left, const float right, const float top, const float bottom) {
+ auto x = left < right ? -left : right;
+ auto y = top < bottom ? -top : bottom;
+ if (fabs(x) < fabs(y))
+ y = 0;
+ if (fabs(x) > fabs(y))
+ x = 0;
+ return { x, y };
+ }
+
+ KVector2D KRect::GetSeparationVector(const KCircle& other) const {
+ const float l = Position.X - Width / 2;
+ const float r = Position.X + Width / 2;
+ const float t = Position.Y - Height / 2;
+ const float b = Position.Y + Height / 2;
+ const auto f = KVector2D(Utils::Clamp(other.GetPosition().X, l, r),
+ Utils::Clamp(other.GetPosition().Y, t, b));
+
+ if (other.GetPosition() == f) {
+ const auto left = other.GetPosition().X - l + other.GetRadius();
+ const auto right = r - other.GetPosition().X + other.GetRadius();
+ const auto top = other.GetPosition().Y - t + other.GetRadius();
+ const auto bottom = b - other.GetPosition().Y + other.GetRadius();
+ return GetSeparationVector(left, right, top, bottom) * -1.f;
+ }
+ return (other.GetPosition() - f) / (other.GetPosition() - f).Length() * (other.GetRadius() - (other.GetPosition() - f).Length()) * 1.f;
+ }
+
+ KVector2D KRect::GetSeparationVector(const KRect& other) const {
+ const auto left = Position.X + Width / 2 - (other.Position.X - other.Width / 2);
+ const auto right = other.Position.X + other.Width / 2 - (Position.X - Width / 2);
+ const auto top = Position.Y + Height / 2 - (other.Position.Y - other.Height / 2);
+ const auto bottom = other.Position.Y + other.Height / 2 - (Position.Y - Height / 2);
+
+ return GetSeparationVector(left, right, top, bottom);
+ }
+
+ bool KRect::IsCollision(const KCircle& other) const {
+ const auto f = KVector2D(Utils::Clamp(other.GetPosition().X, Position.X - Width / 2, Position.X + Width / 2),
+ Utils::Clamp(other.GetPosition().Y, Position.Y - Height / 2, Position.Y + Height / 2));
+
+ return (Position - f).Length() < other.GetRadius();
+ }
+
+ bool KRect::IsCollision(const KRect& other) const {
+ return Position.X + Width / 2 > other.Position.X - other.Width / 2
+ && other.Position.X + other.Width / 2 > Position.X - Width / 2
+ && Position.Y + Height / 2 > other.Position.Y - other.Height / 2
+ && other.Position.Y + other.Height / 2 > Position.Y - Height / 2;
+ }
+
+}
diff --git a/2dgk_7/2dgk_7/KRect.h b/2dgk_7/2dgk_7/KRect.h
new file mode 100644
index 0000000..ae1b42b
--- /dev/null
+++ b/2dgk_7/2dgk_7/KRect.h
@@ -0,0 +1,24 @@
+#pragma once
+#include "KShape.h"
+
+namespace KapitanGame {
+ class KRect final : public KShape
+ {
+ public:
+ KRect(const KVector2D& position, const KVector2D& velocity, const KTexture& texture, float width, float height);
+ void CollisionDetectionWithMapStep(const SDL_Rect& map) override;
+ float GetWidth() const;
+ float GetHeight() const;
+ static KVector2D GetSeparationVector(float left, float right, float top, float bottom);
+ protected:
+ KVector2D GetSeparationVector(const KCircle& other) const override;
+ KVector2D GetSeparationVector(const KRect& other) const override;
+ bool IsCollision(const KCircle& other) const override;
+ bool IsCollision(const KRect& other) const override;
+ private:
+ float Width;
+ float Height;
+ };
+}
+
+
diff --git a/2dgk_7/2dgk_7/KShape.cpp b/2dgk_7/2dgk_7/KShape.cpp
new file mode 100644
index 0000000..b2a847f
--- /dev/null
+++ b/2dgk_7/2dgk_7/KShape.cpp
@@ -0,0 +1,79 @@
+#include "KShape.h"
+#include "KCircle.h"
+#include "KRect.h"
+
+#include
+#include
+
+namespace KapitanGame {
+ std::atomic KShape::IdCounter{ 0 };
+
+ KShape::KShape(const KVector2D& position, const KVector2D& velocity, const KTexture& texture) : Id(++IdCounter),
+ Position(position),
+ Velocity(velocity),
+ Texture(texture) {
+ }
+
+ KShape::~KShape() = default;
+
+ void KShape::CollisionDetectionStep(const std::vector& shapes, const bool& shouldSeparate,
+ const bool& shouldReflect) {
+ for (auto& other : shapes) {
+ if (other.Id == Id) continue;
+ if (IsCollision(other)) {
+ auto separationVector = GetSeparationVector(other);
+ if (shouldSeparate)
+ Position += separationVector;
+ if (shouldReflect)
+ {
+ separationVector.Normalize();
+ Velocity = Velocity.Reflect(separationVector);
+ }
+ }
+ }
+ }
+
+ bool KShape::IsCollision(const KShape& other) const {
+ try {
+ auto& circle = dynamic_cast(other);
+ return IsCollision(circle);
+ }
+ catch (const std::bad_cast&) {
+ }
+ try {
+ auto& rect = dynamic_cast(other);
+ return IsCollision(rect);
+ }
+ catch (const std::bad_cast&) {
+ }
+ throw std::runtime_error("unsupported shape");
+ }
+
+ KVector2D KShape::GetSeparationVector(const KShape& other) const {
+ try {
+ auto& circle = dynamic_cast(other);
+ return GetSeparationVector(circle);
+ }
+ catch (const std::bad_cast&) {
+ }
+ try {
+ auto& circle = dynamic_cast(other);
+ return GetSeparationVector(circle);
+ }
+ catch (const std::bad_cast&) {
+ }
+ throw std::runtime_error("unsupported shape");
+ }
+
+ void KShape::MovementStep(const float& timeStep) {
+ Position += Velocity * timeStep;
+ }
+
+ void KShape::Render(SDL_Renderer* renderer) const {
+ Texture.Render(renderer, static_cast(Position.X) - Texture.GetWidth() / 2, static_cast(Position.Y) - Texture.GetHeight() / 2);
+ }
+
+ KVector2D KShape::GetPosition() const {
+ return Position;
+ }
+}
diff --git a/2dgk_7/2dgk_7/KShape.h b/2dgk_7/2dgk_7/KShape.h
new file mode 100644
index 0000000..743f9f5
--- /dev/null
+++ b/2dgk_7/2dgk_7/KShape.h
@@ -0,0 +1,44 @@
+#pragma once
+#include
+#include
+#include
+
+#include "KTexture.h"
+#include "KVector2d.h"
+
+namespace KapitanGame {
+ class KRect;
+ class KCircle;
+
+ class KShape
+ {
+ public:
+ KShape(const KVector2D& position, const KVector2D& velocity, const KTexture& texture);
+ virtual ~KShape();
+ KShape() = delete;
+ KShape(const KShape& other) = default;
+ KShape(KShape&& other) = default;
+ KShape& operator=(KShape&& other) = delete;
+ KShape& operator=(const KShape& other) = delete;
+ void CollisionDetectionStep(const std::vector& shapes, const bool& shouldSeparate, const bool& shouldReflect);
+ void MovementStep(const float& timeStep);
+ bool IsCollision(const KShape& other) const;
+ KVector2D GetSeparationVector(const KShape& other) const;
+ void Render(SDL_Renderer* renderer) const;
+ virtual void CollisionDetectionWithMapStep(const SDL_Rect& map) = 0;
+ KVector2D GetPosition() const;
+ const int Id;
+ static std::atomic IdCounter;
+ protected:
+ KVector2D Position{ 0.f, 0.f };
+ KVector2D Velocity{ 0.f, 0.f };
+ virtual KVector2D GetSeparationVector(const KCircle& other) const = 0;
+ virtual KVector2D GetSeparationVector(const KRect& other) const = 0;
+ virtual bool IsCollision(const KCircle& other) const = 0;
+ virtual bool IsCollision(const KRect& other) const = 0;
+ private:
+ const KTexture& Texture;
+ };
+}
+
+
diff --git a/2dgk_7/2dgk_7/KTexture.cpp b/2dgk_7/2dgk_7/KTexture.cpp
new file mode 100644
index 0000000..e659cb6
--- /dev/null
+++ b/2dgk_7/2dgk_7/KTexture.cpp
@@ -0,0 +1,121 @@
+#include "KTexture.h"
+
+namespace KapitanGame {
+ KTexture::KTexture()
+ {
+ //Initialize
+ Texture = nullptr;
+ Width = 0;
+ Height = 0;
+ }
+
+ KTexture::KTexture(KTexture&& other) noexcept
+ : Texture(other.Texture),
+ Width(other.Width),
+ Height(other.Height) {
+ }
+
+ KTexture& KTexture::operator=(const KTexture& other) {
+ if (this == &other)
+ return *this;
+ Texture = other.Texture;
+ Width = other.Width;
+ Height = other.Height;
+ return *this;
+ }
+
+ KTexture& KTexture::operator=(KTexture&& other) noexcept {
+ if (this == &other)
+ return *this;
+ Texture = other.Texture;
+ Width = other.Width;
+ Height = other.Height;
+ return *this;
+ }
+
+ KTexture::~KTexture()
+ {
+ //Deallocate
+ Free();
+
+ }
+ bool KTexture::LoadFromFile(const std::string& path, SDL_Renderer* renderer)
+ {
+ //Get rid of preexisting texture
+ Free();
+ //The final texture
+ SDL_Texture* newTexture = nullptr;
+
+ //Load image at specified path
+ SDL_Surface* loadedSurface = SDL_LoadBMP(path.c_str());
+ if (loadedSurface == nullptr)
+ {
+ printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), SDL_GetError());
+ }
+ else
+ {
+ //Color key image
+ SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0xFF, 0, 0));
+ //Create texture from surface pixels
+ newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
+ if (newTexture == nullptr)
+ {
+ printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
+ }
+ else
+ {
+ //Get image dimensions
+ Width = loadedSurface->w;
+ Height = loadedSurface->h;
+ }
+
+ //Get rid of old loaded surface
+ SDL_FreeSurface(loadedSurface);
+ }
+
+ //Return success
+ Texture = newTexture;
+ return Texture != nullptr;
+ }
+
+ void KTexture::Free()
+ {
+ //Free texture if it exists
+ if (Texture != nullptr)
+ {
+ SDL_DestroyTexture(Texture);
+ Texture = nullptr;
+ Width = 0;
+ Height = 0;
+ }
+ }
+
+ void KTexture::Render(SDL_Renderer* renderer, const int x, const int y, SDL_Rect* clip, const float scale) const
+ {
+ //Set rendering space and render to screen
+ SDL_Rect renderQuad = {
+ static_cast(static_cast(x) * scale), static_cast(static_cast(y) * scale),
+ static_cast(static_cast(Width) * scale), static_cast(static_cast(Height) * scale)
+ };
+
+ //Set clip rendering dimensions
+ if (clip != nullptr)
+ {
+ renderQuad.w = clip->w;
+ renderQuad.h = clip->h;
+ }
+
+ //Render to screen
+ SDL_RenderCopy(renderer, Texture, clip, &renderQuad);
+ }
+
+ int KTexture::GetWidth() const
+ {
+ return Width;
+ }
+
+ int KTexture::GetHeight() const
+ {
+ return Height;
+ }
+}
\ No newline at end of file
diff --git a/2dgk_7/2dgk_7/KTexture.h b/2dgk_7/2dgk_7/KTexture.h
new file mode 100644
index 0000000..85aef92
--- /dev/null
+++ b/2dgk_7/2dgk_7/KTexture.h
@@ -0,0 +1,43 @@
+#pragma once
+#include
+#include
+
+namespace KapitanGame {
+ //Texture wrapper class
+ class KTexture {
+ public:
+ KTexture();
+
+ ~KTexture();
+
+ KTexture(const KTexture& other) = default;
+
+ KTexture(KTexture&& other) noexcept;
+
+ KTexture& operator=(const KTexture& other);
+
+ KTexture& operator=(KTexture&& other) noexcept;
+
+ //Loads image at specified path
+ bool LoadFromFile(const std::string& path, SDL_Renderer* renderer);
+
+ //Deallocates texture
+ void Free();
+
+ //Renders texture at given point
+ void Render(SDL_Renderer* renderer, int x, int y, SDL_Rect* clip = nullptr, float scale = 1.f) const;
+
+ //Gets image dimensions
+ int GetWidth() const;
+ int GetHeight() const;
+
+ private:
+ //The actual hardware texture
+ SDL_Texture* Texture;
+
+ //Image dimensions
+ int Width;
+ int Height;
+ };
+
+}
diff --git a/2dgk_7/2dgk_7/KTile.cpp b/2dgk_7/2dgk_7/KTile.cpp
new file mode 100644
index 0000000..9836c73
--- /dev/null
+++ b/2dgk_7/2dgk_7/KTile.cpp
@@ -0,0 +1,33 @@
+#include "KTile.h"
+
+#include
+
+#include "Constants.h"
+#include "KTexture.h"
+
+namespace KapitanGame {
+
+ int KTile::GetType() const {
+ return static_cast(Type);
+ }
+
+ SDL_Rect KTile::GetBox() const {
+ return Box;
+ }
+
+ KTile::KTile(const int x, const int y, const TileType tileType) {
+ //Get the offsets
+ Box.x = x;
+ Box.y = y;
+
+ Box.w = Constants::TILE_WIDTH;
+ Box.h = Constants::TILE_HEIGHT;
+
+ //Get the tile type
+ Type = tileType;
+ }
+
+ void KTile::Render(SDL_Renderer* renderer, KTexture tileTextures[], const SDL_Rect& camera, const float scale) const {
+ tileTextures[GetType()].Render(renderer, Box.x - camera.x, Box.y - camera.y, nullptr, scale);
+ }
+}
diff --git a/2dgk_7/2dgk_7/KTile.h b/2dgk_7/2dgk_7/KTile.h
new file mode 100644
index 0000000..0f60ea3
--- /dev/null
+++ b/2dgk_7/2dgk_7/KTile.h
@@ -0,0 +1,36 @@
+#pragma once
+#include
+#include
+#include
+
+namespace KapitanGame
+{
+ class KTexture;
+
+ enum class TileType : std::uint32_t {
+ Default = 0,
+ HorizontalWall = 1,
+ VerticalWall = 2,
+ Dot = 3
+ };
+ class KTile {
+ public:
+ //Initializes position and type
+ KTile(int x, int y, TileType tileType);
+
+ //Shows the tile
+ void Render(SDL_Renderer* renderer, KTexture tileTextures[], const SDL_Rect& camera, float scale = 1.f) const;
+
+ //Get the tile type
+ int GetType() const;
+
+ SDL_Rect GetBox() const;
+
+ private:
+ //The attributes of the tile
+ SDL_Rect Box{};
+
+ //The tile type
+ TileType Type;
+ };
+}
diff --git a/2dgk_7/2dgk_7/KVector2d.h b/2dgk_7/2dgk_7/KVector2d.h
new file mode 100644
index 0000000..5ff7cfe
--- /dev/null
+++ b/2dgk_7/2dgk_7/KVector2d.h
@@ -0,0 +1,80 @@
+#pragma once
+#include
+
+namespace KapitanGame {
+ struct KVector2D {
+ float X;
+ float Y;
+
+ KVector2D(const float x, const float y) : X(x), Y(y) {}
+
+ float Length() const { return sqrt(X * X + Y * Y); }
+ float Length2() const { return X * X + Y * Y; }
+
+ bool operator==(const KVector2D& v2) const {
+ return (*this - v2).Length2() < FLT_EPSILON * FLT_EPSILON;
+ }
+
+ KVector2D operator+(const KVector2D& v2) const {
+ return { X + v2.X, Y + v2.Y };
+ }
+
+ friend KVector2D& operator+=(KVector2D& v1, const KVector2D& v2) {
+ v1.X += v2.X;
+ v1.Y += v2.Y;
+ return v1;
+ }
+
+ KVector2D operator*(const float scalar) const
+ {
+ return { X * scalar, Y * scalar };
+ }
+
+ KVector2D& operator*=(const float scalar) {
+ X *= scalar;
+ Y *= scalar;
+ return *this;
+ }
+
+ KVector2D operator-(const KVector2D& v2) const {
+ return { X - v2.X, Y - v2.Y };
+ }
+ friend KVector2D& operator-=(KVector2D& v1, const KVector2D& v2) {
+ v1.X -= v2.X;
+ v1.Y -= v2.Y;
+ return v1;
+ }
+
+ KVector2D operator/(const float scalar) const
+ {
+ return { X / scalar, Y / scalar };
+ }
+ KVector2D& operator/=(const float scalar) {
+ X /= scalar;
+ Y /= scalar;
+ return *this;
+ }
+ float DotProduct(const KVector2D& v2) const {
+ return DotProduct(*this, v2);
+ }
+ KVector2D Reflect(const KVector2D& normal) const {
+ return Reflect(normal, *this);
+ }
+
+ void Normalize() {
+ const float l = Length();
+ if (l > 0) {
+ (*this) *= 1 / l;
+ }
+ }
+
+ static KVector2D Reflect(const KVector2D& normal, const KVector2D& vector) {
+ const float dn = 2 * vector.DotProduct(normal);
+ return vector - normal * dn;
+ }
+ static float DotProduct(const KVector2D& v1, const KVector2D& v2) {
+ return v1.X * v2.X + v1.Y * v2.Y ;
+ }
+ };
+}
+
diff --git a/2dgk_7/2dgk_7/Main.cpp b/2dgk_7/2dgk_7/Main.cpp
new file mode 100644
index 0000000..2944d8f
--- /dev/null
+++ b/2dgk_7/2dgk_7/Main.cpp
@@ -0,0 +1,8 @@
+#include "KGame.h"
+
+int main(int argc, char* args[])
+{
+ KapitanGame::KGame game;
+
+ return game.Run(argc, args);
+}
diff --git a/2dgk_7/2dgk_7/Utils.cpp b/2dgk_7/2dgk_7/Utils.cpp
new file mode 100644
index 0000000..6d102ff
--- /dev/null
+++ b/2dgk_7/2dgk_7/Utils.cpp
@@ -0,0 +1,16 @@
+#include "Utils.h"
+
+#include
+
+namespace KapitanGame {
+ namespace Utils {
+ double RandomNumber() {
+ // Making rng static ensures that it stays the same
+ // Between different invocations of the function
+ static std::default_random_engine rng(make_default_random_engine());
+
+ std::uniform_real_distribution dist(0.0, 1.0);
+ return dist(rng);
+ }
+ }
+}
diff --git a/2dgk_7/2dgk_7/Utils.h b/2dgk_7/2dgk_7/Utils.h
new file mode 100644
index 0000000..dc16e7f
--- /dev/null
+++ b/2dgk_7/2dgk_7/Utils.h
@@ -0,0 +1,53 @@
+#pragma once
+#include
+#include
+#include
+#include
+namespace KapitanGame {
+ namespace Utils {
+
+ template
+ std::string StringFormat(const std::string& format, Args ... args)
+ {
+ const int sizeS = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0'
+ if (sizeS <= 0) { throw std::runtime_error("Error during formatting."); }
+ const auto size = static_cast(sizeS);
+ const auto buf = std::make_unique(size);
+ std::snprintf(buf.get(), size, format.c_str(), args ...);
+ return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
+ }
+ template
+ constexpr const T& Clamp01(const T& x)
+ {
+ return Clamp(x, 0.f, 1.f);
+ }
+ template
+ constexpr const T& Clamp(const T& x, const T& min, const T& max)
+ {
+ return std::max(std::min(max, x), min);
+ }
+ template
+ constexpr const T& Lerp(const T& a, const T& b, const float& t)
+ {
+ return a + (b - a) * t;
+ }
+
+ static std::default_random_engine make_default_random_engine()
+ {
+ // This gets a source of actual, honest-to-god randomness
+ std::random_device source;
+
+ // Here, we fill an array of random data from the source
+ unsigned int random_data[10];
+ for (auto& elem : random_data) {
+ elem = source();
+ }
+
+ // this creates the random seed sequence out of the random data
+ std::seed_seq seq(random_data + 0, random_data + 10);
+ return std::default_random_engine{ seq };
+ }
+
+ double RandomNumber();
+ }
+}
\ No newline at end of file
diff --git a/2dgk_7/2dgk_7/level.txt b/2dgk_7/2dgk_7/level.txt
new file mode 100644
index 0000000..41ca7c7
--- /dev/null
+++ b/2dgk_7/2dgk_7/level.txt
@@ -0,0 +1,31 @@
+/------------\/------------\
+|............||............|
+|./--\./---\.||./---\./--\.|
+|#| |.| |.||.| |.| |#|
+|.`--'.`---'.`'.`---'.`--'.|
+|..........................|
+|./--\./\./------\./\./--\.|
+|.`--'.||.`--\/--'.||.`--'.|
+|......||....||....||......|
+`----\.|`--\ || /--'|./----'
+ |.|/--' `' `--\|.|
+ |.|| ||.|
+ |.|| /--==--\ ||.|
+-----'.`' | | `'.`-----
+ . | | .
+-----\./\ | | /\./-----
+ |.|| `------' ||.|
+ |.|| ||.|
+ |.|| /------\ ||.|
+/----'.`' `--\/--' `'.`----\
+|............||............|
+|./--\./---\.||./---\./--\.|
+|.`-\|.`---'.`'.`---'.|/-'.|
+|#..||....... .......||..#|
+`-\.||./\./------\./\.||./-'
+/-'.`'.||.`--\/--'.||.`'.`-\
+|......||....||....||......|
+|./----'`--\.||./--'`----\.|
+|.`--------'.`'.`--------'.|
+|..........................|
+`--------------------------'
\ No newline at end of file
diff --git a/2dgk_7/2dgk_7/textures/00.bmp b/2dgk_7/2dgk_7/textures/00.bmp
new file mode 100644
index 0000000..7ffe609
--- /dev/null
+++ b/2dgk_7/2dgk_7/textures/00.bmp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1dbb92f9be017b00038388d19298e8ee9b4da1cd54577c2b22b6b7ab8d81734a
+size 3126
diff --git a/2dgk_7/2dgk_7/vcpkg.json b/2dgk_7/2dgk_7/vcpkg.json
new file mode 100644
index 0000000..bb1cfbc
--- /dev/null
+++ b/2dgk_7/2dgk_7/vcpkg.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
+ "name": "dgk-7",
+ "version": "0.1.0",
+ "dependencies": [
+ "sdl2"
+ ]
+}
\ No newline at end of file
diff --git a/2dgk_7/readme.txt b/2dgk_7/readme.txt
new file mode 100644
index 0000000..74e0672
--- /dev/null
+++ b/2dgk_7/readme.txt
@@ -0,0 +1,7 @@
+IDE: Microsoft Visual Studio 2019
+Kompilator: Microsoft Visual C++
+Biblioteki: SDL2
+
+Przetestowane pady: DualSense, DualShock 3 (ze sterownikiem/wrapperem XInputowym ScpToolkit), telefon Android (ze sterownikiem/wrapperem XInputowym HandyGamePad).
+
+Uwaga: Do pobrania bibliotek projekt wykorzystuje vcpkg, menedżer bibliotek C++, w trybie Manifest. Należy go pobrać i skonfigurować zgodnie z instrukcją: https://github.com/microsoft/vcpkg#getting-started