拖放組件 link

Ren’Py含有一些拖放組件,這些組件允許使用滑鼠在界面上任意移動。關於拖拽功能的使用有以下要點:

  • 儲存窗口坐標,允許用戶重新調整窗口坐標。

  • 能把卡牌在界面上隨意拖拽的卡牌遊戲。(例如,solitaire紙牌)

  • 背包系統。

  • 拖拽記錄系統。

拖放組件提供不僅限於自身的拖放功能。這裡主要涉及兩個類。Drag類提供讓一些東西能夠在界面上拖動的功能,讓其他可拖動組件掉落在自己上面的功能,或者兩者兼有。DragGroup類提供了Drag的群組功能——當拖放發生時,作為同一個DragGroup內的一部分,組內所有Drag對象都需要做出響應。

拖放系統可以通過 界面語言 使用,或者直接用作可視組件。當你創建Drag對象後不再需要引用這些對象,最好使用界面語言。典型的情況是,可拖拽組件顯示了一個用於放置在界面裡的窗口。如果你在創建Drag對象後還需要引用,通常更好的做法是直接創建Darg對象並把它們添加到一個DragGroup對象中。

組件掉落 link

Ren’Py有兩種處理的組件掉落(drop)事件的方式:

  • 如果 mouse_drop 的值為True,拖拽組件會直接掉落在當前滑鼠位置的可承接組件掉落的組件上。

  • 如果 mouse_drop 的值為False,即預設值,拖拽組件掉落後會調整位置到正好完全覆蓋在背後的組件上。

與開始拖動組件事件不同,當啟用 focus_mask 後,組件掉落時會同時考慮上下兩個組件整個區域是否可拖拽和是否可承接掉落,包括區域內的透明像素。 創作者設計時就需要把這些問題納入考量,保證矩形邊緣的銳度。

可視組件 link

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) link

一個可視組件,提供了一個對象,可以在其有效區域內使用滑鼠拖拽。一個Drag對象還有供其他Drag對象可以掉落在上面的區域。

一個Drag對象可以在父對象(parent)內部隨意移動。通常,Drag的父對象是一個 Fixed()DragGroup

一個Drag對象有一個子組件。子組件的狀態反映出拖放操作的狀態:

  • selected_hover - 當其被拖拽時。

  • selected_idle - 當其被放下時。

  • hover - 滑鼠已點擊而拖拽未發生時。

  • idle - 其他情況。

拖拽句柄(handle)是一個子組件內的矩形。當滑鼠進到拖拽句柄裡的非透明像素上方時,就允許發生拖拽或點擊。 若 focus_mask 為True,可響應滑鼠事件的像素不能是透明的。

一個新創建的可拖拽組件會被添加到預設的DragGroup中。一個可拖拽組件只能在一個DragGroup裡。一旦被添加到另一個組中,就會自動被上一個組移除。

當某個Drag對象首次渲染時,如果從其所在的DragGroup中無法獲得坐標,就使用標準布局算法計算出它的左上角坐標。 一旦計算出其坐標,就將其儲存在Drag對象中,並忽略布局特性。

d

若存在,這就是Drag對象的子組件。如果不是None,Drag對象就使用子組件的樣式。

drag_name

若非None,表示可拖拽組件的名稱。也可以用作可拖拽對象的名稱特性(property)。如果有或曾經有一個可拖拽組件,名稱相同,也在DragGroup當中,它將取代原來的可拖拽對象的起始位置。

draggable

若為True,Drag對象可以使用滑鼠拖拽。

droppable

若為True,其他Drag對象可以放在該Drag對象上。

drag_raise

若為True,該Drag對象被拖拽是會升到最高層。如果其與其他Drag對象連在一起,相連的Drag對象都會升到最高層。

activated

當滑鼠在某個拖放組件上面並按下左鍵時,被調用的一個回調函數。調用時帶一個入參,為一個奏效的拖放組件列表。該回調函數的返回值會被忽略。

dragging

一個回調函數(或回調函數列表),當Drag對象正在被拖拽時調用。 有一個入參,即正在被拖拽的Drag對象列表。 若回調函數返回非None的值,該值會作為此次交互結果並返回。

dragged

一個回調函數(或回調函數列表),當Drag對象被拖拽時被調用。調用時使用兩個入參。第一個參數是被拖拽的Drag對象列表。第二個參數是某個可以掉落其上的Drag對象,或者None表示不會落下。如果回調函數返回一個非None,這個值也會作為此次交互行為的返回結果。

dropped

一個回調函數(或回調函數列表),當Drag對象落下時被調用。調用時使用兩個入參。第一個參數表示掉落在哪個Drag對象上。第二個參數是一個被拖拽的Drag對象列表。如果回調函數返回一個非None,這個值也會作為此次交互行為的返回結果。

