Русский
Русский
English
Статистика
Реклама

Для чайников

Шифрование сообщений в Python. От простого к сложному. Шифр Цезаря

13.04.2021 22:15:19 | Автор: admin

Немного о проекте

Мне, лично, давно была интересна тема шифрования информации, однако, каждый раз погрузившись в эту тему, я осознавал насколько это сложно и понял, что лучше начать с чего-то более простого. Я, лично, планирую написать некоторое количество статей на эту тему, в которых я покажу вам различные алгоритмы шифрования и их реализацию в Python, продемонстрирую и разберу свой проект, созданный в этом направлении. Итак, начнем.


Для начала, я бы хотел рассказать вам какие уже известные алгоритмы мы рассмотрим, в моих статьях. Список вам представлен ниже:

  • Шифр Цезаря

  • Шифр Виженера

  • Шифр замены

  • Омофонический шифр

  • RSA шифрование

Шифр Цезаря

Итак, после небольшого введения в цикл, я предлагаю все-таки перейти к основной теме сегодняшней статьи, а именно к Шифру Цезаря.

Что это такое?

Шифр Цезаря - это простой тип подстановочного шифра, где каждая буква обычного текста заменяется буквой с фиксированным числом позиций вниз по алфавиту. Принцип его действия можно увидеть в следующей иллюстрации:

Какими особенностями он обладает?

У Шифра Цезаря, как у алгоритма шифрования, я могу выделить две основные особенности. Первая особенность - это простота и доступность метода шифрования, который, возможно поможет вам погрузится в эту тему, вторая особенность - это, собственно говоря, сам метод шифрования.

Программная реализация

В интернете существует огромное множество уроков, связанных с криптографией в питоне, однако, я написал максимально простой и интуитивно понятный код, структуру которого я вам продемонстрирую.

Начнем, пожалуй, с создания алфавита. Для этого вы можете скопировать приведенную ниже строку или написать все руками.

alfavit =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ'# Создаем алфавит

Далее, нам нужно обозначить программе шаг, то есть смещение при шифровании. Так, например, если мы напишем букву "а" в сообщении, тот при шаге "2", программа выведет нам букву "в".

Итак, создаем переменную smeshenie, которая будет вручную задаваться пользователем, и message, куда будет помещаться наше сообщение, и, с помощью метода upper(), возводим все символы в нашем сообщении в верхний регистр, чтобы у нас не было ошибок. Потом создаем просто пустую переменную itog, куда мы буем выводить зашифрованное сообщение. Для этого пишем следующее:

smeshenie = int(input('Шаг шифровки: '))    #Создаем переменную с шагом шифровкиmessage = input("Сообщение для шифровки: ").upper()    #создаем переменнную, куда запишем наше сообщениеitog = ''    #создаем переменную для вывода итогового сообщения

Итак, теперь переходим к самому алгоритму шифровки. Первым делом создаем циклfor, где мы определим место букв, задействованных в сообщении, в нашем списке alfavit, после чего определяем их новые места (далее я постараюсь насытить код с пояснениями):

for i in message:    mesto = alfavit.find(i)    #Вычисляем места символов в списке    new_mesto = mesto + smeshenie    #Сдвигаем символы на указанный в переменной smeshenie шаг

Далее, мы создаем внутри нашего цикла условие if , в нем мы записываем в список itog мы записываем наше сообщение уже в зашифрованном виде и выводим его:

if i in alfavit:        itog += alfavit[new_mesto] # здесь мы прибавляем значение правого операнда к левому    else: # и присваиваем эту сумму левому операнду.        itog += iprint (itog)

Итоговый вид программы

Итак, вот мы и написали простейшую программу для шифрования методом Цезаря. Ниже я размещу общий вид программы без моих комментариев, чтобы вы еще раз смогли сравнить свою программу с моей:

alfavit =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ'smeshenie = int(input('Шаг шифровки: '))message = input("Сообщение для ДЕшифровки: ").upper()itog = ''for i in message:    mesto = alfavit.find(i)    new_mesto = mesto + smeshenie    if i in alfavit:        itog += alfavit[new_mesto]    else:        itog += iprint (itog)

Дешифровка сообщения

Возможно это прозвучит несколько смешно, но мы смогли только зашифровать сообщение, а насчет его дешифровки мы особо не задумывались, но теперь дело дошло и до неё.

По сути, дешифровка - это алгоритм обратный шифровке. Давайте немного переделаем наш код (итоговый вид вы можете увидеть выше).

Для начала, я предлагаю сделать "косметическую" часть нашей переделки. Для этого перемещаемся в самое начало кода:

alfavit =  'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЭЮЯ'smeshenie = int(input('Шаг шифровки: '))message = input("Сообщение для ДЕшифровки: ").upper()    #заменяем слово шифровка, на дешифровкаitog = ''

Остальное можно оставить так же, но если у вас есть желание, то можете поменять названия переменных.

По большому счету, самые 'большие' изменения у нас произойдут в той части кода, где у нас находится алгоритм.

Итог

Вы успешно написали алгоритм шифровки и дешифровки сообщения на Python с помощью метода Цезаря. В следующей статье мы с вами рассмотрим Шифр Виженера, а также разберем его реализацию на Python, а пока я предлагаю вам написать в комментариях варианты модернизации программы(код или просо предложения и пожелания). Я обязательно учту ваше мнение.

Подробнее..

Вот вообще этим не занимался, и тут раз, и Data Science

20.01.2021 12:12:03 | Автор: admin

Машинное обучение. Все о нём говорят. Много кто. Большая тема но покрытая этой жуткой мистикой. Как магия есть дар, сможешь что угодно сделать. Если нет... вообще не понятно, как подступиться. Постоянно фигурируют какие-то numpy, pandas, scikit-learn. На каждую из них фигурирует по двухтомному учебнику и куче доков.

Хочу в этой статье развеять мистику. Давайте чего-нибудь обучим, и найдём закономерности в большом наборе данных. Может и предскажем что-нибудь, даже. На простом, добром питоне без всякой библиотеки в два гигабайта, и с минимальным входным порогом знаний.


Интересно? Прошу под кат.

1. Вступление

Есть игра. Популярная довольно игра, The Binding of Isaac называется. Идея простая - ты ходишь по подвалу и собираешь с пола всякие вещи. В зависимости от вещей, которые игрок набрал, игра может стать легче или сложнее. Надо применять стратегию.

