2013-08-15 11 views
10

Próbuję zaimplementować ruchy pierwszoosobowe za pomocą myszy. Mam to działa z klawiaturą, ale mam trudności z zaimplementowaniem go za pomocą myszy, ponieważ ruch do określonej strony nie jest tak oczywisty (tzn. Ruch w lewo może obejmować przesuwanie w górę lub w dół). Chcę użyć matrix3d, aby otrzymać zmienione wartości pozycji. To jest jsfiddle.Sterowany myszką ruch pierwszej osoby JS

EDIT mam wklejony nowy kod udało mi się rozwiązać:

$(document).on('mousemove', function (e) { 
     var MOVE = 10; // how much to move 
     var XTURN = 1; // how much to rotate 
     var YTURN = 1; // how much to rotate 
     var transformer, origMat, translationMatrix, result; 
     transformer = document.getElementById("transformer"); 

     if ($.browser.webkit) 
      origMat = new WebKitCSSMatrix(window.getComputedStyle(transformer).webkitTransform); 

     //turn left 
     if (e.pageX < xPrev) { 
      if (XTURN < 0) { 
       XTURN *= -1; 
      } 
      xPrev = e.pageX; 
     //turn right 
     } else { 
      if (XTURN > 0) { 
       XTURN *= -1; 
      } 
      xPrev = e.pageX; 

     } 
     //look up 
     if (e.pageY < yPrev) { 
      if (YTURN < 0) { 
       YTURN *= -1; 
      } 
      yPrev = e.pageY; 
     //look down 
     } else { 
      if (YTURN > 0) { 
       YTURN *= -1; 
      } 
      yPrev = e.pageY; 
     } 

     translationMatrix = new WebKitCSSMatrix("matrix3d(" + cos(XTURN).toFixed(10) + ",0," + sin(XTURN).toFixed(10) + ",0,0,"+ cos(-YTURN).toFixed(10) +","+ sin(YTURN).toFixed(10) +",0, " + sin(-XTURN).toFixed(10) + ","+ sin(-YTURN).toFixed(10) +"," + cos(XTURN).toFixed(10) + ",0,0,0,0,1)"); 

     transformer.style.webkitTransform = translationMatrix.multiply(origMat).toString(); 
    }); 

Jak widać (Niestety dla matrycy jednej linii) Jestem podając zmiany X i Obroty Y na tej samej zmianie matrycy, a następnie jej zatwierdzenie, problem jest teraz z cos(XTURN).toFixed(10), który może być powiązany z obrotami X i Y, więc możesz zobaczyć, że działa, ale nie jest doskonały. Doceniam wszelkie wskazówki/pomysły.

P.S Nie chcę korzystać z Pointer Lock API, mimo że jest świetny, ponieważ chcę, aby obsługiwał maksymalną liczbę przeglądarek.

+0

Jeśli to działa, po co zmieniać w odpowiedzi lub usunąć pytanie (odpowiedź będzie lepiej). – FakeRainBrigand

+0

To nie działa tak, jak powinno być, dlatego nie jest rozwiązane, więc wysłanie odpowiedzi na nie byłoby niewłaściwe. Opublikowaliśmy zmianę dotyczącą tego, co zmieniło się od czasu mojego pierwszego pytania. –

+0

Przepraszam, źle mnie zrozumiałem. Myślałem, że powiedziałeś, że to rozwiązałeś. – FakeRainBrigand

Odpowiedz

3

Czysta JavaScript jest przeważnie lepsze niż bibliotek (chyba, że ​​jest to „Kod mniej zrobić więcej” rzeczy),
od możesz zrozumieć, co naprawdę robi twój kod.

To jest mój cały kod JavaScript:

var velocity = 0.5; 

