Instancja DragDetector ułatwia i zachęca do interakcji z obiektami 3D w grze, takimi jak otwieranie drzwi i szuflad, przesuwanie części, chwytanie i rzucanie piłką do kręgli, naciąganie i strzelanie z procy i wiele więcej. Kluczowe cechy obejmują:
Umieść DragDetector pod dowolnym BasePart lub Model, aby uczynić go przeciągalnym za pomocą wszystkich wejść (mysz, dotyk, kontroler i VR), wszystko to bez ani jednej linii kodu.
Wybierz z kilku stylów przeciągania, zdefiniuj, jak obiekt reaguje na ruch i opcjonalnie zastosuj ograniczenia osi lub ruchu.
Skrypty mogą reagować na manipulację przeciąganymi obiektami, aby sterować interfejsem użytkownika lub podejmować logiczne decyzje, takie jak dostosowywanie poziomu światła w pomieszczeniu w zależności od suwaka ściennego.
Gracze mogą manipulować zakotwiczonymi częściami lub modelami, a one pozostaną dokładnie tam, gdzie je umieścisz po zwolnieniu.
DragDetectors działają w Studio, o ile nie używasz narzędzi Wybierz, Przesuń, Skaluj lub Obróć, co ułatwia testowanie i dostosowywanie przeciągalnych obiektów podczas edytowania.
Uczyń obiekty przeciągalnymi
Aby uczynić dowolną część lub model przeciągalnym, wystarczy dodać DragDetector jako bezpośredniego potomka.
W oknie Eksploratora najedź na Part, MeshPart lub Model i kliknij przycisk ⊕. Wyświetli się menu kontekstowe.
Z menu wstaw DragDetector.

