Laboratorium 5 done

This commit is contained in:
Michał Leśniak 2021-11-25 13:05:21 +01:00
parent 9af453e459
commit aec76c7378
16 changed files with 214 additions and 237 deletions

View File

@ -154,11 +154,12 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="KCircle.cpp" />
<ClCompile Include="KGame.cpp" />
<ClCompile Include="KInput.cpp" />
<ClCompile Include="KTexture.cpp" />
<ClCompile Include="KTile.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="Utils.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="vcpkg.json" />
@ -166,10 +167,10 @@
<ItemGroup>
<ClInclude Include="Constants.h" />
<ClInclude Include="GamePad.h" />
<ClInclude Include="KCircle.h" />
<ClInclude Include="KGame.h" />
<ClInclude Include="KInput.h" />
<ClInclude Include="KTexture.h" />
<ClInclude Include="KTile.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="KVector2d.h" />
</ItemGroup>

View File

@ -18,9 +18,6 @@
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KTile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KTexture.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -30,6 +27,12 @@
<ClCompile Include="KInput.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="KCircle.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Utils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="vcpkg.json" />
@ -44,9 +47,6 @@
<ClInclude Include="KVector2d.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KTile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KTexture.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -59,5 +59,8 @@
<ClInclude Include="KInput.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="KCircle.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
#pragma once
namespace KapitanGame {
namespace Constants {
constexpr const char* WINDOW_TITLE = "2DGK - Zadanie 5";
constexpr const char* WINDOW_TITLE = "2DGK - Zadanie 7-8";
//Analog joystick dead zone
constexpr int JOYSTICK_DEAD_ZONE = 8000;
//Screen dimension constants
@ -13,5 +13,6 @@ namespace KapitanGame {
constexpr int TILE_HEIGHT = 32;
constexpr float SPEED = 0.16f;
constexpr float SMOOTH = 0.4f;
constexpr int CIRCLES_COUNT = 100;
}
}

78
2dgk_6/2dgk_6/KCircle.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "KCircle.h"
#include <utility>
#include <vector>
#include "Constants.h"
#include "Utils.h"
namespace KapitanGame {
std::atomic<int> KCircle::IdCounter{ 0 };
KCircle::KCircle(const KVector2D& position, const KVector2D& velocity, const float& radius, const KTexture& texture) : Id(++IdCounter),
Position(position),
Velocity(velocity),
Radius(radius),
Texture(texture) {
}
void KCircle::CollisionDetectionStep(const std::vector<KCircle>& circles, const bool& shouldSeparate, const bool& shouldReflect) {
for (auto& other : circles) {
if (other.Id == Id) continue;
if (IsCollision(other)) {
auto separationVector = GetSeparationVector(other);
if (shouldSeparate)
Position += separationVector;
if (shouldReflect)
{
separationVector.Normalize();
Velocity = Velocity.Reflect(separationVector);
}
}
}
}
void KCircle::MovementStep(const float& timeStep) {
//Velocity.Normalize();
//Velocity *= 100;
Position += Velocity * timeStep;
}
bool KCircle::IsCollision(const KCircle& other) const {
return (Position - other.Position).Length() < Radius + other.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);
}
void KCircle::Render(SDL_Renderer* renderer) const {
Texture.Render(renderer, static_cast<int>(Position.X) - Texture.GetWidth() / 2, static_cast<int>(Position.Y) - Texture.GetHeight() / 2);
}
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 });
}
}
}

31
2dgk_6/2dgk_6/KCircle.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <atomic>
#include <SDL_render.h>
#include <vector>
#include "KTexture.h"
#include "KVector2d.h"
namespace KapitanGame {
class KCircle
{
public:
KCircle(const KVector2D& position, const KVector2D& velocity, const float& radius, const KTexture& texture);
void CollisionDetectionStep(const std::vector<KCircle>& circles, const bool& shouldSeparate, const bool& shouldReflect);
void MovementStep(const float& timeStep);
bool IsCollision(const KCircle& other) const;
KVector2D GetSeparationVector(const KCircle& other) const;
void Render(SDL_Renderer* renderer) const;
void CollisionDetectionWithMapStep(const SDL_Rect& map);
const int Id;
static std::atomic<int> IdCounter;
private:
KVector2D Position{ 0.f, 0.f };
KVector2D Velocity{ 0.f, 0.f };
float Radius{ 1.f };
const KTexture& Texture;
};
}

View File

