add collision check when jumping forward (if colisiion then fall down): enum class State { IDLE, WALK, JUMP, FALL, LAND }; class Player { State m_state; float m_velocityY; // Vertical velocity float m_fallVelocity; // Fall speed float m_jumpHeight; float m_gravity; float m_jumpForwardSpeed; // Parameters for state transitions (optional, can be adjusted) const float m_groundThreshold = 0.1f; const float m_rayLength = 3.0f; public: Player(dtCrowd* crowd, World* world); ~Player(); void Update(float deltaTime); void SetRotation(const glm::quat& rotation); glm::quat GetRotation() const; void SetPosition(const glm::vec3& position); glm::vec3 GetPosition() const; void SetLookAt(const glm::vec3& target); glm::vec3 GetLookAt() const; void MoveTo(const glm::vec3& destination); void SetServer(uint16_t serverId); uint16_t GetServer() const; void SetSpeed(const glm::vec3& speed); glm::vec3 GetSpeed() const; bool CalculatePathTo(const glm::vec3& destination); std::pair<glm::vec3, glm::vec3> GetBoundingBox() const; void Jump(); private: void HandleIdle(float deltaTime); void HandleWalk(float deltaTime); void HandleJump(float deltaTime); void HandleFall(float deltaTime); void HandleLand(float deltaTime); void TransitionToState(State newState); dtCrowd* m_crowd; glm::quat m_rotation; glm::vec3 m_lookAt; glm::vec3 m_position; uint16_t m_serverId; glm::vec3 m_moveSpeed; int m_agentIndex; bool m_ProcessMoveToPoint; World* m_world; }; // Implementation inline Player::Player(dtCrowd* crowd, World* world) : m_world(world), m_crowd(crowd), m_rotation(glm::quat(1.0f, 0.0f, 0.0f, 0.0f)), m_lookAt(0.0f), m_serverId(0), m_moveSpeed(0.0f), m_agentIndex(-1), m_ProcessMoveToPoint(false), m_state(State::IDLE), m_velocityY(0.0f), m_fallVelocity(0.0f), m_jumpHeight(1.0f), m_gravity(9.8f), m_jumpForwardSpeed(4.0f) { if (!crowd) return; glm::vec3 dimensions(0.5f, 1.8f, 1.0f); // length, height, width dtCrowdAgentParams agentParams; memset(&agentParams, 0, sizeof(agentParams)); agentParams.radius = std::max(dimensions.x, dimensions.z) * 0.5f; // Slightly smaller radius agentParams.height = dimensions.y; agentParams.maxSpeed = 2.5f; // Slightly slower agentParams.maxAcceleration = 8.0f; // Lower acceleration agentParams.collisionQueryRange = agentParams.radius * 2.0f; agentParams.pathOptimizationRange = agentParams.radius * 20.0f; agentParams.updateFlags = DT_CROWD_ANTICIPATE_TURNS | DT_CROWD_OPTIMIZE_VIS | DT_CROWD_OPTIMIZE_TOPO | DT_CROWD_SEPARATION | DT_CROWD_OBSTACLE_AVOIDANCE; agentParams.obstacleAvoidanceType = 0; agentParams.separationWeight = 10.0f; // High value for active separation float pos[3] = { 0.0f, 0.0f, 0.0f }; m_agentIndex = m_crowd->addAgent(pos, &agentParams); if (m_agentIndex == -1) { std::cout << "Player::Player: m_agentIndex == -1! " << std::endl; } } inline Player::~Player() { if (m_agentIndex != -1 && m_crowd) { m_crowd->removeAgent(m_agentIndex); } } inline void Player::Update(float deltaTime) { switch (m_state) { case State::IDLE: HandleIdle(deltaTime); break; case State::WALK: HandleWalk(deltaTime); break; case State::JUMP: HandleJump(deltaTime); break; case State::FALL: HandleFall(deltaTime); break; case State::LAND: HandleLand(deltaTime); break; default: break; } } inline void Player::HandleIdle(float deltaTime) { // Implement idle behavior if needed // For example, check if movement input is received to transition to WALK } inline void Player::HandleWalk(float deltaTime) { // Implement walking behavior // For example, move the player based on input and update position } inline void Player::HandleJump(float deltaTime) { // Apply gravity to vertical movement m_velocityY -= m_gravity * deltaTime; glm::vec3 newPosition = GetPosition(); newPosition.y += m_velocityY * deltaTime; // Apply forward movement during jump glm::quat rotation = GetRotation(); glm::vec3 forward = glm::normalize(rotation * glm::vec3(0, 0, 1)); // Forward direction newPosition += forward * m_jumpForwardSpeed * deltaTime; SetPosition(newPosition); // Check if upward movement has stopped if (m_velocityY <= 0.0f) { TransitionToState(State::FALL); } } inline void Player::HandleFall(float deltaTime) { // Apply gravity to vertical movement float fallGravityScale = 2.0f; m_fallVelocity = std::min(m_fallVelocity + (m_gravity * fallGravityScale * deltaTime), 30.0f); // Limit max fall speed glm::vec3 newPosition = GetPosition(); newPosition.y -= m_fallVelocity * deltaTime; // Apply forward movement during fall glm::quat rotation = GetRotation(); glm::vec3 forward = glm::normalize(rotation * glm::vec3(0, 0, 1)); // Forward direction newPosition += forward * m_jumpForwardSpeed * deltaTime; SetPosition(newPosition); // Raycast to check for ground collision glm::vec3 currentPosition = GetPosition(); glm::vec3 rayStart = currentPosition + glm::vec3(0, 0.1f, 0); glm::vec3 rayEnd = rayStart - glm::vec3(0, m_rayLength, 0); auto hitResult = m_world->RaycastWithPlayerCollision(rayStart, rayEnd, *this); if (hitResult) { glm::vec3 hitPosition = *hitResult; float groundY = hitPosition.y; if (newPosition.y <= groundY + m_groundThreshold) { newPosition.y = groundY + m_groundThreshold; // Keep slightly above ground SetPosition(newPosition); TransitionToState(State::LAND); } } } inline void Player::HandleLand(float deltaTime) { // Implement landing behavior // For example, reset fall velocity and transition to IDLE or WALK m_fallVelocity = 0.0f; TransitionToState(State::IDLE); } inline void Player::TransitionToState(State newState) { if (m_state == newState) return; // Exit logic for current state switch (m_state) { case State::JUMP: // Cleanup after jump if necessary break; case State::FALL: // Cleanup after fall if necessary break; case State::LAND: // Cleanup after landing if necessary break; default: break; } // Enter logic for new state switch (newState) { case State::JUMP: m_velocityY = sqrt(2.0f * m_gravity * m_jumpHeight); break; case State::FALL: // Initialize fall parameters if needed break; case State::LAND: // Initialize landing parameters if needed break; default: break; } m_state = newState; } inline void Player::Jump() { if (m_state == State::IDLE || m_state == State::WALK) { TransitionToState(State::JUMP); } } inline void Player::SetRotation(const glm::quat& rotation) { m_rotation = rotation; // Update look-at based on new rotation glm::vec3 currentPos = GetPosition(); m_lookAt = currentPos + glm::rotate(m_rotation, glm::vec3(0.0f, 0.0f, -1.0f)); } inline glm::quat Player::GetRotation() const { return m_rotation; } inline void Player::SetPosition(const glm::vec3& newPosition) { m_position = newPosition; } inline glm::vec3 Player::GetPosition() const { return m_position; } inline void Player::SetLookAt(const glm::vec3& target) { m_lookAt = target; glm::vec3 currentPos = GetPosition(); // Calculate direction on XZ plane glm::vec3 direction = target - currentPos; direction.y = 0.0f; // Ignore Y component if (glm::length(direction) > 0.0f) { direction = glm::normalize(direction); // Calculate the angle between the forward vector and the direction float angle = std::atan2(direction.x, direction.z); // Create a quaternion for rotation around Y-axis m_rotation = glm::angleAxis(angle, glm::vec3(0.0f, 1.0f, 0.0f)); } } inline glm::vec3 Player::GetLookAt() const { return m_lookAt; } inline void Player::MoveTo(const glm::vec3& destination) { if (m_agentIndex == -1 || !m_crowd) return; float pos[3] = { destination.x, destination.y, destination.z }; m_crowd->requestMoveTarget(m_agentIndex, 0, pos); } inline void Player::SetServer(uint16_t serverId) { m_serverId = serverId; } inline uint16_t Player::GetServer() const { return m_serverId; } inline void Player::SetSpeed(const glm::vec3& speed) { m_moveSpeed = speed; if (glm::length(speed) > 0.0f && (m_state == State::IDLE)) { TransitionToState(State::WALK); } else if (glm::length(speed) == 0.0f && (m_state == State::WALK)) { TransitionToState(State::IDLE); } } inline glm::vec3 Player::GetSpeed() const { return m_moveSpeed; } inline bool Player::CalculatePathTo(const glm::vec3& destination) { if (m_agentIndex == -1 || !m_crowd) return false; float pos[3] = { destination.x, destination.y, destination.z }; return m_crowd->requestMoveTarget(m_agentIndex, 0, pos); } inline std::pair<glm::vec3, glm::vec3> Player::GetBoundingBox() const { glm::vec3 dimensions(0.5f, 1.8f, 1.0f); // length, height, width glm::vec3 pos = GetPosition(); glm::quat rotation = GetRotation(); // Create a rotation matrix from the quaternion glm::mat4 rotationMatrix = glm::mat4_cast(rotation); // Calculate the new bounding box after rotation glm::vec3 corners[8]; corners[0] = glm::vec3(-dimensions.x / 2, -dimensions.y / 2, -dimensions.z / 2); corners[1] = glm::vec3(dimensions.x / 2, -dimensions.y / 2, -dimensions.z / 2); corners[2] = glm::vec3(-dimensions.x / 2, dimensions.y / 2, -dimensions.z / 2); corners[3] = glm::vec3(dimensions.x / 2, dimensions.y / 2, -dimensions.z / 2); corners[4] = glm::vec3(-dimensions.x / 2, -dimensions.y / 2, dimensions.z / 2); corners[5] = glm::vec3(dimensions.x / 2, -dimensions.y / 2, dimensions.z / 2); corners[6] = glm::vec3(-dimensions.x / 2, dimensions.y / 2, dimensions.z / 2); corners[7] = glm::vec3(dimensions.x / 2, dimensions.y / 2, dimensions.z / 2); glm::vec3 min = pos, max = pos; for (int i = 0; i < 8; ++i) { glm::vec3 rotatedCorner = pos + glm::vec3(rotationMatrix * glm::vec4(corners[i], 1.0f)); min = glm::min(min, rotatedCorner); max = glm::max(max, rotatedCorner); } return std::make_pair(min, max); }