Пользователи · Новые посты · Правила форума · Поиск по форуму · · · Регистрация
Страница 1 из 11
Форум о визуальных новеллах » Для разработчиков » Ren'Py » Помогите с созданием экрана (screen)
Помогите с созданием экрана (screen)

MH
Дата: Среда, 13 Декабря 2017, 00:55 | Сообщение # 1
Проверенный
Сообщений: 12
Награды: 0
Репутация: 0
Замечания: 0%
Привет.
Помогите, плиз, гуманитарию - в программировании почти не разбираюсь, и при создании новеллы столкнулся с трудностями. Суть проблемы такая - нужно создать экран, как на приложенной картинке используя свойства drag and drop - т.е. чтобы объекты можно перетаскивать.

Описание экрана:
1) Есть несколько условных объектов (1,2, 3...), которые можно перетаскивать по экрану.
2) Есть две неподвижных зоны, зона №1 и зона №2, куда эти объекты можно сбрасывать, после чего объекты должны пропадать, вернув некоторые переменные (ну, допустим. если сбросить объект 1 в зону 1, то переменная answer станет равной 42) не прерывая работу с экраном
3) Зона №3 - информационная - там выводятся сообщения о всех манипуляциях с объектами (допустим "объект 1 сброшен в зону №1") Также там должны выводиться сообщения, если просто кликнуть по объекту, но никуда его не тащить (пример "это объект 5 - переместите его в зону 1 или зону 2")
4) В правом нижнем углу отображается значение переменной, которая увеличивается или уменьшается в зависимости от того, какой объект куда сброшен. Условно говоря - это мана, она расходуется на операции с объектами, но может и возрастать, если нужный объект попадет в нужную зону.
5) в левом нижнем углу - кнопка возврата/выхода.

Собственно, в мануале по renpy есть код, позволяющий реализовать нечто подобное (пример с детективами):

Код
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 "Ivy"
            child "ivy.png"
            droppable False
            dragged detective_dragged
            xpos 10 ypos 10
            
        if game:
            drag:
                drag_name "Zack"
                child "zack.png"
                droppable False
                dragged detective_dragged
                xpos 10 ypos 300

        # The cities they can go to.
        drag:
            drag_name "London"
            child "london.png"
            draggable False
            xpos 450 ypos 100
            
        if game:
                drag:
                    drag_name "Paris"
                    draggable False
                    child "paris.png"
                    xpos 450 ypos 300


Этот код худо бедно позволяет мне создать зоны №1 и №2 плюс перемещаемые drag-объекты, но с остальным полный провал. Когда читаю мануалы на английском, просто башка трещать начинает от всех этих функций.
Подскажите хотя бы примерно как должен выглядеть код, реализующий все пять пунктов.
Если лень писать весь код (я Вас понимаю...) подскажите хотя бы как сделать зону №3 и как сделать, чтобы переменным присваивались разные значения в зависимости от того, какой объект куда сброшен.
Помогите, плиз.
Прикрепления: 7152371.jpg(100Kb)

Ikuku
Дата: Среда, 13 Декабря 2017, 14:14 | Сообщение # 2
Проверенный
Сообщений: 21
Награды: 2
Репутация: 55
Замечания: 0%
1. Первое, что нужно - это экран, который, во избежание ненужных кликов вне его, будем вызывать командой call. В этот экран мы запихнем все нужные зоны и кнопки.

Объекты - это draggable drag-объекты. Их можно таскать и бросать, но нельзя бросать на них. Неподвижные зоны 1 и 2 - это droppable drag-объекты. На них можно бросать, но их нельзя таскать. За это отвечают свойства draggable и droppable, которые мы установим в False для соответствующих зон.

2. Инфо-зона (№3) будет фреймом с текстом внутри. Нам от нее требуемтся только чтобы она выводила на экран содержимое переменной. Назовем эту переменную current_message, и зададим ей дефолтное значение оператором default.

3. Функции. Для объектов нам нужно два вида функций: срабатывание по клику и срабатывание по перетаскиванию и сбросу. Первая функция устанавливается в свойстве clicked drag-объекта. Вторая - в свойстве dropped.

От функции по клику нам пока требуется исключительно смена сообщения. Т.е. изменение значения переменной current_message в зависимости от кликнутого объекта. Т.е. внутри функции должно быть что-то вроде:

если кликнутый объект == объекту 1:
запихнуть в переменную current_message такой-то текст
иначе если кликнутый объект == объекту 2:
запихнуть в переменную current_message сякой-то текст

И так далее. Current_message нам доступна внутри функции (надо только объявить, что мы обращаемся именно к ней, с помощью оператора global). А что насчет "кликнутый объект"? А текущий кликнутый объект мы, по идее, должны были бы передать в виде параметра в функцию-обработчик клика, но! В свойство clicked мы должны положить такую функцию, которая не принимает параметров (так уж оно устроено). Так как сказать функции о том, на какой именно объект клацнули? Тут два способа. Один - с помощью curried-функций, это долго объяснять, можете сами почитать вот тут: https://lemmasoft.renai.us/forums/viewtopic.php?p=319388#p319388 (это тема!). Второй чуть проще для понимания: мы заводим глобальную переменнию currently_clicked, в которую будем класть идентификатор текущего объекта.