@ -13,7 +13,7 @@
#include "KVector2d.h"
namespace KapitanGame {
KGame::KGame() {
KGame::KGame() : HasSeparation(true), HasCollision(true) {
//Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
@ -45,11 +45,8 @@ namespace KapitanGame {
KGame::~KGame() {
//Free loaded images
for (int i = 0; i <= static_cast<int>(TileType::Dot); ++i)
TileTextures[i].Free();
PlayerTexture.Free();
PlayerTexture2.Free();
CircleTexture.Free();
Input.Free();
@ -68,64 +65,11 @@ namespace KapitanGame {
//Loading success flag
bool success = true;
if (!PlayerTexture.LoadFromFile("textures/player.bmp", Renderer))
if (!CircleTexture.LoadFromFile("textures/00.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<int>(TileType::Default); i <= static_cast<int>(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<int>(line.length() * Constants::TILE_WIDTH))
MapWidth = static_cast<int>(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;
}
@ -170,51 +114,21 @@ namespace KapitanGame {
//Event handler
SDL_Event e;
SDL_Rect viewport;
viewport.x = Constants::SCREEN_WIDTH / 2;
viewport.y = Constants::SCREEN_HEIGHT / 2;
viewport.w = Constants::SCREEN_WIDTH;
viewport.h = Constants::SCREEN_HEIGHT;
SDL_Rect map{ 0,0,Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT };
KVector2D playerOnePosition(0, 0);
KVector2D playerOneInput(0, 0);
KVector2D playerOneVelocity(0, 0);
KVector2D playerTwoPosition(0, 0);
KVector2D playerTwoInput(0, 0);
KVector2D playerTwoVelocity(0, 0);
SDL_Rect playerAreaModeGauntlet;
playerAreaModeGauntlet.x = 0;
playerAreaModeGauntlet.y = 0;
playerAreaModeGauntlet.w = Constants::SCREEN_WIDTH - Constants::WINDOW_DEAD_ZONE;
playerAreaModeGauntlet.h = Constants::SCREEN_HEIGHT - Constants::WINDOW_DEAD_ZONE;
SDL_Rect playerAreaModeZoom;
playerAreaModeZoom.x = 0;
playerAreaModeZoom.y = 0;
playerAreaModeZoom.w = (MapWidth <= MapHeight ? MapWidth : static_cast<int>(static_cast<float>(MapHeight) * Constants::SCREEN_RATIO)) - Constants::WINDOW_DEAD_ZONE;
playerAreaModeZoom.h = (MapHeight <= MapWidth ? MapHeight : static_cast<int>(static_cast<float>(MapWidth) / Constants::SCREEN_RATIO)) - Constants::WINDOW_DEAD_ZONE;
float scale = 1.f;
//Normalized direction
int xDir = 0;
int yDir = 0;
uint32_t time, previousTime;
time = previousTime = SDL_GetTicks();
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");
bool zoom = false;
//While application is running
while (!quit)
{
previousTime = time;
time = SDL_GetTicks();
playerOneInput.X = 0;
playerOneInput.Y = 0;
Input.HandleInputPreEvents();
@ -231,123 +145,31 @@ namespace KapitanGame {
}
Input.HandleInputPostEvents();
playerTwoInput.X = Input.GetControllerAxis(Controllers::Controller1, SDL_CONTROLLER_AXIS_LEFTX);
playerTwoInput.Y = Input.GetControllerAxis(Controllers::Controller1, SDL_CONTROLLER_AXIS_LEFTY);
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_W))
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_S) || Input.IsControllerButtonPressed(Controllers::Controller1, SDL_CONTROLLER_BUTTON_START))
{
playerOneInput.Y = -1;
HasSeparation = !HasSeparation;
}
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_S))
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_C) || Input.IsControllerButtonPressed(Controllers::Controller1, SDL_CONTROLLER_BUTTON_BACK))
{
playerOneInput.Y = 1;
}
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_A))
{
playerOneInput.X = -1;
}
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_D))
{
playerOneInput.X = 1;
HasCollision = !HasCollision;
}
playerOneInput.Normalize();
playerOneVelocity = playerOneInput * Constants::SPEED * (1 - Constants::SMOOTH) + playerOneVelocity * Constants::SMOOTH;
playerOnePosition += playerOneVelocity;
for (auto& circle : Circles)
circle.CollisionDetectionStep(Circles, HasSeparation, HasCollision);
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_F))
{
zoom = !zoom;
}
const auto& playerArea = zoom ? playerAreaModeZoom : playerAreaModeGauntlet;
// keep player one near player two
if (playerOnePosition.X < playerTwoPosition.X - playerArea.w) playerOnePosition.X = playerTwoPosition.X - playerArea.w;
if (playerOnePosition.X > playerTwoPosition.X + playerArea.w) playerOnePosition.X = playerTwoPosition.X + playerArea.w;
if (playerOnePosition.Y < playerTwoPosition.Y - playerArea.h) playerOnePosition.Y = playerTwoPosition.Y - playerArea.h;
if (playerOnePosition.Y > playerTwoPosition.Y + playerArea.h) playerOnePosition.Y = playerTwoPosition.Y + playerArea.h;
// keep player one in map
if (playerOnePosition.X < 0) playerOnePosition.X = 0;
if (playerOnePosition.X > MapWidth - PlayerTexture.GetWidth()) playerOnePosition.X = (MapWidth - PlayerTexture.GetWidth());
if (playerOnePosition.Y < 0) playerOnePosition.Y = (0);
if (playerOnePosition.Y > MapHeight - PlayerTexture.GetHeight()) playerOnePosition.Y = (MapHeight - PlayerTexture.GetHeight());
//playerTwoInput.Normalize();
//printf("\r \rX:%f, Y:%f", playerTwoInput.x, playerTwoInput.y);
playerTwoVelocity = playerTwoInput * Constants::SPEED * (1 - Constants::SMOOTH) + playerTwoVelocity * Constants::SMOOTH;
playerTwoPosition += playerTwoVelocity;
if (playerTwoPosition.X < playerOnePosition.X - static_cast<float>(playerArea.w)) playerTwoPosition.X = playerOnePosition.X - playerArea.w;
if (playerTwoPosition.X > playerOnePosition.X + playerArea.w) playerTwoPosition.X = playerOnePosition.X + playerArea.w;
if (playerTwoPosition.Y < playerOnePosition.Y - playerArea.h) playerTwoPosition.Y = playerOnePosition.Y - playerArea.h;
if (playerTwoPosition.Y > playerOnePosition.Y + playerArea.h) playerTwoPosition.Y = playerOnePosition.Y + playerArea.h;
if (playerTwoPosition.X < 0) playerTwoPosition.X = 0;
if (playerTwoPosition.X > MapWidth - PlayerTexture.GetWidth()) playerTwoPosition.X = MapWidth - PlayerTexture.GetWidth();
if (playerTwoPosition.Y < 0) playerTwoPosition.Y = 0;
if (playerTwoPosition.Y > MapHeight - PlayerTexture.GetHeight()) playerTwoPosition.Y = MapHeight - PlayerTexture.GetHeight();
scale = 1.f;
if (zoom)
{
float xDist = std::abs(playerTwoPosition.X - playerOnePosition.X) - playerAreaModeGauntlet.w;
float yDist = std::abs(playerTwoPosition.Y - playerOnePosition.Y) - playerAreaModeGauntlet.h;
float xMaxDist = playerAreaModeZoom.w - playerAreaModeGauntlet.w;
float yMaxDist = playerAreaModeZoom.h - playerAreaModeGauntlet.h;
float t1 = Utils::Clamp(xDist / xMaxDist);
float t2 = Utils::Clamp(yDist / yMaxDist);
float t = std::max(std::max(t1, 0.f), std::max(t2, 0.f));
scale = Utils::Lerp(scale, std::max(Constants::SCREEN_WIDTH * 1.0f / MapWidth, Constants::SCREEN_HEIGHT * 1.0f / MapHeight), t);
//printf("\rscale: %f, t1: %f, t2: %f, t: %f", scale, t1, t2, t);
}
KVector2D focusPoint = (playerOnePosition + playerTwoPosition + KVector2D(PlayerTexture.GetWidth(), PlayerTexture.GetHeight())+KVector2D(Constants::WINDOW_DEAD_ZONE, Constants::WINDOW_DEAD_ZONE)/2)*scale/2;
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_Q))
{
focusPoint = KVector2D(MapWidth / 2.f, MapHeight / 2.f);
}
//viewport.x += clamp((time - previous_time) * .05f) * (focusPoint.x - SCREEN_WIDTH / 2 - viewport.x);
viewport.x = Utils::Lerp(viewport.x, static_cast<int>(focusPoint.X - Constants::SCREEN_WIDTH / 2.f), static_cast<float>(time - previousTime) * .05f);
if ((focusPoint.X - Constants::SCREEN_WIDTH / 2.f) - static_cast<float>(viewport.x) <= 0.005f)
{
viewport.x = static_cast<int>(focusPoint.X - Constants::SCREEN_WIDTH / 2.f)/scale;
}
viewport.y = Utils::Lerp(viewport.y, static_cast<int>(focusPoint.Y - Constants::SCREEN_HEIGHT / 2.f), static_cast<float>(time - previousTime) * .05f);
if (focusPoint.Y - static_cast<float>(Constants::SCREEN_HEIGHT) / 2.0f - static_cast<float>(viewport.y) <= 0.005f)
{
viewport.y = static_cast<int>(focusPoint.Y - Constants::SCREEN_HEIGHT / 2.f) / scale;
}
if (viewport.x < 0)
viewport.x = 0;
if (viewport.y < 0)
viewport.y = 0;
if (viewport.x > MapWidth - static_cast<int>(static_cast<float>(viewport.w) / scale))
viewport.x = MapWidth - static_cast<int>(static_cast<float>(viewport.w) / scale);
if (viewport.y > MapHeight - static_cast<int>(static_cast<float>(viewport.h) / scale))
viewport.y = MapHeight - static_cast<int>(static_cast<float>(viewport.h) / scale);
//printf("\rp1(%f, %f), p2(%f, %f), viewport:(%d, %d, %d, %d), map(%d, %d), focus(%f, %f)", playerOnePosition.X * scale, playerOnePosition.Y * scale, playerTwoPosition.X * scale, playerTwoPosition.Y * scale, static_cast<int>(viewport.x * scale), static_cast<int>(viewport.y * scale), static_cast<int>(static_cast<float>(viewport.w) / scale), static_cast<int>(static_cast<float>(viewport.h) / scale), MapWidth, MapHeight, focusPoint.X, focusPoint.Y);
for (auto& circle : Circles)
circle.CollisionDetectionWithMapStep(map);
for (auto& circle : Circles)
circle.MovementStep(static_cast<float>(time - previousTime) / 1000.f);
//Clear screen
SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF);
SDL_RenderClear(Renderer);
for (auto& tile : Tiles)
tile.Render(Renderer, TileTextures, viewport, scale);
PlayerTexture.Render(Renderer, playerOnePosition.X - viewport.x, playerOnePosition.Y - viewport.y, nullptr, scale);
PlayerTexture2.Render(Renderer, playerTwoPosition.X - viewport.x, playerTwoPosition.Y - viewport.y, nullptr, scale);
for (auto& circle : Circles)
circle.Render(Renderer);
//Update screen
SDL_RenderPresent(Renderer);

