top of page

PlayerController: Throwing

Gameplay: Throwing Helpful Creatures

The code behind the main gameplay mechanics for the DigiPen Team Cloud Cat's game: Return to the Skyway.

The throwing mechanic of the game relied heavily on the physics system composed by a fellow team member and the helpful creature behavior class that I implemented. Since the player was limited to throwing only the helpful creatures, I decided to separate the implementation code into a function triggered through collision with a helpful creature over being initiated by a player key trigger. This way, the player could only grab and throw when they were colliding with a helpful creature.


As the main gameplay mechanic, this code was foundational in creating the game itself. As such, a lot of the internal variables were opened to the designer so they could adjust and refine as may aspect of the mechanic as possible. Aspects such as how strong the player could throw, how long it would take the player to charge their throw, and the actual key triggers were all left to the designers to determine and modify through the custom editor in engine.


This code mainly deals in the computer's view of the grab and throw but works closely with the associtaed player behavior to establish a correlation between the animations and douns through the state-based behavior system of the engine.


This code went through a lot of testing with the multiple playtest sessions that the designers held since there was little to no backend testing we could do beyond "does the object get propelled along the correct vector". Thankfully, since the code was abstracted and reliant only on the variables that the designers had access to, the back-end (code heavy) section of the mechanic didn't have to go through a lot of iteration.