當dragged和dropped回調函數被同一個事件消息觸發,那麼僅在dragged回調函數返回None的情況dropped回調函數才會被調用。

clicked

不使用入參的回調函數,當Drag對象僅僅被點擊而沒有移動的情況下調用。droppalbe也可以得到焦點並被點擊。如果回調函數返回一個非None,這個值也會作為此次交互行為的返回結果。

alternate

在電腦上點擊滑鼠右鍵,或行動裝置上長按螢幕,將會讓Drag對象運行該參數設置的行為。 移動平台需要先增加一個 config.longpress_duration

drag_handle

一個(x, y, width, height)形式的元組,給定了子組件拖拽句柄的坐標資訊。在這個元組中,整數被看作像質數的值,浮點數被看作子組件尺寸的比例分數。

drag_joined

使用當前Drag對象作為入參的回調函數。一般返回一個[ (drag, x, y) ]格式的元組列表,表示可拖拽組件連在一起組層一個單元。 xy 是各個拖拽組件之間的偏移量,與Drag左上角的坐標無關。

drag_offscreen

若為True,draggable可以移出螢幕。這樣設置有潛在風險,使用drag_joined或可以改變尺寸的Drag對象,一旦被拖離出螢幕,就沒有辦法把它們弄回來了。

mouse_drop

若為True,Drag對象會掉落在游標下第一層的droppable上。若為False,也是預設值,Drag對象掉落在overlap最大度數的droppab上。

drop_allowable

一個回調函數,調用後判斷當前Drag對象此次是否可以落下。 調用時需要兩個參數。第一個參數是用於判斷此次下落合理性的Drag對象。第二個參數是正在發生拖拽的Drag對象列表。

除了 d ,所有的參數都在Drag對象的欄位(field)中。除此之外,在Drag對象被渲染後,下列欄位裡的值也變成可用狀態:

x, y

Drag對象相對於自身父組件的坐標,單位為像素。

start_x, start_y

Drag對象相對於自身父組件的起始坐標,單位為像素。

w, h

Drag對象子組件的寬度和高度,單位為像素。

bottom(self) link

降低該可視組件的高度,降到其所在drag_group的底層。

set_child(d) link

將該Drag對象的子組件設為d。

snap(x, y, delay=0) link

修改Drag對象的坐標。如果Drag對象沒有顯示,坐標的改變瞬時完成。否則,坐標的改變會耗時 dalay 秒,生成線性移動的動畫。

top(self) link

升高該可視組件的高度,升到其所在drag_group的頂層。

class DragGroup(*children, **properties) link

表示一個Drag對象組。某個Drag對象受限於整個DragGroup。掉落只在同一個組內的Drag對象間發生。組內的Drag對象可以會被抬升高度。

DragGroup的布局類似 Fixed()

DragGroup構造函數的所有固定位置參數都是需要添加到DragGroup的Drag對象。

add(child) link

添加Drag對象作為DragGroup的子組件(child)。

get_child_by_name(name) link

返回該DragGroup中名為 name 的第一個子組件。

remove(child) link

移除該DragGroup中的子組件child。

樣例 link

這個樣例中,在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":
            # 確保窗口尺寸小於整個界面。
            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:

    # 作為背景的地圖。
    add "europe.jpg"

    # DragGroup確保每個偵探可以拖拽到每個城市。
    draggroup:

        # 偵探們
        drag:
            drag_name "Ivy"
            child "ivy.png"
            droppable False
            dragged detective_dragged
            xpos 100 ypos 100
            add "ivy.png"
        drag:
            drag_name "Zack"
            child "zack.png"
            droppable False
            dragged detective_dragged
            xpos 150 ypos 100
            add "zack.png"

        # 可選的城市。
        drag:
            drag_name "London"
            child "london.png"
            draggable False
            xpos 450 ypos 140
            add "london.png"
        drag:
            drag_name "Paris"
            draggable False
            child "paris.png"
            xpos 500 ypos 280
            add "paris.png"

label send_detective:
    "我們需要調查!應該派誰去哪裡?"

    call screen send_detective_screen

    "好的,我們派 [detective] 去 [city]。"

更複雜的系統需要使用更重要的程式技巧才能搞定。 Ren’Py cardgame framework 是一個在複雜系統如何使用拖放功能和製作卡牌遊戲兩方面都很有用的例子。

使用 as 分句可以將一個拖拽組件綁定到變數,這樣就可以直接調用組件的各類方法。

screen snap():

    drag:
        as carmen
        draggable True
        xpos 100 ypos 100
        frame:
            style "empty"
            background "carmen.png"
            xysize (100, 100)

            vbox:
                textbutton "London" action Function(carmen.snap, 450, 140, 1.0)
                textbutton "Paris" action Function(carmen.snap, 500, 280, 1.0)