Bomberman-OverlordEngine-x64 / BombermanGame / Prefabs / Bomberman / Player.cpp
Player.cpp
Raw
#include "stdafx.h"
#include "Player.h"

#include "Bomb.h"

#include <Materials/Shadow/DiffuseMaterial_Shadow_Skinned.h>


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<DiffuseMaterial_Shadow_Skinned>();
	pFaceMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bom_face01.png");
	m_NormalFaceMatId = pFaceMaterial->GetMaterialId();

	auto pBodyMaterial = MaterialManager::Get()->CreateMaterial<DiffuseMaterial_Shadow_Skinned>();
	pBodyMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bomberman00.png");

	auto pHappyFaceMaterial = MaterialManager::Get()->CreateMaterial<DiffuseMaterial_Shadow_Skinned>();
	pHappyFaceMaterial->SetDiffuseTexture(L"Textures/Bomberman/Player/bom_face02.png");
	m_HappyFaceMatId = pHappyFaceMaterial->GetMaterialId();

	auto pSadFaceMaterial = MaterialManager::Get()->CreateMaterial<DiffuseMaterial_Shadow_Skinned>();
	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;
		}
	}
}