added double jump and sitting state

This commit is contained in:
nihonium 2023-02-25 19:34:24 +03:00
parent 2e5c5a8dde
commit 90d07dde3f
Signed by: nihonium
GPG key ID: 0251623741027CFC
148 changed files with 13050 additions and 0 deletions

View file

@ -0,0 +1,9 @@
#path = ../../../3rdparty/SFML-2.5.1
#select_move_delete:
# g++ ./select_move_delete.cpp -std=c++11 -o select_move_delete.exe -I $(path)/include -L $(path)/lib/ -lsfml-graphics -lsfml-window -lsfml-system
#select_move_delete:
# g++ ./select_move_delete.cpp -std=c++11 -o select_move_delete.exe -I $(path)/include -L $(path)/lib/ -lsfml-graphics -lsfml-window -lsfml-system
build:
g++ ./select_move_delete.cpp -std=c++11 -o select_move_delete -lsfml-graphics -lsfml-window -lsfml-system
build_debug:
g++ ./select_move_delete.cpp -std=c++11 -o select_move_delete -lsfml-graphics -lsfml-window -lsfml-system -D_DEBUG

View file

@ -0,0 +1,143 @@
#pragma once
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
/*
Класс ContextMenu - контекстное меню
При нажатии правой кнопки мыши на экране появляется контекстное меню
Публичные методы:
ContextMenu(sf::RenderWindow&, const sf::Font&)
Конструктор принимает окно для отрисовки и шрифт
void addButton(const sf::String& name)
Добавить новый элемент в контекстное меню по имени name
void draw()
Нарисовать контекстное меню в окне, которое было передано в конструктор
int handleEvent(const sf::Event& event)
Обрабатывает событие event и возвращает целое число
Если это событие MousePressed и был выбран один из вариантов
контекстного меню, то вернёт номер этого варианта
Нумерация начинается с нуля
В ином случае вернёт -1
*/
class ContextMenu
{
private:
inline static const sf::Color kDefaultColor {sf::Color(190, 210, 190)};
inline static const sf::Color kHoverColor {sf::Color(150, 170, 150)};
inline static const sf::Color kTextColor {sf::Color::Black};
inline static const int kButtonHeight = 20;
inline static const int kCharacterSize = 16;
inline static const float kMenuWidthMultiplier = 1.2;
sf::RenderWindow& mRenderWindow;
sf::RectangleShape mShape;
sf::RectangleShape mHoverShape;
sf::Text mText;
std::vector<sf::String> mButtons;
bool mIsOpened = false;
bool mIsUpdated = false;
int mHoverPosition = -1;
int onMousePressed(const sf::Event& event)
{
if (event.mouseButton.button == sf::Mouse::Right) {
mIsOpened = true;
sf::Vector2f mousePosition = mRenderWindow.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
mShape.setPosition(mousePosition);
}
if (event.mouseButton.button == sf::Mouse::Left && mIsOpened) {
mIsOpened = false;
return mHoverPosition;
}
return -1;
}
void onMouseMove(const sf::Event& event)
{
if (!mIsOpened) {
return;
}
sf::Vector2f mousePosition = mRenderWindow.mapPixelToCoords({event.mouseMove.x, event.mouseMove.y});
if (mShape.getGlobalBounds().contains(mousePosition)) {
mHoverPosition = (mousePosition.y - mShape.getPosition().y) / kButtonHeight;
}
else {
mHoverPosition = -1;
}
}
public:
ContextMenu(sf::RenderWindow& window, const sf::Font& font) : mRenderWindow(window)
{
mText.setFont(font);
mText.setCharacterSize(kCharacterSize);
mText.setFillColor(kTextColor);
mShape.setFillColor(kDefaultColor);
mHoverShape.setFillColor(kHoverColor);
mIsOpened = false;
mIsUpdated = false;
mHoverPosition = -1;
}
void addButton(const sf::String& name)
{
mButtons.push_back(name);
mIsUpdated = false;
}
void draw()
{
if (!mIsOpened) {
return;
}
// Если добавили новый вариант, то её текст может быть длиннее
// чем у других. Нужно расширить прямоугольники.
if (!mIsUpdated) {
int maxSizeX = 0;
for (int i = 0; i < mButtons.size(); i++) {
mText.setString(mButtons[i]);
if (mText.getLocalBounds().width > maxSizeX) {
maxSizeX = mText.getLocalBounds().width;
}
}
maxSizeX *= kMenuWidthMultiplier;
mShape.setSize({(float)maxSizeX, (float)(kButtonHeight * mButtons.size())});
mHoverShape.setSize({(float)maxSizeX, (float)(kButtonHeight)});
mIsUpdated = true;
}
mRenderWindow.draw(mShape);
if (mHoverPosition >= 0){
mHoverShape.setPosition(mShape.getPosition().x, mShape.getPosition().y + mHoverPosition * kButtonHeight);
mRenderWindow.draw(mHoverShape);
}
for (int i = 0; i < mButtons.size(); i++) {
mText.setString(mButtons[i]);
mText.setPosition(mShape.getPosition().x, mShape.getPosition().y + i * kButtonHeight);
mRenderWindow.draw(mText);
}
}
int handleEvent(const sf::Event& event) {
if (event.type == sf::Event::MouseMoved) {
onMouseMove(event);
}
else if (event.type == sf::Event::MouseButtonPressed) {
return onMousePressed(event);
}
return -1;
}
};