Domyślnie obiekt będzie teraz przeciągalny w płaszczyźnie podłogi, ale możesz dostosować jego DragStyle, zdefiniować, jak reaguje na ruch i opcjonalnie zastosować ograniczenia osi lub ruchu.
Dostosuj detektory przeciągania
Styl przeciągania
DragDetectors mapują ruch kursora na wirtualne linie i płaszczyzny, aby obliczyć proponowany ruch 3D. Dzięki właściwości DragStyle możesz wybierać spośród różnych mapowań, aby dostosować je do swoich potrzeb. Na przykład, TranslatePlane produkuje translację w wirtualnej płaszczyźnie, podczas gdy RotateAxis produkuje rotację wokół wirtualnej osi.
| Ustawienie | Opis |
|---|---|
| TranslateLine | Ruch 1D wzdłuż Axis detektora, domyślnie wzdłuż osi Y świata. |
| TranslatePlane | Ruch 2D w płaszczyźnie prostopadłej do Axis detektora, domyślnie w płaszczyźnie XZ świata. |
| TranslatePlaneOrLine | Ruch 2D w płaszczyźnie prostopadłej do Axis detektora, a gdy modyfikator jest aktywny, ruch 1D wzdłuż Axis detektora. |
| TranslateLineOrPlane | Ruch 1D wzdłuż Axis detektora, a gdy modyfikator jest aktywny, ruch 2D w płaszczyźnie prostopadłej do Axis detektora. |
| TranslateViewPlane | Ruch 2D w płaszczyźnie prostopadłej do widoku kamery. W tym trybie płaszczyzna jest stale aktualizowana, nawet podczas przeciągania, i zawsze będzie zwrócona w kierunku aktualnego widoku kamery. |
| RotateAxis | Rotacja wokół Axis detektora, domyślnie wzdłuż osi Y świata. |
| RotateTrackball | Rotacja trackball, dodatkowo dostosowywana przez właściwości TrackballRadialPullFactor i TrackballRollFactor. |
| BestForDevice | TranslatePlaneOrLine dla myszy i kontrolera; TranslatePlane dla dotyku; 6DOF dla VR. |
| Scriptable | Oblicza pożądany ruch za pomocą funkcji niestandardowej dostarczonej przez SetDragStyleFunction(). |
Kierunek przeciągania
Domyślnie ruch 3D i związany z nim DragStyle mapują się na przestrzeń świata. Możesz jednak chcieć zmienić ReferenceInstance, Orientation lub Axis, na przykład podczas budowania detektorów przeciągania w modelach z regulowanymi częściami.
| Właściwość | Opis | Domyślnie |
|---|---|---|
| ReferenceInstance | Instancja, której pivot zapewnia ramkę odniesienia dla detektora przeciągania. DragFrame jest wyrażona w odniesieniu do tej ramki odniesienia, którą można uzyskać za pomocą GetReferenceFrame(). Jeśli ramka odniesienia jest nil, translacja będzie w kierunku (lub w płaszczyźnie prostopadłej do) właściwości Axis w przestrzeni świata. | nil |
| Orientation | Określa YXZ rotację osi ruchu w odniesieniu do ramki odniesienia (nie zmienia orientacji samej ramki odniesienia). Translacja liniowa i rotacja osiowa będą na tej zreorientowanej osi Y, a translacja płaszczyzny w płaszczyźnie XZ. Zmiana tej wartości automatycznie aktualizuje Axis i odwrotnie. | (0, 0, 0) |
| Axis | Główna oś ruchu, wyrażona w odniesieniu do ramki odniesienia. Zmiana tej wartości automatycznie aktualizuje Orientation i odwrotnie. | (0, 1, 0) |
Reakcja na ruch
Właściwość ResponseStyle określa, jak obiekt reaguje na proponowany ruch, w zależności od tego, czy obiekt jest Anchored, czy nie.
| Ustawienie | Zachowanie zakotwiczone | Zachowanie niezwiązane |
|---|---|---|
| Geometric | Zarówno w działającej grze, jak i w trybie edycji Studio, pozycja/orientacja zakotwiczonego obiektu będzie aktualizowana, aby dokładnie odzwierciedlać proponowany ruch. | Dla niezwiązanego obiektu zachowanie jest takie samo jak dla zakotwiczonego obiektu. Jednak w działającej grze obiekt będzie zakotwiczony na początku przeciągania i przywrócony do niezwiązanego po zwolnieniu przeciągania. |
| Physical | Zakotwiczony obiekt domyślnie będzie miał zachowanie Geometric, ponieważ nie jest pod wpływem sił. | Niezwiązany obiekt będzie poruszany przez siły ograniczające, które próbują przynieść go do pożądanej pozycji i/lub orientacji określonej przez proponowany ruch. |
| Custom | Obiekt w ogóle się nie poruszy, ale DragFrame nadal będzie aktualizowany, a Ty możesz reagować na manipulację przeciąganiem w dowolny sposób. | (tak samo jak zakotwiczony) |
Ograniczenia osi i ruchu
Domyślnie nie ma ograniczeń dla ruchu 3D poza wrodzonymi ograniczeniami DragStyle. W razie potrzeby możesz zastosować minimalne i maksymalne ograniczenia zarówno dla translacji, jak i rotacji. Należy jednak pamiętać, że nie są to ograniczenia; jedynie utrudniają próby detektora przeciągania generowania ruchu, aby pozostać w granicach.
| Właściwości | Opis | Domyślnie |
|---|---|---|
| MinDragTranslation MaxDragTranslation | Ograniczenia dla translacji przeciągania w każdej osi. Jeśli MaxDragTranslation jest większe niż MinDragTranslation, translacja będzie ograniczona w tym zakresie. | (0, 0, 0) |
| MinDragAngle MaxDragAngle | Relewantne tylko wtedy, gdy DragStyle jest ustawione na RotateAxis. Jeśli MaxDragAngle jest większe niż MinDragAngle, rotacja będzie ograniczona w tym zakresie. | 0 |
Uprawnienia do przeciągania
Uprawnienia graczy do interakcji z danym instancją detektora przeciągania można określić za pomocą właściwości PermissionPolicy. Domyślnie jest ustawione na Enum.DragDetectorPermissionPolicy.Everybody, a także można je zmienić, aby wspierać skryptowe kontrole uprawnień, jak pokazano w przykładzie kodu.
| Ustawienie | Opis |
|---|---|
| Nobody | Żaden gracz nie może interagować z DragDetector. |
| Everybody | Wszyscy gracze mogą interagować z DragDetector. |
| Scriptable | Uprawnienia do przeciągania graczy będą określane przez funkcję zarejestrowaną za pomocą SetPermissionPolicyFunction(). W tym ustawieniu brak rejestracji funkcji lub zwrócenie nieprawidłowego wyniku uniemożliwi wszystkim graczom przeciąganie. |
local dragDetector = script.Parent.DragDetector
dragDetector.PermissionPolicy = Enum.DragDetectorPermissionPolicy.Scriptable
dragDetector:SetPermissionPolicyFunction(function(player, part)
if player and player:GetAttribute("IsInTurn") then
return true
elseif part and not part:GetAttribute("IsDraggable") then
return false
else
return true
end
end)Reakcja fizyczna
Zakładając, że styl reakcji detektora jest ustawiony na Physical i jest zastosowany do niezwiązanego obiektu, ten obiekt będzie poruszany przez siły ograniczające, które próbują przynieść go do pozycji/orientacji określonej przez proponowany ruch. Możesz dodatkowo dostosować reakcję fizyczną za pomocą następujących właściwości:
| Właściwość | Opis | Domyślnie |
|---|---|---|
| ApplyAtCenterOfMass | Gdy fałsz, siła przeciągania jest stosowana w punkcie, w którym użytkownik klika. Gdy prawda, siła jest stosowana w centrum masy obiektu. | false |
| MaxForce | Maksymalna siła stosowana, aby obiekt osiągnął swój cel. | 10000000 |
| MaxTorque | Maksymalny moment obrotowy stosowany, aby obiekt osiągnął swój cel. | 10000 |
| Responsiveness | Wyższe wartości powodują, że obiekt szybciej osiąga swój cel. | 10 |
Modyfikator wejściowy
Niektóre tryby DragStyle pozwalają użytkownikom przytrzymać klawisz/przycisk modyfikatora, aby manipulować przeciąganym obiektem w różny sposób. Domyślnie modyfikator to LeftControl na PC, ButtonR1 na kontrolerze lub ButtonL2 na VR. Możesz dostosować te modyfikatory za pomocą właściwości KeyboardModeSwitchKeyCode, GamepadModeSwitchKeyCode lub VRSwitchKeyCode instancji detektora przeciągania.
Replikacja
Gdy właściwość RunLocally jest fałszywa (domyślnie), klient interpretuje wszystkie wejścia, aby wygenerować dane, które wysyła do serwera w celu wykonania przeciągania. W tym trybie wszystkie niestandardowe sygnały zdarzeń i zarejestrowane funkcje muszą znajdować się w serwerowych Scripts.
Gdy właściwość RunLocally jest prawdziwa, żadne zdarzenia nie są replikowane do serwera. Wszystkie niestandardowe sygnały zdarzeń i zarejestrowane funkcje muszą znajdować się w lokalnych LocalScripts, a Ty musisz używać zdarzeń zdalnych, aby propagować niezbędne zmiany do serwera.
Odpowiedzi skryptów na klikanie i przeciąganie
Dzięki sygnałom zdarzeń, zmianom właściwości, stylowi przeciągania Scriptable i funkcjom niestandardowym, skrypty mogą reagować na manipulację przeciąganymi obiektami, aby sterować interfejsem użytkownika lub podejmować logiczne decyzje, takie jak dostosowywanie poziomu światła w pomieszczeniu w zależności od suwaka ściennego.
Sygnały zdarzeń
Dzięki następującym sygnałom zdarzeń możesz wykryć, kiedy użytkownik zaczyna, kontynuuje i kończy przeciąganie obiektu.
| Zdarzenie | Opis |
|---|---|
| DragStart | Wywoływane, gdy użytkownik zaczyna przeciągać obiekt. |
| DragContinue | Wywoływane, gdy użytkownik kontynuuje przeciąganie obiektu po zainicjowaniu DragStart. |
| DragEnd | Wywoływane, gdy użytkownik przestaje przeciągać obiekt. |
local dragDetector = script.Parent.DragDetector
local highlight = Instance.new("Highlight")
highlight.Enabled = false
highlight.Parent = script.Parent
dragDetector.DragStart:Connect(function()
highlight.Enabled = true
end)
dragDetector.DragContinue:Connect(function()
end)
dragDetector.DragEnd:Connect(function()
highlight.Enabled = false
end)Zmiany w ramce przeciągania
Oprócz sygnałów zdarzeń możesz monitorować zmiany w DragFrame detektora bezpośrednio.
local dragDetector = script.Parent.DragDetector
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
local currentDragTranslation = dragDetector.DragFrame.Position
print(currentDragTranslation)
end)Skryptowany styl przeciągania
Jeśli ustawisz DragStyle detektora na Scriptable, możesz dostarczyć własną funkcję, która przyjmuje Ray i zwraca CFrame w przestrzeni świata. Detektor przeniesie ruch tak, aby przeciągany obiekt trafił w to niestandardowe miejsce/orientację.
local Workspace = game:GetService("Workspace")
local dragDetector = script.Parent.DragDetector
dragDetector.DragStyle = Enum.DragDetectorDragStyle.Scriptable
local cachedHitPoint = Vector3.zero
local cachedHitNormal = Vector3.yAxis
local function followTheCursor(cursorRay)
-- Wyklucz przeciągany obiekt z detekcji raycast
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {dragDetector.Parent}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
local hitPoint = Vector3.zero
local hitNormal = Vector3.yAxis
local raycastResult = Workspace:Raycast(cursorRay.Origin, cursorRay.Direction, raycastParams)
if raycastResult then
hitPoint = raycastResult.Position
hitNormal = raycastResult.Normal.Unit
else
hitPoint = cachedHitPoint
hitNormal = cachedHitNormal
end
cachedHitPoint = hitPoint
cachedHitNormal = hitNormal
local lookDir1 = hitNormal:Cross(Vector3.xAxis)
local lookDir2 = hitNormal:Cross(Vector3.yAxis)
local lookDir = if lookDir1.Magnitude > lookDir2.Magnitude then lookDir1.Unit else lookDir2.Unit
return CFrame.lookAt(hitPoint, hitPoint + lookDir, hitNormal)
end
dragDetector:SetDragStyleFunction(followTheCursor)Niestandardna funkcja ograniczająca
Detektory przeciągania nie mają wbudowanych zasad ruchu dotyczących siatek i przyciągania, ale możesz zarejestrować niestandardowe funkcje ograniczające, aby edytować DragFrame detektora przed jego zastosowaniem. Na przykład możesz utrzymać ruch na siatce, zaokrąglając pozycje do wielokrotności przyrostu siatki lub symulować grę w szachy z zasadami ruchu legalnymi dla każdego figury.
local dragDetector = script.Parent.DragDetector
local startPartPosition = nil
local SNAP_INCREMENT = 4
dragDetector.DragStart:Connect(function()
startPartPosition = script.Parent.Position
end)
dragDetector.DragEnd:Connect(function()
startPartPosition = nil
end)
local function snapToWorldGrid(proposedMotion)
if startPartPosition == nil then
return proposedMotion
end
local snapIncrement = SNAP_INCREMENT // 1
if snapIncrement < 1 then
return proposedMotion
end
local newWorldPosition = startPartPosition + proposedMotion.Position
local roundedX = ((newWorldPosition.X / snapIncrement + 0.5) // 1) * snapIncrement
local roundedY = ((newWorldPosition.Y / snapIncrement + 0.5) // 1) * snapIncrement
local roundedZ = ((newWorldPosition.Z / snapIncrement + 0.5) // 1) * snapIncrement
local newRoundedWorldPosition = Vector3.new(roundedX, roundedY, roundedZ)
return proposedMotion.Rotation + (newRoundedWorldPosition - startPartPosition)
end
local connection = dragDetector:AddConstraintFunction(2, snapToWorldGrid)
-- Gdy to konieczne, usuń funkcję ograniczającą, wywołując connection:Disconnect()Przykład użycia
Niezwiązane obiekty fizyczne
Podstawowa implementacja detektorów przeciągania to gra w równowagę wieży, w której gracze muszą ostrożnie usuwać elementy i próbować utrzymać wieżę w pionie. W poniższej strukturze wieży każdy element ma dziecko DragDetector z domyślnym DragStyle ustawionym na TranslatePlane, aby gracze mogli ciągnąć elementy na zewnątrz, ale nie w górę lub w dół.
Zakotwiczone modele z regulowanymi częściami
Możesz łatwo tworzyć i udostępniać modele, które są głównie zakotwiczone, ale mają jedną lub więcej części/modeli, które gracze mogą przeciągać. Na przykład, poniższe biurko ma dwie szuflady, które gracze mogą otworzyć, aby sprawdzić, co jest w środku.
Detektory przeciągania i ograniczenia
Możesz łączyć detektory przeciągania z Constraints, na przykład marionetką. W poniższym ustawieniu uchwyty kontrolne są zakotwiczone, części ciała są niezwiązane, a ograniczenia trzymają marionetkę razem. Przesuwanie uchwytów za pomocą DragStyle TranslateViewPlane sprawia, że marionetka tańczy, a poszczególne części ciała mogą być również przesuwane za pomocą detektorów przeciągania, wszystko to podczas gdy model zachowuje swoją integralność.
Interfejsy użytkownika 3D
Interfejsy użytkownika 3D są łatwe do osiągnięcia dzięki detektorom przeciągania, na przykład dostosowywaniu jasności SpotLight w zależności od suwaka. Możesz również wykrywać osie X i Z indywidualnie, aby kontrolować dwa różne aspekty interfejsu użytkownika 3D, takie jak Size, Speed i Color emitera cząsteczek.
local model = script.Parent
local slider = model.SliderPart
local originPart = model.OriginPart
local emitter = script.Parent.EmitterPart.ParticleEmitter
local dragDetector = slider.DragDetector
dragDetector.ReferenceInstance = originPart
dragDetector.MinDragTranslation = Vector3.zero
dragDetector.MaxDragTranslation = Vector3.new(10, 0, 10)
local dragRangeX = dragDetector.MaxDragTranslation.X - dragDetector.MinDragTranslation.X
local dragRangeZ = dragDetector.MaxDragTranslation.Z - dragDetector.MinDragTranslation.Z
local MIN_PARTICLE_SIZE = 1
local MAX_PARTICLE_SIZE = 1.5
local MIN_PARTICLE_SPEED = 2.5
local MAX_PARTICLE_SPEED = 5
local COLOR1 = Color3.fromRGB(255, 150, 0)
local COLOR2 = Color3.fromRGB(255, 0, 50)
local function updateParticles(emitter)
local dragFactorX = (dragDetector.DragFrame.Position.X - dragDetector.MinDragTranslation.X) / dragRangeX
local dragFactorZ = (dragDetector.DragFrame.Position.Z - dragDetector.MinDragTranslation.Z) / dragRangeZ
-- Dostosuj rozmiar cząsteczek i prędkość na podstawie czynnika X detektora przeciągania
emitter.Size = NumberSequence.new{
NumberSequenceKeypoint.new(0, 0),
NumberSequenceKeypoint.new(0.1, MIN_PARTICLE_SIZE + ((MAX_PARTICLE_SIZE - MIN_PARTICLE_SIZE) * dragFactorX)),
NumberSequenceKeypoint.new(1, 0)
}
local speed = MIN_PARTICLE_SPEED + ((MAX_PARTICLE_SPEED - MIN_PARTICLE_SPEED) * dragFactorX)
emitter.Speed = NumberRange.new(speed, speed * 1.2)
-- Dostosuj kolor cząsteczek na podstawie czynnika Z detektora przeciągania
local color = COLOR2:Lerp(COLOR1, dragFactorZ)
emitter.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, color),
ColorSequenceKeypoint.new(1, color)
}
end
dragDetector:GetPropertyChangedSignal("DragFrame"):Connect(function()
updateParticles(emitter)
end)