Conversation
DogTales/dog/dogtales.cpp
Outdated
| if (ImGui::BeginTabItem("Player")) { | ||
| bave::im_text("Controller States"); | ||
| ImGui::Separator(); | ||
| ImGui::Text("Test (Press 'T'): %.2f", m_player.get_controller_state("test")); |
There was a problem hiding this comment.
| ImGui::Text("Test (Press 'T'): %.2f", m_player.get_controller_state("test")); | |
| bave::im_text("Test (Press 'T'): {:.2f}", m_player.get_controller_state("test")); |
DogTales/dog/player.cpp
Outdated
|
|
||
| void Player::draw(bave::Shader& shader) const { m_sprite.draw(shader); } | ||
|
|
||
| float const Player::get_controller_state(std::string key) { |
There was a problem hiding this comment.
Returning a float const is redundant, compiler/linter should be warning about this.
There was a problem hiding this comment.
should I just make it float?
DogTales/dog/player.cpp
Outdated
| try { | ||
| return m_player_controller.key_map[key]; | ||
| } catch (std::out_of_range const& oor) { return -1.f; } |
There was a problem hiding this comment.
We don't want to risk exceptional paths in hot code like this.
There was a problem hiding this comment.
what should i do instead? just return the element? or use if()?
There was a problem hiding this comment.
First of all the member function should be const. Once that's done key_map[key] will not compile anyway. So you'd want to use key_map.find(key) and return a default value / nullopt if the key isn't present.
DogTales/dog/player.hpp
Outdated
| void draw(bave::Shader& shader) const; | ||
|
|
||
| float const get_controller_state(std::string key); |
There was a problem hiding this comment.
Taking a std::string_view would optimize calls made with literals.
|
|
||
| void tick(bave::Seconds dt, const bave::App& app); | ||
|
|
||
| std::unordered_map<std::string, float> key_map{}; |
There was a problem hiding this comment.
This shouldn't be public, as anybody can modify it. Also, use an annoyingly long form to enable string_view lookups:
#include <bave/core/string_hash.hpp>
// ...
std::unordered_map<std::string, float, bave::StringHash, std::equal_to<>> key_map{};|
Also, |
…dd actual keymaps, and update debug menu accordingly.
| std::optional<float> PlayerController::get_controller_state(std::string_view key) const { | ||
| if (auto search = key_map.find(key); search != key_map.end()) { | ||
| return search->second; | ||
| } else { | ||
| return -1.f; | ||
| } | ||
| } |
There was a problem hiding this comment.
Two points:
- Since this always returns a float, there's not much point in wrapping it in
std::optional. Even in the user code above you don't check if the returned optional has a value, just call.value()anyway. Returning afloatwill eliminate that redundancy. - The default returned value should be
0.0fnot-1.0f, because that implies: eg "pulling left all the time" if interpreted as a horizontal direction for a key that isn't mapped. 😄
There was a problem hiding this comment.
Nitpick: else is redundant if all paths in the if above it return.
| std::optional<float> get_controller_state(std::string_view key) const; | ||
|
|
||
| private: | ||
| std::unordered_map<std::string, float, bave::StringHash, std::equal_to<>> key_map{}; |
There was a problem hiding this comment.
Nitpick: non-public members have the m_ prefix everywhere else.
| void PlayerController::tick(bave::Seconds dt, bave::App const& app) { | ||
|
|
||
| auto const& key_state = app.get_key_state(); | ||
|
|
||
| bool left = key_state.is_pressed(bave::Key::eA) || key_state.is_pressed(bave::Key::eLeft); | ||
| bool right = key_state.is_pressed(bave::Key::eD) || key_state.is_pressed(bave::Key::eRight); | ||
| bool up = key_state.is_pressed(bave::Key::eW) || key_state.is_pressed(bave::Key::eUp); | ||
| bool down = key_state.is_pressed(bave::Key::eS) || key_state.is_pressed(bave::Key::eDown); | ||
|
|
||
| key_map["move_x"] = 0.f; | ||
| key_map["move_x"] = left && !right ? -1.f : key_map["move_x"]; | ||
| key_map["move_x"] = right && !left ? 1.f : key_map["move_x"]; | ||
| key_map["move_x"] = right && left ? 0.f : key_map["move_x"]; | ||
|
|
||
| key_map["move_y"] = 0.f; | ||
| key_map["move_y"] = down && !up ? -1.f : key_map["move_y"]; | ||
| key_map["move_y"] = up && !down ? 1.f : key_map["move_y"]; | ||
| key_map["move_y"] = up && down ? 0.f : key_map["move_y"]; | ||
|
|
||
| key_map["jump"] = key_state.is_pressed(bave::Key::eW) || key_state.is_pressed(bave::Key::eUp) ? 1.f : 0.f; | ||
| } |
There was a problem hiding this comment.
A few things to refactor here.
- Don't call
key_map["move_x"]a dozen times - it will perform the hash, comparison, and lookup each time. Instead get a reference once and operate on the reference throughout the function. - The
left/rightlogic is unnecessarily complicating things, instead just append the delta to a tempfloat, and clamp it between-1.0fand1.0fat the end. - This is still pretty hard-coded and there's no indirection for which key corresponds to which game-action. I think at this level it's better for the logic to be generic: the controller should cycle through its mappings and update the
key_mapaccording to the currentkey_state, without knowing how many mappings there are / what they are.
There was a problem hiding this comment.
Filename should be in snake_case like others.
added a
PlayerControllerclass and gave one tom_player. Also added a small debug menu with ImGui for testing.