View file

@ -0,0 +1,287 @@
#include <iostream>
#include <cmath>
#include <list>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "context_menu.hpp"
using namespace std;
float distance(sf::Vector2f start, sf::Vector2f finish)
{
return sqrtf((start.x - finish.x)*(start.x - finish.x) + (start.y - finish.y)*(start.y - finish.y));
}
void drawLine(sf::RenderWindow& window, sf::Vector2f start, sf::Vector2f finish, sf::Color color = sf::Color::White)
{
sf::Vertex line_vertices[2] = {sf::Vertex(start, color), sf::Vertex(finish, color)};
window.draw(line_vertices, 2, sf::Lines);
}
struct Ball
{
sf::Vector2f position;
float radius;
bool isChoosen;
sf::Color color;
Ball(sf::Vector2f position, float radius, sf::Color color = sf::Color::White) : position(position), radius(radius), color(color)
{
isChoosen = false;
}
void draw(sf::RenderWindow& window) const
{
sf::CircleShape circle(radius);
circle.setFillColor(color);
circle.setOrigin({radius, radius});
circle.setPosition(position);
window.draw(circle);
if (isChoosen) {
const float fraction = 0.7;
drawLine(window, {position.x - radius, position.y + radius}, {position.x - radius, position.y + radius*fraction});
drawLine(window, {position.x - radius, position.y + radius}, {position.x - fraction * radius, position.y + radius});
drawLine(window, {position.x + radius, position.y + radius}, {position.x + radius, position.y + radius*fraction});
drawLine(window, {position.x + radius, position.y + radius}, {position.x + radius*fraction, position.y + radius});
drawLine(window, {position.x + radius, position.y - radius}, {position.x + radius*fraction, position.y - radius});
drawLine(window, {position.x + radius, position.y - radius}, {position.x + radius, position.y - radius*fraction});
drawLine(window, {position.x - radius, position.y - radius}, {position.x - radius*fraction, position.y - radius});
drawLine(window, {position.x - radius, position.y - radius}, {position.x - radius, position.y - radius*fraction});
}
}
void setColor(sf::Uint8 r, sf::Uint8 g, sf::Uint8 b, sf::Uint8 a = 255) {
color.r = r;
color.g = g;
color.b = b;
color.a = a;
}
};
void deleteChoosen(list<Ball>& balls) {
for (list<Ball>::const_iterator it = balls.begin(); it != balls.end();) {
if (it->isChoosen)
it = balls.erase(it);
else
++it;
}
}
void copyBalls(list<Ball>& balls, list<Ball>& buffer) {
buffer.clear();
for (list<Ball>::iterator it = balls.begin(); it != balls.end(); ++it)
if (it->isChoosen)
buffer.push_back(*it);
}
void pasteBalls(list<Ball>& balls, list<Ball>& buffer) {
for (list<Ball>::iterator it = buffer.begin(); it != buffer.end(); ++it)
balls.push_back(*it);
}
void recolorChoosen(list<Ball>& balls) {
for (Ball& b : balls)
if (b.isChoosen)
b.setColor(rand() % 255, rand() % 255, rand() % 255);
}
void resizeChoosen(list<Ball>& balls, double scaling) {
for (list<Ball>::iterator it = balls.begin(); it != balls.end(); ++it) {
if (it->isChoosen)
it->radius += it->radius * scaling;
}
}
int main()
{
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(800, 600), "Select, Move, Delete!", sf::Style::Default, settings);
window.setFramerateLimit(60);
std::list<Ball> balls;
balls.push_back(Ball({200, 200}, 26));
balls.push_back(Ball({400, 300}, 20));
balls.push_back(Ball({500, 100}, 16));
balls.push_back(Ball({200, 400}, 18));
balls.push_back(Ball({350, 150}, 22));
balls.push_back(Ball({750, 400}, 21));
list<Ball> buffer{};
sf::RectangleShape selectionRect;
selectionRect.setFillColor(sf::Color(150, 150, 240, 50));
selectionRect.setOutlineColor(sf::Color(200, 200, 255));
selectionRect.setOutlineThickness(1);
bool isSelecting = false;
/* "Режим выделения" */
bool isMovingMode = false;
/* Необходимо, чтоб в режиме выделения считать смещение */
Ball* selected_ball = nullptr;
bool isContextMenu = false;
sf::Vector2f mousePosition{};
sf::Font font;
if (!font.loadFromFile("consolas.ttf")) {
std::cout << "Can't load button font" << std::endl;
}
std::vector<sf::String> contextMenuStrings {"Delete", "Create", "Random Color", "Increase", "Decrease", "Copy", "Cut", "Paste"};
ContextMenu contextMenu(window, font);
for (const auto& el : contextMenuStrings) {
contextMenu.addButton(el);
}
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
int result = contextMenu.handleEvent(event);
switch(result) {
case 0:
deleteChoosen(balls);
break;
case 1:
mousePosition = window.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
balls.push_back(Ball(mousePosition, 5 + rand() % 40));
break;
case 2:
recolorChoosen(balls);
break;
case 3:
resizeChoosen(balls, 0.25);
break;
case 4:
resizeChoosen(balls, -0.25);
break;
case 5:
copyBalls(balls, buffer);
break;
case 6:
copyBalls(balls, buffer);
deleteChoosen(balls);
break;
case 7:
pasteBalls(balls, buffer);
break;
}
if (event.type == sf::Event::MouseMoved) {
mousePosition = window.mapPixelToCoords({event.mouseMove.x, event.mouseMove.y});
// Если мы находимся в режиме выделения и не попали в уже выделенный шарик, то меняем прямоугольник выделения
if (isSelecting & !isMovingMode) {
selectionRect.setSize(mousePosition - selectionRect.getPosition());
}
// Если в режиме перемещения, то двигаем все выделенные шарики
if (isMovingMode) {
sf::Vector2f direction = selected_ball->position - mousePosition;
for (Ball& b : balls) {
if (b.isChoosen) {
b.position -= direction;
}
}
}
}
if (event.type == sf::Event::MouseButtonPressed) {
mousePosition = window.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
if (event.mouseButton.button == sf::Mouse::Left) {
for (Ball& b : balls) {
/* Если попали в какой-то шарик */
if (distance(mousePosition, b.position) < b.radius) {
selected_ball = &b;
// Если попали в еще не выбранный шарик и не зажат левый Ctrl, то снимаем выделение со всех остальных
if(!b.isChoosen && !sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) {
for (Ball& ball : balls) {
ball.isChoosen = false;
}
}
b.isChoosen = true; //
isMovingMode = true;
break;
}
}
// ЛКМ + левый Alt - добавляем новый случайный шарик
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt)) {
balls.push_back(Ball(mousePosition, 5 + rand() % 40));
}
// Задаём новое положения прямоугольника выделения
if (!isMovingMode) {
isSelecting = true;
selectionRect.setPosition(mousePosition);
selectionRect.setSize({0, 0});
}
}
if (event.mouseButton.button == sf::Mouse::Right) {
isContextMenu = true;
}
}
// При отпускании кнопки мыши выходим из режима выделения
if (event.type == sf::Event::MouseButtonReleased) {
// Если не зажат левый Ctrl и не в режиме перемещения шариков, то все выделения снимаются
if (!sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) && !isMovingMode && !isContextMenu) {
for (Ball& b : balls) {
b.isChoosen = false;
}
}
if (isSelecting) {
sf::Vector2f size = selectionRect.getSize();
sf::Vector2f position = selectionRect.getPosition();
for (Ball& b : balls) {
if ( ((b.position.x - b.radius > position.x) && (b.position.x + b.radius < position.x + size.x)) ||
((b.position.x + b.radius < position.x) && (b.position.x - b.radius > position.x + size.x))
)
if (((b.position.y - b.radius > position.y) && (b.position.y + b.radius < position.y + size.y)) ||
((b.position.y + b.radius < position.y) && (b.position.y - b.radius > position.y + size.y))
)
b.isChoosen = true;
}
}
isSelecting = false;
isMovingMode = false;
isContextMenu = false;
}
if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::C)
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl))
copyBalls(balls, buffer);
if (event.key.code == sf::Keyboard::V)
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl))
pasteBalls(balls, buffer);
if (event.key.code == sf::Keyboard::X)
if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) {
copyBalls(balls, buffer);
deleteChoosen(balls);
}
if (event.key.code == sf::Keyboard::Space)
recolorChoosen(balls);
if (event.key.code == sf::Keyboard::Delete) {
deleteChoosen(balls);
}
}
}
window.clear(sf::Color::Black);
// Рисуем все шарики
for (Ball& b : balls) {
b.draw(window);
}
// Рисуем прямоугольник выделения
if (isSelecting) {
window.draw(selectionRect);
}
contextMenu.draw();
window.display();
}
return 0;
}

