Перейти к содержанию

cliva

Игроделы
  • Постов

    383
  • Зарегистрирован

  • Посещение

  • Победитель дней

    57

Весь контент cliva

  1. Порифьевич какой-то, а от яндекса только абзац получить можно, ограничение по знакам в поле ввода
  2. @dmitryartist Победит Вонка, это его поприще Вообще идея не такая и плохая, давно витала в воздухе, но всё никак не не была сформулирована, я бы вероятно даже поучаствовал UPD: Именно истории достаточно сложно сочинять с ИИ, достаточно часто сбивается, пытается всех с друг другом подружить или влюбить, путается во временных рамках, нужно направлять ход генерации в нужное русло, за 20-25 минут получилось что-то такое: Но, я пробовал только один генератор историй, который понимает русский язык напрямую, возможно чатГПТ справится куда лушче, просто мне было лень регать)
  3. @gzoolooz hac.c3p Добавлять объекты в иерархию - через шифт выделить и правой кнопкой мыши: контейнеры в колонке слева: Про иерархию в с3 лучше загугли, по моему даже где-то на форуме есть объяснение что это такое, а контейнер - просто добавляет невидимый pick для объектов в контейнере, допустим есть герой и его полоска хп, условие "если герой столкнулся с шипами - отнять 5 из полоски хп" - будет работать нормально для всех копий героя
  4. meshes.c3p Далеко до математического идеала, но приближённо, формула спирали дискретно похоже выводится) Жёлтые точки - для дебага нужны были, без них всё работает - можно удалить без последствий
  5. Проблема в построении логики, сейчас я прочитаю как это работает: При столкновении ракеты со стеной - перестать спавнить весь дым(ибо какой именно - не указано), уничтожить весь огонь(опять же не указано), уничтожить ракету(тут указано - та которая столкнулась со стеной) Твою проблему можно решить кучей способов, вот один из них: Или не мучаться с pin и воспользоваться иерархией или контейнером
  6. Можно, если не использовать "use strict", то есть - в консоли допустимо, в С3 строгий режим включён по дефолту, и кажется выключить будет более проблемно, чем не забыть написать let)
  7. Если билеты и деньги выдавал кодом - то 4 цикла норма, но вообще подразумевалось, что деньги и билеты будут выданы в самом С3 редакторе) Второе задание наверное немного перегнул, рассчитывая что азы приёмов программирования знать должен каждый, либо не совсем корректно расписал задание, вот решение: Именно!) А вообще это достаточно удобный формат хранения каких-нибудь карт, только вместо 0 и 1 - куда больше значений Второй рантайм - где-то ты ошибся, обозначив рантайм с большой буквы, он попал в подсказки А функции - с маленькой это функция из js, стандартная, с большой - какой-то объект, с которым мне ещё не доводилось работать, а функция со звёздочкой - вообще не понятная для меня сущность, её даже в консоль вывести нельзя)
  8. @Parahod в четвёртом уроке - 2048 вторая часть полностью завязана на объектах, правда пик там сугубо через UID
  9. .destroy() - метод уничтожающий объект, не совсем, это свойства объекта, объект стоит рассматривать не как понятие из С3, а как понятие из JS, методы - это функции которые принадлежат классу (а в следствие и определённому объекту), а класс - можно понимать как инициализатор объекта, то есть - с помощью класса мы описываем как появится объект, с какими параметрами и методами, наверное в следующем уроке будут классы)
  10. Math.PI, вся математика js - в Math контейнер в С3 - неосязаемая связь, реализованная через IID, авто-pick грубо говоря, реализация его замены - не сложная штука один цикл где счётчик i - IID объектов человечка и текста, но кодом можно управлять иерархией, getChild.. кажется Да, так как это добавляемы скрипт, а не главный, он служит надстройкой к игре, управлять переменными можно через методы классов или функциями, условия в привычном для С3 понимании будут в четвёртом уроке, а сам условный оператор if, кажется я рассматривал в третьем тут уже недостаток самой среды разработки и её разрозненности "код отдельно, ивенты отдельно и между ними интерфейсный мост", тут только замена через ctrl+F
  11. @Parahod Попробуй тоже самое на виз. программировании сделать)
  12. @dmitryartist возвращает массив пикнутых объектов, если объект один - не уверен что есть доп. обработчик, но проверить не сложно будет)
  13. test11.c3p https://ru.wikipedia.org/wiki/Теорема_Пифагора
  14. @dmitryartist scroll_test.c3p Просто нужно указать с какого слоя брать координату курсора LayerScrollX("normal")+(scroll_start_x-Mouse.X("normal"))
  15. Отлично! отправка съела весь текст и оставила только картинки, но я смог достать его из дампа, так что если где-то мысль выглядит не цельно - сильно не удивляемся, а задаём вопросы, постараюсь ответить и привести урок в более надлежащий вид А восстановление выглядело весьма забавно... Некоторые куски повторялись, где то буквы были заменены уже чем-то непонятным, один блок вообще был разорван, наверное не стоило пытаться доставать вкладку из истории, а сразу в дамп лезть
  16. Собственно само продолжение Добавим в проект спрайт сделаем ему размер 64х64 и нарисуем что-то такое: также добавим текст, который будет поверх этого блока, подберём размер, чтобы влезало "2048" и добавим текст в иерархию к блоку Теперь добавим tiledbackground, он будет выполнять декоративную роль сетки, а также будет точкой отсчёта для заполнения, чтобы было покрасивее - я сделал сдвиг по одному пикселю, это стоит позже учитывать: Вот весь список объектов в проекте: Теперь самое время оживить всё это дело, добавим пару постоянных и пару переменных, чтобы потом было легче работать: В OnBeforeProjectSart добавляем положение сетки: +1 из-за сдвига, учитываем сразу Удаляем все блоки во время gameInit() и разрешим игроку нажимать кнопки (мы пока и не запрещали, это шаг немного в будущее): Запустим, чтобы увидеть ошибку в консоли и подвинем строку rt = runtime; выше чем gameInit() - его лучше вообще в самом низу держать, иначе баги могут вылезать из-за неправильной очерёдности комманд Теперь при запуске все блоки удаляются, сколько бы мы их не оставили на макете Модифицируем addBlock - заставим его создавать блоки в нужном месте с помощью конструкции runtime.objects.block.createInstance(слой, X, Y, иерархия включена = true/false ); Запускаем и видим такую картину: а значит мы на верном пути, теперь заставим текст отображать правильное число, для этого добавим пару вспомогательных переменных - текущего IID и текущего блока - и чтобы выбрать текст из иерархии воспользуемся .getChildAt(0): Запускаем и любуемся: Теперь вспоминаем, что мы использовали трехмерный массив, у нашей двумерной таблицы есть как бы второй слой, сделаем чтобы он хранил uid блоков, чтобы потом можно было к ним удобно обращаться: можно реализовать перемещение, так как блоки будут двигаться какое-то время - во время их движения нужно запретить игроку управление, для этого у нас и есть переменная control - модифицируем условие ввода клавиш управления: и после нажатия сразу control = false, код уже подходил к границе экрана, а потому я поставил переносы: Запускаем и проверяем, что после первого хода управление отключено, если всё так - продолжаем, сдвинем блоки на новые места, в этом нам поможет как раз таки второй слой, как помним он хранит uid блоков, а значит всё что надо сделать - когда произошли все движения в массиве заставить твином двигаться блок к своему новому месту, создадим функцию, которая будет приводить в движение все блоки: (ещё и картинка скораптилась, если размыта -> нажмите на неё) и будем вызывать эту функцию по нажатию кнопки, перед step(), запускаем и наблюдаем: Двигается, но новый блок надо создавать после движения блоков, воспользуемся задержкой: В остальных трёх случаях - аналогично + можно возвращать контроль игроку во время step(), можно запускать и двигать блоки, пока не замусорим всё поле, пора добавить обработку удаления блоков, добавим к началу файла и усовершенствуем merge*(): С остальными тремя - также, создадим функцию удаления блоков: И будем вызывать её во время step(): Можем уже запустить, поиграть и заметить, что положение блоков на поле соответствует положению на тайлмапе Теперь нужно привести в соответствие числа, для этого модифицируем moveAllBlock(): И по сути - игра уже готова: Можно добавить цветовое различие блокам, я срежу и сделаю не очень красиво - через color, добавлю строку rt.getInstanceByUid(y[1]).colorRgb = [1-getBaseLog(2,y[0])/12,1,1-getBaseLog(2,y[0])/12] в moveAllBlock(): И остался ещё визуальный глич когда два блока стоят далеко от стены у которой соединятся, один из блоков остаётся на месте, исправляется этот глич не сильно сложно - нужен дополнительный массив, в который нужно собрать остающиеся на месте блоки и подвинуть их насильно, написать решение этого глича - часть домашнего задания Домашнее задание: - Сделать цвета плиткам покрасивее - с помощью кадров анимаций или заготовленного массива с rgb цветами - решить глич (решение уже есть в исходнике, но всё же пытаемся сами) - Сделать 8192: увеличить поле до 5х5 и * добавить эффектов пульсации блокам, когда они появляются и сливаются, сделать игру более дружелюбной Исходник урока - twentyFortyEight.c3p
  17. Четвёртый урок - 2048 без единого ивента В нём мы создадим всеми любимую игру 2048, забудем для чего нужны ивенты, немного пострадаем и будем много дебажить
  18. 2048 Этот урок обещает быть долгим и не очень простым, потому советую включить спокойную музыку, запастись терпением, чаем/кофе и какой-нибудь закуской, оливье тоже подойдёт) Безотлагательно начинаем начинать! Создаём новый пустой проект и сразу делаем так: Это нам ни к чему, сегодня играем по серьёзному Создаём файл скрипта и не меняем настройки, этот файл будет являться основным/главным скриптом, о чём и гласит его название на английском, в файле уже будет какой-то код и он нам пригодится, для лучшего понимания комментарии из кода можно вставить в переводчик, но если просто то в этом коде: Есть три заготовленные области, с 5 по 11 строку - код выполнится до того, как подгрузятся все объекты, поэтому с ними работать ещё нельзя, с 13 по 20 строку - аналог условия on start of layout, но если бы мы ещё использовали ивенты, то эта часть кода сработала бы до start of layout, объекты уже загружены и с ними можно начинать работу, а с 22 по 25 аналог условия every tick Теперь добавим в проект Tilemap, сначала сделаем простой варианты игры, без плавностей и лишних спрайтов, так скажем дебаг-версия, для того чтобы быстрее написать основную логику и не отвлекаться на лишние действия, добавим и нарисуем в нём такие циферки: Ага, я не заморачивался) На макете зададим ему правильный размер и разместим где-нибудь на видном месте, и можем приступать к коду, нам нужно как-то отследить нажатие клавиши, мы бы могли использовать стандартный плагин клавиатуры, но зачем он нам, когда js и так позволяет отследить нажатие клавиш, при чём даже более гибко, чем это позволяет интерфейс встроенного плагина, но для этого нам нужно познакомиться с браузерными событиями, а именно с методом addEventListener, он позволяет один раз задать, что именно будет происходить после определённого события, событием может быть клик, поднятие клавиши, опускание клавиши, наведение мышью и тд, но это уже ближе к веб-разработке, нам же нужна работа с клавиатурой, и нам в этом поможет такая конструкция: document.addEventListener('keydown', function(event) { if (event.code == "KeyR") { alert("R"); } }) //также в некоторых кругах приняты такие сокращения: document.addEventListener('keydown', (e) => { if (e.code=="KeyR") alert("R") }) //этот код лучше проверять на отдельной вкладке браузера, иначе придётся перезагружать страницу Как можно увидеть мы добавили в обработчик браузера действие, для события нажатия клавиши R (document. - это интерфейс работы с веб-страницей) Игру с чего-то надо начинать, начнём с трёхмерного массива - 4х4х2, зачем нам третье измерение, если игра двумерная? - верхний слой ячеек 4х4 будет отображать числа, нижний слой нам пригодится позже, создаём пустой массив в самом начале кода, я его назвал ary и сразу задумался о рестарте/конце игры, а потому заполнять нулями буду его в функции gameInit(): Теперь можно запустить проект и убедится в своих выведя в консоль содержимое массива, но уже хотелось бы видеть результат на тайлмапе, вспоминаем что мы делаем игру 2048 и как нумеруются тайлы в тайлмапе, нужно как-то выявить закономерность между парами чисел: 0-0 2-1 4-2 8-3 16-4 32-5 64-6 ... если знакома математика и информатика можно заметить что не считая пары нулей это степени двойки, а значит нам понадобятся логарифмы, в js есть только натуральный логарифм, но нам это не сильно помешает, пользуемся свойствами логарифмов: function getBaseLog(x, y) { return Math.log(y) / Math.log(x); } Чтобы отображать все плитки на тайлмапе воспользуемся тем, что если прибавить 1 к значению, посчитать из этого значения логарифм и выделить целую часть получится логарифм начального значения (кроме 0 естественно, для этого мы и делаем это): Избавились от -inf и ничего не потеряли, хак работает, никаких доп. условий не надо, а значит добавляем в Tick(runtime) отображение всего массива на tilemap: для проверки можно заполнить массив значениями : и получить: Возвращаем нули взад, время написать функцию, которая будет ставить блок в случайное пустое место, в этой игре есть есть правило появления блока - с шансом в 10% это будет 4, в остальных случаях - 2, есть два способа поставить блок в случайное пустое место - тыкаться в случайное место и если оно пустое ставить туда блок или же знать все пустые места и выбрать одно из них. Первый способ не очень любим теорией вероятности и очень сложно определить, а есть ли вообще место для установки, потому реализуем второй способ - проходимся по всем ячейкам, если текущая ячейка пустая - заносим в новый массив положение этой ячейки, после того как прошлись по всем ячейкам - выбираем случайный элемент из нового массива и ставим по координатам новый блок, это место гарантированно пустое. Чтобы получить случайное значение от 0 до 1 в js используется метод Math.random(), можете использовать эту функцию, для удобства, я тоже буду пользоваться ей: function randToNum(n){ //возвращает целые значения от 0 до n return Math.round( Math.random()*n ) } Теперь напишем функцию добавления блока addBlock(): и в gameInit() добавим два вызова addBlock(), можем запускать и смотреть всё ли работает, но сначала я бы хотел добавить быстрый рестарт на кнопку R: Игровое поле уже готово, осталось добавить игровую логику, сначала озаботимся перемещением, начнём с движения влево на простом массиве в консоли, есть такой массив и нам нужно 1, 2, 3 собрать в левой части, логика не сложная - идём с лева на право и если элемент не равен нулю - перемещаем его с права на лево до упора: Теперь постарайтесь сами приспособить этот алгоритм для нашего случая Не забудем повесить вызов функции на нажатие кнопки: проверяем работоспособность и по аналогии делаем движение в оставшиеся 3 стороны Не забываем назначить клавиши: И проверяем работоспособность, после каждого движения в оригинальной игре появляется плитка, и нужно удостовериться а было ли вообще движение, перед тем как её добавлять, создадим новую переменную moved = false, перед движение будем её делать обнулять(false), а после движения, если оно было - true, и если если moved = true - будем добавлять новую плитку, я вынесу всю эту проверку с добавлением в отдельную функцию step(), буду вызывать её после moveUp()/Down()/Left()/Right() Не забудьте поставить {} : Сам step() очень простой: теперь озаботимся тем, чтобы плитки одинакового номинала соединялись, по сути похожий код у нас уже есть - код движения плитки, надо просто его переработать, названия дам по той же логике, что и с движением, только вместо move -> merge (соединить): по аналогии сделаем для оставшихся 3 направлений: и добавлю вызов merge*() после move*(), если уже сейчас запустить - в принципе уже можно играть, но будут оставаться отверстия после слияния блоков, а потому после merge*() ещё раз пропишем move*(): Запускаем и наслаждаемся по сути готовой игрой, хоть и дёрганной, осталось лишь добавить счёт в игру, счёт в 2048 добавляется за успешные слияния, добавим переменную score=0 и bestScore=0, так тоже можно: для пополнения счёта тоже добавим отдельную функцию addScore(runtime,num): Ага, runtime надо протянуть до этой функции, иначе он не определён, как минимум во всех merge*(runtime)... шутка (^_^) мы в js и тут можно создать ссылку на объект, не будем пока вдаваться в подробности что такое объект, просто верим, что это содержимое переменной между {} с удобным интерфейсом доступа: и если мы создадим переменную и присвоим ей объект - она сохранит ссылку на него, а не его содержимое: и runtime как раз является таким же объектом, потому мы можем создать ссылку на runtime в начале файла: -> и использовать rt вместо runtime, где это необходимо: (да я добавил текст для отображения счёта и забыл упомянуть об этом) Также добавим обнуление счёта при выполнении gameInit(), проиграть пока нельзя, пора исправить это, проигрыш наступает тогда, когда всё поле заполнено и ни один ход не сдвинет/объединит плитку, для этой цели можем модифицировать addBlock() ещё для того чтобы успеть показать игроку поле перед тем как вылезет окошко - добавим задержку в 1 сек: 1000 - это миллисекунды , проверяем, пытаемся как можно скорее проиграть и проверяем честно ли это происходит Поздравляю! игра готова, но она всё ещё не очень симпатичная, это мы исправим в следующей части этого же урока, который выйдет через пару часов, ибо пост уже получился достаточно объёмным и мне всё сложнее это укладывать это всё в один пост исходник урока - debugDSV.c3p
  19. Это не странное, а крутое решение, в 3 уроке будет подробнее об этом рассказано, если просто - на лету можно менять код программы
  20. @Alessiof21 Можно, но сложно - нужно вычислять угол между двумя точками через Math.atan2, но легче будет использовать связку pathFinder + moveTo, pathfinder будет искать путь, а moveto - двигать объект по вейпоинтам, у moveTo есть в апи angleOfMotion
  21. Это метод(функция) все функции в js вызываются со скобками в конце, иначе js просто вернёт текст внутри функции после объявления функции я попытался вызвать её без скобок - получил просто её содержимое, со скобками получил результат её выполнения Да, именно так, пик констаркта работает таким же образом, просто в массив заносит объекты, но в js нет понятия "глобальная переменная" есть области видимости, наверное этот принцип легче показать на практике: так всё нормально так уже нельзя, так как num2 находится между { }, это непреступный забор но работает этот забор только в одну сторону, то есть - посмотреть что внутри нельзя, а что снаружи - можно Сначала выполняется проверка условия цикла, потом код внутри цикла, и уже после операции с переменной цикла, если записать это псевдокодом получится что-то такое: Всё ровно по тому же принципу, мы получили в предыдущей итерации 16, оно всё ещё меньше или равно 256 - выводим в консоль и возводим в квадрат, получили 256, условие (<=256) всё ещё верное - выводим в консоль и возводим вновь в квадрат, получили 65536 и оно уже не удовлетворяет условию, а потому цикл закончен Если сильно углубляться, то скорее всего понятно не смогу объяснить что именно это такое, но если просто - это интерфейс взаимодействия с самим движком construct 3, и он не является глобальным, а работает только в определённых местах, и если к этому интерфейсу нет доступа - мы не сможем работать с игровыми объектами, переменными движка и вообще всем, что связанно с движком, у нас будет только js В подключаемом к ивентам скрипте runtime изначально не доступен, чтобы дать к нему доступ - нужно где-то получить его, и как мы помним в ивентах runtime определён, поэтому и отправляем его параметром тут на самом деле зависит от обстоятельств, потому что код который написан в событиях работает только тогда, когда выполняется само событие и если в событии объявить функцию, то достать её из другого события уже не получится, потому, если к коду нужен постоянный/множественный доступ - его переношу в файл скрипта По последнему вопросу - попробуй открыть консоль и решить сам, подсказка - js чувствителен к регистру
×
×
  • Создать...