Есть ещё человек. Этот человек регулярно играет в игру. Больше тысячи получасовых (а то и больше) эпизодов. Ветеран как игры, так и лёгких и непринуждённых рассказов про свою жизнь. Очень рекомендую его старые эпизоды, кстати, если вражеский язык не проблема вот ссылка.

Есть ещё приятный сюрприз.

Какие-то люди, очень преданные любимому видеоблоггеру, сделали в его славу очень удобоваримую базу данных. Каждое из (ох господи, как их много) тысяч разных прохождений удобно расчерчено в SQL табличке, и с помощью пары простых запросов можно узнать почти всё про то, что происходило в видео. Вот ссылка, если интересно кому. Давайте используем это.

Вопрос возникает естественно вот бегает человек по подвалу, собирает хлам. Кладёт это на YouTube. Люди потом его судят палец вверх, или, может, палец вниз. Люди судят его по тому, как интересно им смотреть. Обычно, это харизма и качество повествования самого Northernlion-а. Но их в таблице нет. ...Но есть собранные им вещи.

А какие вещи делают видео интересным?

Узнаем.

2. Готовимся к работе

Прежде чем углубиться в теорию, давайте чуть поговорим, как загнать эту таблицу в наш любимый язык программирования.

Если ваш любимый язык программирования не Python, то я с вами не согласен.

Что нам нужно?

Нам нужно понятное преобразование того, что мы видим в базе в большую кучу примеров. Пример то же самое, что пара из кучи всех вещей, которые собрали в одном видео, и рейтинга этого видео на YouTube. Мы поговорим об этом поглубже в следующей части, когда будем обсуждать машинное обучение как идею.

Вот образец (внимание, сложная структура данных):

Я пошутил. Она совсем не сложная.

У нас есть простой кортеж (tuple), в нём ID видео (чисто с декоративной целью), счётчик (словарь счетов сколько раз вещь подобрали) для подобранных вещей и количество лайков в этом видео.

Ещё у нас есть файл в 21,5 мегабайт.

Как сделать из второго первое?

Идея 1: SQLite 3

Python это славный язык. Особенно славная в нём стандартная библиотека. Про один модуль unittest можно писать и писать... но это не наша тема. Давайте поговорим про модуль sqlite3.

Обычная идея базы данных в SQL заключается в специальном сервере, который всю жизнь свою занимается запросами к нему со стороны других модулей вашей программы. Это не самая приятная парадигма, если вы не хотите хостить сервер.

Однако в таком случае, таким как вы на помощь приходит SQLite 3 - друг, товарищ и альтернативная парадигма. Вместо отдельного сервера SQLite оперирует обычным файлом, который хранит в себе сущность нашей базы. Этим файлом мы можем оперировать прямо из стандартной библиотеки языка. Это очень приятно приятнее, чем хостить сервер.

Но наш файл не база данных SQLite 3. Наш файл дамп PostgreSQL. Если заглянуть внутрь, увидим там последовательность команд. Запустив их все в консоли Postgres, мы как раз получим нашу базу. Это хорошо, но с модулем sqlite3 в языке Python это не сработает.

Давайте проведём исследование.

Исследование показало, что есть много хороших инструментов и подходов. Конвертировать вряд ли будет сложно, да и сам язык SQL вроде везде один и тот же... Но проблема ждёт впереди.

Все диалекты SQL очень разные. Казалось бы, язык один но то, что работало бы, скажем, на MySQL, может и не сработать на PostgreSQL или на SQLite, опять же. Конечно, база языка одна, и во многих случаях всё работает это не так дико, как, скажем, конвертировать Java в Javascript.

Но конкретно в нашем случае не сработало. Переходим к плану Б.

Идея 2: PostgreSQL и Psycopg2

Давайте просто запустим сервер.

С кучей документации по быстрой установке это совершенно несложно. Простым запуском пары команд мы получаем сервер специальная библиотека psycopg2 (её нужно скачать) позволяет нам легко работать с серверами PostgreSQL. Может быть, и с другими серверами. Но с PostgreSQL точно.

Напишем запрос!

Любой ресурс-вступление в SQL вам скажет, что краеугольный камень любого запроса к базе данных следующее выражение.

SELECT * FROM table

Оно даёт нам всё, что есть в таблице table.

Давайте запустим это и посмотрим.

Сверху видим код, снизу результат. Круто.

Вместо * можно поставить один столбец. Или несколько столбцов. Так мы можем собирать только те данные, которые нас интересуют.

Вот так, например.

Если посмотреть в нашу таблицу поглубже, мы увидим она содержит ID. Эти ID очень полезны они помогают найти элемент в другой таблице.

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

Мы возьмём и добавим такую вещь как WHERE, которая фильтрует все ряды и даёт нам только тот, который совпадает с каким-нибудь условием.

Это плохой скрипт. Не верьте мне на слово просто так посчитайте, сколько запросов к базе данных (нашему Postgres, который пришлось запустить в предыдущей части) он делает. На каждый ряд таблицы он делает по запросу и ещё один запрос, чтобы получить эти ряды. Это очень много бедный Postgres. Давайте сделаем, чтобы запросов было меньше.

JOIN команда, которая сделана специально для такой ситуации. Она берёт таблицу, в которой есть ID (или любой другой параметр) из другой таблицы и вместо каждого ID нашлёпывает оттуда целый элемент.

Тут есть много нюансов что если элементов с таким ID несколько? Если такого элемента нет? Если кому интересны подробности, рекомендую читать документацию. А тут можно посмотреть на исходный код и порадоваться, как мало запросов получилось.

Теперь, если мы хотим получить кучу пар типа (видео, название вещи, которую мы подобрали), вот таким запросом (снизу) мы можем воспользоваться. Про значения всех этих полей можно почитать в документации нашей конкретной базы данных event_type = 2, например, означает событие типа игрок подобрал вещь. Это больше JOINов, чем то, с чем мы сталкивались выше но принцип работает, и получаем мы как раз то, что нужно.

На этом можно и закончить написать ещё по запросу на лайки к каждому видео, забить всё в массив и жить хорошо... но давайте поговорим ещё про GROUP BY.

GROUP BY позволяет нам превращать большие множества рядов в один. Тут на каждое видео у нас есть много пар если мы превратим все эти пары в массив, наша жизнь будет чуть-чуть проще. Давайте объединим все пары с одинаковым videos.id в один ряд.

(Примечание этот запрос не работает.)

Почему же не работает? Ответ простой - непонятно, что делать с videos.likes и isaac_resources.name.

