Tworzenie Interaktywnej Kostki Rubika 3D w Vue 3 i WebGL

Grafika trójwymiarowa w przeglądarce już od dawna nie jest jedynie technologiczną ciekawostką, lecz potężnym narzędziem służącym do budowania wysoce interaktywnych doświadczeń. Kiedy myślimy o 3D w sieci, często pierwszym skojarzeniem jest biblioteka Three.js. W tym wpisie chciałbym jednak przedstawić podejście architektoniczne polegające na zbudowaniu Interaktywnej Kostki Rubika 3D w pełni we frameworku Vue 3, mocno polegając na natywnym API interfejsu WebGL oraz precyzyjnym zarządzaniu stanem ułamków obrotu.
Mój najnowszy, otwartoźródłowy projekt – rubic-cube – możecie przestudiować w całości na mojej publicznej instancji Gitea. Zapraszam do czytania!
Architektura i Wyzwania
Głównym problemem przy implementacji gier logicznych takich jak Kostka Rubika nie jest samo "narysowanie" sześcianów w przestrzeni, ale bezkolizyjne zarządzanie ich obrotami w poszczególnych osiach.
Dlaczego Vue 3?
Wykorzystanie reaktywnego stanu Vue 3 (dzięki API kompozycji – ref, computed, watch) pozwala w elegancki sposób odciąć logikę i matematykę samej kostki (stan poszczególnych 27 mniejszych sześcianików tzw. Cubies) od warstwy renderującej rzucającej klatki na <canvas>. Silnik reaktywny Vue rewelacyjnie sprawdza się w śledzeniu "zamiaru" obrotu danej ściany przez użytkownika (np. przeciągnięcie palcem po ekranie).
Pętla Renderująca a Reactivity Board
W typowym webowym flow 3D mamy pętlę requestAnimationFrame, która przelicza i odrysowuje świat 60 (lub 120) razy na sekundę. Rzucenie do niej całego reaktywnego stanu Vue drastycznie ubiłoby wydajność na słabszych urządzeniach (mobilki).
Moim rozwiązaniem na utrzymanie 60 FPS było ograniczenie reaktywności. Reprezentacja ułożenia kostki (model danych) jest uaktualniana tylko na żądanie podczas konkretnych ruchów algorytmu, natomiast WebGL po prostu rzutuje wielokąty w przestrzeń kosmiczną w surowej pętli odpytując macierze transformacji, niemal całkowicie omijając proxy od Vue podczas czystego malowania (renderingu).
TIP
Najlepszą taktyką optymalizacyjną w Vue dla ciężkich operacji rzutowania renderowania na ramy, jest oddzielenie macierzy transformacji jako surowych obiektów JS bez narzutu Reaktywności.
Model Matematyczny – Quaternions i Euler Angles
Aby zapobiec słynnemu problemowi [Gimbal Lock] (blokadzie osi), obroty całej matrycy 3x3x3 poszczególnych płaszczyzn implementujemy korzystając z Kwaternionów, a nie Kątów Eulera. Gdy robimy "szybki obrót" prawej ściany, animacja CSS jest zbyt mało rzetelna by na niej polegać.
Obrót odbywa się dzięki mapowaniu pozycji względem centralnej iteracji:
// Koncepcyjny fragment algorytmu obrotu kolumny w Vue
function rotateSlice(axis, index, direction) {
state.isAnimating = true;
const targetAngle = direction * (Math.PI / 2); // 90 stopni
animateValue(0, targetAngle, 300, (currentAngle) => {
// Aktualizacja macierzy rotacji dla aktywnych klocków
updateActiveCubiesMatrix(axis, currentAngle);
}).then(() => {
snapToGrid();
state.isAnimating = false;
});
}Dzięki temu uzyskujemy efekt "przyklejania się" kostki na swoje twarde miejsca równo co 90 stopni z zachowaniem perfekcyjnie płynnego, rzutowanego izometrycznie obrotu.
Kwestie responsywności i Mobile Touch
Kostka Rubika wymaga trójwymiarowego przeciągania – nie mamy tutaj płaskiego scrollowania. Rozwiązane to zostało rejestracją natywnych eventów touchstart, touchmove na obiekcie <canvas>, co pozwala analizować wektory delta dotyku:
<template>
<div class="cube-container">
<canvas
ref="webglCanvas"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
></canvas>
</div>
</template>Całość jest w pełni grywalna na ekranie dotykowym i działa z odczuwalnie niskimi opóźnieniami – niemal tak dobrze, jak natywna aplikacja, co jest fenomenalnym sukcesem współczesnych standardów sieciowych.
Spróbuj sam!
Budowanie grafiki opartych o matematykę przestrzenną rozwija umysł inżyniera dużo mocniej niż standardowe składanie formularzy i zapytań REST. Otwiera oczy na optymalizacje sprzętowe.
Zajrzyjcie do publicznego repozytorium na mojej domenie by samodzielnie pobrać kod i uruchomić go lokalnie: 👉 Repozytorium Rubic Cube na Gitea 7u.pl
Złóżcie ją całą i pochwalcie się czasem PRowania algorytmu mieszania! 🚀