BI-PGA Programování grafických aplikací
Jdi na navigaci předmětu

Systém pro interakce s objekty - rozšíření o obouruční interakce

  • Toto vychází z předešlého příkladu (odkaz?), kde se vytvořil systém pro interakce s objekty. Zpracování vstupů, definice interagovatelných objektů, nalezení a sebrání objektů, jejich držení a fyzikální chování, a reprezentace rukou.

Požadavky:

  • seznámení s předchozím příkladem: Vlastní systém pro interakci s objekty a jejich fyzikální chování

doublehanded

  • V předchozím příkladu jsme ale pořád nevyřešili případ kdy chce uživatel změnit držení objektu z jedné ruky do druhé nebo případně co s objekty, které bysme chtěli držet pomocí obou dvou rukou.
  • Několik částí bude třeba poupravit a některé části bude třeba rozšířit o novou logiku rozdělenou na jednotlivé typy a stavy držení objektu.
  • Určitě musíme přidat parametry popisující naše interagovatelné objekty. Typ držení, který tento objekt používá (mělo by smysl objekt držet obouručně nebo to logicky dává smysl jen jednoručně?), dále další pozice, kde se případně chytí druhá ruka při obouručním držení. (u velké části objektů/nástrojů v reálném světě dává největší smysl jen jedna kombinace držených pozic. Dynamické vybírání pozic, kde si vybereme přesně kde držený objekt uchopíme by vyžadovalo množství nové neobtížné, ale rozsáhlé logiky)
  • Takže nejprve rozšíření InteractibleItem - skript, který definuje vlastnosti a chování držených objektů.
    • Přidáme atribut ukazující zda lze objekt vůbec uchopit a držet více rukami
    • A atributy pro místa, kde objekt uchopíme
public class InteractableItem : MonoBehaviour
{
    public bool doubleHanded = false; // ***
    public Transform primaryGripPosition; // ***
    public Transform secondaryGripPosition; // ***

    public Vector3 itemHandAlignement = Vector3.zero;

    public bool offsetGrab = false;
}
  • Dále rozšíření ItemPickupManager skriptu o atributy, které nám definují nové vlastnosti a pomocí kterých můžeme větvit logiku sebrání, zahození a držení předmětů
    • Pro držení a interagování s objekty pomocí obou rukou budeme muset chování manažerů koordinovat, proto potřebujeme referenci na skript druhé ruky
    • Dále ukazatele stavu obouručního držení - tedy ukazatel toho jestli momentálně držíme objekt pomocí obou rukou a jestli v tomto obouručním držení figuruje tato ruka jako primární ruka
public class ItemPickupManager : MonoBehaviour
{
	--//--

	public ItemPickupManager otherHandManager; // ***

	bool isDoubleHanding = false; // ***
    bool doubleHandingIsPrimaryHand = false; // ***
}
  • Následně rozvětvení logiky sebrání předmětu. Může nastat pár případů:
    • Sebraný objekt již držíme v druhé ruce a umožňuje obouruční držení - v tom případě nastavíme příslušné stavové atributy sobě i druhé ruce a získáme od ní reference na potřebné objekty - rigidbody, zápěstí a joint
    • V dalším případě sebraný objekt také již držíme, ale neumožňuje obouruční držení - takže chceme objekt v druhé ruce pustit a regulérně ho sebrat v této ruce
    • Poslední případ je regulérní sebrání objektu - tedy sebraný objekt v druhé ruce nedržíme