Когда мы объединяем много разных рядов, из кучи разных значений в этих рядах всегда должно получиться одно. Куда пихать тогда все значения - в массив? Но массивы (по парадигме) в SQL считаются ересью. В отличие от столбцов, в которых значение всегда одно (или нет вовсе, но это тоже значение в своём роде), в массиве может быть несколько значений. Например, 5. Или 6. Или даже 8. Заранее неизвестно, а такую неизвестность в SQL не любят.

Обычно в GROUP BY для этого надо использовать агрегатные функции. Мы можем получить (если в столбце числа), например, среднее. Это одно число. Или сумму тоже одно число. Не пять, не восемь. Проблема усугубляется тем, что в разных диалектах (PostgreSQL, MySQL, SQLite) эти функции разные и тут мы стоим на зыбком дне спецификации.

К счастью, несмотря на недостаток массивов в самом языке SQL, много серверов их кое-как всё же поддерживают. Так, например, в PostgreSQL есть такая функция ARRAY_AGG() - которая нам и вернёт массив всех значений в столбце.

Так мы получаем наши примеры.

(P.S.: Counter из модуля collections хорошая штука, если вам надо что-то легко и изящно посчитать. Возьмите на заметку!)

3. Что такое это обучение вообще?

Ведущая парадигма в написании софта писать код, чтобы решить проблему. Соответственно, чем сложнее проблема, тем сложнее код этот будет. Это называется software наверное, читателю уже приходилось столкнуться с таким.

Машинное обучение даёт нам другую парадигму написать код, умеющий учиться на примерах (или взять готовый), а потом вместо того, чтобы обрабатывать проблему вручную, как раз научить машину на примерах проблемы, ответ к которой надо найти. Это иногда называют Software 2.0. Звучит, несомненно, громко. Можно сказать, не заслуживает имени даже. Но в некоторых местах всё же применяется. Сама идея того, что есть алгоритмы, в которые достаточно запихать много данных, очень хороша хотя бы тем, что отслеживать закономерности вручную это дело затратное и сложное.

Сегодня поговорим о такой вещи, как линейная регрессия. Крайне простая, но очень красивая штука, которая позволит нам сделать предсказание количества лайков, а потом смотреть и радоваться таким точным оно в итоге будет.

Идея за линейной регрессией такова. Есть у нас N ситуаций, и каждой пусть соответствует число ответ, который мы хотим знать. Число чего угодно может быть лайков, долларов, секунд система не дискриминирует.

И пусть из каждой ситуации мы можем каким-то алгоритмом выдёргивать признаки любые свойства, которые можно описать каким-то числом. Или булевой переменной то есть, числом 1 или 0. Такие признаки в литературе ещё называются фичами.

И вот, если дать компьютеру много пар типа (признаки, ответ) то он будет искать числа - веса. На каждый признак по числу чтобы если для примеров просуммировать значения всех их признаков, помноженных каждый на своё число-вес, получилось бы в среднем как можно ближе к ответу. Говоря технически, алгоритм минимизирует сумму квадратов разностей ответов и скалярных произведений соответствующих векторов признаков на один общий вектор весов.

Предположим, что у нас веса уже есть напишем функцию predict, которая даст нам ответ, который мы будем тогда ожидать.

Возьмите на заметку - defaultdict из того же модуля collections здесь уж очень хорош. Работает он как обычный словарь но если ключа в нём нет, он возвращает значение по умолчанию. Как, например, 0 здесь. Про ошибки вроде KeyError можно вообще забыть!

Но весов у нас нет. Как их будем добывать?

Делается это методом градиентного спуска тут тоже просто. Главная идея смотреть, в какую сторону надо менять вектор весов, чтобы эта сумма квадратов уменьшилась и просто менять его туда, раз за разом. Если у нас есть функция f(x) некий, то мы от x отнимаем градиент от f по x, помноженный на какое-нибудь постоянное число большое, если мы хотим быстро, и маленькое, если точно. Градиент это как производная функции, скорость её изменения - только вместо одного числа, это несколько чисел. Это потому, что весов несколько. Если был бы один вес, мы бы смотрели на производную.

А чтобы не смотреть на все примеры каждый раз, когда мы хотим посчитать шаг спуска (медленно!), был придуман ещё один хитрый трюк. Вместо того, чтобы обновлять наши числа-веса, используя все примеры за раз мы можем каждый раз брать по одному, или по совсем немного примеров и обновлять на них. Это стохастический градиентный спуск, и работает он тоже неплохо.

Так вот, какой же градиент у функции, которую мы хотим минимизировать, для одного примера? Так уж получилось, что это просто разница между нашим теперешним предсказанием и ответом, только помноженная на значение каждого признака.

Для первого признака, (предсказание ответ) * первый признак, для второго (предсказание ответ) * второй, и так далее. Считать наш градиент несложно, и осуществлять по нему спуск тоже.

Давайте такой напишем.

Мы добавили функцию train - пять строчек кода. Для каждого примера двигаем веса, чтобы квадрат разницы был немного поменьше. И всё - можно тестить.

4. И что это только что было?

Хорошо. У нас есть... что-то. Оно выдаёт какие-то числа, даже. Это очень хорошо но ведь это далеко не результат. Давайте рассмотрим, куда стоит копать дальше.

Во-первых, не совсем понятно, как мы будем считать, насколько этот алгоритм хорош. Давайте введём среднюю ошибку, как нашу главную метрику.

Во-вторых, мы даём нашему алгоритму все данные, а потом проверяем его на них же. В машинном обучении это не самая лучшая идея. Сами посудите - мы хотим, чтобы алгоритм хорошо справлялся в общем случае. Если ему дать все ответы, он просто их заучит и совершенно не факт, что будет разбираться в общем случае - с теми примерами, где ответов он не видел. Как с билетами на экзамене если студенту дать ответы на все вопросы, то ответить он правильно может но не факт, что материал он понял.

Для этого обычно примеры делятся на три набора: train, validation и test.

Train это тренировочный набор. На нём мы наш алгоритм тренируем учим.

Validation (или dev) набор, который мы кормим нашему алгоритму после тренировки. Как экзамен смотря на среднюю ошибку на нём, мы смотрим, как хорошо программа разбирает случаи, конкретно которых она не видела. Проверять вместе с этим на тренировочном наборе (те случаи, которые она видела) - это тоже хорошо. Обычно смотрят на эти два числа по мере тренировки мы будем ожидать, что оба будут падать, и train будет немного ниже dev.

