open-ar/src/chapter3.4.md

3.5 KiB

Collision

Quando si ha a che fare con più nodi presenti sulla scena può risultare utile verificare se due o più di questi si sovrappongono. In questo progetto viene mostrato come eseguire questo controllo mediante l'API di ARCore.

Per questo progetto si è utilizzata una rivisitazione dell'applicazione vista nel progetto precedente, con la differenza che l'aggiunta di un oggetto non è consentita se questo va in collisione con un altro già presente nella scena (vedi fig. \ref{c}).

Schermata di errore dovuta ad una collisione{#c width=225px height=400px}

Rilevamento della collisione

Attualmente ARCore e Sceneform non forniscono nessun listener o metodo che può essere sovrascritto per la gestione della collisione, quindi seguendo un approccio già esaminato precedentemente andiamo ad aggiungere un listener all'evento dell'aggiornamento della scena.

override fun onCreate(savedInstanceState: Bundle?) {
  // ...
  arScene.addOnUpdateListener(this::onUpdate)
  // ...
}

La verifica di una collisione può essere effettuata o attraverso il metodo overlapTest, che dato un nodo in input restituisce il primo nodo che entra in collisione con questo, oppure mediante il metodo overlapTestAll, che dato un nodo in input restituisce una lista con tutti i nodi che collidono con esso. Nel caso in cui non siano state riscontrate collisioni, i metodi restituiscono rispettivamente null e una lista vuota.

La funzione onUpdate si occupa di verificare la presenza di collisioni.


private fun onUpdate(frameTime: FrameTime) {
  val node = lastNode ?: return
  val overlappedNodes = arScene.overlapTestAll(node)
  if (overlappedNodes.isNotEmpty())
    onCollision()
}

Mentre la funzione onCollision si occupa di notificare all'utente l'avvenuta collisione mediante un Toast1 e di eliminare il nodo dalla scena.

private fun onCollision() {
  Toast.makeText(this, "collision", Toast.LENGTH_LONG)
    .show()
  lastNode?.isEnabled = false
  lastNode = null
}

Il test di collisione non avviene direttamente sul Renderable, ma sulla CollisionShape ovvero una "scatola" invisibile che racchiude il modello renderizzato vero e proprio. Di default ARCore utilizza CollisionShape o di forma rettangolare o sferica, ma può essere cambiata mediante il metodo setCollisionShape della classe Node. In questo progetto si è usata la CollisionShape di default.

Aggiunta del nodo alla scena

L'aggiunta del nodo alla scena avviene mediante l'aggiunta di un listener all'evento di tocco su un piano.

override fun onCreate(savedInstanceState: Bundle?) {
  // ...
  arFragment.setOnTapArPlaneListener(this::addShape)
  // ...
}

La funzione addShape, utilizzando le funzioni buildMaterial e buildShape analizzate in precedenza, si occupa dell'effettiva aggiunta dell'oggetto alla scena.

private fun addShape(
  hitResult: HitResult,
  plane: Plane,
  motionEvent: MotionEvent
) {
  val red = Color(android.graphics.Color.RED)

  buildMaterial(this, red) {
    val cube = buildShape(Shape.CUBE, it)

    lastNode = addNodeToScene(
      arFragment,
      hitResult.createAnchor(),
      cube
    )
  }
}

Risulta importante notare come attraverso questa strategia l'aggiunta del modello alla scena avvenga incondizionatamente, ed è solo all'aggiornamento di quest'ultima che si effettua il controllo di collisione sull'ultimo nodo aggiunto.


  1. Oggetto nativo di Android mediante il quale è possibile informare l'utente in modo non invasivo. ↩︎