View file

@ -0,0 +1,11 @@
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
!_TAG_PROC_CWD /home/nihonium/projects/mipt_cpp/seminar11_events/01_select_move_delete/ //
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
!_TAG_PROGRAM_VERSION 5.9.0 /p5.9.20220828.0/

View file

@ -0,0 +1,6 @@
build:
g++ ./slider.cpp -std=c++11 -o slider -lsfml-graphics -lsfml-window -lsfml-system
build_debug:
g++ ./slider.cpp -std=c++11 -o slider -lsfml-graphics -lsfml-window -lsfml-system -D_DEBUG
build_circle:
g++ ./circle.cpp -std=c++11 -o circle -lsfml-graphics -lsfml-window -lsfml-system

View file

@ -0,0 +1,57 @@
#include <iostream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "slider.hpp"
int main()
{
int result;
unsigned char r, g, b;
sf::RenderWindow window(sf::VideoMode(800, 600), "Slider");
window.setFramerateLimit(60);
sf::Font font;
if (!font.loadFromFile("consolas.ttf")) {
std::cout << "Can't load button font" << std::endl;
}
Slider slider(window, font, sf::Vector2f{200, 100}, 10, 250);
Slider slider_r(window, font, sf::Vector2f{800, 200}, 0, 255);
Slider slider_g(window, font, sf::Vector2f{800, 300}, 0, 255);
Slider slider_b(window, font, sf::Vector2f{800, 400}, 0, 255);
sf::CircleShape circle(10);
circle.setPosition(sf::Vector2f{400, 400});
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
result = slider.handleEvent(event);
/* Centering of the circle */
circle.setPosition(circle.getPosition() + sf::Vector2f{circle.getRadius() - result, circle.getRadius() - result});
circle.setRadius(result);
r = slider_r.handleEvent(event);
g = slider_g.handleEvent(event);
b = slider_b.handleEvent(event);
circle.setFillColor(sf::Color{r, g, b});
}
window.clear(sf::Color::Black);
slider.draw();
slider_r.draw();
slider_g.draw();
slider_b.draw();
window.draw(circle);
window.display();
}
return 0;
}