Test набор, который мы используем для финальной проверки. По мере того, как мы пишем алгоритм, у нас появляется много параметров, которые можно менять размер шага в градиентном спуске, количество разных признаков, и много других вещей, о которых мы не упомянули. (Но поскольку имя параметры уже занято весами (в некоторых терминологиях) эти параметры мы называем гиперпараметрами.)

Меняя гиперпараметры, мы хотим, чтобы ошибка на нашем dev-наборе стала как можно ниже. Но делая это, легко можно начать подгонять под ответ если по какой-то случайности с шагом 0.01301 ошибка будет на самую малость ниже, чем с шагом 0.013, то не факт что это мастерское оттачивание гиперпараметров помогло тут просто могла быть удача, на которую в какой-нибудь научной публикации полагаться не стоит. Поэтому во время разработки обычно берётся третий набор, и даётся священная клятва этот набор особо часто не тестить а когда приходит время тестить, не менять гиперпараметры по итогам результатов.

В игрушечной программе, такой как наша, test-набор не обязателен.

Давайте введём среднюю ошибку, как нашу метрику - и начнём отслеживать её в наших двух наборах.

Дальше будем смотреть на график ошибок. Вот такой.

Здесь средняя ошибка на тренировочном наборе отрисована синим, а на проверочном оранжевым.

Консоль уже ничего интересного нам выводить не будет. В весах, конечно, можно уследить много чего интересного, но для этого нужно знать саму игру, и что какие вещи значат для игрока. А статья всё же по машинному обучению, а не по The Binding of Isaac.

Для сравнения посмотрим на среднюю ошибку, которую мы бы получили, просто выбирая среднее количество лайков каждый раз. (Если мы ничего не знаем про видео, то этот способ даст нам наименьшую ошибку из всех.) Чему она равна? Легко узнать.

906! А наш результат даёт где-то 850... Это очень даже неплохо. Вспоминаем мы не показывали нашему алгоритму ни примера из этого набора. А ошибку он даёт меньше простого выбора среднего значения. Существуют, значит, эти закономерности можно даже к игре как-то отследить. Давайте улучшим наш результат ещё немного.

Сначала вспомним самое начало статьи там мы упоминали, что харизма самого комментатора наверняка оказывает самое сильное влияние на лайки. С чего б нам её не учесть? Добавим ещё один признак, и назовём его INTERCEPT. Он будет равен единице в каждом примере. Натренируем модель снова...

Большой минус нашего представления на данный момент в том, что любое видео, в котором наш товарищ Northernlion не собрал ни одной вещи, будет всегда предсказываться, как набравшее ровно ноль лайков. Чтобы избежать подобных казусов, в машинном обучении к набору признаков часто принято добавлять ещё один, равный единице для всех примеров. Это позволяет учесть факторы, которые одинаково влияют на все случаи.

Как видим, ошибка нашей модели упала. Лайков на 50 это прогресс.

Давайте, кстати, заметим, что график наш выглядит немного странно. С обучением ошибка на нашем тренировочном наборе падает, а на проверочном... начинает подниматься.

Заметно, например, в примере снизу, где мы сделали пять тысяч шагов в десять раз больше обычного.

Это типичный случай переобучения (также известного как overfitting). Оставив программу искать закономерности там, где их нет, мы обрекаем её на суеверия. В попытках объяснить то, что, кроме как удачей, ничем объяснить и нельзя, программа изобретает всё более и более дикие мнения о том, как устроен этот мир. Эти мнения, конечно, неплохо описывают тренировочный набор но в общем случае совершенно неуместны. Психологи подобное с голубями делали.

Чтобы такого не происходило, надо сделать нашу программу менее чувствительной к мелким стимулам. Менее зоркой, более забывчивой. Для этого применяется такая техника как регуляризация.

Сама идея регуляризации проста. Наш традиционный градиентный спуск старается тянуть веса к тому, чтобы сумма квадратов ошибок была как можно меньше нам надо просто сделать, чтобы веса тянуло ещё и к нулю.

Математически говоря, наша функция потерь (т.е., мерка того, как неприятны нам эти веса) вместо суммы квадратов становится суммой квадратов плюс нормой нашего вектора весов (помноженного на постоянное число). Градиент меняется соответственно.

Один из самых простых способов исполнения после изменения множить вес на какое-то постоянное число, немного меньшее единицы. На выделенных строчках мы добавили этот функционал.

Ценой такого плавного графика может быть повышение в средней ошибке... здесь важно найти правильный REGULARIZATION_FACTOR.

Бывают и другие методы улучшения результатов.

Здесь мы предполагаем линейную зависимость то есть, если найти вещь два раза, смотреть на результат станет ровно в два раза веселее, чем если бы мы её нашли один раз. (Или в два раза грустнее.) Оно так не всегда поэтому используют так называемый биннинг. Вместо использования числа (счёта обьектов, например) как значение признака, возможные значения делят на несколько интервалов (например, 0, 1, 2-3, 4+) - и потом дают каждому интервалу по своему признаку, со значением либо 0, либо 1. Таким образом, наш признак вещь: 3 превращается в вещь_0:0, вещь_1:0, вещь_2-3:1, вещь_4+:0 - и подстраиваться уже можно, исходя из этого.

Можно добавить больше признаков. База данных большая, и в видео происходит очень много вещей, которые могли бы повлиять на наш рейтинг. Но чем больше признаков, тем больше комбинаций. Тем больше и примеров, на которых нужно учить и тем больше шанс переучить слишком сильно.

Да и сама линейная регрессия это самый простой алгоритм. Впереди ЛДА, Байесовские сети, нейронки... Машинное обучение глубокое поле, и эта статья простое болтание ногами на его мели. Посмотрите в воду и быть может, глубина тоже позовёт вас.

Нырнёте?

(Весь код из этой статьи можно найти здесь.)

Подробнее..

Синтезатор на Unity 3D

09.04.2021 12:18:12 | Автор: admin

Учебные материалы для школы программирования. Часть13

Предыдущие уроки можно найти здесь:

