@a1nsworth

Как реализовать управление для двух игроков на одной клавиатуре SFML?

В моей игре 2 игрока, управление на ОДНОЙ клавиатуре. Каким образом мне сделать так, чтобы я мог управлять каждым из них ?
Параллельность ? Если да , то как это сделать ?
  • Вопрос задан
  • 167 просмотров
Пригласить эксперта
Ответы на вопрос 3
@code_panik
В самом простом случае можно реализовать так же, как реализовано управление одним игроком. Достаточно регистрировать события нажатия/отпускания клавиш. В sfml функция sf::Window::setKeyRepeatEnabled с аргументом false включает режим однократной регистрации нажатия клавиши. То есть событие keyPressed будет зарегистрировано однажды при нажатии клавиши, даже если она остается нажатой.

Можем считать, что между событиями keyPressed и keyReleased клавиша остается нажатой. Сохраним нажатые клавиши с списке и на каждом шаге игрового цикла будем менять соответственно нажатым клавишам состояние игры, в том числе игрока.

Пример реализации (C++17):
#include <SFML/Graphics.hpp>
#include <unordered_set>
#include <unordered_map>

using namespace std;

void normalize(sf::Vector2i& v) {
	int length = sqrt(1ll * v.x * v.x + 1ll * v.y * v.y);
	if (length != 0) {
		v.x /= length;
		v.y /= length;
	}
}

class Input {
public:
	using Keys = std::unordered_set<sf::Keyboard::Key>;

	void registerKeyEvent(sf::Event e) {
		if (e.type == sf::Event::KeyPressed)
			pressedKeys_.insert(e.key.code);
		else if (e.type == sf::Event::KeyReleased)
			pressedKeys_.erase(e.key.code);
	}

	const Keys& pressedKeys() const {
		return pressedKeys_;
	}

private:
	Keys pressedKeys_;
};

class Player {
public:
	enum class Control {
		moveUp, moveLeft, moveRight, moveDown
	};

	Player(sf::Vector2i initialPosition, sf::Color color)
		: position_{ initialPosition } {
		sprite_.setPosition(sf::Vector2f(initialPosition.x, initialPosition.y));
		sprite_.setFillColor(color);
		sprite_.setRadius(50);
	}

	Player& map(Control control, sf::Keyboard::Key key) {
		keyControls_[key] = control;
		return *this;
	}

	void process(const Input& input) {
		velocity_ = {};
		for (sf::Keyboard::Key k : input.pressedKeys()) {
			if (auto it = keyControls_.find(k); it != keyControls_.end()) {
				switch (it->second) {
				case Control::moveDown:
					velocity_.y = 1;
					break;
				case Control::moveUp:
					velocity_.y = -1;
					break;
				case Control::moveLeft:
					velocity_.x = -1;
					break;
				case Control::moveRight:
					velocity_.x = 1;
					break;
				default:
					break;
				}
			}
		}
		normalize(velocity_);
		velocity_ *= speed_;
	}

	void move(sf::Vector2i position) {
		position_ = position;
		sprite_.setPosition(sf::Vector2f(position.x, position.y));
	}

	const sf::CircleShape& sprite() const {
		return sprite_;
	}

	sf::Vector2i position() const {
		return position_;
	}

	sf::Vector2i velocity() const {
		return velocity_;
	}

private:
	int speed_ = 300; // pixels / s
	sf::Vector2i velocity_;
	sf::Vector2i position_;
	
	sf::CircleShape sprite_;

	std::unordered_map<sf::Keyboard::Key, Control> keyControls_;
};

class Game {
public:
	Game(sf::RenderWindow* gameWindow)
		: window_(gameWindow) {
		window_->setKeyRepeatEnabled(false);
		auto width = window_->getSize().x;
		auto height = window_->getSize().y;
		Player mrRed(sf::Vector2i(width / 3, height / 3 * 2), sf::Color::Red);
		mrRed.map(Player::Control::moveUp, sf::Keyboard::W)
			.map(Player::Control::moveDown, sf::Keyboard::S)
			.map(Player::Control::moveLeft, sf::Keyboard::A)
			.map(Player::Control::moveRight, sf::Keyboard::D);
		players_.emplace_back(std::move(mrRed));

		Player mrBlue(sf::Vector2i(width / 3 * 2, height / 3 * 2), sf::Color::Blue);
		mrBlue.map(Player::Control::moveUp, sf::Keyboard::Up)
			.map(Player::Control::moveDown, sf::Keyboard::Down)
			.map(Player::Control::moveLeft, sf::Keyboard::Left)
			.map(Player::Control::moveRight, sf::Keyboard::Right);
		players_.emplace_back(std::move(mrBlue));

		Player mrGreen(sf::Vector2i(width / 2, height / 3), sf::Color::Green);
		mrGreen.map(Player::Control::moveUp, sf::Keyboard::I)
			.map(Player::Control::moveDown, sf::Keyboard::K)
			.map(Player::Control::moveLeft, sf::Keyboard::J)
			.map(Player::Control::moveRight, sf::Keyboard::L);
		players_.emplace_back(std::move(mrGreen));
	}

	void update() {
		processInputEvents();
		sf::Time dt = clock_.restart();
		processMovement(dt.asMilliseconds());
		drawScene();
	}

private:
	void processInputEvents() {
		for (sf::Event e; window_->pollEvent(e); ) {
			if (e.type == sf::Event::Closed) {
				window_->close();
				return;
			}
			input_.registerKeyEvent(e);
		}
		for (auto& player : players_)
			player.process(input_);
	}

	void processMovement(sf::Int32 dt) {
		for (int id = 0; id < players_.size(); ++id) {
			auto& player = players_[id];
			sf::Vector2i position = player.position() + sf::Vector2i(
				player.velocity().x * 1e-3f * dt,
				player.velocity().y * 1e-3f * dt
			);
			player.move(position);
		}
	}

	void drawScene() {
		window_->clear(sf::Color::White);
		for (const auto& player : players_)
			window_->draw(player.sprite());
		window_->display();
	}

	std::vector<Player> players_;
	Input input_;
	sf::RenderWindow* window_;
	sf::Clock clock_;
};


int main() {
	sf::RenderWindow mainWindow(sf::VideoMode(600, 600), "Circles", sf::Style::Close);
	mainWindow.setFramerateLimit(120);
	Game game(&mainWindow);
	while (mainWindow.isOpen())
		game.update();
	return 0;
}


Мы создаем три круга, которые могут двигаться независимо. Клавиши движений назначаются в конструкторе Game.

FrameLimit ограничен, потому что скорость и другие результаты расчетов целочисленные и привязаны к пискелям. При слишком высокой частоте кадров изменения расстояний на каждом шаге симуляции будут незначительными, округлятся к 0. Лучше заменить целочисленную арифметику на float-арифметику, чтобы не было грубых ошибок округления как в normalize, и промежуточных преобразований sf::Vector.

За поддержание актуальности списка нажатых клавиш отвечает class Input.
Ответ написан
GavriKos
@GavriKos Куратор тега Разработка игр
Так же как одним. Считываете коды клавиш, и исходя из нажатых клавишь делаете действия для того игрока, которому принадлежит нажатая клавиша.
Ответ написан
saboteur_kiev
@saboteur_kiev Куратор тега C++
software engineer
Советую почитать о том, что такое NKRO
Не факт что даже если будет верным код, найдешь клавиатуру где все будет работать
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы