드래그 앤 드롭
Ren'Py에는 마우스로 화면의 요소를 움직일 수 있는 끌어 놓기 디스플레이어블이 있습니다. 다음과 같은 용도로 사용할 수 있습니다.
사용자가 창을 옮기고 그 위치를 저장하도록 하기.
화면에서 카드를 끌어야 하는 카드 게임. 예: 솔리테어.
인벤토리 시스템.
끌어서 순서를 바꾸는 시스템.
끌어 놓기 디스플레이어블로 이러한 기능과 그 밖의 끌어 놓기 기능을 구현할 수 있습니다. 관련 클래스는 두 개입니다. Drag 클래스는 화면에서 끌 수 있는 요소, 다른 요소를 놓을 수 있는 대상, 또는 두 기능을 모두 하는 요소를 나타냅니다. DragGroup 클래스는 Drag의 그룹을 나타냅니다. 끌어 놓기가 이루어지려면 양쪽 Drag가 같은 그룹에 속해야 합니다.
끌어 놓기 시스템은 스크린 언어로 사용하거나 디스플레이어블로 직접 사용할 수 있습니다. 생성한 Drag를 나중에 참조할 필요가 없다면 스크린 언어가 적합합니다. 예를 들어 사용자가 화면에 배치하는 창을 끌 수 있게 할 때가 그렇습니다. 생성 후에도 Drag를 참조해야 한다면 직접 Drag를 만들고 DragGroup에 추가하는 편이 나은 경우가 많습니다.
놓기
Ren'Py가 놓기를 처리하는 방식은 두 가지입니다.
mouse_drop이 참이면 마우스 커서 바로 아래의 대상에 Drag를 놓습니다.
mouse_drop가 기본값인 거짓이면 Drag와 가장 많이 겹치는 드롭 대상에 놓습니다.
끌기를 시작할 때 focus_mask를 사용하는 것과 달리, 놓을 때는 투명 픽셀을 포함한 끄는 요소와 놓는 대상의 사각형 영역 전체를 고려합니다. 이를 고려해 끌어 놓기 디스플레이어블을 대체로 사각형에 가깝게 설계해야 할 수 있습니다.
디스플레이어블
- class Drag(d=None, drag_name=None, draggable=True, droppable=True, drag_raise=True, dragging=None, dragged=None, dropped=None, drag_handle=(0.0, 0.0, 1.0, 1.0), drag_joined=..., clicked=None, hovered=None, unhovered=None, mouse_drop=False, **properties)
자신을 감싸는 영역 안에서 끌 수 있는 객체를 나타내는 디스플레이어블입니다. Drag는 다른 Drag를 놓을 수 있는 영역을 나타낼 수도 있습니다.
Drag는 부모 안에서 움직일 수 있습니다. 일반적으로 부모는
Fixed()또는DragGroup이어야 합니다.Drag에는 자식이 하나 있습니다. 자식의 상태는 끌어 놓기 작업의 상태를 반영합니다.
selected_hover- 끌고 있는 동안입니다.selected_idle- 다른 요소를 놓을 수 있을 때입니다.hover- 마우스를 클릭하면 끌기가 시작되는 상태입니다.idle- 그 밖의 상태입니다.
끌기 핸들은 자식 내부의 사각형입니다. 끌거나 클릭하려면 마우스가 핸들 내부 픽셀 위에 있어야 합니다.
focus_mask가 True이면 해당 픽셀이 투명해서는 안 됩니다.새로 만든 끌기 요소는 기본 DragGroup에 추가됩니다. 하나의 DragGroup에만 속할 수 있으므로 두 번째 그룹에 추가하면 첫 번째 그룹에서 제거됩니다.
Drag를 처음 렌더링할 때 소속 DragGroup에서 위치를 알 수 없으면 표준 배치 알고리즘으로 왼쪽 위 모서리의 위치를 계산합니다. 위치를 한 번 계산한 뒤에는 배치 속성을 무시하고 Drag 안에 저장된 위치를 사용합니다.
Drag에 트랜스폼을 직접 적용하지 않아야 합니다. 대신 Drag의 자식에 적용하세요.
- d
지정하면 이 Drag의 자식입니다. child 스타일이 None이 아니라면 Drag는 이 인수보다 해당 스타일을 우선 사용합니다.
- drag_name
None이 아니라면 이 끌기 요소의 이름입니다. 끌기 객체의 drag_name 속성으로 접근할 수 있습니다. 같은 이름의 Drag가 DragGroup에 있거나 있었으면 그 Drag의 위치를 시작 위치로 사용합니다.
- draggable
참이면 화면에서 마우스로 이 Drag를 끌 수 있습니다.
- droppable
참이면 이 Drag 위에 다른 Drag를 놓을 수 있습니다.
- drag_raise
참이면 끌 때 이 Drag를 맨 위로 올립니다. 다른 Drag와 연결되어 있으면 연결된 Drag를 모두 올립니다.
- activated
Drag 위에서 마우스 버튼을 누를 때 호출하는 콜백 또는 콜백 리스트입니다. 끌고 있는 Drag 리스트를 인수 하나로 받습니다. 반환값은 무시합니다.
- dragging
Drag를 끄는 동안 호출하는 콜백 또는 콜백 리스트입니다. 끌고 있는 Drag 리스트를 인수 하나로 받습니다. None 이외의 값을 반환하면 그 값이 상호작용의 결과가 됩니다.
- dragged
Drag를 끈 뒤 호출하는 콜백 또는 콜백 리스트입니다. 인수 두 개를 받습니다. 첫 번째는 끌고 있는 Drag 리스트이며, 두 번째는 놓는 대상 Drag 또는 놓기가 이루어지지 않았을 때의 None입니다. None 이외의 값을 반환하면 그 값이 상호작용의 결과가 됩니다.
- dropped
이 Drag 위에 다른 Drag를 놓으면 호출하는 콜백 또는 콜백 리스트입니다. 인수 두 개를 받습니다. 첫 번째는 놓는 대상 Drag이며, 두 번째는 끌고 있는 Drag 리스트입니다. None 이외의 값을 반환하면 그 값이 상호작용의 결과가 됩니다.
같은 이벤트로 dragged와 dropped 콜백이 모두 발생하면 dragged가 None을 반환한 경우에만 dropped를 호출합니다.
- clicked
Drag를 움직이지 않고 클릭하면 호출하는 콜백입니다. 클릭한 Drag를 인수 하나로 받습니다. 놓기 대상에도 포커스를 주고 클릭할 수 있습니다. None 이외의 값을 반환하면 그 값이 상호작용의 결과가 됩니다.
- alternate
데스크톱에서는 마우스 오른쪽 버튼으로 클릭할 때, 모바일에서는 움직이지 않고 길게 누를 때 실행하는 액션입니다. 모바일에서 너무 일찍 실행된다면
config.longpress_duration을 늘려야 할 수 있습니다.- hovered
Drag가 포커스를 얻을 때 실행할 액션입니다.
- unhovered
Drag가 포커스를 잃을 때 실행할 액션입니다.
- tooltip
Drag 위에 마우스를 올렸을 때 표시할 툴팁입니다.
- drag_handle
자식 내부의 끌기 핸들 위치를 나타내는 (x, y, width, height) 튜플입니다. 이 튜플은 위치 값을 받습니다.
- drag_joined
현재 Drag를 인수로 받아 호출됩니다. 함께 끌 요소들을 나타내는 [ (drag, x, y) ] 튜플 리스트를 반환해야 합니다. x와 y는 Drag 사이의 상대 오프셋이며, 현재 Drag의 모서리를 기준으로 한 값이 아닙니다. drag는 연결할 Drag 객체 또는 해당 Drag의 drag_name입니다.
- drag_offscreen
화면 밖으로 끌 수 있는 조건을 결정합니다. drag_joined를 사용하거나 크기가 바뀌는 Drag에 화면 밖 끌기를 허용하면, Drag가 화면에서 완전히 사라져 다시 가져올 방법이 없어질 수 있으므로 주의해야 합니다.
다음 중 하나여야 합니다.
- False
화면 밖으로 끌 수 없습니다. 기본값입니다.
- True
어느 방향으로든 화면 밖으로 끌 수 있습니다.
- "horizontal"
가로 방향으로만 화면 밖으로 끌 수 있습니다.
- "vertical"
세로 방향으로만 화면 밖으로 끌 수 있습니다.
- (width, height)
width와 height는 모두 정수여야 합니다. (width, height) 크기의 부분이 화면에 남아 있는 동안 화면 밖으로 끌 수 있습니다. 따라서 (100, 100)이면 나머지 부분이 화면 밖으로 나가도 최소 100x100픽셀 영역은 화면에 남습니다. 끄는 디스플레이어블의 너비와 높이를 지정하면 화면 밖 끌기를 전혀 허용하지 않는 것과 같습니다.
- (min_x, max_x, min_y, max_y)
min_x, max_x, min_y, max_y는 각각 정수입니다. min_x는 왼쪽 경계와의 픽셀 거리, max_x는 오른쪽 경계와의 픽셀 거리입니다. min_y와 max_y도 각각 위쪽과 아래쪽 경계에 같은 방식으로 적용됩니다. Drag의 가장자리 중 하나가 지정한 경계에 닿을 때까지 이동할 수 있습니다. (0, 0, 0, 0)은 화면 밖 끌기를 전혀 허용하지 않는 것과 같습니다.
예를 들어 (-100, 200, 0, 0)은 화면 왼쪽으로 100픽셀, 오른쪽으로 200픽셀까지 벗어나도록 허용하지만 위아래로는 화면 밖으로 끌 수 없습니다.
화면 내부에서 이동 범위를 제한하는 데 사용할 수도 있습니다. (200, -200, 200, -200)은 화면 가장자리에서 200픽셀 안쪽 범위에만 Drag가 있도록 제한합니다.
Drag가 머물러야 하는 추가 경계를 주위에 두는 것으로 생각할 수 있습니다. 이 경계는 화면 밖으로 나갈 수도 있습니다.
- callable
drag_offscreen에 호출 가능 객체를 지정할 수 있습니다. 끈 위치의 왼쪽 위 모서리를 나타내는 x, y 두 인수를 받아야 하며, Drag가 위치해야 할 새 (x, y) 튜플을 반환해야 합니다. Drag를 움직일 때마다 자주 호출됩니다. 예를 들어 다음 함수는 300픽셀 간격으로 Drag의 위치를 맞춥니다.
def drag_snap(x, y): if y < 300: y = 0 elif y < 600: y = 300 else: y = 600 return 200, y
- mouse_drop
참이면 커서 아래의 첫 놓기 대상에 놓습니다. 기본값인 거짓이면 가장 많이 겹치는 대상에 놓습니다.
- drop_allowable
이 대상이 현재 끌고 있는 Drag들을 받아들일지 결정하는 콜백입니다. 인수 두 개를 받습니다. 첫 번째는 허용 여부를 결정할 Drag이며, 두 번째는 끌고 있는 Drag 리스트입니다.
- snapped
Drag의 스냅 애니메이션이 끝날 때 호출하는 콜백 또는 콜백 리스트입니다. 인수 네 개를 받습니다. 첫 번째는 스냅 애니메이션 중이던 Drag, 두 번째와 세 번째는 목표 x, y 좌표, 네 번째는 애니메이션이 정상 완료되었으면 True, 도중에 잡는 등의 이유로 중단되었으면 False인 불리언입니다. 예를 들어 다음 함수는 스냅 애니메이션이 중단되면 Drag의 start_x와 start_y를 원래 목표 위치로 설정합니다.
def snapped_callback(drag, x, y, completed): if not completed: drag.start_x = x drag.start_y = y
d를 제외한 모든 매개변수는 Drag 객체에서 같은 이름의 필드로 접근할 수 있습니다. 또한 렌더링된 뒤에는 다음 필드도 사용할 수 있습니다.
- x, y
부모를 기준으로 한 Drag의 픽셀 단위 위치입니다.
- start_x, start_y
부모를 기준으로 한 끌기 시작 위치입니다. 단위는 픽셀입니다.
- grab_x, grab_y
Drag를 잡은 위치의 x, y 좌표입니다. 부모를 기준으로 하며 단위는 픽셀입니다.
- last_drop
현재 Drag를 놓으면 받아들일 수 있는 마지막 Drag입니다. 유효한 Drag가 없으면 None입니다.
- snapping
이 Drag가 스냅 애니메이션 중이면 True입니다.
- w, h
Drag 자식의 픽셀 단위 너비와 높이입니다.
- bottom()
이 디스플레이어블을 drag_group의 맨 아래로 내립니다.
- set_child(d)
이 Drag의 자식을 d로 바꿉니다.
- snap(x, y, delay=0, warper=None)
Drag의 위치를 바꿉니다. 표시 중이 아니면 즉시 바뀝니다. 표시 중이면 delay초 동안 이동하며 선택적으로 워퍼를 사용할 수 있습니다. 워퍼를 지정하지 않으면 선형으로 이동합니다.
- top()
이 디스플레이어블을 drag_group의 맨 위로 올립니다.
- class DragGroup(*children, **properties)
Drag 그룹을 나타냅니다. Drag는 자신의 DragGroup 경계로 제한됩니다. 같은 DragGroup 안의 Drag끼리만 놓기가 작동합니다. DragGroup 안에 있을 때만 Drag를 위로 올릴 수 있습니다.
DragGroup은
Fixed()와 같은 방식으로 배치됩니다.DragGroup 생성자의 모든 위치 인수는 Drag여야 하며, 해당 DragGroup에 추가됩니다.
- min_overlap
놓기를 허용하는 데 필요한 최소 겹침 픽셀 수를 나타내는 정수입니다.
- add(child)
Drag여야 하는 child를 이 DragGroup에 추가합니다. 다른 모든 자식보다 위에 추가됩니다.
- get_child_by_name(name)
drag_name이 name인 첫 번째 자식을 반환합니다.
- remove(child)
이 DragGroup에서 child를 제거합니다.
예제
다음 say 스크린은 사용자가 창을 끌어 화면의 원하는 위치에 놓도록 합니다.
screen say(who, what):
drag:
drag_name "say"
yalign 1.0
drag_handle (0, 0, 1.0, 30)
xalign 0.5
window id "window":
# Ensure that the window is smaller than the screen.
xmaximum 600
has vbox
if who:
text who id "who"
text what id "what"
다음은 끌기가 게임 진행에 영향을 주는 방법을 보여 주는 더 복잡한 예제입니다. 끌기로 캐릭터를 특정 장소에 보내는 방법을 보여 줍니다.
init python:
def detective_dragged(drags, drop):
if not drop:
return
store.detective = drags[0].drag_name
store.city = drop.drag_name
return True
screen send_detective_screen:
# A map as background.
add "europe.jpg"
# A drag group ensures that the detectives and the cities can be
# dragged to each other.
draggroup:
# Our detectives.
drag:
drag_name "아이비"
droppable False
dragged detective_dragged
xpos 100 ypos 100
add "ivy.png"
drag:
drag_name "잭"
droppable False
dragged detective_dragged
xpos 150 ypos 100
add "zack.png"
# The cities they can go to.
drag:
drag_name "런던"
draggable False
xpos 450 ypos 140
add "london.png"
drag:
drag_name "파리"
draggable False
xpos 500 ypos 280
add "paris.png"
label send_detective:
"조사해야 해요! 누구를 어디로 보낼까요?"
call screen send_detective_screen
"좋아요. [detective] 탐정을 [city]에 보내죠."
더 복잡한 시스템을 제대로 구현하려면 상당한 프로그래밍 실력이 필요합니다.
as 절로 Drag를 변수에 연결하면 그 변수로 Drag의 메서드를 호출할 수 있습니다.
screen snap():
drag:
as carmen
draggable True
xpos 100 ypos 100
frame:
style "empty"
background "carmen.png"
xysize (100, 100)
vbox:
textbutton "런던" action Function(carmen.snap, 450, 140, 1.0)
textbutton "파리" action Function(carmen.snap, 500, 280, 1.0)