first commit

This commit is contained in:
2026-06-05 11:45:04 +03:00
commit b457544071
29 changed files with 1725 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
#include "Engine.h"
#include <chrono>
#include <thread>
Engine::ErrorCode Engine::run()
{
ErrorCode errorCode =
ErrorCode::success;
auto lastUpdate =
std::chrono::steady_clock::now();
auto lag =
std::chrono::milliseconds(0);
const auto timeQuantum =
std::chrono::milliseconds(15);
while (!end())
{
// INPUT
//=================================
updateInput();
//=================================
// UPDATE
//=================================
auto now =
std::chrono::steady_clock::now();
lag +=
std::chrono::duration_cast
<
std::chrono::milliseconds
>
(
now - lastUpdate
);
lastUpdate = now;
while (lag >= timeQuantum)
{
lag -= timeQuantum;
update(
static_cast<int>(
timeQuantum.count()
)
);
}
//=================================
// RENDER
//=================================
if (!m_PaintDevice.ready())
{
errorCode =
ErrorCode::paint_device_not_ready;
break;
}
m_PaintDevice.clear();
render(m_PaintDevice);
m_PaintDevice.render();
//=================================
std::this_thread::sleep_for(
std::chrono::milliseconds(1)
);
}
return errorCode;
}
void Engine::updateInput()
{
int key = getch();
while (key != ERR)
{
if (
m_TrackedKeys.find(key)
!=
m_TrackedKeys.end()
)
{
on_button_press(key);
}
key = getch();
}
}
void Engine::track_key(int key)
{
m_TrackedKeys.insert(key);
}
void Engine::untrack_key(int key)
{
auto it =
m_TrackedKeys.find(key);
if (it != m_TrackedKeys.end())
{
m_TrackedKeys.erase(it);
}
}
+50
View File
@@ -0,0 +1,50 @@
#pragma once
#include <set>
#include "PaintDevice.h"
class Engine
{
public:
enum class ErrorCode
{
success = 0,
paint_device_not_ready
};
virtual ~Engine() = default;
ErrorCode run();
protected:
void track_key(int key);
void untrack_key(int key);
PaintDevice& paint_device()
{
return m_PaintDevice;
}
virtual bool end() const = 0;
virtual void on_button_press(
int button
) = 0;
virtual void update(
int dt
) = 0;
virtual void render(
PaintDevice& paintDevice
) = 0;
private:
void updateInput();
PaintDevice m_PaintDevice;
std::set<int> m_TrackedKeys;
};
+347
View File
@@ -0,0 +1,347 @@
#include "ExampleGame.h"
#include <cstdlib>
void Bullet::render(PaintDevice& paintDevice)
{
paintDevice.set_char(
m_Position,
'|'
);
}
void Bullet::update(const int dt)
{
m_Lag += dt;
const int quant = 50;
if (m_Lag <= quant)
{
return;
}
m_Lag -= quant;
m_Position.y()--;
}
Gun::Gun()
{
m_Body =
{
Vector2(5, 0),
Vector2(5, 1),
Vector2(4, 2),
Vector2(4, 3),
Vector2(4, 4),
Vector2(6, 2),
Vector2(6, 3),
Vector2(6, 4),
Vector2(3, 5),
Vector2(7, 5),
Vector2(2, 6),
Vector2(5, 6),
Vector2(8, 6),
Vector2(1, 7),
Vector2(5, 7),
Vector2(9, 7),
Vector2(0, 8),
Vector2(1, 8),
Vector2(2, 8),
Vector2(8, 8),
Vector2(9, 8),
Vector2(10, 8),
Vector2(3, 9),
Vector2(7, 9),
Vector2(3, 10),
Vector2(7, 10),
Vector2(4, 11),
Vector2(6, 11),
Vector2(4, 12),
Vector2(5, 12),
Vector2(6, 12)
};
}
std::vector<Bullet> Gun::fire()
{
std::vector<Bullet> bullets;
if (m_CooldownCenter == 0)
{
bullets.push_back(Bullet());
bullets.back().m_Position =
Vector2(
m_Position.x() + 4,
m_Position.y() + 1
);
bullets.push_back(Bullet());
bullets.back().m_Position =
Vector2(
m_Position.x() + 6,
m_Position.y() + 1
);
m_CooldownCenter = 300;
}
if (m_CooldownSide == 0)
{
bullets.push_back(Bullet());
bullets.back().m_Position =
Vector2(
m_Position.x(),
m_Position.y() + 7
);
bullets.push_back(Bullet());
bullets.back().m_Position =
Vector2(
m_Position.x() + 10,
m_Position.y() + 7
);
m_CooldownSide = 150;
}
return bullets;
}
void Gun::update(const int dt)
{
m_CooldownCenter -= dt;
m_CooldownSide -= dt;
if (m_CooldownCenter < 0)
{
m_CooldownCenter = 0;
}
if (m_CooldownSide < 0)
{
m_CooldownSide = 0;
}
}
void Gun::render(PaintDevice& paintDevice)
{
for (const Vector2& point : m_Body)
{
paintDevice.set_char(
Vector2(
m_Position.x() + point.x(),
m_Position.y() + point.y()
),
'#'
);
}
}
void Enemy::render(PaintDevice& paintDevice)
{
for (const Vector2& point : m_Enemys)
{
paintDevice.set_char(
point,
'V'
);
}
}
void Enemy::update(const int dt)
{
m_Lag += dt;
const int quant = 800;
if (m_Lag <= quant)
{
return;
}
m_Lag -= quant;
for (Vector2& point : m_Enemys)
{
point.y()++;
}
for (int i = 0; i < 20; ++i)
{
if (rand() % 2)
{
m_Enemys.push_back(
Vector2(i, 0)
);
}
}
}
bool Enemy::hit(Vector2 point)
{
for (const Vector2& enemy : m_Enemys)
{
if (enemy == point)
{
return true;
}
}
return false;
}
void Enemy::remove(Vector2 point)
{
for (int i = 0; i < m_Enemys.size(); ++i)
{
if (
point.x() == m_Enemys[i].x()
&&
point.y() == m_Enemys[i].y()
)
{
for (int j = i; j < m_Enemys.size() - 1; ++j)
{
m_Enemys[j] = m_Enemys[j + 1];
}
m_Enemys.pop_back();
return;
}
}
}
ExampleGame::ExampleGame()
{
paint_device().resize(
Size(
m_Width,
m_Height
)
);
track_key(KEY_LEFT);
track_key(KEY_RIGHT);
m_Gun.m_Position =
Vector2(
m_Width / 2 - 2,
m_Height - 14
);
}
void ExampleGame::on_button_press(const int button)
{
switch (button)
{
case KEY_LEFT:
{
m_Gun.m_Position.x()--;
if (m_Gun.m_Position.x() < 0)
{
m_Gun.m_Position.x() = 0;
}
break;
}
case KEY_RIGHT:
{
m_Gun.m_Position.x()++;
if (m_Gun.m_Position.x() > m_Width - 11)
{
m_Gun.m_Position.x() = m_Width - 11;
}
break;
}
default:
{
break;
}
}
}
void ExampleGame::update(const int dt)
{
m_Enemy.update(dt);
for (int i = 0; i < m_Bullets.size(); i++)
{
m_Bullets[i].update(dt);
}
for (int i = 0; i < m_Bullets.size(); i++)
{
if (m_Bullets[i].m_Position.y() < 0)
{
for (int j = i; j < m_Bullets.size() - 1; j++)
{
m_Bullets[j] = m_Bullets[j + 1];
}
m_Bullets.pop_back();
}
}
for (int i = 0; i < m_Bullets.size(); i++)
{
if (m_Enemy.hit(m_Bullets[i].m_Position))
{
m_Enemy.remove(
m_Bullets[i].m_Position
);
for (int j = i; j < m_Bullets.size() - 1; ++j)
{
m_Bullets[j] = m_Bullets[j + 1];
}
m_Bullets.pop_back();
}
}
m_Gun.update(dt);
std::vector<Bullet> newBullets =
m_Gun.fire();
m_Bullets.insert(
m_Bullets.end(),
newBullets.begin(),
newBullets.end()
);
}
void ExampleGame::render(PaintDevice& paintDevice)
{
for (Bullet& bullet : m_Bullets)
{
bullet.render(paintDevice);
}
m_Enemy.render(paintDevice);
m_Gun.render(paintDevice);
}
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include "Engine.h"
#include <vector>
class Bullet {
public:
void render(PaintDevice& paintDevice);
void update(const int dt);
Vector2 m_Position;
int m_Lag = 0;
};
class Gun {
public:
Gun();
std::vector<Bullet> fire();
void render(PaintDevice& paintDevice);
void update(const int dt);
Vector2 m_Position;
std::vector<Vector2> m_Body;
int m_CooldownCenter = 0;
int m_CooldownSide = 0;
};
class Enemy {
public:
void render(PaintDevice& paintDevice);
void update(const int dt);
int m_Lag = 0;
bool hit(Vector2 point);
void remove(Vector2 point);
Vector2 first() const { return m_Enemys.front(); }
bool empty() const { return m_Enemys.empty(); }
private:
std::vector<Vector2> m_Enemys;
};
class ExampleGame : public Engine {
public:
ExampleGame();
private:
virtual bool end() const { return !m_Enemy.empty() && m_Enemy.first().y() >= m_Height; }
virtual void on_button_press(const int button);
virtual void update(const int dt);
virtual void render(PaintDevice& paintDevice);
const size_t m_Width = 20;
const size_t m_Height = 40;
Gun m_Gun;
std::vector<Bullet> m_Bullets;
Enemy m_Enemy;
};
+89
View File
@@ -0,0 +1,89 @@
#include "Figure.h"
Figure::Figure(Point position)
:
m_Position(position)
{
}
void Figure::update(double dt)
{
m_TimeFromLastUpdate += dt;
if (m_TimeFromLastUpdate >= m_TimeForUpdate)
{
m_TimeFromLastUpdate = 0;
++m_Position.y;
}
}
void Figure::render(PaintDevice& paintDevice)
{
for (const Point& point : m_Body[m_CurrentRotate])
{
Vector2 v(
point.x + m_Position.x,
point.y + m_Position.y
);
paintDevice.set_char(
v,
0x25D8
);
}
}
void Figure::move_left()
{
--m_Position.x;
}
void Figure::move_right()
{
++m_Position.x;
}
void Figure::boost()
{
m_TimeForUpdate = 50;
}
void Figure::rotate()
{
++m_CurrentRotate;
if (m_CurrentRotate >= m_Body.size())
{
m_CurrentRotate = 0;
}
}
void Figure::backup()
{
m_PositionBackup = m_Position;
m_CurrentRotateBackup = m_CurrentRotate;
}
void Figure::restore()
{
m_Position = m_PositionBackup;
m_CurrentRotate = m_CurrentRotateBackup;
}
Point Figure::get_position() const
{
return m_Position;
}
void Figure::set_position(Point position)
{
m_Position = position;
}
const std::vector<Point>& Figure::get_body() const
{
return m_Body[m_CurrentRotate];
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <vector>
#include "Point.h"
#include "PaintDevice.h"
class Figure
{
protected:
Point m_Position;
Point m_PositionBackup;
double m_TimeFromLastUpdate = 0;
double m_TimeForUpdate = 500;
std::vector<std::vector<Point>> m_Body;
size_t m_CurrentRotate = 0;
size_t m_CurrentRotateBackup = 0;
public:
explicit Figure(Point position);
virtual ~Figure() = default;
void update(double dt);
void render(PaintDevice& paintDevice);
void move_left();
void move_right();
void boost();
void rotate();
void backup();
void restore();
Point get_position() const;
void set_position(Point position);
const std::vector<Point>& get_body() const;
};
+189
View File
@@ -0,0 +1,189 @@
#include "GameField.h"
void GameField::resize(
size_t width,
size_t height
)
{
m_Width = width;
m_Height = height;
m_Field =
std::vector<
std::vector<wchar_t>
>(
m_Height - 2,
std::vector<wchar_t>(
m_Width - 2,
0x0387
)
);
}
void GameField::render(
PaintDevice& paintDevice
)
{
for (int x = 1; x < m_Width - 1; x++)
{
paintDevice.set_char(
Vector2(x,0),
'-'
);
paintDevice.set_char(
Vector2(
x,
m_Height - 1
),
'-'
);
}
for (int y = 1; y < m_Height - 1; y++)
{
paintDevice.set_char(
Vector2(0,y),
'|'
);
paintDevice.set_char(
Vector2(
m_Width - 1,
y
),
'|'
);
}
paintDevice.set_char(
Vector2(0,0),
'+'
);
paintDevice.set_char(
Vector2(
m_Width - 1,
0
),
'+'
);
paintDevice.set_char(
Vector2(
0,
m_Height - 1
),
'+'
);
paintDevice.set_char(
Vector2(
m_Width - 1,
m_Height - 1
),
'+'
);
for (size_t y = 0; y < m_Field.size(); y++)
{
for (size_t x = 0;
x < m_Field[y].size();
x++)
{
paintDevice.set_char(
Vector2(
x + 1,
y + 1
),
m_Field[y][x]
);
}
}
}
bool GameField::has_collision(
const Figure& figure)
{
Point position =
figure.get_position();
for (const Point& point :
figure.get_body())
{
int x =
point.x + position.x;
int y =
point.y + position.y;
if (x < 1 ||
x > m_Width - 2)
return true;
if (y < 1 ||
y > m_Height - 2)
return true;
if (
m_Field[y - 1][x - 1]
!= 0x0387
)
return true;
}
return false;
}
void GameField::merge(const Figure& figure)
{
Point position = figure.get_position();
for (const Point& point : figure.get_body())
{
int x = point.x + position.x;
int y = point.y + position.y;
if (x < 1 || x > m_Width - 2 ||
y < 1 || y > m_Height - 2)
continue;
m_Field[y - 1][x - 1] = L'#';
}
}
int GameField::clear_lines()
{
int cleared = 0;
for (int y = (int)m_Field.size() - 1; y >= 0; y--)
{
bool full = true;
for (size_t x = 0; x < m_Field[y].size(); x++)
{
if (m_Field[y][x] == L' ')
{
full = false;
break;
}
}
if (full)
{
cleared++;
for (int j = y; j > 0; j--)
{
m_Field[j] = m_Field[j - 1];
}
m_Field[0] = std::vector<wchar_t>(
m_Width - 2,
L' '
);
y++; // 🔥 ВАЖНО: перескан строки после сдвига
}
}
return cleared;
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <vector>
#include "Figure.h"
class GameField
{
private:
size_t m_Width = 0;
size_t m_Height = 0;
std::vector<std::vector<wchar_t>> m_Field;
public:
void resize(
size_t width,
size_t height
);
void render(
PaintDevice& paintDevice
);
bool has_collision(
const Figure& figure
);
void merge(
const Figure& figure
);
int clear_lines();
};
+37
View File
@@ -0,0 +1,37 @@
#include "IBlock.h"
IBlock::IBlock(Point position)
:
Figure(position)
{
m_Body =
{
{
Point(1,0),
Point(1,1),
Point(1,2),
Point(1,3)
},
{
Point(0,2),
Point(1,2),
Point(2,2),
Point(3,2)
},
{
Point(2,0),
Point(2,1),
Point(2,2),
Point(2,3)
},
{
Point(0,1),
Point(1,1),
Point(2,1),
Point(3,1)
}
};
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "Figure.h"
class IBlock : public Figure
{
public:
explicit IBlock(Point position);
};
+107
View File
@@ -0,0 +1,107 @@
#include "PaintDevice.h"
PaintDevice::PaintDevice()
: m_Size(40, 40)
{
initscr();
noecho();
curs_set(0);
keypad(stdscr, TRUE);
nodelay(stdscr, TRUE);
resize(m_Size);
m_Ready = true;
}
PaintDevice::~PaintDevice()
{
endwin();
}
bool PaintDevice::ready() const
{
return m_Ready;
}
void PaintDevice::resize(const Size& size)
{
m_Size = size;
m_Buffer =
std::vector<std::vector<wchar_t>>
(
m_Size.height(),
std::vector<wchar_t>(
m_Size.width(),
L' '
)
);
}
void PaintDevice::clear()
{
for (int y = 0; y < m_Size.height(); y++)
{
for (int x = 0; x < m_Size.width(); x++)
{
m_Buffer[y][x] = L' ';
}
}
}
void PaintDevice::set_char(
const Vector2& position,
wchar_t c
)
{
if (
position.x() >= 0 &&
position.x() < m_Size.width() &&
position.y() >= 0 &&
position.y() < m_Size.height()
)
{
m_Buffer[position.y()][position.x()] = c;
}
}
wchar_t PaintDevice::get_char(
const Vector2& position
)
{
if (
position.x() >= 0 &&
position.x() < m_Size.width() &&
position.y() >= 0 &&
position.y() < m_Size.height()
)
{
return m_Buffer[position.y()][position.x()];
}
return L'\0';
}
void PaintDevice::render()
{
::clear();
for (int y = 0; y < m_Size.height(); y++)
{
for (int x = 0; x < m_Size.width(); x++)
{
mvaddch(
y,
x,
m_Buffer[y][x]
);
}
}
refresh();
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include <vector>
#include <ncurses.h>
#include "Size.h"
#include "Vector2.h"
class PaintDevice
{
public:
PaintDevice();
~PaintDevice();
bool ready() const;
void resize(const Size& size);
void clear();
void set_char(
const Vector2& position,
wchar_t c
);
wchar_t get_char(
const Vector2& position
);
void render();
private:
std::vector<std::vector<wchar_t>> m_Buffer;
Size m_Size;
bool m_Ready = false;
};
+17
View File
@@ -0,0 +1,17 @@
#pragma once
struct Point
{
int x;
int y;
Point(
int x = 0,
int y = 0
)
:
x(x),
y(y)
{
}
};
+35
View File
@@ -0,0 +1,35 @@
#include "Size.h"
Size::Size(
PointType width,
PointType height
)
{
m_Width = width;
m_Height = height;
}
Size::PointType Size::width() const
{
return m_Width;
}
Size::PointType Size::height() const
{
return m_Height;
}
Size::PointType& Size::width()
{
return m_Width;
}
Size::PointType& Size::height()
{
return m_Height;
}
Size::PointType Size::area() const
{
return m_Width * m_Height;
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
class Size
{
using PointType = long long;
public:
Size(
PointType width = 0,
PointType height = 0
);
PointType width() const;
PointType height() const;
PointType& width();
PointType& height();
PointType area() const;
private:
PointType m_Width;
PointType m_Height;
};
+52
View File
@@ -0,0 +1,52 @@
#include "Square.h"
Square::Square(
const Vector2& position,
const Size& size
)
: m_Position(position),
m_Size(size)
{
}
bool Square::hit(
const Vector2& point
) const
{
return
point.x() >= top_left().x() &&
point.x() <= bottom_right().x() &&
point.y() >= top_left().y() &&
point.y() <= bottom_right().y();
}
Vector2 Square::top_left() const
{
return m_Position;
}
Vector2 Square::bottom_right() const
{
return Vector2(
m_Position.x() + m_Size.width() - 1,
m_Position.y() + m_Size.height() - 1
);
}
bool Square::collide(
const Square& other
) const
{
bool xProjectionCollide =
top_left().x() <= other.bottom_right().x() &&
bottom_right().x() >= other.top_left().x();
bool yProjectionCollide =
top_left().y() <= other.bottom_right().y() &&
bottom_right().y() >= other.top_left().y();
return
xProjectionCollide &&
yProjectionCollide;
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "Vector2.h"
#include "Size.h"
class Square
{
public:
Square(
const Vector2& position,
const Size& size
);
bool hit(
const Vector2& point
) const;
bool collide(
const Square& other
) const;
Vector2 top_left() const;
Vector2 bottom_right() const;
private:
Vector2 m_Position;
Size m_Size;
};
+120
View File
@@ -0,0 +1,120 @@
#include "Tetris.h"
Tetris::Tetris()
{
paint_device().resize(
Size(
m_Width + 6,
m_Height
)
);
m_GameField.resize(
m_Width,
m_Height
);
m_Figure =
new IBlock(
Point(5,1)
);
track_key(KEY_LEFT);
track_key(KEY_RIGHT);
track_key(KEY_DOWN);
track_key(' ');
}
Tetris::~Tetris()
{
delete m_Figure;
}
bool Tetris::end() const
{
return m_End;
}
void Tetris::render(
PaintDevice& paintDevice
)
{
m_GameField.render(
paintDevice
);
if (m_Figure)
{
m_Figure->render(
paintDevice
);
}
Vector2 scorePos(m_Width + 1, 2);
std::wstring text = L"SCORE: " + std::to_wstring(m_Score);
for (size_t i = 0; i < text.size(); i++)
{
paintDevice.set_char(
Vector2(scorePos.x() + i, scorePos.y()),
text[i]
);
}
}
void Tetris::update(int dt)
{
m_Figure->backup();
m_Figure->update(dt);
if (m_GameField.has_collision(*m_Figure))
{
m_Figure->restore();
m_GameField.merge(*m_Figure);
int lines = m_GameField.clear_lines();
if (lines > 0)
{
m_Score += lines * 100;
}
delete m_Figure;
m_Figure =
new IBlock(Point(5,1));
}
}
void Tetris::on_button_press(
int button
)
{
m_Figure->backup();
switch (button)
{
case KEY_LEFT:
m_Figure->move_left();
break;
case KEY_RIGHT:
m_Figure->move_right();
break;
case KEY_DOWN:
m_Figure->boost();
break;
case ' ':
m_Figure->rotate();
break;
}
if (
m_GameField.has_collision(
*m_Figure
)
)
{
m_Figure->restore();
}
}
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include "Engine.h"
#include "GameField.h"
#include "IBlock.h"
class Tetris : public Engine
{
private:
GameField m_GameField;
Figure* m_Figure = nullptr;
bool m_End = false;
const size_t m_Width = 14;
const size_t m_Height = 26;
int m_Score = 0;
public:
Tetris();
~Tetris() override;
protected:
bool end() const override;
void on_button_press(int button) override;
void update(int dt) override;
void render(PaintDevice& paintDevice) override;
};
+121
View File
@@ -0,0 +1,121 @@
#include "Vector2.h"
Vector2::Vector2()
: m_X(0),
m_Y(0)
{
}
Vector2::Vector2(
PointType x,
PointType y
)
: m_X(x),
m_Y(y)
{
}
Vector2::PointType Vector2::x() const
{
return m_X;
}
Vector2::PointType Vector2::y() const
{
return m_Y;
}
Vector2::PointType& Vector2::x()
{
return m_X;
}
Vector2::PointType& Vector2::y()
{
return m_Y;
}
Vector2 Vector2::operator*(
const PointType& rhs
) const
{
return Vector2(
m_X * rhs,
m_Y * rhs
);
}
Vector2& Vector2::operator+=(
const Vector2& rhs
)
{
m_X += rhs.m_X;
m_Y += rhs.m_Y;
return *this;
}
Vector2& Vector2::operator-=(
const Vector2& rhs
)
{
m_X -= rhs.m_X;
m_Y -= rhs.m_Y;
return *this;
}
bool operator==(
const Vector2& lhs,
const Vector2& rhs
)
{
return
lhs.m_X == rhs.m_X &&
lhs.m_Y == rhs.m_Y;
}
bool operator!=(
const Vector2& lhs,
const Vector2& rhs
)
{
return !(lhs == rhs);
}
Vector2 operator+(
Vector2 lhs,
const Vector2& rhs
)
{
lhs += rhs;
return lhs;
}
Vector2 operator-(
Vector2 lhs,
const Vector2& rhs
)
{
lhs -= rhs;
return lhs;
}
std::ostream& operator<<(
std::ostream& os,
const Vector2& obj
)
{
os
<< "{"
<< obj.x()
<< ", "
<< obj.y()
<< "}";
return os;
}
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <ostream>
class Vector2
{
using PointType = long long;
friend bool operator==(
const Vector2& lhs,
const Vector2& rhs
);
friend Vector2 operator+(
Vector2 lhs,
const Vector2& rhs
);
friend Vector2 operator-(
Vector2 lhs,
const Vector2& rhs
);
public:
Vector2();
Vector2(
PointType x,
PointType y
);
PointType x() const;
PointType y() const;
PointType& x();
PointType& y();
Vector2 operator*(
const PointType& rhs
) const;
Vector2& operator+=(
const Vector2& rhs
);
Vector2& operator-=(
const Vector2& rhs
);
private:
PointType m_X;
PointType m_Y;
};
bool operator==(
const Vector2& lhs,
const Vector2& rhs
);
bool operator!=(
const Vector2& lhs,
const Vector2& rhs
);
Vector2 operator+(
Vector2 lhs,
const Vector2& rhs
);
Vector2 operator-(
Vector2 lhs,
const Vector2& rhs
);
std::ostream& operator<<(
std::ostream& os,
const Vector2& obj
);