При тематическом планировании уроков, мы сталкиваемся с интересной задачкой: в нашей группе учатся и мальчишки и девчонки. У них разные вкусы, разные любимые герои, жанры. И если все занятия будут на тему гонок или Silent Hill - ряды девочек на ваших занятиях, - поредеют. Эту ситуацию мы решаем двумя способами:

  • спрашиваем мнение ребят и проводим голосование на уроке. Да-да, только дети смогут сделать вашу программу интересной, поэтому их мнение действительно значимо. Не для галочки. Опрос и обсуждение важно проводить регулярно, ибо все группы разные, темы меняются от возраста к возрасту, меняются в зависимости от того, во что сейчас играют и что смотрят в кино;

  • чередуем и комбинируем. Мы стараемся чередовать "спокойные" и "агрессивные" проекты. Например, в этом месяце мы делаем лабиринт ужасов, но после его защиты - приступаем к созданию острова с лесом и домиком. При комбинировании, мы предлагаем всем ребятам уделить внимание визуальной составляющей (раскрасить, добавить элементов, составить композицию и пр.), и девочки с большим энтузиазмом берутся за такую задачу.

Сегодня мы сделаем проект из категории: спокойный, для девчат. Мальчишкам тоже хорошо заходит - особенно момент, когда в конце урока получается звуковая какофония =)

Рассмотрим следующие темы:

  • выставление вращения объектов в локальной системе координат посредством конверсии из углов Эйлера в кватернионы;

  • события объектов OnMouseEnter и OnMouseExit;

  • метод POW класса Mathf - возведение в степень;

  • парсинг float из имени объекта через системный метод Parse ;

  • стек постэффектов от Unity Technologies;

  • функция движка RequireComponent.

Особое внимание обратим на:

  • изменение скорости воспроизведения и высоты звука через Pitch;

  • использование аудиомикшера и постэффектов на мастер-канале микшера, в частности, реверберации и эмуляции комнаты.

Порядок выполнения

Создаётся новый проект, импортируется приложенный ассет, открывается сцена piano (в проекте заранее заготовлена сцена, ключевыми объектами которой являются клавиши, расположенные на сцене в "Пианино/Клавиши").
Клавиши пронумерованы в соответствии с полутонами, начиная с ноты "до" и заканчивается нотой "фа" следующей октавы, т.е. "до", "до диез", "ре", "ре диез", "ми", "фа" и т.д.

Клавиши расположены с начала октавы в той же последовательности, что и на реальном пианино.

Весь проект умещается в один скрипт. Полный листинг содержит пояснения:

using System.Collections;using System.Collections.Generic;using UnityEngine;using System;[RequireComponent(typeof(AudioSource))] // необходимо для того, чтобы скрипт требовал установленный аудиосорсpublic class Piano : MonoBehaviour {public KeyCode Key; // энумератор для выбора клавиши клавиатуры, на которую реагирует скриптAudioSource src; // Аудиосорс, приват-переменная     void Start () {src = GetComponent<AudioSource>(); // получаем аудиосорс        src.pitch = Mathf.Pow(1.059462f, float.Parse(name) - 1f); // высота звука равна 1.059462f в степени (имя_клавиши - 1).}    void Update () { // Для уменьшения отклика стоит использовать FixedUpdateif (Input.GetKeyDown(Key)) { // если нажали клавишу.playNote(); // играем        }        if (Input.GetKeyUp(Key)) { // если отпустили клавишу.stopNote(); // не играем        }}    private void OnMouseEnter() { // если мышь над коллайдером клавиши        playNote(); // играем}    private void OnMouseExit() { // если мышь вышла из коллайдера клавиши         stopNote(); // не играем}    private void playNote() { // играемtransform.localRotation = Quaternion.Euler(-3, 0, 0); // ставим локальный угол поворота на -3 градуса по Хsrc.Play(); // Включаем звук с начала}    private void stopNote(){ // не играемtransform.localRotation = Quaternion.Euler(0, 0, 0); // ставим локальный угол поворота на 0 градусов по всем осям    src.Stop(); // Останавливаем звук    }}

Особое внимание стоит уделить строке:

src.pitch = Mathf.Pow(1.059462f, float.Parse(name) - 1f);//высота звука равна 1.059462f в степени (имя_клавиши - 1).

Число1.059462высчитано математически и является простой заменой логарифмической функции, делящей одну октаву на 12 полутонов. Таким образом, каждый последующий полутон в 1.059462 раза выше предыдущего по частоте, что при количестве 12 полутонов даёт умножение частоты на 2 с ошибкой в 0.00003 Гц на октаву. С учётом того, что динамический диапазон нашего пианино не превышает полторы октавы, звук практически не искажается.

Тем не менее, звуки пианино плохо ложатся в микс, при отпускании клавиши отсутствует затухание, потому было принято решение немного "размазать" всё реверберацией с небольшой задержкой и эмуляцией большой комнаты, которая добавит "хвостов" затухания резко прерывающимся звукам.

Создана новая группа аудиомикшера, на мастер-канал которой установлена реверберация со следующими параметрами.

А всем аудиосорсам в качестве output установлен мастер-канал аудиомикшера.

Каждой клавише пианино необходимо вручную выставить необходимую клавишу клавиатуры. Так как мы имеем дело с энумератором, необходимо выбрать из представленных вариантов. Клавиши установлены в следующем порядке:

Далее, немного оформляем сцену и добавляем следующие эффекты:

  • SSAO - подчеркнёт тени между клавишами, добавит глубины картинке.

  • Bloom - высветлит светлые участки ещё сильнее, сделает картинку более приятной на глаз, интенсивность нужно выбрать довольно низкую.

  • Антиалиасинг, чтобы убрать пикселизацию.

  • Винетка, чтобы затенить края, выделив основной объект.

    Можно добавить SSR, для легкого отражения на вертикальных плоскостях, что немного улучшит картинку.

Готово!

Подробнее..

Судно на воздушной подушке на Unity 3D

16.04.2021 18:11:03 | Автор: admin

Учебные материалы для школы программирования. Часть14

Предыдущие уроки можно найти здесь:

Сегодня мы настроены на отдых и развлечения! Поэтому, этот урок будет простой и "короткий". Мы не будем работать с графикой (но вас никто не ограничивает в праве усовершенствовать проект), уделим внимание управлению и работе с физикои, на примере создания судна на воздушнои подушке.

Порядок выполнения

Создадим новыи проект, импортируем в него приложенныи ассет. В данном ассете содержится модели, звук и простая сцена.

Первое, что нам необходимо сделать - это установить на сцену модели карты и СВП, затем создать материал с нулевым трением и назначить его юбке СВП

На само судно устанавливаем Rigidbody со следующими параметрами:

Обратите внимание, что на коллаидерах установлена галочка Convex, а Rigidbody не имеет галочку использования гравитации. Вместо нее используется ConstantForce с довольно большим значением, направленная вниз.