public void Grab(GameObject grabbedItem)
{
    heldItem = grabbedItem;
    heldItemComponent = grabbedItem.GetComponent<InteractableItem>();

    bool itemAlreadyHeld = isOtherHandHoldingItem(grabbedItem);
    if (itemAlreadyHeld && HeldItemComponent.doubleHanded) // double handed secondary hand
    {
        isDoubleHanding = true;
        otherHandManager.isDoubleHanding = true;
        otherHandManager.doubleHandingIsPrimaryHand = true;
        doubleHandingIsPrimaryHand = false;

        heldItemGripPosition = heldItemComponent.secondaryGripPosition;

        heldItemWrist = otherHandManager.heldItemWrist;
        heldItemWristRigidbody = otherHandManager.heldItemWristRigidbody;
        wristJoint = otherHandManager.wristJoint;
    }
    else // regular grab
    {
        isDoubleHanding = false;
	if(itemAlreadyHeld) // hand swap
	{
	    otherHandManager.Drop();
	}
        heldItemGripPos = heldItemComponent.primaryGripPosition;

        // Set up item wrist
	heldItemWrist = Instantiate(itemWristPrefab, heldItemGripPosition.position, heldItemGripPosition.rotation);
        heldItemWristRigidbody = heldItemWrist.GetComponent<Rigidbody>();

        // Set up wrist joint
	wristJoint = heldItemWrist.GetComponent<ConfigurableJoint>();
        wristJoint.connectedBody = heldItem.GetComponent<Rigidbody>();
    }

    handAlignmentObject.localEulerAngles = heldItemComponent.itemHandAlignement;
    isHoldingItem = true;
}
  • Ještě jednoduchá implementace funkce zjišťující, zda již druhá ruka nedrží sebraný objekt
bool isOtherHandHoldingItem(grabbedItem)
{
    if (otherHandManager.isHoldingItem && otherHandManager.Helditem == grabbedItem)
        return true;
    return false;
}
  • Dále potřebujeme při upuštění objektu upravit stavy v několika rozvětvených případech
    • Pokud držíme objekt v obou rukách a tato ruka je primární, musíme z druhé , sekundární, ruky udělat primární a změnit stav na jednoruční držení
    • Pokud držíme objekt obouručně, ale toto je sekundární ruka, stačí nám změnit stav na jednoruční držení
    • Pokud držíme objekt jen v jedné ruce, provedeme standardní upuštění - zničíme zápěstí
void Drop()
{
    if(isDoubleHanding && doubleHandingIsPrimaryHand)
    {
	otherHandManager.isDoubleHanding = false;
	otherHandManager.heldItemGripPosition = heldItemGripPosition;
    }
    else if(isDoubleHanding)
    {
	otherHandManager.isDoubleHanding = false;
    }
    else
    {
        wristJoint.connectedBody = null;
        Destroy(heldItemWrist);
    }

    heldItem = null;
    heldItemWrist = null;
    heldItemGripPosition = null;
    heldItemWristRigidbody = null;

    isHoldingItem = false;
}
  • Poslední co zbývá upravit je metoda UpdateHeldItem(), tedy vytvořit rozvětvení a logiku pro různé druhy držení
public void UpdateHeldItem()
{
    if(isDoubleHanding)
    {
	if(doubleHandingIsPrimaryHand)
	{
	    UpdateHeldItemPosition();
	    UpdateHeldItemRotationDoublehanded();
	}
    }
    else
    {
	UpdateHeldItemPosition();
        UpdateHeldItemRotation();
    }
}
  • Tady by bylo dobré vysvětlit, co se primární a sekundární rukou vlastně myslí. Při držení objektů typů nástroj, zbraně(tyčové, střelné) atd. pomocí obou rukou se intuitivně jedna ruka stará o přesnou manipulaci a druhá ruka spíše jen lehce pomáhá a stabilizuje. V tomto příkladu se podle primární ruky aktualizuje lokace a z vektoru mezi primární a sekundární rukou se odvozuje rotace
  • Kód pro rotaci obouručních předmětů je tedy následující:
UpdateHeldItemRotationDoublehanded()
{
    Vector3 handToHandVec = handAlignmentObject.position - otherHandManager.handAlignmentObject.position;
    heldWeaponWristRigidbody.MoveRotation(Quaternion.LookRotation(handToHandVec, handAlignmentObject.up));
}
  • Tak a tímto způsobem můžeme uchopit a ovládat předměty pomocí obou rukou. Pokud bychom měli další předměty co by při obouručním držení vyžadovali jiný způsob držení, můžeme logiku dále větvit a nebo ještě lépe, využít více objektově orinetované řešení.

doublehanded2