From eb7435d2d4a5836c84e451581c4c4c507e09a201 Mon Sep 17 00:00:00 2001 From: tritonas00 Date: Mon, 29 May 2023 10:15:14 +0300 Subject: [PATCH 1/2] single player character collisions --- source/main/Application.cpp | 1 + source/main/Application.h | 1 + source/main/gameplay/Character.cpp | 97 +++++++++++++++++++++-- source/main/gameplay/Character.h | 15 +++- source/main/gui/panels/GUI_TopMenubar.cpp | 6 ++ source/main/system/CVar.cpp | 1 + 6 files changed, 112 insertions(+), 9 deletions(-) diff --git a/source/main/Application.cpp b/source/main/Application.cpp index 828a47f330..6f54d449d3 100644 --- a/source/main/Application.cpp +++ b/source/main/Application.cpp @@ -108,6 +108,7 @@ CVar* sim_no_self_collisions; CVar* sim_gearbox_mode; CVar* sim_soft_reset_mode; CVar* sim_quickload_dialog; +CVar* sim_character_collisions; // Multiplayer CVar* mp_state; diff --git a/source/main/Application.h b/source/main/Application.h index 4b487b6ee5..e0ea808d34 100644 --- a/source/main/Application.h +++ b/source/main/Application.h @@ -301,6 +301,7 @@ extern CVar* sim_no_self_collisions; extern CVar* sim_gearbox_mode; extern CVar* sim_soft_reset_mode; extern CVar* sim_quickload_dialog; +extern CVar* sim_character_collisions; // Multiplayer extern CVar* mp_state; diff --git a/source/main/gameplay/Character.cpp b/source/main/gameplay/Character.cpp index 686fc36579..239faf17dc 100644 --- a/source/main/gameplay/Character.cpp +++ b/source/main/gameplay/Character.cpp @@ -145,15 +145,19 @@ void Character::update(float dt) // Trigger script events and handle mesh (ground) collision Vector3 query = position; - App::GetGameContext()->GetTerrain()->GetCollisions()->collisionCorrect(&query); + if (App::GetGameContext()->GetTerrain()->GetCollisions()->collisionCorrect(&query)) + { + m_inertia = false; + } // Auto compensate minor height differences - float depth = calculate_collision_depth(position); - if (depth > 0.0f) + float terrain_depth = calculate_collision_depth(position); + if (terrain_depth > 0.0f) { m_can_jump = true; m_character_v_speed = std::max(0.0f, m_character_v_speed); - position.y += std::min(depth, 2.0f * dt); + position.y += std::min(terrain_depth, 2.0f * dt); + m_inertia = false; } // Submesh "collision" @@ -173,15 +177,72 @@ void Character::update(float dt) if (result.first && result.second < 1.8f) { depth = std::max(depth, result.second); + if (depth > 0 && m_contacting_actor == nullptr) // check fresh contacting actor also, prevents knockbacks with multiple actors that have node position overlapping + { + // first contact - initialize 'last' values to avoid big knockbacks + Vector3 cab_position = CalcCabAveragePos(actor, i); + m_last_vehicle_position = cab_position; + m_last_vehicle_rotation = Ogre::Radian(actor->getRotation()); + m_last_contacting_cab = i; + m_contacting_actor = actor; + } + m_contacting_cab = i; + } + } + + if (depth > 0) + { + m_can_jump = true; + m_character_v_speed = std::max(0.0f, m_character_v_speed); + position.y += std::min(depth, 0.05f); + } + + if (m_contacting_actor != nullptr && App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) + { + int motion_cab = -1; + if (m_contacting_cab == m_last_contacting_cab) // we're on the same cab - just get it's current pos. + { + motion_cab = m_contacting_cab; } + else // we're on different cab - use current position of the previous cab. + { + motion_cab = m_last_contacting_cab; + } + Vector3 cab_position = CalcCabAveragePos(m_contacting_actor, motion_cab); + m_vehicle_position = cab_position; + m_vehicle_rotation = Ogre::Radian(m_contacting_actor->getRotation()); + + position += (m_vehicle_position - m_last_vehicle_position); + this->setRotation(m_character_rotation + (m_vehicle_rotation - m_last_vehicle_rotation)); + + m_inertia = true; + m_inertia_position = (m_vehicle_position - m_last_vehicle_position); + m_inertia_rotation = (m_vehicle_rotation - m_last_vehicle_rotation); } + else if (m_inertia && App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) + { + position += m_inertia_position; + this->setRotation(m_character_rotation + m_inertia_rotation); + } + } + else if (m_contacting_actor != nullptr && !m_contacting_actor->ar_bounding_box.contains(position) && App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) // we lost contact, reset contacting actor + { + m_contacting_actor = nullptr; } } - if (depth > 0.0f) + if (App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) { - m_can_jump = true; - m_character_v_speed = std::max(0.0f, m_character_v_speed); - position.y += std::min(depth, 0.05f); + if (m_contacting_cab == m_last_contacting_cab) + { + m_last_vehicle_position = m_vehicle_position; + } + else if (m_contacting_actor != nullptr) // we used last_contacting_cab's position for the motion, but we'll need contacting_cab's position next frame. + { + Vector3 cab_position = CalcCabAveragePos(m_contacting_actor, m_contacting_cab); + m_last_vehicle_position = cab_position; + } + m_last_contacting_cab = m_contacting_cab; + m_last_vehicle_rotation = m_vehicle_rotation; } } @@ -214,6 +275,7 @@ void Character::update(float dt) position.y = pheight; m_character_v_speed = 0.0f; m_can_jump = true; + m_inertia = false; } // water stuff @@ -373,6 +435,12 @@ void Character::update(float dt) } else if (m_actor_coupling) // The character occupies a vehicle or machine { + // Submesh collision - Prevent knockbacks on vehicle exit + if (m_contacting_actor != nullptr) + { + m_contacting_actor = nullptr; + } + // Animation float angle = m_actor_coupling->ar_hydro_dir_wheel_display * -1.0f; // not getSteeringAngle(), but this, as its smoothed float anim_time_pos = ((angle + 1.0f) * 0.5f) * m_driving_anim_length; @@ -398,6 +466,19 @@ void Character::update(float dt) #endif // USE_SOCKETW } +Ogre::Vector3 Character::CalcCabAveragePos(ActorPtr actor, int cab_index) +{ + int tmpv = actor->ar_collcabs[cab_index] * 3; + Vector3 a = actor->ar_nodes[actor->ar_cabs[tmpv + 0]].AbsPosition; + Vector3 b = actor->ar_nodes[actor->ar_cabs[tmpv + 1]].AbsPosition; + Vector3 c = actor->ar_nodes[actor->ar_cabs[tmpv + 2]].AbsPosition; + Vector3 result; + result.x = (a.x + b.x + c.x) / 3; + result.y = (a.y + b.y + c.y) / 3; + result.z = (a.z + b.z + c.z) / 3; + return result; +} + void Character::move(Vector3 offset) { m_character_position += offset; //ASYNCSCENE OLD m_character_scenenode->translate(offset); diff --git a/source/main/gameplay/Character.h b/source/main/gameplay/Character.h index 21c6071367..2fe1784624 100644 --- a/source/main/gameplay/Character.h +++ b/source/main/gameplay/Character.h @@ -91,6 +91,19 @@ class Character Ogre::Timer m_net_timer; unsigned long m_net_last_update_time; GfxCharacter* m_gfx_character; + + // Collision with actor: + Ogre::Vector3 m_vehicle_position; + Ogre::Radian m_vehicle_rotation; + Ogre::Vector3 m_last_vehicle_position; + Ogre::Radian m_last_vehicle_rotation; + bool m_inertia = false; + Ogre::Vector3 m_inertia_position; + Ogre::Radian m_inertia_rotation; + ActorPtr m_contacting_actor; + int m_contacting_cab; + int m_last_contacting_cab; + Ogre::Vector3 CalcCabAveragePos(ActorPtr actor, int cab_index); }; /// @} // addtogroup Character @@ -105,7 +118,7 @@ struct GfxCharacter Ogre::UTFString simbuf_net_username; bool simbuf_is_remote; int simbuf_color_number; - ActorPtr simbuf_actor_coupling; + ActorPtr simbuf_actor_coupling; std::string simbuf_anim_name; float simbuf_anim_time; // Intentionally left empty = forces initial update. }; diff --git a/source/main/gui/panels/GUI_TopMenubar.cpp b/source/main/gui/panels/GUI_TopMenubar.cpp index 82d6128586..cfbc2544f3 100644 --- a/source/main/gui/panels/GUI_TopMenubar.cpp +++ b/source/main/gui/panels/GUI_TopMenubar.cpp @@ -603,6 +603,12 @@ void TopMenubar::Update() DrawGCheckbox(App::mp_pseudo_collisions, _LC("TopMenubar", "Collisions")); DrawGCheckbox(App::mp_hide_net_labels, _LC("TopMenubar", "Hide labels")); } + if (App::mp_state->getEnum() != MpState::CONNECTED) + { + ImGui::Separator(); + ImGui::TextColored(GRAY_HINT_TEXT, "%s", _LC("TopMenubar", "Miscellaneous:")); + DrawGCheckbox(App::sim_character_collisions, _LC("TopMenubar", "Character collisions")); + } ImGui::PopItemWidth(); m_open_menu_hoverbox_min = menu_pos; m_open_menu_hoverbox_max.x = menu_pos.x + ImGui::GetWindowWidth(); diff --git a/source/main/system/CVar.cpp b/source/main/system/CVar.cpp index 2500aebad0..a91d37015a 100644 --- a/source/main/system/CVar.cpp +++ b/source/main/system/CVar.cpp @@ -58,6 +58,7 @@ void Console::cVarSetupBuiltins() App::sim_gearbox_mode = this->cVarCreate("sim_gearbox_mode", "GearboxMode", CVAR_ARCHIVE | CVAR_TYPE_INT); App::sim_soft_reset_mode = this->cVarCreate("sim_soft_reset_mode", "", CVAR_TYPE_BOOL, "false"); App::sim_quickload_dialog = this->cVarCreate("sim_quickload_dialog", "", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "true"); + App::sim_character_collisions = this->cVarCreate("sim_character_collisions", "CharacterCollisions", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false"); App::mp_state = this->cVarCreate("mp_state", "", CVAR_TYPE_INT, "0"/*(int)MpState::DISABLED*/); App::mp_join_on_startup = this->cVarCreate("mp_join_on_startup", "Auto connect", CVAR_ARCHIVE | CVAR_TYPE_BOOL, "false"); From 206b75150c25405819917a0cb7f4cfb9bccf9e3b Mon Sep 17 00:00:00 2001 From: tritonas00 Date: Mon, 29 May 2023 19:32:17 +0300 Subject: [PATCH 2/2] code cleanup --- source/main/gameplay/Character.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/source/main/gameplay/Character.cpp b/source/main/gameplay/Character.cpp index 239faf17dc..d0daf14012 100644 --- a/source/main/gameplay/Character.cpp +++ b/source/main/gameplay/Character.cpp @@ -197,7 +197,7 @@ void Character::update(float dt) position.y += std::min(depth, 0.05f); } - if (m_contacting_actor != nullptr && App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) + if (m_contacting_actor != nullptr) { int motion_cab = -1; if (m_contacting_cab == m_last_contacting_cab) // we're on the same cab - just get it's current pos. @@ -212,26 +212,31 @@ void Character::update(float dt) m_vehicle_position = cab_position; m_vehicle_rotation = Ogre::Radian(m_contacting_actor->getRotation()); - position += (m_vehicle_position - m_last_vehicle_position); - this->setRotation(m_character_rotation + (m_vehicle_rotation - m_last_vehicle_rotation)); + if (App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) + { + position += (m_vehicle_position - m_last_vehicle_position); + this->setRotation(m_character_rotation + (m_vehicle_rotation - m_last_vehicle_rotation)); + } m_inertia = true; m_inertia_position = (m_vehicle_position - m_last_vehicle_position); m_inertia_rotation = (m_vehicle_rotation - m_last_vehicle_rotation); } - else if (m_inertia && App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) + else if (m_inertia) { - position += m_inertia_position; - this->setRotation(m_character_rotation + m_inertia_rotation); + if (App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) + { + position += m_inertia_position; + this->setRotation(m_character_rotation + m_inertia_rotation); + } } } - else if (m_contacting_actor != nullptr && !m_contacting_actor->ar_bounding_box.contains(position) && App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) // we lost contact, reset contacting actor + else if (m_contacting_actor != nullptr && !m_contacting_actor->ar_bounding_box.contains(position)) // we lost contact, reset contacting actor { m_contacting_actor = nullptr; } } - if (App::sim_character_collisions->getBool() && App::mp_state->getEnum() != MpState::CONNECTED) - { + if (m_contacting_cab == m_last_contacting_cab) { m_last_vehicle_position = m_vehicle_position; @@ -243,7 +248,6 @@ void Character::update(float dt) } m_last_contacting_cab = m_contacting_cab; m_last_vehicle_rotation = m_vehicle_rotation; - } } // Obstacle detection