Итак, код (пока в ошметках, потом в конце будет целиком код проекта):
для curried-функции:
Код
def object_clicked(objId):
    global current_message

    if objId == 1:
        current_message = "Вы кликнули на объект %s" % objId
    if objId == 2:
        current_message = "Вы жмякнули на объект %s" % objId
    return None

curriedObjClicked = renpy.curry(object_clicked)

drag:
    drag_name "Айви"
    droppable False
    clicked curriedObjClicked(1)


Для комбо с SetVariable:
Код
def object_clicked():
    global current_message, objId

    if objId == 1:
        current_message = "Вы кликнули на объект %s" % objId
    if objId == 2:
        current_message = "Вы жмякнули на объект %s" % objId
    return None

drag:
    drag_name "Зак"
    droppable False
    clicked [ SetVariable("objId", 2), object_clicked ]

Во втором случае список функций/экшенов в свойстве clicked берется в квадратные скобки, а функция object_clicked там же вызывается без круглых скобок (это важно).

4. С кликом разобрались. Что с перетаскиванием и сбросом? Тут все проще, потому что функция внутри свойства dragged получает два параметра: объект, который бросили, и объект, в который бросили. То есть просто пишем функцию (код ниже).

Комментарии по коду:
drags[0].drag_name переводится как "возьми значение атрибута drag_name первого объекта в списке перетаскиваемых объектов" (он у нас единственный, мы таскаем объекты по одному). drags - это массив объектов, drags[0] берет из него первый элемент (нумерация списков/массивов идет с нуля)
drop.drag_name перевдится как "возьми значение атрибута drag_name объекта, на который уронили тягаемый объект". поскольку наша дроп-зона по сути своей - тоже draggable-объект, только с иными свойствами, его имя хранится в том же атрибуте.

Внутри if'ов можно задавать значения переменным по своему усмотрению и в зависимости от нужд: вычитать очки из общего числа очков маны, менять сообщения и менять значения других переменных, которые будут управлять отображением объектов на экране. Обратите внимание на условие вывода на экран объекта "Камень".

5. Кнопка возврата/выхода будет прятать наш экран экшеном Return.

Код тестового проекта:
Код
default current_message = ""
default mana_points = 42
default stones_left = 3

init python:
    def object_dragged(drags, drop):
        global current_message, mana_points, stones_left

        if not drop:
            return

        if drags[0].drag_name == "Атака" and drop.drag_name == "Враг":
            mana_points -= 10
            current_message = "Отличным ударом вы прожгли во враге дыру!"

        if drags[0].drag_name == "Атака" and drop.drag_name == "Герой":
            mana_points -= 10
            current_message = "Какое нелепое самоубийство!"
            # return True # этой командой мы можем закрыть окно и прыгнуть на метку конца игры, например!

        if drags[0].drag_name == "Камень":
            current_message = "Герой бросается камнями."
            stones_left -= 1

        if drags[0].drag_name == "Шутка":
            current_message = "Ого! Какой смешной, оказывается, %s!" % drop.drag_name.lower()

        renpy.restart_interaction()
        
        return None

    def object_clicked(objId):
        global current_message

        if objId == 1:
            current_message = "Вы кликнули на объект %s" % objId

        else:
            current_message = "Вы жмякнули на объект %s" % objId

        renpy.restart_interaction()
        return None

    curriedObjClicked = renpy.curry(object_clicked)

screen objects_screen:
    draggroup:
        xysize(1.0, 1.0)

        # Объект
        drag:
            drag_name "Атака"
            droppable False
            clicked curriedObjClicked(1)
            dragged object_dragged
            xalign .5 yalign .4

            label "Файербол" background "#000"

        # Объект
        if stones_left > 0:
            drag:
                drag_name "Камень"
                droppable False
                clicked curriedObjClicked(3)
                dragged object_dragged
                xalign .5 yalign .5

                label "Камень" background "#000"

        # Объект
        drag:
            drag_name "Шутка"
            droppable False
            clicked curriedObjClicked(2)
            dragged object_dragged
            xalign .5 yalign .3

            label "Шутеечка за триста" background "#000"

        # Зона слева
        drag:
            drag_name "Враг"
            draggable False

            frame:
                xysize(.3, .7)
                background "#000"
                text "Кинуть во врага"

        # Зона справа
        drag:
            drag_name "Герой"
            draggable False
            xalign 1.0
            
            frame:
                xysize(.3, .7)
                background "#000"
                text "Наложить на себя"

    # Третья зона, контейнер для сообщений
    frame:
        xysize(.7, .3)
        xalign .5
        yalign 1.0
        background "#000"

        text current_message xalign .5

    # кнопка выхода
    textbutton "Выход" action Return xalign .1 yalign .9

    # Индикаторы
    vbox:
        xalign .9 yalign .9
        label "Очки маны: %s" % mana_points
        label "Камней в карманах: %s" % stones_left

label start:
    call screen objects_screen

    "Конец!"

MH
Дата: Среда, 13 Декабря 2017, 15:08 | Сообщение # 3
Проверенный
Сообщений: 12
Награды: 0
Репутация: 0
Замечания: 0%
Огромное спасибо за помощь с кодом! И отдельная благодарность - за развёрнутое описание реализации всех функций. Теперь чуть лучше понимаю, как оно должно работать.
Ещё раз спасибо love Ушел разбираться)
Форум о визуальных новеллах » Для разработчиков » Ren'Py » Помогите с созданием экрана (screen)
Страница 1 из 11
Поиск: