#include "Player.h" #include "Camera.h" #include "LevelMap.h" #include "PlayerInputComponent.h" #include Player::Player(glm::vec3 position, glm::vec3 rotation, std::vector& sectors, GLFWwindow* Window) : GameObject(position, rotation, glm::vec3(1.0f, 1.0f, 1.0f)), PlayerCamera(std::make_unique(Window)), cPlayerInput(std::make_unique(Window, *this)), LevelSectors(sectors), CurrentSector(§ors[0]) { PlayerCamera->SetOffset(glm::vec3(0.0f, 1.0f, 0.0f)); PlayerCamera->MoveCamera(Position); PlayerCamera->Rotation = Rotation; } void Player::Update(float DeltaTime) { GameObject::Update(DeltaTime); cPlayerInput->Update(DeltaTime); if (bJumping) { Position.y += JumpVelocity * DeltaTime; JumpVelocity -= GRAVITY * DeltaTime; if (Position.y <= CurrentSector->floor_height) { JumpVelocity = 0.0f; Position.y = CurrentSector->floor_height; bJumping = false; std::cout << "STOP JUMPING" << std::endl; } } else if (Position.y > CurrentSector->floor_height) { Position.y -= GRAVITY * DeltaTime; } else { Position.y = CurrentSector->floor_height; bJumping = false; } PlayerCamera->MoveCamera(Position); } void Player::Move(EInputDirection Direction, float Delta) { float Velocity = bIsRunning ? RunSpeed : WalkSpeed; Velocity = Velocity * Delta; glm::vec3 newPosition = Position; if (Direction == FORWARD) newPosition += Front * Velocity; if (Direction == BACKWARD) newPosition -= Front * Velocity; if (Direction == LEFT) newPosition -= Right * Velocity; if (Direction == RIGHT) newPosition += Right * Velocity; if (!CrossingWall(newPosition)) { Position = newPosition; } for (Sector& Sector : LevelSectors) { glm::vec2 playerPoint = { Position.x, Position.z }; if (PointInPolygon(playerPoint, Sector.walls)) { /*if (!bJumping) Position.y = Sector.floor_height;*/ if (CurrentSector != &Sector) { std::cout << "Updating CurrentSector (" << (CurrentSector ? CurrentSector->index : -1) << ") to Sector (" << Sector.index << ")" << std::endl; CurrentSector = &Sector; } break; // TO DO: Pretty sure this limits me for sectors that contain sectors, i.e. if I want multiple floor/ceiling heights within a sector it will have to be made of multiple sectors //std::cout << "Player in: " << sector.index << " | Player Y: " << sector.floor_height << std::endl; } } } void Player::Jump() { if (bJumping) return; //std::cout << "JUMP!" << std::endl; bJumping = true; JumpVelocity = JumpForce; } void Player::Sprint() { bIsRunning = true; } void Player::StopSprint() { bIsRunning = false; } void Player::Look(float Yaw, float Pitch, GLboolean bConstrainPitch) { Yaw *= MOUSE_SENSITIVITY; Pitch *= MOUSE_SENSITIVITY; Rotation.y += Yaw; PlayerCamera->RotateCamera(Yaw, Pitch); UpdateVectors(); } glm::mat4 Player::GetViewMatrix() const { return PlayerCamera->GetViewMatrix(); } bool Player::CrossingWall(glm::vec3 NewPosition) { // Convert 3D positions to 2D (XZ plane) for wall collision glm::vec2 CurrentPosition = glm::vec2(Position.x, Position.z); glm::vec2 NextPosition = glm::vec2(NewPosition.x, NewPosition.z); //bool bCrossed = false; // Check each wall in the sector for (const Wall& Wall : CurrentSector->walls) { glm::vec2 WallStart = Wall.a; glm::vec2 WallEnd = Wall.b; // Check if movement line segment intersects with wall line segment if (LineSegmentsIntersect(CurrentPosition, NextPosition, WallStart, WallEnd)) { if (Wall.portal == 0) { return true; // Collision detected } else if (CanFitInNextSector(LevelSectors[Wall.portal-1]))// check if valid portal (floor height < player y and floor and ceiling tall enough for player) { CurrentSector = &LevelSectors[Wall.portal - 1]; return false; } else { return true; } //return true; // Collision detected } } //if (bCrossed) return bCrossed; // breakout early, attempting to move through wall // Check outward facing sectors to ensure not moving into pillars for (const Sector& Sector : LevelSectors) { if (!Sector.outward_facing) { continue; } // Check each wall in the sector for (const Wall& Wall : Sector.walls) { glm::vec2 WallStart = Wall.a; glm::vec2 WallEnd = Wall.b; // Check if movement line segment intersects with wall line segment if (LineSegmentsIntersect(CurrentPosition, NextPosition, WallStart, WallEnd)) { //if (Wall.portal != 0) //{ // return false; // Collision but to another sector //} return true; // Collision detected } } } return false; // No collisions detected } bool Player::LineSegmentsIntersect(const glm::vec2& StartPosition, const glm::vec2& EndPosition, const glm::vec2& WallPointA, const glm::vec2& WallPointB) { // Implementation of the cross product test for line segment intersection auto cross = [](const glm::vec2& a, const glm::vec2& b) { return a.x * b.y - a.y * b.x; }; glm::vec2 PlayerNormal = EndPosition - StartPosition; glm::vec2 WallNormal = WallPointB - WallPointA; glm::vec2 WallToPlayerNormal = WallPointA - StartPosition; float PlayerWallCross = cross(PlayerNormal, WallNormal); // If lines are not parallel if (std::abs(PlayerWallCross) >= std::numeric_limits::epsilon()) { float t = cross(WallToPlayerNormal, PlayerNormal) / PlayerWallCross; float u = cross(WallToPlayerNormal, WallNormal) / PlayerWallCross; // If intersection point is on both segments if ((t >= 0 && t <= 1) && (u >= 0 && u <= 1)) return true; } // If no intersection, check distance from EndPosition to wall segment return PointToLineSegmentDistance(EndPosition, WallPointA, WallPointB) <= 1.0f; } float Player::PointToLineSegmentDistance(const glm::vec2& point, const glm::vec2& segA, const glm::vec2& segB) { glm::vec2 segVec = segB - segA; float segLengthSquared = glm::dot(segVec, segVec); // If segment is actually a point if (segLengthSquared < std::numeric_limits::epsilon()) return glm::distance(point, segA); // Project point onto the segment float t = glm::dot(point - segA, segVec) / segLengthSquared; t = glm::clamp(t, 0.0f, 1.0f); glm::vec2 projection = segA + t * segVec; return glm::distance(point, projection); } // Checking if a point is inside a polygon bool Player::PointInPolygon(glm::vec2 point, std::vector polygon) { int num_vertices = polygon.size(); double x = point.x, y = point.y; bool inside = false; // Store the first point in the polygon and initialize // the second point glm::vec2 p1 = polygon[0].a, p2; // Loop through each edge in the polygon for (int i = 1; i <= num_vertices; i++) { // Get the next point in the polygon p2 = polygon[i % num_vertices].a; // Check if the point is above the minimum y // coordinate of the edge if (y > std::min(p1.y, p2.y)) { // Check if the point is below the maximum y // coordinate of the edge if (y <= std::max(p1.y, p2.y)) { // Check if the point is to the left of the // maximum x coordinate of the edge if (x <= std::max(p1.x, p2.x)) { // Calculate the x-intersection of the // line connecting the point to the edge double x_intersection = (y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; // Check if the point is on the same // line as the edge or to the left of // the x-intersection if (p1.x == p2.x || x <= x_intersection) { // Flip the inside flag inside = !inside; } } } } // Store the current point as the first point for // the next iteration p1 = p2; } // Return the value of the inside flag return inside; } bool Player::CanFitInNextSector(Sector Sector) { std::cout << "Checking sector " << Sector.index << " | Floor height : " << Sector.floor_height << " | Player Y : " << Position.y << " | Sector height : " << Sector.ceiling_height - Sector.floor_height << std::endl; if (Position.y < Sector.floor_height - STEP_HEIGHT) { return false; } if (Sector.ceiling_height - Sector.floor_height < PLAYER_HEIGHT) { return false; } return true; }