diff --git a/libbettola/include/bettola/network/player_input_message.h b/libbettola/include/bettola/network/player_input_message.h index b60ae37..fb2c7e9 100644 --- a/libbettola/include/bettola/network/player_input_message.h +++ b/libbettola/include/bettola/network/player_input_message.h @@ -13,6 +13,10 @@ struct PlayerInputMessage { bool left; bool right; float dt; + float yaw; + float cam_front_x; + float cam_front_y; + float cam_front_z; }; } /* namespace Network. */ diff --git a/libbettola/include/bettola/network/player_state_message.h b/libbettola/include/bettola/network/player_state_message.h index 2acf7b0..e94d602 100644 --- a/libbettola/include/bettola/network/player_state_message.h +++ b/libbettola/include/bettola/network/player_state_message.h @@ -9,6 +9,7 @@ struct PlayerStateMessage { unsigned int player_id; float x; float y; + float yaw; unsigned int last_processed_sequence; }; diff --git a/src/bettola.cpp b/src/bettola.cpp index 937733c..b94a148 100644 --- a/src/bettola.cpp +++ b/src/bettola.cpp @@ -100,26 +100,26 @@ void Bettola::process_events(void) { } void Bettola::update(double dt) { - /* Set local player's velocity for client-side prediction. */ - float dir_x = (_input.right ? 1.0f : 0.0f) - (_input.left ? 1.0f : 0.0f); - float dir_y = (_input.down ? 1.0f : 0.0f) - (_input.up ? 1.0f : 0.0f); - _game_client.get_player_for_write().set_velocity_direction(dir_x, dir_y); + /* Get a mutable reference to the local player. */ + Player& player = _game_client.get_player_for_write(); - /* Update camera to follow the player. */ - const auto& player = _game_client.get_player(); - BettolaMath::Vec3 player_pos = { player.get_x(), 0.0f, player.get_y() }; - _camera.update(player_pos); + /* Set local player's velocity based on input and cam direction. */ + player.set_velocity_direction(_input, _camera.get_front()); /* Process network messages and send input to the server. */ _game_client.process_network_messages(); - _game_client.send_input(_input, dt); + _game_client.send_input(_input, _camera, dt); /* Update all players (local prediction and remote interpolation). */ _game_client.update_players(dt); + + /* Update camera to follow the local player. */ + BettolaMath::Vec3 player_pos = { player.get_x(), 0.0f, player.get_y() }; + _camera.update(player_pos); } void Bettola::render(void) { - _renderer.render(_camera.get_view_matrix(), _game_client.get_player(), + _renderer.render(_camera, _game_client.get_player(), _game_client.get_remote_players()); SDL_GL_SwapWindow(_window); } diff --git a/src/game/player.cpp b/src/game/player.cpp index fb2bec8..bdf4221 100644 --- a/src/game/player.cpp +++ b/src/game/player.cpp @@ -1,5 +1,6 @@ #include "player.h" -#include "bettola/math/vec2.h" +#include +#include "math/vec3.h" #include "network/player_input_message.h" Player::Player(void) : @@ -9,7 +10,7 @@ Player::Player(void) : _height(50.0f), _vx(0.0f), _vy(0.0f), - _speed(200.0f) {} + _speed(10.0f) {} void Player::set_position(float x, float y) { _x = x; _y = y; @@ -21,18 +22,33 @@ void Player::update(double dt) { } void Player::apply_input(const BettolaLib::Network::PlayerInputMessage& input) { - float dir_x = (input.right ? 1.0f : 0.0f) - (input.left ? 1.0f : 0.0f); - float dir_y = (input.down ? 1.0f : 0.0f) - (input.up ? 1.0f : 0.0f); - set_velocity_direction(dir_x, dir_y); + /* Server side replay doesn't have a cam, so we use a default forward vector. */ + BettolaMath::Vec3 fake_cam_front = { 0.0f, 0.0f, -1.0f }; + InputState state = { input.up, input.down, input.left, input.right }; + set_velocity_direction(state, fake_cam_front); update(input.dt); } -void Player::set_velocity_direction(float dir_x, float dir_y) { - BettolaMath::Vec2 direction = { dir_x, dir_y }; - - if(direction.length() > 0.0f) { - direction.normalise(); +void Player::set_velocity_direction(const InputState& input , const BettolaMath::Vec3& cam_front) { + BettolaMath::Vec3 forward = { cam_front.x, 0.0f, cam_front.z }; + float f_mag = sqrt(forward.x*forward.x + forward.z*forward.z); + if(f_mag > 0.0f) { + forward.x /= f_mag; forward.z /= f_mag; + } + + BettolaMath::Vec3 right = { -forward.z, 0.0f, forward.x }; + + BettolaMath::Vec3 move_dir = { 0.0f, 0.0f, 0.0f }; + if(input.up) move_dir = move_dir + forward; + if(input.down) move_dir = move_dir + BettolaMath::Vec3{-forward.x, 0.0f, -forward.z}; + if(input.left) move_dir = move_dir + BettolaMath::Vec3{-right.x, 0.0f, -right.z}; + if(input.right) move_dir = move_dir + right; + + float move_mag = sqrt(move_dir.x*move_dir.x + move_dir.z*move_dir.z); + if(move_mag > 0.0f) { + _vx = (move_dir.x / move_mag) * _speed; + _vy = (move_dir.z / move_mag) * _speed; /* vy controls Z-axis movement. */ + } else { + _vx = 0.0f; _vy = 0.0f; } - _vx = direction.x * _speed; - _vy = direction.y * _speed; } diff --git a/src/game/player.h b/src/game/player.h index 887d605..b3ef901 100644 --- a/src/game/player.h +++ b/src/game/player.h @@ -1,5 +1,6 @@ #pragma once +#include "math/vec3.h" namespace BettolaLib { namespace Network { struct PlayerInputMessage; } } class Player { @@ -17,7 +18,7 @@ public: void set_position(float x, float y); void apply_input(const BettolaLib::Network::PlayerInputMessage& input); - void set_velocity_direction(float dir_x, float dir_y); + void set_velocity_direction(const InputState& input, const BettolaMath::Vec3& cam_front); float get_x() const { return _x; } float get_y() const { return _y; } diff --git a/src/game/remote_player.cpp b/src/game/remote_player.cpp index a8faf5c..9e25baf 100644 --- a/src/game/remote_player.cpp +++ b/src/game/remote_player.cpp @@ -1,15 +1,16 @@ #include "remote_player.h" RemotePlayer::RemotePlayer(unsigned int id, float x, float y) : - _id(id), _x(x), _y(y), _target_x(x), _target_y(y) {} + _id(id), _x(x), _y(y), _yaw(0.0f), _target_x(x), _target_y(y) {} void RemotePlayer::update(double dt) { const float interp_speed = 15.0f; _x += (_target_x - _x) * interp_speed * dt; _y += (_target_y - _y) * interp_speed * dt; + /* TODO: Snap the yaw, we'll interpolate later if we need. */ } -void RemotePlayer::set_target_position(float x, float y) { - _target_x = x; _target_y = y; +void RemotePlayer::set_target_position(float x, float y, float yaw) { + _target_x = x; _target_y = y; _yaw = yaw; } diff --git a/src/game/remote_player.h b/src/game/remote_player.h index f2d4edf..da71ee8 100644 --- a/src/game/remote_player.h +++ b/src/game/remote_player.h @@ -5,16 +5,18 @@ public: RemotePlayer(unsigned int id, float x, float y); void update(double dt); - void set_target_position(float x, float y); + void set_target_position(float x, float y, float yaw); unsigned int get_id(void) const { return _id; } - float get_x(void) const { return _x; } - float get_y(void) const { return _y; } + float get_x(void) const { return _x; } + float get_y(void) const { return _y; } + float get_yaw(void) const { return _yaw; } private: unsigned int _id; float _x; float _y; + float _yaw; float _target_x; float _target_y; }; diff --git a/src/game_client.cpp b/src/game_client.cpp index 856a35f..733424c 100644 --- a/src/game_client.cpp +++ b/src/game_client.cpp @@ -7,6 +7,7 @@ #include "game_client.h" #include "game/remote_player.h" +#include "math/vec3.h" #include "network/game_state_message.h" #include "network/message.h" @@ -125,7 +126,7 @@ void GameClient::_process_game_state(const BettolaLib::Network::GameStateMessage if(it != _remote_players.end()) { /* Found 'em! update their target pos. */ - it->set_target_position(ps.x, ps.y); + it->set_target_position(ps.x, ps.y, ps.yaw); } else { /* They are new, add them to our list. */ _remote_players.emplace_back(ps.player_id, ps.x, ps.y); @@ -138,7 +139,7 @@ void GameClient::_process_game_state(const BettolaLib::Network::GameStateMessage players_in_message.end(); }), _remote_players.end()); } -void GameClient::send_input(const Player::InputState& input, float dt) { +void GameClient::send_input(const Player::InputState& input, const Camera& camera, float dt) { if(_our_player_id > 0) { /* Sent our current input state to the server. */ BettolaLib::Network::PlayerInputMessage input_msg; @@ -149,6 +150,11 @@ void GameClient::send_input(const Player::InputState& input, float dt) { input_msg.left = input.left; input_msg.right = input.right; input_msg.dt = dt; + input_msg.yaw = camera.get_yaw(); + const auto& cam_front = camera.get_front(); + input_msg.cam_front_x = cam_front.x; + input_msg.cam_front_y = cam_front.y; + input_msg.cam_front_z = cam_front.z; BettolaLib::Network::MessageHeader header; header.type = BettolaLib::Network::MessageType::PlayerInput; diff --git a/src/game_client.h b/src/game_client.h index 53a96b3..bba7f89 100644 --- a/src/game_client.h +++ b/src/game_client.h @@ -6,6 +6,8 @@ #include "game/player.h" #include "game/remote_player.h" +#include "graphics/camera.h" +#include "math/vec3.h" #include "network/tcpsocket.h" #include "network/udpsocket.h" #include "network/player_input_message.h" @@ -17,7 +19,7 @@ public: bool connect(const char* host, unsigned short port); void process_network_messages(void); - void send_input(const Player::InputState& input, float dt); + void send_input(const Player::InputState& input, const Camera& camera, float dt); const Player& get_player(void) const { return _player; } Player& get_player_for_write(void) { return _player; } diff --git a/src/graphics/camera.h b/src/graphics/camera.h index 90f2d2a..561ff3a 100644 --- a/src/graphics/camera.h +++ b/src/graphics/camera.h @@ -8,11 +8,13 @@ public: Camera(void); void update(const BettolaMath::Vec3& target_pos); - void process_mouse_movement(float x_offset, float y_offset); void process_mouse_scroll(float y_offset); BettolaMath::Mat4 get_view_matrix(void) const; + const BettolaMath::Vec3& get_front(void) const { return _front; } + float get_yaw(void) const { return _yaw; } + private: void _update_camera_vectors(void); diff --git a/src/graphics/renderer.cpp b/src/graphics/renderer.cpp index f61cd93..ca864b3 100644 --- a/src/graphics/renderer.cpp +++ b/src/graphics/renderer.cpp @@ -2,6 +2,7 @@ #include #include #include "game/player.h" +#include "graphics/camera.h" #include #ifndef M_PI @@ -135,7 +136,7 @@ bool Renderer::init(int screen_width, int screen_height) { return true; } -void Renderer::render(const BettolaMath::Mat4& view_matrix, const Player& player, +void Renderer::render(const Camera& camera, const Player& player, const std::vector& remote_players) { glClearColor(0.1f, 0.1f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Need to clear depth buffer too. */ @@ -144,6 +145,7 @@ void Renderer::render(const BettolaMath::Mat4& view_matrix, const Player& player _shader.use(); GL_CHECK_ERROR(); + BettolaMath::Mat4 view_matrix = camera.get_view_matrix(); BettolaMath::Mat4 projection = BettolaMath::Mat4::perspective((45.0f * M_PI) / 180.0f, 800.0f/600.0f, 0.1f, 100.0f); GLint view_loc = glGetUniformLocation(_shader.get_id(), "view"); @@ -168,7 +170,11 @@ void Renderer::render(const BettolaMath::Mat4& view_matrix, const Player& player glUniform3f(color_loc, 0.2f, 0.5f, 0.8f); /* Draw the local player's cube. */ - BettolaMath::Mat4 model = BettolaMath::Mat4::translation(player.get_x(), 0.0f, player.get_y()); + BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(player.get_x(), + 0.0f, player.get_y()); + BettolaMath::Mat4 rot_matrix = BettolaMath::Mat4::rotation(-camera.get_yaw()-90.0f, + {0.0f,1.0f,0.0f}); + BettolaMath::Mat4 model = trans_matrix.multiply(rot_matrix); glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr()); GL_CHECK_ERROR(); @@ -179,8 +185,11 @@ void Renderer::render(const BettolaMath::Mat4& view_matrix, const Player& player /* Draw remote players' cube. */ for(const auto& remote_player : remote_players) { glBindVertexArray(_vao); /* bind cube VAO for each remote player. */ - BettolaMath::Mat4 remote_model = BettolaMath::Mat4::translation(remote_player.get_x(), + BettolaMath::Mat4 remote_trans = BettolaMath::Mat4::translation(remote_player.get_x(), 0.0f, remote_player.get_y()); + BettolaMath::Mat4 remote_rot = BettolaMath::Mat4::rotation(-remote_player.get_yaw()-90.0f, + {0.0f,1.0f,0.0f}); + BettolaMath::Mat4 remote_model = remote_trans.multiply(remote_rot); glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr()); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); /* Unbind it! */ diff --git a/src/graphics/renderer.h b/src/graphics/renderer.h index 8f668cd..f5d9aa4 100644 --- a/src/graphics/renderer.h +++ b/src/graphics/renderer.h @@ -1,6 +1,7 @@ #pragma once #include +#include "graphics/camera.h" #include "shader.h" #include "game/player.h" #include "game/remote_player.h" @@ -12,7 +13,7 @@ public: ~Renderer(void); bool init(int screen_width, int screen_height); - void render(const BettolaMath::Mat4& view_matrix, const Player& player, + void render(const Camera& camera, const Player& player, const std::vector& remote_players); private: diff --git a/srv/game/game.cpp b/srv/game/game.cpp index ce469e7..5aab2ae 100644 --- a/srv/game/game.cpp +++ b/srv/game/game.cpp @@ -3,6 +3,7 @@ #include #include "game.h" +#include "math/vec3.h" #include "network/game_state_message.h" #include "network/player_input_message.h" #include "network/tcpsocket.h" @@ -47,9 +48,10 @@ void Game::process_udp_message(const char* buffer, size_t size, const sockaddr_i player->set_udp_addr(from_addr); } player->set_last_processed_sequence(msg.sequence_number); - float dir_x = (msg.right ? 1.0f : 0.0f) - (msg.left ? 1.0f : 0.0f); - float dir_y = (msg.down ? 1.0f : 0.0f) - (msg.up ? 1.0f : 0.0f); - player->set_velocity_direction(dir_x, dir_y); + player->set_yaw(msg.yaw); + Player::InputState input = { msg.up, msg.down, msg.left, msg.right }; + BettolaMath::Vec3 cam_front = { msg.cam_front_x, msg.cam_front_y, msg.cam_front_z }; + player->set_velocity_direction(input, cam_front); player->update(msg.dt); } } @@ -62,8 +64,9 @@ void Game::broadcast_game_state(BettolaLib::Network::UDPSocket& udp_socket) { for(size_t i = 0; i < _players.size(); ++i) { msg.players[i].player_id = _players[i]->get_id(); - msg.players[i].x = _players[i]->get_x(); - msg.players[i].y = _players[i]->get_y(); + msg.players[i].x = _players[i]->get_x(); + msg.players[i].y = _players[i]->get_y(); + msg.players[i].yaw = _players[i]->get_yaw(); msg.players[i].last_processed_sequence = _players[i]->get_last_processed_sequence(); } diff --git a/srv/game/player.cpp b/srv/game/player.cpp index ad2dbb4..1b33486 100644 --- a/srv/game/player.cpp +++ b/srv/game/player.cpp @@ -17,7 +17,8 @@ Player::Player(BettolaLib::Network::TCPSocket* socket) : _id = _next_player_id++; _x = 0.0f; _y = 0.0f; _vx = 0.0f; _vy = 0.0f; - _speed = 200.0f; /* Must match client! */ + _yaw = 0.0f; + _speed = 10.0f; /* Must match client! */ _last_processed_sequence = 0; memset(&_udp_addr, 0, sizeof(_udp_addr)); } @@ -27,13 +28,27 @@ void Player::update(double dt) { _y += _vy * dt; } -void Player::set_velocity_direction(float dir_x, float dir_y) { - if(dir_x == 0.0f && dir_y == 0.0f) { - _vx = _vy = 0.0f; +void Player::set_velocity_direction(const InputState& input , const BettolaMath::Vec3& cam_front) { + BettolaMath::Vec3 forward = { cam_front.x, 0.0f, cam_front.z }; + float f_mag = sqrt(forward.x*forward.x + forward.z*forward.z); + if(f_mag > 0.0f) { + forward.x /= f_mag; forward.z /= f_mag; + } + + BettolaMath::Vec3 right = { -forward.z, 0.0f, forward.x }; + + BettolaMath::Vec3 move_dir = { 0.0f, 0.0f, 0.0f }; + if(input.up) move_dir = move_dir + forward; + if(input.down) move_dir = move_dir + BettolaMath::Vec3{-forward.x, 0.0f, -forward.z}; + if(input.left) move_dir = move_dir + BettolaMath::Vec3{-right.x, 0.0f, -right.z}; + if(input.right) move_dir = move_dir + right; + + float move_mag = sqrt(move_dir.x*move_dir.x + move_dir.z*move_dir.z); + if(move_mag > 0.0f) { + _vx = (move_dir.x / move_mag) * _speed; + _vy = (move_dir.z / move_mag) * _speed; /* vy controls Z-axis movement. */ } else { - float mag = sqrt(dir_x * dir_x + dir_y * dir_y); - _vx = (dir_x / mag) * _speed; - _vy = (dir_y / mag) * _speed; + _vx = 0.0f; _vy = 0.0f; } } diff --git a/srv/game/player.h b/srv/game/player.h index 87114ae..edc33d2 100644 --- a/srv/game/player.h +++ b/srv/game/player.h @@ -3,19 +3,29 @@ #include #include "bettola/network/tcpsocket.h" +#include "math/vec3.h" class Player { public: + struct InputState { + bool up; + bool down; + bool left; + bool right; + }; + Player(BettolaLib::Network::TCPSocket* socket); void update(double dt); - void set_velocity_direction(float dir_x, float dir_y); + void set_velocity_direction(const InputState& input, const BettolaMath::Vec3& cam_front); void set_position(float x, float y) { _x = x; _y = y; } + void set_yaw(float yaw) { _yaw = yaw; } void set_udp_addr(const sockaddr_in& addr) { _udp_addr = addr; _has_udp_addr = true; } unsigned int get_id(void) const { return _id; } - float get_x(void) const { return _x; } - float get_y(void) const { return _y; } + float get_x(void) const { return _x; } + float get_y(void) const { return _y; } + float get_yaw(void) const { return _yaw; } BettolaLib::Network::TCPSocket& get_socket(void) const { return *_socket; } const sockaddr_in& get_udp_addr(void) const { return _udp_addr; } @@ -29,6 +39,7 @@ private: unsigned int _id; float _x; float _y; + float _yaw; BettolaLib::Network::TCPSocket* _socket; sockaddr_in _udp_addr; bool _has_udp_addr;