View File

@ -1,13 +1,13 @@
#pragma once
#include "KTexture.h"
#include <vector>
#include <SDL_joystick.h>
#include <SDL_render.h>
#include "KCircle.h"
#include "KInput.h"
namespace KapitanGame {
class KTile;
class KGame
{
@ -24,15 +24,13 @@ namespace KapitanGame {
SDL_Renderer* Renderer = nullptr;
KTexture TileTextures[4];
KTexture PlayerTexture;
KTexture PlayerTexture2;
std::vector<KTile> Tiles;
KTexture CircleTexture;
int MapHeight = -1;
int MapWidth = -1;
std::vector<KCircle> Circles;
KInput Input;
bool HasSeparation;
bool HasCollision;
private:
bool LoadMedia();
SDL_Texture* LoadTexture(const std::string& path) const;

View File

@ -9,6 +9,11 @@ namespace KapitanGame {
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 };
@ -49,6 +54,12 @@ namespace KapitanGame {
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();
@ -56,6 +67,14 @@ namespace KapitanGame {
(*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 ;
}
};
}

16
2dgk_6/2dgk_6/Utils.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "Utils.h"
#include <random>
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<double> dist(0.0, 1.0);
return dist(rng);
}
}
}

View File

@ -2,6 +2,7 @@
#include <memory>
#include <string>
#include <stdexcept>
#include <random>
namespace KapitanGame {
namespace Utils {
@ -16,15 +17,37 @@ namespace KapitanGame {
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
template <class T>
constexpr const T& Clamp(const T& x)
constexpr const T& Clamp01(const T& x)
{
return std::max(std::min(1.f, x), 0.f);
return Clamp(x, 0.f, 1.f);
}
template <class T>
constexpr const T& Clamp(const T& x, const T& min, const T& max)
{
return std::max(std::min(max, x), min);
}
template <class T>
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();
}
}

BIN
2dgk_6/2dgk_6/textures/00.bmp (Stored with Git LFS)

Binary file not shown.

BIN
2dgk_6/2dgk_6/textures/01.bmp (Stored with Git LFS)

Binary file not shown.

BIN
2dgk_6/2dgk_6/textures/02.bmp (Stored with Git LFS)

Binary file not shown.

BIN
2dgk_6/2dgk_6/textures/03.bmp (Stored with Git LFS)

Binary file not shown.

BIN
2dgk_6/2dgk_6/textures/player.bmp (Stored with Git LFS)

Binary file not shown.

BIN
2dgk_6/2dgk_6/textures/player2.bmp (Stored with Git LFS)

Binary file not shown.