document.onmousemove = function(e) { 
    var angleX = e.pageY * velocity * Math.PI/180; 
    var angleY = e.pageX * velocity * Math.PI/180; 
    document.getElementById('transformer').style.webkitTransform = 'matrix3d(' + Math.cos(-angleY) + ',0,' + Math.sin(-angleY) + ',0,' + (Math.sin(angleX)*Math.sin(-angleY)) + ',' + Math.cos(angleX) + ',' + (-Math.sin(angleX)*Math.cos(-angleY)) + ',0,' + (-Math.cos(angleX)*Math.sin(-angleY)) + ',' + Math.sin(angleX) + ',' + (Math.cos(angleX)*Math.cos(-angleY)) + ',0,0,0,0,1)'; 
}; 

I this is the fiddle.

Działa!

(I nawet się tego przykład używając API Pointer Lock: fiddle (Kliknij kwadrat, aby rozpocząć)


Objaśnienie:

pierwsze, zmienna prędkość łatwo ustawić prędkość obrotowa:
Następnie, zdarzenie mousemove, które ma ustawione dwa zestawy zmiennych obrotu
Ostatnia linia ma zostać przekształcona z rotateX iTransformacje, zgodnie z wymaganiami, do matrix3d. This Stackoverflow question pomógł mi uzyskać następujące rozwiązanie.


rotateX(angleX) jest równa następującej macierzy:

1    0    0    0 


0    cos(angleX)  -sin(angleX) 0 


0    sin(angleX)  cos(angleX)  0 


0    0    0    1 

rotateY(angleY) jest równa następującej macierzy:

cos(angleY)  0    sin(angleY)  0 


0    1    0    0 


-sin(angleY) 0    cos(angleY)  0 


0    0    0    1 

i wykorzystanie ich obu razem, trzeba pomnożyć dwa macierze. Napisałem więc małe narzędzie JavaScript, aby podać obliczenia, które muszę wykonać, aby uzyskać wynik tego mnożenia.

Rezultat:

cos(angleY)    sin(angleX)*sin(angleY) cos(angleX)*sin(angleY) 0 



0       cos(angleX)    -sin(angleX)    0 



-sin(angleY)    sin(angleX)*cos(angleY) cos(angleX)*cos(angleY) 0 



0       0       0       1 

I to jest sposób na konwersję rotateX i rotateY do matrix3d.

Nadzieja to pomaga :)

+0

To jest naprawdę świetne wytłumaczenie. Postanowiłem, że ta odpowiedź jest lepsza. (nawet jeśli @ IcanDivideBy0 miał taką samą odpowiedź, jeszcze mniej szczegółową). Zastanawiam się, czy użycie kwaternionów, jak wspomniano w innych odpowiedziach, przyczynia się w inny sposób do rozwiązania. (nie tylko lepszy kod) –

3

Nie jest dla mnie jasne, jaki jest twój cel wysokiego poziomu. Wygląda na to, że próbujesz zaimplementować grę podobną do Counterstrike w JS i CSS. Co jest niesamowite! W dalszej części tej odpowiedzi zakładam, że próbujesz coś takiego zrobić.

Realistycznie musisz użyć użyć API blokady wskaźnika. W przeciwnym razie nie będzie można się odwrócić, przesuwając mysz w lewo. Uderzysz w krawędź okna przeglądarki i przestaniesz obracać. Obsługa przeglądarki nie jest świetna, ale zdecydowanie lepsze wrażenia dla graczy!

Aby oddać swój świat za pomocą transformacji CSS, należy wykonać skomplikowaną serię transformacji, aby wygenerować matrycę dla każdej strony każdego obiektu widocznego w świecie gry. Dzieje się tak, ponieważ perspektywa przeglądarki zawsze patrzy bezpośrednio wzdłuż osi Z. Aby więc animować rzeczy "wokół" oka widza, trzeba je tłumaczyć i obracać. Po krótkim pokręceniu doszedłem do wniosku, że wykonanie wszystkich transformacji w CSS jest zbyt powolne (i skomplikowane!). Ale nigdy się nie bój, jest inny sposób! WebGL lub Canvas na ratunek!