/*****************************************************************//** * \file PlayerController.cpp (snippet) * \author Jennifer Assid * \param Return to the Skyway * \param GAM200AF21 * * \date 8 Oct 2021 * * \param Copyright � 2021 DigiPen (USA) Corporation. *********************************************************************/ namespace CloudEngine { namespace Player { /** * Handles all collisions pertaining to the player. * * \param collision Collision information */ void PlayerCollisionHandler(const Physics::Collision& collision) { // If the player is colliding with a creature if ((collision.aCollider_.GetColliderProperties() == Physics::ColliderProperties::cpPlayer && collision.bCollider_.GetColliderProperties() == Physics::ColliderProperties::cpCreature) || (collision.aCollider_.GetColliderProperties() == Physics::ColliderProperties::cpCreature && collision.bCollider_.GetColliderProperties() == Physics::ColliderProperties::cpPlayer)) { Physics::Collider* playerCollider; Physics::Collider* creatureCollider; // Determine which collider is which and assign them to their local variants if (collision.aCollider_.GetColliderProperties() == Physics::ColliderProperties::cpPlayer) { playerCollider = &collision.aCollider_; creatureCollider = &collision.bCollider_; } else { playerCollider = &collision.bCollider_; creatureCollider = &collision.aCollider_; } // If the player controller is not active - return out of the function if (playerCollider->Parent()->GetComponent<PlayerController>()->GetActive() == false) return; // If the current camera is not the player camera - return out of the function (disable's player functionality while editor camera is active) if (Graphics::Camera::GetActiveCamera() != playerCollider->Parent()->GetComponent<Graphics::Camera>()) return; Components::BehaviorHelpful* creature_behavior = NULL; // Iterate through the components of the creature and retrieve the sprecific helpful behavior component (this is to support all helpful creatures) std::vector<Components::Component*> components = creatureCollider->Parent()->GetComponents(); for (Components::Component* component : components) { creature_behavior = dynamic_cast<Components::BehaviorHelpful*>(component); if (creature_behavior) break; } // Pointer check if (!creature_behavior) return; // If the creature is not grabbed or thrown - tell the character to enter the "scare" behavior if (!creature_behavior->GetIsGrabbed() && !creature_behavior->GetIsThrown()) creature_behavior->SetNextState(Components::Behavior_Helpful_Enum::b_Helpful_Scare); // Get the local variables established Components::BehaviorPlayer* behavior_player = playerCollider->Parent()->GetComponent<Components::BehaviorPlayer>(); if (!behavior_player) return; // Pointer Check Player::PlayerController* player_controller = playerCollider->Parent()->GetComponent<Player::PlayerController>(); if (!player_controller) return; // Pointer Check Components::Transform* player_transform = playerCollider->Parent()->GetComponent<Components::Transform>(); // If the player goes to throw the creature... if (InputController::rightMouseButtonPressed()) { // If the player is currently holding something if (player_controller->GetHasGrabbed()) { // Set the necessary variables player_controller->SetIsThrowing(false); player_controller->SetIsHolding(true); // If the hold timer has been reached if (player_controller->GetHoldCurrTimer() > player_controller->GetHoldTimer()) { // Set the current value to the max player_controller->SetHoldCurrTimer(player_controller->GetHoldTimer()); } else { // Continue the timer player_controller->SetHoldCurrTimer(player_controller->GetHoldCurrTimer() + 0.5f * Time::DeltaTime()); } // Get the position of the mouse in world space glm::vec2 mousePos = Graphics::Camera::GetActiveCamera()->GetScreenToWorldMatrix() * glm::vec4(InputController::getMousePos(), 0.0f, 1.0f); // Determine the facing of the character based on the position of the mouse if (mousePos.x > player_transform->GetTranslation()->x) { player_controller->SetFacingRight(true); } else { player_controller->SetFacingRight(false); } } } // If the right mouse is released... if (InputController::rightMouseButtonUp()) { // Stio the charge sound FMODCore::Audio* instance = FMODCore::audioInstance_->getAudioInstance(); while (instance->eventIsPlaying("event:/SFX/Character/Throw_Charge")) { instance->StopSound("event:/SFX/Character/Throw_Charge"); } // And the player is holding something... if (player_controller->GetHasGrabbed() && creature_behavior->GetIsGrabbed()) { // Pointer check if (!playerCollider->Parent()->GetComponent<Graphics::Camera>()) return; // Set the necessary variables player_controller->SetIsHolding(false); player_controller->SetIsThrowing(true); player_controller->SetAnimalPtr(NULL); player_controller->SetAnimalHeld(Components::Creature_Helpful_Type::c_Invalid); // Move the creature to the player's head (this will help the visual feedback for the throw later on) Components::Transform* creature_trans = creatureCollider->Parent()->GetComponent<Components::Transform>(); creature_trans->SetTranslation(glm::vec3(creature_trans->GetTranslation()->x, creature_trans->GetTranslation()->y + creature_trans->GetScale()->y + 1.0f, 0.0f)); // Get the vector from the creature to the mouse glm::vec2 mousePos = Graphics::Camera::GetActiveCamera()->GetScreenToWorldMatrix() * glm::vec4(InputController::getMousePos(), 0.0f, 1.0f); glm::vec2 creatureToMouse = mousePos - glm::vec2(creature_trans->GetTranslation()->x, creature_trans->GetTranslation()->y); // Determine the strength of the throw vector (based on the player's hold timer and the determined max throw force) float tf = playerCollider->Parent()->GetComponent<PlayerController>()->GetThrowForce() * (player_controller->GetHoldCurrTimer() / player_controller->GetHoldTimer()); // Normalize the vector and scale it according to the calculated length creatureToMouse = glm::normalize(creatureToMouse); creatureToMouse *= tf; // Remove the creature from the player's hands (set the following values are necessary) player_controller->SetHasGrabbed(false); creature_behavior->SetIsGrabbed(false); creature_behavior->SetPlayer(NULL); // Tell the creature to enter the "throw" behavior creature_behavior->SetNextState(Components::Behavior_Helpful_Enum::b_Helpful_Thrown); // Exert the throw force on the creature to propel them Physics::RigidBody* cRB = creatureCollider->Parent()->GetComponent<Physics::RigidBody>(); cRB->SetVelocity(creatureToMouse); cRB->SetGrounded(false); // Reset the timer player_controller->SetHoldCurrTimer(0.0f); } } // If the player goes to grab the creature... if (InputController::leftMouseButtonTriggered()) { // Deactivate the player controllers so the animation can play through player_controller->SetActive(false); // If the player is already grabbing a creature if (player_controller->GetHasGrabbed()) { // Remove the creature from the player's hands and drop it. player_controller->SetHasGrabbed(false); player_controller->SetAnimalPtr(NULL); player_controller->SetIsHolding(false); player_controller->SetIsThrowing(false); player_controller->SetAnimalHeld(Components::Creature_Helpful_Type::c_Invalid); player_controller->SetAnimalPtr(NULL); player_controller->SetHoldCurrTimer(0.0f); behavior_player->SetNextState(Components::Behavior_Player_Enum::b_Player_Drop); // This handles the animation / activating the controls creature_behavior->SetIsGrabbed(false); creature_behavior->SetPlayer(NULL); // Set creature's next state to idle creature_behavior->Parent()->GetComponent<Components::Transform>()->SetTranslation(*player_transform->GetTranslation()); creature_behavior->SetNextState(Components::Behavior_Helpful_Enum::b_Helpful_Idle); } else { // Put the creature in the player's hands player_controller->SetHasGrabbed(true); player_controller->SetAnimalHeld(creature_behavior->GetType()); player_controller->SetAnimalPtr(creature_behavior); // Retrieve the BehaviorHelpful component from the creature (this is to support all helpful creatures) Components::BehaviorHelpful* creature_behavior = NULL; std::vector<Components::Component*> components = creatureCollider->Parent()->GetComponents(); for (Components::Component* component : components) { creature_behavior = dynamic_cast<Components::BehaviorHelpful*>(component); if (creature_behavior) break; } // Pointer check if (!creature_behavior) return; // Set its necessary values creature_behavior->SetIsGrabbed(true); creature_behavior->SetPlayer(playerCollider->Parent()); // Set the plater behavior accordingly behavior_player->SetNextState(Components::Behavior_Player_Enum::b_Player_Grab); } } } } } }


 

bottom of page