Binary file not shown.

View file

@ -0,0 +1,36 @@
#include <iostream>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include "slider.hpp"
int main()
{
int result;
sf::RenderWindow window(sf::VideoMode(800, 600), "Slider");
window.setFramerateLimit(60);
sf::Font font;
if (!font.loadFromFile("consolas.ttf")) {
std::cout << "Can't load button font" << std::endl;
}
Slider slider(window, font);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
result = slider.handleEvent(event);
std::cout << result << std::endl;
}
window.clear(sf::Color::Black);
slider.draw();
window.display();
}
return 0;
}

View file

@ -0,0 +1,113 @@
#pragma once
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <sstream>
class Slider
{
private:
const int kCharacterSize = 14;
int mMinValue;
int mMaxValue;
float mSliderPosition = 0;
sf::RenderWindow& mRenderWindow;
sf::RectangleShape mSlider;
sf::RectangleShape mAxis;
sf::Text mText;
bool mIsPressed = false;
void onMousePressed(const sf::Event& event)
{
if (event.mouseButton.button == sf::Mouse::Left) {
sf::Vector2f mousePosition = mRenderWindow.mapPixelToCoords({event.mouseButton.x, event.mouseButton.y});
if (mSlider.getGlobalBounds().contains(mousePosition))
mIsPressed = true;
else if (mAxis.getGlobalBounds().contains(mousePosition))
{
mIsPressed = true;
if (mousePosition.x + mSlider.getSize().x <= mAxis.getPosition().x + mAxis.getSize().x) {
mSlider.setPosition({mousePosition.x, mSlider.getPosition().y});
mSliderPosition = (mSlider.getPosition().x - mAxis.getPosition().x) / (mAxis.getSize().x - mSlider.getSize().x);
}
else {
mSlider.setPosition({mAxis.getPosition().x + mAxis.getSize().x - mSlider.getSize().x, mSlider.getPosition().y});
mSliderPosition = 1;
}
}
}
}
void onMouseMove(const sf::Event& event)
{
if (!mIsPressed) {
return;
}
sf::Vector2f mousePosition = mRenderWindow.mapPixelToCoords({event.mouseMove.x, event.mouseMove.y});
if ((mousePosition.x >= mAxis.getPosition().x) && (mousePosition.x + mSlider.getSize().x <= mAxis.getPosition().x + mAxis.getSize().x)) {
mSlider.setPosition({mousePosition.x, mSlider.getPosition().y});
}
else if (mousePosition.x < mAxis.getPosition().x) {
mSlider.setPosition({mAxis.getPosition().x, mSlider.getPosition().y});
}
else if (mousePosition.x + mSlider.getSize().x > mAxis.getPosition().x + mAxis.getSize().x) {
mSlider.setPosition({mAxis.getPosition().x + mAxis.getSize().x - mSlider.getSize().x, mSlider.getPosition().y});
}
mSliderPosition = (mSlider.getPosition().x - mAxis.getPosition().x) / (mAxis.getSize().x - mSlider.getSize().x);
}
public:
Slider(sf::RenderWindow& window, const sf::Font& font, sf::Vector2f position = {100,200},int min = 0, int max = 100) : mRenderWindow(window)
{
mSlider.setFillColor(sf::Color::Red);
mSlider.setSize({10,30});
mSlider.setPosition(position - sf::Vector2f{0, 10});
mSlider.setOutlineColor(sf::Color::Black);
mSlider.setOutlineThickness(2);
mAxis.setFillColor(sf::Color::White);
mAxis.setSize({500,10});
mAxis.setPosition(position);
std::stringstream ss;
ss << mMinValue;
mText.setString(ss.str());
mText.setFont(font);
mText.setCharacterSize(kCharacterSize);
mText.setFillColor(sf::Color::White);
mText.setPosition(position + sf::Vector2f{mAxis.getSize().x + 10, -4});
mMinValue = min;
mMaxValue = max;
mIsPressed = false;
}
void draw()
{
std::stringstream ss;
ss << mMinValue + (mMaxValue - mMinValue) * mSliderPosition;
mText.setString(ss.str());
mRenderWindow.draw(mAxis);
mRenderWindow.draw(mSlider);
mRenderWindow.draw(mText);
}
int handleEvent(const sf::Event& event) {
if (event.type == sf::Event::MouseMoved) {
onMouseMove(event);
}
else if (event.type == sf::Event::MouseButtonPressed) {
onMousePressed(event);
}
else if (event.type == sf::Event::MouseButtonReleased) {
mIsPressed = false;
}
return mMinValue + (mMaxValue - mMinValue) * mSliderPosition;
}
};

View file

@ -0,0 +1,11 @@
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
!_TAG_PROC_CWD /home/nihonium/projects/mipt_cpp/seminar11_events/02_slider/ //
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
!_TAG_PROGRAM_VERSION 5.9.0 /p5.9.20220828.0/

Binary file not shown.