Как видите, на скриншоте уже наложен скрипт. Но до того, как приступать к нему, необходимо установить рулевые лопатки.
Также, на модель в точках установки рулевых лопаток, установлены пустые геим-обджекты с названием Gizmo, в них и уложены сами лопатки.

Скрипт конфигурируется согласно позапрошлому скриншоту. Полныи листинг скрипта выглядит таким образом:

using UnityEngine;using System.Collections; public class Howercraft: MonoBehaviour {    public Rigidbody HowercraftRigidbody; // риджитбади     public Transform CenterOfMass; // центр масс    public float power = 25000; // мощность вперёд/назад    public float torque = 25000; // мощность влево/вправо    float finAngle; // угол отклонения лопаток     float pitch; // питч для звука    public Transform[] Fins; // массив с лопатками    public AudioSource mainEngine; // звук основного двигателя       public AudioSource pushEngine; // звук турбин     // Use this for initialization     void Start() {        HowercraftRigidbody.centerOfMass = CenterOfMass.position - HowercraftRigidbody.position; // устанавливаем центр масс    }     // Update is called once per frame    void Update() {                float inpFB = Input.GetAxis("Vertical"); // ввод вперёд/назад        float inpLR = Input.GetAxis("Horizontal"); // и влево/вправо              Vector3 vely = new Vector3(HowercraftRigidbody.transform.forward.x, 0, HowercraftRigidbody.transform.for ward.z); // находим вектор приложения силы          float gain = Mathf.Clamp01(HowercraftRigidbody.transform.up.y); // если перевёрнуты, силы будут равны нулю             HowercraftRigidbody.AddForce(vely * power * inpFB * gain, ForceMode.Force); // добавляем линейные силы              HowercraftRigidbody.AddRelativeTorque(0, torque * inpLR * inpFB * gain, 0, ForceMode.Force); // и поворот              finAngle = Mathf.Lerp(finAngle, -45 * inpLR, Time.deltaTime / 0.2f); // угол лопаток            foreach(Transform Fin in Fins) {            Fin.localEulerAngles = new Vector3(0, finAngle, 0); // выставляем угол         }        mainEngine.pitch = 0.9f + HowercraftRigidbody.velocity.magnitude / 60f; //высота звука основного двигателя               pitch = Mathf.Lerp(pitch, Mathf.Abs(inpFB) * 1.3f, Time.deltaTime / 0.5f); // высчитываем высоту звука турбины               pushEngine.pitch = 1f + 2f * pitch;        pushEngine.volume = 0.3f + pitch / 3f;    }}

При этом скрипт лучше давать последовательно, сначала физическии движок, потом звуковои.

Готово!

Подробнее..

Игровые механики на уроке геометрии или векторы на Unity 3D

20.04.2021 16:13:49 | Автор: admin

Учебные материалы для школы программирования. Часть16

Предыдущие уроки можно найти здесь:

В этой статье, мы обратим свой взор в прошлое, и вспомним, с чего начиналась детская школа программирования Step to Science. Первоначальная идея проекта состояла в том, чтобы быть не просто кружком технического творчества, а стать для детей ответом на вопрос, "зачем учиться в школе?"

К чему нам физика, алгебра и геометрия, если мы не планируем проектировать космические корабли, если для счета у нас есть калькулятор в телефоне, расплачиваемся мы чаще картой, так что даже сдачу в уме считать не надо.
Я тоже в детстве вела такие рассуждения, и у родителей не было иных способов донести до меня истину, кроме фразы "нет слова не хочу, есть слово надо" и ремня, который без лишней полемики мотивировал садиться за уроки.

С возрастом, перейдя на другую сторону баррикад, я поняла что хочу ребятам объяснить, показать, доказать, что учиться в школе действительно важно! И игровой проект, который мы разберем сегодня - один из цикла занятий по изучению школьных предметов через игры на Unity 3D.

Кроссплатформенный движок Unity дает огромные возможности учителю: через увлекательный процесс создания игр мы изучаем законы физики, геометрии, делаем расчеты, проектируем окружение, используем сторителлинг, сценарные механики. И конечно-же программируем. Вариантов интеграций Unity в другие образовательные и предметные области - бесчисленное множество!

Порядок выполнения

На примере создания 2D игры баскетбол, рассмотрим векторы (скорости, сил, локальнои и глобальнои систем координат). Разберем принципы представления систем координат и представления векторов. Также будет затронута работа с LineRenderer и многокамерность.

Поехали!

Создадим новыи проект и импортируем в него приложенныи ассет.
Ассет содержит в себе все ресурсы, необходимые для создания полноценного 2D приложения.

Для начала создадим небольшую сцену, в качестве фона выберем спраит спортзал и установим на него сетку. Обратите внимание, что необходимо выставить коллаидеры для щита и корзины.

Конечно, необходимо выставить правильныи Order in layer у спраитов. Добавим мяч, применим к нему Circle collider и Rigidbody.

Внутри мяча должен находиться пустои объект с Audio Source, настроенным на воспроизведение звука удара.

Чтобы воспроизводить этот звук, напишем простои скрипт, закинем его на мяч и сконфигурируем.

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Ball : MonoBehaviour {    public AudioSource hitSound;    public Rigidbody2D rig;    // Use this for initialization    void Start () {    }    // Update is called once per frame    void FixedUpdate() {    }    private void OnCollisionEnter2D(Collision2D other) {        if (other.relativeVelocity.magnitude > 1f) {            hitSound.Play();            hitSound.volume = Mathf.Clamp01(other.relativeVelocity.magnitude / 10);            rig.velocity *= 0.8f;        }    }}

В скрипте нет автопоиска Rigidbody, так что придется закинуть его руками. Если нажать на Play, наш мяч упадет, издавая звуки. Чтобы мяч отскакивал, создадим физическии материал и закинем его на коллаидер мяча.

Теперь подумаем о том, чтобы мяч показывал свое направление. Для этого создадим скрипт, которыи рисует стрелки: нам понадобятся два пустых объекта с LineRenderer, один в другом.

Создадим материал для стрелки:

И добавим скрипт, которыи будет выставлять вершины LineRenderer'ов, делая из них стрелки:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Arrow : MonoBehaviour {   public Vector3 showVector;    public LineRenderer lrenderer1;    public LineRenderer lrenderer2;    Transform myTransform;    // Use this for initialization    void Start () {        //lrenderer1 = GetComponent<LineRenderer>();        myTransform = transform;    }   // Update is called once per frame    void Update () {        showVector = new Vector3(showVector.x, showVector.y, 0f);        lrenderer1.SetPosition(0, myTransform.position);        lrenderer1.SetPosition(1, myTransform.position + showVector);          if (showVector.magnitude >= 2f) { // длинная стрелка            lrenderer2.SetPosition(0, myTransform.position + showVector - showVector.normalized);        } else {            lrenderer2.SetPosition(0, myTransform.position + showVector * 0.5f);        }        lrenderer2.SetPosition(1, myTransform.position + showVector);        if (showVector.magnitude < 0.1f) {            lrenderer1.enabled = lrenderer2.enabled = false;        } else {            lrenderer1.enabled = lrenderer2.enabled = true;        }    }}

Закинем скрипт на объект-родитель стрелки и настроим его.

Теперь надо написать скрипт, которыи будет вектор скорости передавать в наш скрипт "показывания" стрелки. Он очень простои:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class VectorVelocity : MonoBehaviour {    public Rigidbody2D rig;    public Arrow arrow;    // Use this for initialization    void Start () {     }    // Update is called once per frame    void Update () {        if (rig.bodyType == RigidbodyType2D.Dynamic) {            arrow.showVector =  rig.velocity / 5f;        }    }}

Закинем его на мяч, в скрипте укажем риджибади мяча и объект со скриптом стрелки.

Теперь вектор скорости показывается верно. Вектор скорости уменьшен в 15 раз, чтобы его было хорошо видно. А для того, чтобы было видно траекторию мяча - добавим ему Trail Renderer на любои привязанныи к мячу объект.

Теперь сделаем так, чтобы мяч можно было кидать. Для этого необходимо выставить ему тип Rigidbody как Kinematic и написать небольшои скрипт.

Листинг скрипта:

using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;public class Spawner : MonoBehaviour {    public Rigidbody2D ball;    public TrailRenderer tr;    Quaternion oldRotation;    Vector3 oldPosition;    public bool readyToShoot = true;    // Use this for initialization    void Start () {        oldPosition = ball.transform.position;        oldRotation = ball.transform.rotation;    }    // Update is called once per frame    public void Respawn () {        ball.transform.position = oldPosition;        ball.transform.rotation = oldRotation;        ball.velocity = Vector3.zero;        ball.angularVelocity = 0;        ball.bodyType = RigidbodyType2D.Kinematic;        readyToShoot = true;        tr.Clear();    }    public void Shoot(Vector3 speed) {        if (!readyToShoot) {            return;        }        ball.bodyType = RigidbodyType2D.Dynamic;        ball.velocity = speed;        readyToShoot = false;    }}

Скрипт выкладываем на пустои объект в мире и устанавливаем ему наш мяч в качестве риджитбади и его треил.

Этот скрипт сам по себе ничего не делает. Чтобы он работал, необходимо организовать ввод. Создадим UI -> Panel на сцене, выставим панели нулевую альфу и установим на него скрипт TouchPanel.cs , приложенныи в проект.

Внутри панели должен лежать спраит со следующими параметрами (обратите внимание на привязку):

Для того, чтобы при компиляции не возникало ошибок из-за отсутствия класса спавнера, данныи скрипт приложен отдельным ассетом, и импортировать его можно только после создания спавнера.

Для того, чтобы сделать включение/выключение стрелок, используется скрипт Toggle, которыи реализован через эвент-систему юнити. Его необходимо закинуть на кнопку и сконфигурировать следующим образом.

Готово!

P.S. Делитесь ссылкой на статью с коллегами, друзьями и любопытными учениками. Будет здорово, если вы попробуете провести один из уроков в своей школе или в кружке детского технического творчества, и напишите пару слов обратной связи о том, как прошел урок по Unity 3D. Успехов!

Подробнее..

Тир. Стрельба рейкастами на Unity 3D

06.05.2021 18:22:32 | Автор: admin

Учебные материалы для школы программирования. Часть17

Предыдущие уроки можно найти здесь:

В этом проекте рассмотрим процесс работы:

- с рейкастами и векторами;
- с методами других пользовательских классов;
- с AudioSource и с Rigidbody через код;
- три основных составляющих выстрела, психологически действующих на игрока (звук, свет и свечение, анимация и след от выстрела);
- инстанцирование префабов.

Ключевыми темами проекта выступают именно рейкасты и векторы. Последним, необходимо уделять довольно много времени каждый день, и на простых примерах объяснять, как они устроены. Но если вам удалось быстро выполнить проект с учениками, то в этом уроке будет уместно рассмотреть mecanim-систему.

Порядок выполнения

Создаём новый проект, импортируем приложенный ассет.
Помимо стандартных ресурсов пакет имеет сторонний плагин для рисовки декалей. Его работа в контексте данного урока не рассматривается.

Проект урока разбит на 2 части - тир и гранаты.

Тир

Тир - самый простой способ научить детей пользоваться рейкастами. Для упрощения, был заранее подготовлен ассет, содержащий все необходимые ресурсы, группе же перекидывается ассет, не содержащий скрипта мишени и её префаба.
Во время этой части урока необходимо объяснить, каким образом работает рейкаст и то, как можно взаимодействовать с другими скриптами из него.

Внутри проекта есть скрипт DecalShooter, который создаёт декали, и в котором расположен весь код стрельбы, включая рейкаст. В нём будет вводиться код взаимодействия с мишенью.
Для начала, необходимо подготовить саму мишень. Ею служит цилиндр, который необходимо уменьшить по Y до состоянии платины, удалить CapsuleCollider и поставить MeshCollider с галочкой Convex. Дополнительно, на цилиндр устанавливается текстура мишени, внутри цилиндра создаётся point light, подсвечивающий мишень, и объект с AudioSource для воспроизведения звука, а на сам цилиндр устанавливается Rigidbody с обработкой коллизий типа Continius Dynamic и галочкой isKinematik. У AudioSource не забудьте убрать галочку PlayOnAwake и закинуть звук попадания в мишень.

Следующим шагом становится создание скрипта Target, который сразу же накладываем на нашу мишень.

Мишень после этих действий необходимо добавить в новый префаб, чтобы при размножении мишени и внесении изменений не возникало необходимости править каждую. Теперь можно перейти к коду мишени.

using System.Collections;using System.Collections.Generic;using UnityEngine; public class Target : MonoBehaviour {    public GameObject light;    public Rigidbody rig;    public AudioSource src;     bool enabled = true;     // Use this for initialization     void Start() {        rig = GetComponent<Rigidbody>();        src = GetComponent<AudioSource>();     }     // Update is called once per frame     void Update() {     }     public void shoot() {        if (!enabled) {           return;        }         rig.isKinematic = false;         light.SetActive(false);         src.Play();         enabled = false;    } }

Код мишени содержит публичный методshoot, который необходим для того, чтобы другие скрипты могли обращаться к нашей мишени. Следующий шаг - проверка из файла DecalShooter попали ли мы в мишень и вызов методаshoot. В рейкаст необходимо включить следующий участок кода:

   if (Input.GetKeyDown(KeyCode.Mouse0)) {            time = 0.3f;             ShootSource.Play();             anim.Play("fire");             Muzzleflash.SetActive(true);            // Сама стрельба             RaycastHit hitInfo;            Vector3 fwd = transform.TransformDirection(Vector3.forward);                         if (Physics.Raycast(transform.position, fwd, out hitInfo, 100f)) {                GameObject go = Instantiate(                    DecalPrefab,                    hitInfo.point,                     Quaternion.LookRotation(                        Vector3.Slerp(-hitInfo.normal, fwd, normalization)                     )                ) as GameObject;                go.GetComponent<DecalUpdater>().UpdateDecalTo(                    hitInfo.collider.gameObject,                     true                );                Vector3 explosionPos = hitInfo.point;                Target trg = hitInfo.collider.GetComponent<Target>();                                if (trg) {                    trg.shoot();                }                                Rigidbody rb = hitInfo.collider.GetComponent<Rigidbody>();                                if (rb != null) {                    rb.AddForceAtPosition(fwd * power, hitInfo.point, ForceMode.Impulse);                    Debug.Log("rb!");                }             }            // Сама стрельба         }

Данный код пытается получить компонент из объекта hitInfo и, если это удаётся, вызывает методshoot. Мишень падает, свет от мишени выключается, звук попадания воспроизводится. Далее, желательно дать группе свободное задание по кастомизации своего проекта. Как альтернативу, можно предложить изменить код таким образом, чтобы мишень меняла цвет при попадании. Делается это заменой в Target строк:

light.SetActive(false);

на

light.GetComponent<Light>().color = Color.red;

Таким образом, свет меняется и не удаляется.

Гранаты

Эта часть урока должна ещё больше закрепить понимание векторов и работы рейкастов.

Для начала, создадим другой скрипт - дальномер.

using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI; public class Lenght :  MonoBehaviour {    public Text Dalnost;    float rasstoyanie = 0; // переменная для расстояния до цели     // Use this for initialization     void Start() {     }     // Update is called once per frame     void Update() {        RaycastHit hitInfo;                if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hitInfo, 200)) {            rasstoyanie = hitInfo.distance;            Dalnost.text = rasstoyanie.ToString();        }    }}

Скрипт закинем в FPScontroller/FirstPersonCharacter. В Canvas создадим текст, закинем его в скрипт.
В этом скрипте реализован простейший рейкаст, и на его примере мы разбираем, как рейкаст передаёт информацию в структуру и как нам получать из структуры эту информацию.
При срабатывании рейкаста мы выводим дальность на экран.

Следующий шаг - бросок гранаты. Мы не используем никаких анимаций, мы создаём гранату на небольшом расстоянии от игрока и придаём ей скорость по направлению взгляда. Изменим скрипт, добавив в него поля для префаба гранаты и код броска.

using System.Collections;using System.Collections.Generic; using UnityEngine;using UnityEngine.UI; public class Length :  MonoBehaviour {    public Text Dalnost;    float rasstoyanie = 0; // переменная для расстояния до цели     public GameObject sharik;     // Use this for initialization    void Start() {     }     // Update is called once per frame     void Update() {        RaycastHit hitInfo;                 if(Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), outhitInfo, 200)) {            rasstoyanie = hitInfo.distance;             Dalnost.text = rasstoyanie.ToString ();            if(Input.GetKeyDown(KeyCode.Mouse1)) {                GameObject go = Instantiate(                    sharik,                     transform.position + Vector3.Normalize(hitInfo.point - transform.position),                     transform.rotation                );                Rigidbody rig = go.GetComponent<Rigidbody>();                rig.velocity = Vector3.Normalize(hitInfo.point - transform.position) * 10;            }         }    } }

Сразу же возникнет вопрос - зачем такое решение? К тому же, при направлении в небо бросок не произойдёт. Сделано всё это в учебных целях. Т.к. в рамках урока код пишется поэтапно, на данном участке объясняется, как найти вектор направления на объект по конечным и начальным координатам. Теперь дело за самой гранатой. Создадим скрипт Grenade.

using System.Collections;using System.Collections.Generic;using UnityEngine; public class Grenade :  MonoBehaviour {    public Transform explosionPrefab;     void OnCollisionEnter(Collision collision) {        ContactPoint contact = collision.contacts[0];                // Rotate the object so that the y-axis faces along the normal of the surface        Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal);        Vector3 pos = contact.point;         Instantiate(explosionPrefab, pos, rot);        Destroy(gameObject);    }}

Закидываем на сцену гранату, на меш Body ставим коллайдеру Convex, добавляем гранате RIgidbody и наш скрипт. Получившуюся гранату добавляем в префаб и удаляем со сцены.

Скрипт удаляет объект, на котором висит, но до этого инстантиирует объект, который мы выбираем в качестве эффекта взрыва.

Создадим эффект взрыва. В нём должен быть свет от взрыва, AudioSource с галочкой PlayOnAwake и звуком взрыва, Spital Blend на 90 процентов переведённый в 3д и увеличенный радиус распространения звука.
Для правильной отработки всех эффектов и разлёта Rigidbody нужно создать ещё один скрипт. Его мы назовём Explosion:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Explosion :  MonoBehaviour {    public float radius = 5.0f;    public float power = 10.0f;    public GameObject svet;    void Start() {        Destroy(svet, 0.1f);        Vector3 explosionPos = transform.position;        Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);        foreach(Collider hit in colliders) {            Rigidbodyrb = hit.GetComponent<Rigidbody>();            if (rb != null) {                rb.AddExplosionForce(power, explosionPos, radius, 3.0f);            }        }    }}

Его нужно закинуть на эффект взрыва и создать префаб.
Данный префаб и используется в скрипте гранаты для создания взрыва.

Готово!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru