#include "stdafx.h" #include "Player.h" #include "Bomb.h" #include Player::Player(const PlayerDesc& playerDesc, float tileSize) : m_PlayerDesc{ playerDesc }, m_MoveAcceleration(playerDesc.maxMoveSpeed / playerDesc.moveAccelerationTime), m_State{ Player::State::IDLE }, m_TileSize{ tileSize } {} void Player::Kill() { m_State = State::DEAD; m_pModel->SetMaterial(m_SadFaceMatId, 0); m_pAnimator->SetAnimation(6); m_pAnimator->Play(); GetTransform()->Rotate(0, 0, 0); } void Player::Win() { m_State = State::WIN; m_pModel->SetMaterial(m_HappyFaceMatId, 0); m_pAnimator->SetAnimation(7); m_pAnimator->Play(); GetTransform()->Rotate(0, 0, 0); } void Player::Initialize(const SceneContext& /*sceneContext*/) { //Controller m_pControllerComponent = AddComponent(new ControllerComponent(m_PlayerDesc.controller)); //Set Collision group for player so that we can later disable it for bombs that spawn inside the player m_pControllerComponent->SetCollisionGroup(CollisionGroup::Player0); //m_pControllerComponent->SetCollisionIgnoreGroup(CollisionGroup::Bombs); //Player Materials auto pFaceMaterial = MaterialManager::Get()->CreateMaterial(); pFaceMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bom_face01.png"); m_NormalFaceMatId = pFaceMaterial->GetMaterialId(); auto pBodyMaterial = MaterialManager::Get()->CreateMaterial(); pBodyMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bomberman00.png"); auto pHappyFaceMaterial = MaterialManager::Get()->CreateMaterial(); pHappyFaceMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bom_face02.png"); m_HappyFaceMatId = pHappyFaceMaterial->GetMaterialId(); auto pSadFaceMaterial = MaterialManager::Get()->CreateMaterial(); pSadFaceMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bom_face04.png"); m_SadFaceMatId = pSadFaceMaterial->GetMaterialId(); m_pModel = new ModelComponent(L"Meshes/Bomberman/Player/player01_skinned.ovm"); m_pModel->SetMaterial(pBodyMaterial); m_pModel->SetMaterial(pFaceMaterial, 0); GameObject* pModelContainer = new GameObject(); AddChild(pModelContainer); pModelContainer->AddComponent(m_pModel); pModelContainer->GetTransform()->Translate(0, -m_PlayerDesc.controller.height /2 - m_PlayerDesc.controller.radius, 0); m_pAnimator = m_pModel->GetAnimator(); m_pAnimator->SetUseBlending(true); m_pAnimator->SetAnimation(1); //pAnimator->SetAnimation(2); m_pAnimator->Play(); } void Player::DropBomb(const SceneContext& sceneContext) { XMFLOAT3 snappedSpawnPos{ m_pControllerComponent->GetFootPosition() }; // snap bomb to tiles snappedSpawnPos.x = std::round(snappedSpawnPos.x / m_TileSize) * m_TileSize; snappedSpawnPos.z = std::round(snappedSpawnPos.z / m_TileSize) * m_TileSize; GameObject* pBomb = new Bomb(this, snappedSpawnPos, sceneContext.pCamera->GetTransform()->GetRotation(), m_Power); GetScene()->AddChild(pBomb); //sfx FMOD::Sound* sound; auto fmodSystem = SoundManager::Get()->GetSystem(); fmodSystem->createSound("Resources/Sounds/place_bomb.wav", FMOD_DEFAULT, 0, &sound); fmodSystem->playSound(sound, 0, false, 0); } void Player::Update(const SceneContext& sceneContext) { //constexpr float epsilon{ 0.01f }; //Constant that can be used to compare if a float is near zero //*************** //HANDLE INPUT const float elapsedSec = sceneContext.pGameTime->GetElapsed(); bool isInput{ false }; if (m_State != State::DEAD && m_State != State::WIN) { //Drop Bomb if (sceneContext.pInput->IsActionTriggered(m_PlayerDesc.actionId_DropBomb)) { DropBomb(sceneContext); } //## Input Gathering (move) XMFLOAT2 move{ 0.f, 0.f }; //move.y will be applied to world.z movement //move.y should contain a 1 (Forward) or -1 (Backward) based on the active input (check corresponding actionId in m_PlayerDesc) if (sceneContext.pInput->IsActionTriggered(m_PlayerDesc.actionId_MoveUp)) { move.y = 1; } else if (sceneContext.pInput->IsActionTriggered(m_PlayerDesc.actionId_MoveDown)) { move.y = -1; } //Optional: if move.y is near zero (abs(move.y) < epsilon), you could use the Left ThumbStickPosition.y for movement if (abs(move.y) < FLT_EPSILON) { move.y = InputManager::GetThumbstickPosition().y; } //move.x should contain a 1 (Right) or -1 (Left) based on the active input (check corresponding actionId in m_PlayerDesc) if (sceneContext.pInput->IsActionTriggered(m_PlayerDesc.actionId_MoveRight)) { move.x = 1; } else if (sceneContext.pInput->IsActionTriggered(m_PlayerDesc.actionId_MoveLeft)) { move.x = -1; } //Optional: if move.x is near zero (abs(move.x) < epsilon), you could use the Left ThumbStickPosition.x for movement if (abs(move.x) < FLT_EPSILON) { move.x = InputManager::GetThumbstickPosition().x; } //************************ //GATHERING TRANSFORM INFO XMVECTOR up{ 0, 0, 1 }; XMVECTOR right{ 1, 0, 0 }; //******** //MOVEMENT //## Horizontal Velocity (Forward/Backward/Right/Left) //Calculate the current move acceleration for this frame (m_MoveAcceleration * ElapsedTime) float deltaMoveAcceleration = m_MoveAcceleration * elapsedSec; //If the character is moving (= input is pressed) if (abs(move.y) > FLT_EPSILON || abs(move.x) > FLT_EPSILON) { isInput = true; // handle rotation based on input //float moveAngleY = atan2f(-move.x, -move.y); //GetTransform()->Rotate(0, moveAngleY, 0, false); m_TargetRotationY = atan2f(-move.x, -move.y); // Calculate change in rotation float rotationChange = m_TargetRotationY - m_CurrentRotationY; // If the rotation change is larger than PI, we adjust it to rotate in the opposite direction // This prevents unnecessary full rotations if (rotationChange > XM_PI) { rotationChange -= 2 * XM_PI; } else if (rotationChange < -XM_PI) { rotationChange += 2 * XM_PI; } // Calculate how much we should rotate this frame float rotationThisFrame = rotationChange * elapsedSec / m_RotationLerpTime; // adjust over 0.1 seconds // Add the calculated rotation to the current rotation m_CurrentRotationY += rotationThisFrame; // Ensure the current rotation remains within the range [-PI, PI] if (m_CurrentRotationY > XM_PI) { m_CurrentRotationY -= 2 * XM_PI; } else if (m_CurrentRotationY < -XM_PI) { m_CurrentRotationY += 2 * XM_PI; } // Apply the rotation GetTransform()->Rotate(0, m_CurrentRotationY, 0, false); //Calculate & Store the current direction (m_CurrentDirection) >> based on the forward/right vectors and the pressed input XMVECTOR currentDir = XMVectorZero(); up = XMVectorScale(up, move.y); right = XMVectorScale(right, move.x); currentDir = XMVectorAdd(currentDir, up); currentDir = XMVectorAdd(currentDir, right); XMStoreFloat3(&m_CurrentDirection, currentDir); //Increase the current MoveSpeed with the current Acceleration (m_MoveSpeed) m_MoveSpeed += deltaMoveAcceleration; //Make sure the current MoveSpeed stays below the maximum MoveSpeed (PlayerDesc::maxMoveSpeed) if (m_MoveSpeed > m_PlayerDesc.maxMoveSpeed) m_MoveSpeed = m_PlayerDesc.maxMoveSpeed; } else //Else (character is not moving, or stopped moving) { //Decrease the current MoveSpeed with the current Acceleration (m_MoveSpeed) m_MoveSpeed -= deltaMoveAcceleration; //Make sure the current MoveSpeed doesn't get smaller than zero if (m_MoveSpeed < 0) m_MoveSpeed = 0; } } float curRotationDegrees{ m_CurrentRotationY * 180 / XM_PI }; const float lookUpAngle{ 55.f }; switch (m_State) { default: break; case State::IDLE: if (isInput) { if (abs(curRotationDegrees) < lookUpAngle) { m_pAnimator->SetAnimation(3); m_pAnimator->Play(); m_State = State::RUNNING_LOOK_UP; } else { m_pAnimator->SetAnimation(4); m_pAnimator->Play(); m_State = State::RUNNING; } } break; case State::IDLE_LOOK_UP: if (isInput) { if (abs(curRotationDegrees) < lookUpAngle) { m_pAnimator->SetAnimation(3); m_pAnimator->Play(); m_State = State::RUNNING_LOOK_UP; } else { m_pAnimator->SetAnimation(4); m_pAnimator->Play(); m_State = State::RUNNING; } } break; case State::RUNNING: if (isInput) { if (abs(curRotationDegrees) < lookUpAngle) { m_pAnimator->SetAnimation(3); m_pAnimator->Play(); m_State = State::RUNNING_LOOK_UP; } } else { m_pAnimator->SetAnimation(2); m_pAnimator->Play(); m_State = State::IDLE; } break; case State::RUNNING_LOOK_UP: if (isInput) { if (abs(curRotationDegrees) > lookUpAngle) { m_pAnimator->SetAnimation(4); m_pAnimator->Play(); m_State = State::RUNNING; } } else { m_pAnimator->SetAnimation(1); m_pAnimator->Play(); m_State = State::IDLE_LOOK_UP; } break; } XMVECTOR horizontalVelocity = XMVectorScale(XMLoadFloat3(&m_CurrentDirection), m_MoveSpeed); XMFLOAT3 result; XMStoreFloat3(&result, horizontalVelocity); m_TotalVelocity.x = result.x; m_TotalVelocity.z = result.z; if (!m_pControllerComponent->GetCollisionFlags().isSet(PxControllerCollisionFlag::eCOLLISION_DOWN) /* || raycast for more responsiveness*/) { //Decrease the y component of m_TotalVelocity with a fraction (ElapsedTime) of the Fall Acceleration (m_FallAcceleration) m_TotalVelocity.y -= 200.f * elapsedSec; //Make sure that the minimum speed stays above -CharacterDesc::maxFallSpeed (negative!) if (m_TotalVelocity.y < -1000.f) m_TotalVelocity.y = -1000.f; } else //Else (=Character is grounded, no input pressed) { //m_TotalVelocity.y is zero m_TotalVelocity.y = 0; } //************ //DISPLACEMENT XMVECTOR deltaHorizontalVelocity = XMVectorScale(XMLoadFloat3(&m_TotalVelocity), elapsedSec); XMFLOAT3 displacement; XMStoreFloat3(&displacement, deltaHorizontalVelocity); PxFilterData filterData; filterData.word0 = PxU32(CollisionGroup::Player0 | CollisionGroup::Bombs); filterData.word1 = PxU32(CollisionGroup::SpawnedBombs); if (m_State != State::DEAD && m_State != State::WIN) m_pControllerComponent->Move(displacement, filterData); } void Player::DrawImGui() { if (ImGui::CollapsingHeader("Player")) { ImGui::Text(std::format("Move Speed: {:0.1f} m/s", m_MoveSpeed).c_str()); ImGui::Text(std::format("Move Acceleration: {:0.1f} m/s2", m_MoveAcceleration).c_str()); ImGui::Dummy({ 0.f,5.f }); if (ImGui::DragFloat("Max Move Speed (m/s)", &m_PlayerDesc.maxMoveSpeed, 0.1f, 0.f, 0.f, "%.1f") || ImGui::DragFloat("Move Acceleration Time (s)", &m_PlayerDesc.moveAccelerationTime, 0.1f, 0.f, 0.f, "%.1f")) { m_MoveAcceleration = m_PlayerDesc.maxMoveSpeed / m_PlayerDesc.moveAccelerationTime; } } }