Spójrz na grę Isaaca Sukina Nemesis. To doskonały przykład, a on napisał tutorial, żeby wymyślić coś podobnego! Oparta na niej biblioteka Three.js jest bardzo szeroko stosowana i ma bardzo zrozumiały interfejs API. Potrwa prawie cały trudny kawałek i pozwala stworzyć świat 3D!

Powodzenia w grze!

+0

Połączona gra [Nemsis] (http://icecreamyou.github.io/Nemesis/) nie jest zbyt trudna do kontrolowania, nawet jeśli nie ma blokady wskaźnika. Zmieniasz swoją postać nie za pomocą ruchu myszy, ale z odchyleniem myszy od środka okna. Kiedy mysz znajduje się po lewej stronie ekranu, skręcasz w lewo. Strzelasz również tam, gdzie wskaźnik jest zamiast prosto do przodu. Blokada wskaźnika byłaby lepsza i byłoby miło, gdyby gra Shahara została wykryta, gdyby Blokada pointeru była obsługiwana i jeśli tak, użyłaby go, ale nie nazwałbym tego * wymagane *. –

+0

Dziękuję za szczegółową odpowiedź, ale to naprawdę nie rozwiązuje mojego problemu, wyjaśnię: jestem w stanie osiągnąć rotację X i rotację Y oddzielnie za pomocą ruchu myszy, tak samo jak w Nemezis (tylko tam obrót Y), ale Chcę być w stanie i połączyć te 2 ruchy, aby użytkownik mógł doświadczyć stylu FPS rozglądając się, poruszanie się po osi Z będzie odbywało się za pomocą klawiatury, więc nie ma tam żadnych problemów. Jeśli chodzi o WebGL, to jest niesamowite, ale zgadzam się, ale nie ma to znaczenia dla mojego konkretnego projektu ani celu tutaj, a ponieważ istniejące środowisko, którego nie stworzyłem przy użyciu Canvas, również mi nie pomaga. –

+0

Oh, okay. Jeśli chcesz tylko obrócić widok w obu kierunkach jednocześnie, wykonaj dwie transformacje na macierzy3d(). lub obróć jeden raz wokół numeru <1,1,0> (lub podobnego). Spójrz na [dokumentację Mozilli dla 'rotate3d()'] (https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate3d()), a następnie użyj tej transformacji macierzy zamiast tego, który masz. – tangphillip

2

Korzystanie z kwaternionów jest naprawdę łatwiejsze. Znalazłem implementacja w Google closure library więc zrobiłem przykład (także sprawdzić the jsFiddle):

goog.require('goog.vec.Quaternion'); 

var velocity = 0.5; 

var lastX = null; 
var lastY = null; 
var angleX = 0; 
var angleY = 0; 

$(document).on('mousemove', function (e) { 
    if (lastX == null) lastX = e.pageX; 
    if (lastY == null) lastY = e.pageY; 

    angleX += (e.pageX - lastX) * velocity * Math.PI/180; 
    angleY += (e.pageY - lastY) * velocity * Math.PI/180; 

    lastX = e.pageX; 
    lastY = e.pageY; 

    var quat = goog.vec.Quaternion.concat(
     goog.vec.Quaternion.fromAngleAxis(angleX, [0, 1, 0], []), 
     goog.vec.Quaternion.fromAngleAxis(-angleY, [1, 0, 0], []), []); 

    var matrix = goog.vec.Quaternion.toRotationMatrix4(quat, []); 

    $("#transformer").css({ 
     webkitTransform: "matrix3d(" + matrix.join(",") + ")" 
    }); 
}); 
+0

Nie wiedziałem o tej wspaniałej bibliotece, a rozwiązanie naprawdę elegancko z niej korzysta. Nie poddałem go jeszcze testowi, ale wydaje się, że rozwiązałem problem, z którym miałem problemy, i jako bonus poznałem tę bibliotekę. Czy mógłbyś dokładniej wyjaśnić, jak używać quaternion i jak to rozwiązuje do matrix3d, aby w pełni zrozumieć kod i matematykę za nim, dziękuję. –