Как в Visual Studio Code компилировать код Си?
Что устанавливать из Visual Studio 2017 Community, если собираюсь писать код на Си?
- Вопрос задан более трёх лет назад
- 29355 просмотров
Комментировать
Решения вопроса 0
Ответы на вопрос 1

Электроньщик, программист
1. компиляция на Code — нудный процесс, ибо нужно использовать компилятор из вне, для самой отладки нужно создать launch.json (и в нём прописать пути для GDB и настроить файл который будет компилироваться и всё это нужно лишь для отладки, да и не обязательно), а для компиляции .
Ссылка
Note: The C/C++ extension does not include a C++ compiler or debugger. You will need to install these tools or use those already installed on your computer. Popular C++ compilers are mingw-w64 for Windows, Clang for XCode for macOS, and GCC on Linux. Make sure your compiler executable is in your platform path so the extension can find it. The extension also supports the Windows Subsystem for Linux.
. создаёте c_cpp_properties.json, в вашем воркспэйсе и прописываете:
< "name": "Win32", "includePath": [ "$" ], "defines": [ "_DEBUG", "UNICODE" ], "compilerPath": "C:\\mingw-w64\\bin\\gcc.exe", "intelliSenseMode": "clang-x64", "browse": < "path": [ "$" ], "limitSymbolsToIncludedHeaders": true, "databaseFilename": "" > >
Предварительно скачав mingw-w64/GCC.
Думаю вас это не устроит, но этого мало, нужен tasks.json примерно такого вида:
А параметры для вашего компилятора думаю найдёте сами, из хода выполнения в DevC`и++, «command» это обычное выполнение в cmd, на сколько знаю для «C`и» будет нужен gcc.exe, параметры будут содержать в себе пути к стандартным библиотекам и тип компилируемого приложения по типу консольное или оконное.
2. Обычный проект C++ пустой, ручкой создаёшь файл с расширением «*.c», и в параметрах проекта поставить компилятор gcc или Си (что-то такое)
Пошаговое руководство. Формирование кода с помощью текстовых шаблонов
Область применения:
Visual Studio Visual Studio для Mac
Visual Studio Code ![]()
Создание кода позволяет создавать программный код, который является строго типизированным, но при этом может быть легко изменен при изменении исходной модели. Сравните это с альтернативным методом написания полностью универсальной программы, принимающей файл конфигурации. Он является более гибким, однако в результате получается код, который труден для чтения и редактирования, а также не отличается высокой производительностью. Данное преимущество рассматривается в этом пошаговом руководстве.
Типизированный код для чтения XML-файла
Пространство имен System.Xml предоставляет комплексные средства для загрузки XML-документа и последующего свободного просмотра его в памяти. К сожалению, все эти узлы имеют одинаковый тип — XmlNode. Поэтому очень легко допустить ошибки программирования, например, ожидая неверный тип дочернего узла или неверные атрибуты.
В данном примере проекта шаблон считывает пример файла XML и создает классы, соответствующие каждому типу узла. В написанном вручную коде эти классы можно использовать для перехода по XML-файлу. Можно также запустить приложение для любых других файлов, использующих те же типы узлов. Пример XML-файла служит для предоставления примеров всех типов узлов, с которыми должно работать ваше приложение.
Приложение xsd.exe, которое входит в состав Visual Studio, может создавать строго типизированные классы из XML-файлов. Показанный здесь шаблон представлен в качестве примера.
Ниже приведен пример файла:
В проекте, создаваемом в данном пошаговом руководстве, можно написать следующий код, при этом IntelliSense будет предлагать правильные имена атрибутов и дочерних элементов:
Catalog catalog = new Catalog(xmlDocument); foreach (Artist artist in catalog.Artist) < Console.WriteLine(artist.name); foreach (Song song in artist.Song) < Console.WriteLine(" " + song.Text); >>
Сравните это с нетипизированным кодом, который можно написать без шаблона:
XmlNode catalog = xmlDocument.SelectSingleNode("catalog"); foreach (XmlNode artist in catalog.SelectNodes("artist")) < Console.WriteLine(artist.Attributes["name"].Value); foreach (XmlNode song in artist.SelectNodes("song")) < Console.WriteLine(" " + song.InnerText); >>
В строго типизированной версии изменение схемы XML приводит к изменениям классов. Компилятор выделяет части кода приложения, которые необходимо изменить. В нетипизированной версии, использующей универсальный XML-код, такая поддержка отсутствует.
В этом проекте применяется один файл шаблона для создания классов, которые делают типизированную версию возможной.
Настройка проекта
Создание или открытие проекта C#
Эту методику можно применить к любому проекту кода. В этом пошаговом руководстве используется проект C#, а в целях тестирования применяется консольное приложение.
- В меню Файл последовательно выберите пункты Создать и Проект.
- Выберите узел Visual C# а затем в области Шаблоны щелкните Консольное приложение.
Добавление XML-файла прототипа в проект
Этот файл служит для предоставления примеров всех типов узлов XML, которые ваше приложение должно уметь считывать. Это может быть файл, который будет использоваться для тестирования приложения. Шаблон создаст класс C# для каждого типа узла в этом файле.
Файл должен быть частью проекта, чтобы шаблон мог считать его, однако он не будет встроен в скомпилированное приложение.
- В обозревателе решенийщелкните проект правой кнопкой мыши, выберите пункт Добавить, а затем щелкните Новый элемент.
- В диалоговом окне Добавление нового элемента выберите в области Шаблоны элемент XML-файл.
- Добавьте образец содержимого в файл.
- В рамках данного пошагового руководства назовите файл exampleXml.xml . Используйте в качестве содержимого файла XML-код, приведенный в предыдущем разделе.
Добавление файла кода тестирования
Добавьте в проект файл C# и напишите в нем пример кода, который вы хотите иметь возможность записать. Например:
using System; namespace MyProject < class CodeGeneratorTest < public void TestMethod() < Catalog catalog = new Catalog(@"..\..\exampleXml.xml"); foreach (Artist artist in catalog.Artist) < Console.WriteLine(artist.name); foreach (Song song in artist.Song) < Console.WriteLine(" " + song.Text); >> > > >
На этом этапе скомпилировать код не удастся. При написании шаблона вы создадите классы, позволяющие успешно скомпилировать код.
Более полный тест позволит проверить выходные данные этой тестовой функции на соответствие известному содержимому из примера XML-файла. Однако в данном пошаговом руководстве нам достаточно успешной компиляции тестового метода.
Добавление файла текстового шаблона
Добавьте текстовый файл шаблона и задайте для файла выходных данных значение .cs.
- В обозревателе решенийщелкните проект правой кнопкой мыши, выберите пункт Добавить, а затем щелкните Новый элемент.
- В диалоговом окне Добавление нового элемента выберите в области Шаблоны элемент Текстовый шаблон.
Примечание. Убедитесь, что добавляется именно текстовый шаблон, а не предварительно преобразованный текстовый шаблон.
Обратите внимание, что CS-файл отображается в обозревателе решений как дочерний элемент файла шаблона. Его можно просмотреть, нажав кнопку [+] рядом с именем файла шаблона. Этот файл создается из файла шаблона при каждом сохранении или перемещении фокуса с файла шаблона. Созданный файл компилируется как часть проекта.
Для удобства в процессе разработки файла шаблона расположите окна файла шаблона и созданный файл рядом друг с другом. Это позволит сразу просматривать выходные данные шаблона. Также можно заметить, что когда шаблон создает недопустимый код C#, в окне сообщений об ошибке отображаются ошибки.
Любые изменения, вносимые непосредственно в созданный файл, будут теряться при каждом сохранении файла шаблона. Поэтому следует либо избегать изменения созданного файла, либо редактировать его лишь для кратковременных экспериментов. Иногда бывает удобно проверить небольшой фрагмент кода в созданном файле, где работает технология IntelliSense, и затем скопировать его в файл шаблона.
Разработка шаблона текста
Следуя рекомендациям в отношении гибкой разработки, мы будем разрабатывать шаблон небольшими этапами, устраняя ошибки на каждом шаге, пока тестовый код не будет компилироваться и выполняться правильно.
Прототип для создаваемого кода
Тестовый код требует класс для каждого узла в файле. Таким образом, некоторые ошибки компиляции исчезнут, если добавить в шаблон следующие строки и сохранить его:
class Catalog <> class Artist <> class Song <>
Это поможет увидеть, что именно требуется, но объявления должны создаваться из типов узлов в образце XML-файла. Удалите эти экспериментальные строки из шаблона.
Создание кода приложения из модели XML-файла
Чтобы прочитать XML-файл и создать объявления классов, замените содержимое шаблона следующим кодом:
public partial class <> #>
Замените путь к файлу правильным путем для проекта.
Обратите внимание на разделители блока кода . Эти разделители обозначают фрагмент кода программы, создающий текст. Разделители блока выражения обозначают выражение, которое может быть вычислено в строку.
При создании шаблона, формирующего исходный код для приложения, вы работаете с двумя отдельными текстами программ. Программа внутри разделителей блока кода выполняется каждый раз, когда вы сохраняете шаблон или перемещаете фокус на другое окно. Создаваемый ею текст, который отображается вне разделителей, копируется в создаваемый файл и становится частью кода приложения.
Директива ведет себя как ссылка, предоставляя коду шаблона доступ к сборке. Список сборок, видимых шаблоном, отличается от списка ссылок в проекте приложения.
Директива действует как оператор using , позволяя использовать короткие имена классов в импортированном пространстве имен.
К сожалению, хотя этот шаблон и формирует код, он создает объявление класса для каждого узла в примере XML-файла, так что если существует несколько экземпляров узла , появится несколько объявлений класса song.
Чтение файла модели и последующее создание кода
Многие текстовые шаблоны следуют общему правилу, где первая часть шаблона считывает исходный файл, а вторая — создает шаблон. Необходимо считать весь пример файла, чтобы подвести итоги по содержащимся в нем типам узлов, а затем создать объявления классов. Другой необходим, чтобы мы могли использовать Dictionary<>:
nodeTypes = new Dictionary(); foreach (XmlNode node in doc.SelectNodes("//*")) < nodeTypes[node.Name] = ""; >// Generate the code foreach (string nodeName in nodeTypes.Keys) < #>public partial class <> #>
Добавление вспомогательного метода
Блок управления возможностями класса — это блок, в котором можно определить вспомогательные методы. Блок разделяется с помощью и должен быть последним блоком в файле.
Если вы предпочитаете, чтобы имена классов начинались с прописной буквы, замените последнюю часть шаблона следующим кодом:
// Generate the code foreach (string nodeName in nodeTypes.Keys) < #>public partial class <> #> #>
На этом этапе созданный CS-файл содержит следующие объявления:
public partial class Catalog <> public partial class Artist <> public partial class Song <>
Аналогичным образом можно добавить дополнительные сведения, такие как свойства дочерних узлов, атрибуты и внутренний текст.
Доступ к API Visual Studio
hostspecific Задание атрибута директивы позволяет шаблону получить доступ к API Visual Studio. Благодаря этому шаблон может получить сведения о расположении файлов проекта, чтобы избежать использования абсолютного пути к файлу в коде шаблона.
. . EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host) .GetService(typeof(EnvDTE.DTE)); // Open the prototype document. XmlDocument doc = new XmlDocument(); doc.Load(System.IO.Path.Combine(dte.ActiveDocument.Path, "exampleXml.xml"));
Завершение текстового шаблона
Следующее содержимое шаблона создает код, позволяющий скомпилировать и запустить тестовый код.
using System;using System.Collections.Generic;using System.Linq;using System.Xml;namespace MyProject < child name --> child node type Dictionary> nodeTypes = new Dictionary>(); // The Visual Studio host, to get the local file path. EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host) .GetService(typeof(EnvDTE.DTE)); // Open the prototype document. XmlDocument doc = new XmlDocument(); doc.Load(System.IO.Path.Combine(dte.ActiveDocument.Path, "exampleXml.xml")); // Inspect all the nodes in the document. // The example might contain many nodes of the same type, // so make a dictionary of node types and their children. foreach (XmlNode node in doc.SelectNodes("//*")) < Dictionarysubs = null; if (!nodeTypes.TryGetValue(node.Name, out subs)) < subs = new Dictionary(); nodeTypes.Add(node.Name, subs); > foreach (XmlNode child in node.ChildNodes) < subs[child.Name] = child.NodeType; >foreach (XmlNode child in node.Attributes) < subs[child.Name] = child.NodeType; >> // Generate a class for each node type. foreach (string className in nodeTypes.Keys) < // Capitalize the first character of the name. #>partial class < private XmlNode thisNode; public (XmlNode node) < thisNode = node; > public IEnumerable> < get < foreach (XmlNode node in thisNode.SelectNodes("")) yield return new (node); > > public string < get < return thisNode.Attributes[""].Value; > > public string Text < get < return thisNode.InnerText; >> // switch > // foreach class child // End of the generated class: #> > // foreach class // Add a constructor for the root class // that accepts an XML filename. string rootClassName = doc.SelectSingleNode("*").Name; #> partial class < public (string fileName)< XmlDocument doc = new XmlDocument(); doc.Load(fileName); thisNode = doc.SelectSingleNode("");> >> #>
Запуск тестовой программы
В функции main консольного приложения за выполнение тестового метода отвечают приведенные ниже строки. Нажмите клавишу F5, чтобы запустить программу в режиме отладки:
using System; namespace MyProject < class Program < static void Main(string[] args) < new CodeGeneratorTest().TestMethod(); // Allow user to see the output: Console.ReadLine(); >> >
Запись и обновление приложения
Теперь приложение можно написать в строго типизированном стиле, используя вместо универсального XML-кода созданные классы.
При изменении схемы XML можно легко создать новые классы. Компилятор сообщит разработчику, где именно требуется обновить код приложения.
Чтобы повторно создать классы при изменении примера XML-файла, нажмите кнопку «Преобразовать все шаблоны» на панели инструментов Обозреватель решений.
Заключение
В этом пошаговом руководстве показано несколько методов и преимуществ создания кода:
- Создание кода — это создание части исходного кода приложения на базе модели. Модель содержит информацию в форме, приемлемой для домена приложения, и может измениться в течение жизненного цикла приложения.
- Одно из преимуществ создания кода — строгая типизация. Хотя модель представляет данные в форме, более удобной для пользователя, созданный код позволяет другим частям приложения работать с информацией, используя набор типов.
- IntelliSense и компилятор помогают создавать код, который соответствует схеме модели, как при написании нового кода, так и при обновлении схемы.
- Эти преимущества можно использовать, добавив в проект один несложный файл шаблона.
- Текстовый шаблон можно разрабатывать и тестировать быстро и последовательно.
В этом пошаговом руководстве код программы фактически создается из экземпляра модели — репрезентативного примера XML-файлов, которые будут обрабатывать приложение. При более формальном подходе схема XML будет выполнять роль входных данных для шаблона в форме XSD-файла или определения доменного языка. Такой подход позволит шаблону проще определить такие характеристики, как кратность отношения.
Устранение неполадок с текстовым шаблоном
Если при преобразовании или компиляции шаблона в списке ошибок появились ошибки или выходной файл был создан неправильно, эти неполадки текстового шаблона можно устранить с помощью методов, описанных в статье Создание файлов с помощью служебной программы TextTransform.
См. также
- Создание кода во время разработки с помощью текстовых шаблонов T4
- Написание текстового шаблона T4
Пишем с комфортом на Rust в Visual Studio Code

Нет денег на CLion или Intellij Ultimate, но тебе нужно отлаживать код?
Очень нравится Visual Studio Code, но при виде десятков плагинов разбегаются глаза?
Если на любой из вопросов выше ты ответил «да», то эта статья для тебя.
Во многом эта статья обращена мне самому в прошлом, когда я долго мучался с настройкой vscode и плевался от кучи конфликтов и непонятных ошибок.
На данный момент я использую CLion с плагином Intellij Rust
А, ну и это моя первая статья в принципе, так что любая критика и вопросы приветствуются.
Устанавливаем тулчейн
Это самое простое, и при этом самое важное, без этого не получится собрать твой прекрасный код.
- Скачай и установи Инструменты сборки для Visual Studio отсюда:


При каждом запросе на перезагрузку — перезагружайся. - Скачай и установи rustup-init отсюда: https://rustup.rs/
При его запуске появится окно консоли со следующим содержанием:
Welcome to Rust! This will download and install the official compiler for the Rust programming language, and its package manager, Cargo. Rustup metadata and toolchains will be installed into the Rustup home directory, located at: C:\Users\Vabka\.rustup This can be modified with the RUSTUP_HOME environment variable. The Cargo home directory located at: C:\Users\Vabka\.cargo This can be modified with the CARGO_HOME environment variable. The cargo, rustc, rustup and other commands will be added to Cargo's bin directory, located at: C:\Users\Vabka\.cargo\bin This path will then be added to your PATH environment variable by modifying the HKEY_CURRENT_USER/Environment/PATH registry key. You can uninstall at any time with rustup self uninstall and these changes will be reverted. Current installation options: default host triple: x86_64-pc-windows-msvc default toolchain: stable (default) profile: default modify PATH variable: yes 1) Proceed with installation (default) 2) Customize installation 3) Cancel installation >1
Устанавливаем и настраиваем Visual Studio Code
Предположим, что ты уже установил vs code: https://code.visualstudio.com/
Теперь нам нужно научить его работать с Rust, это делается при помощи расширений.
Нам понадобятся расширения для подсветки синтаксиса и автодополнений, отладки, и что-то ещё для комфортного редактирования cargo.toml.
Для подсветки синтаксиса и автодополнений есть два наиболее популярных расширения: Rust и rust-analyzer. Они работают немного по-разному и конфликтуют между собой. Я не буду глубоко разбирать, в чём у них различия, но rust-analyzer работает лучше, потому берём его.
Для отладки устанавливаем CodeLLDB.
Для работы с toml ставим Even Better TOML (для подсветки), crates (для удобного обновления зависимостей), и crates completer (для автодополнения имён пакетов и версий)
Также, тебе может пригодится Test Explorer UI и Rust Test Explorer для того чтобы запускать тесты из боковой панели, а не из консоли.
Проверяем всё вместе
Создаём пустой проект в консоли
mkdir rust-demo # создаём папку rust-demo cd rust-demo # переходим в неё cargo init # инициализируем в ней минимальный проект code . ./src/main.rs # открываем vscode

Для того чтобы просто запустить — нажимай Run.
Для того чтобы запустить в режиме отладки — жми Debug.
Также можно создать launch.json через раздел Run and Debug (четвёртая иконка на боковой панели на скриншоте).
UPD1: Спасибо @TrueBersза поправку, что Better TOML устарел, и вместо него надо использовать Even Better TOML
C# Урок 1. Установка Visual Studio Code
Че так долго = P.S. Словом, разлил газировку на ноут, был в депрессии, ай пофиг, начали.
1. C чего начать
Для работы с текстом вам нужен текстовый редактор (World), для обработки фотографий вам нужен графический редактор (Photoshop), для программирования требуется IDE(интегрированная среда разработки).
IDE – это комплекс программных средств для разработки ПО, такие как
a. Текстовой редактор (Ну это просто, это там, где мы пишем код)
b. Компилятор/интерпретатор (переводит наш код в машинный код, способный к выполнению процессором)
c. Средства автоматизации сборки (Помогает собрать наш проект. К примеру, в исполняемый .exe файл)
d. Отладчик/Debugger (Можно ставить всякие точки остановки (breakpoint и watch) чтобы отслеживать, что выполняет код на данной строчке кода)
Под C# основной IDE является Visual Studio (Community является бесплатной, и в принципе там есть практически всё). Но(!) в наших уроках мы будем писать в Visual Studio Code (редактор кода).

Итак, определились. Скачиваем Visual Studio Code ( https://code.visualstudio.com/ )

Нажимаем на нужную кнопку “Windows” и скачиваем установщик. Там всё стандартно, ставим галочку «Добавить в Path».
А затем скачиваем .NET Core SDK ( https://dotnet.microsoft.com/download ). Просто ждем установки, после установки вроде потребуется перезагрузить ПК, я уже не помню.

Далее запускаем VSCode, переходим на вкладку Extension и устанавливаем расширение для VSCode ->C#. Для этого нужно в поиске расширений просто написать C#

Далее создаем какую-нибудь папку, где у нас будет проект, и открываем эту папку в VSCode File->Open Folder…
Далее нам нужно создать консольное приложение на языке C#. Переходим по этой ссылке, https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-ne. , и находим среди всех команд ту что нужно.

Далее в VSCode выбираем меню Terminal->New Terminal и пишем туда команду
dotnet new console
Ждем и затем пишем
dotnet restore
чтобы скачались нужные компоненты (требуется один раз). Далее пишем
и в терминале происходит запуск нашей программы. Выглядит не Айс, поэтому идет до конца.
Запускаем нашу программу Debug-Start Debugging. Выбираем (.NET Core), чем будем компилировать наш код и открывается окно настройки.

В данном окне настройки поменяем строку
«console»: «internalConsole»,
«console»: «externalTerminal»,

Затем добавляем в наш код следующий код Console.ReadLine(); и вновь запустим Debug-Start Debugging. Мы должны увидеть следующее


Конечно, это не конечный вариант, конечный вариант будет в виде видеоурока на youtube (писать что и как устанавливать, это конечно же жесть) https://www.youtube.com/channel/UCxHlqdzYPZCKHTDWFZ895Qg?vie. . Спасибо за внимание.

4 года назад
Много раз видел здесь уроки, но обычно авторы после первой статьи сливаются
раскрыть ветку
1 год назад
Уважаемые пикабушники, а что делать, если я нажимаю запустить код, а он мне открывает вот это?

4 года назад
«Для работы с текстом вам нужен текстовый редактор (World)»
Я конечно понимаю, что опечатка, но.
Мне нужен этот Чертов «world», причем весь 🙂
4 года назад
Подпишусь. Продолжай. А форму с кнопкой оно умеет делать как в делфи? Я в делфи пописывал иногда, потом мне сказали что делфи умер.
раскрыть ветку
3 года назад
Где делают приятные интерфейсы, те же диаграммы?
Вот например пробовал в VB+ASPNet — используя стандартные контроллы, выходит не очень
Похожие посты
1 год назад

Пишем примитивную 3D игру. Расширяем пространство. Запускаем корабль. Часть 2
Всем добрый вечер! Материал к данному посту уже давно был готов, но не получилось выпустить его так быстро, как хотелось. Но, тем не менее.
Часть 1. Введение, строим 2D объект

Я прочитал некоторые отзывы на первый пост, особенно, касательно программной части. Ещё раз акцентирую внимание — данные примеры не являются идеальными в плане кода, так как написаны исключительно для демонстрации алгоритма. Рекомендую не использовать их, а попытаться самостоятельно разработать их аналоги.
Итак, в прошлый раз мы остановились на том, что научились поворачивать 2D-область с помощью матриц. Некоторые просили рассказать подробнее о том, как их решать. Думаю, в этом посте завеса тайны будет чуть приоткрыта. Давайте начнём операции по расширению пространства до трёхмерного и добавим z-ось. Для этого мы сделаем следующее:
Задача 1. Изменим структуру нашего объекта, позволив хранить трёхмерные координаты.


Для того, чтобы понять, почему количество значений увеличилось на столько порядков, сразу же небольшая беседа о структуре 3D объекта. По-научному это называется «полигональная сетка».
Если кратко, то мы можем хранить описание 3D объекта в виде:
1. Списка вершин (Vertices)
2. Списка ребёр (Edges)
3. Списка, ну скажем так, граней (Faces)
В нашем случае, третье пока не рассматривается (хотя до него руки тоже дойдут). Рассмотрим первые два варианта. Список крайних точек может быть представлен следующим образом:

Рассмотрим куб. Куб имеет 8 крайних точек. Описываются их координаты по 3 осям, скажем так, в явно заданном виде (обычно они задаются в виде векторов, т.е. и т.п.). Далее следует порядок их объединения.
Специальный алгоритм, который мы сами должны написать, восстановит список рёбер по списку вершин. Так как основной задачей на данный момент не является написание такого алгоритма, то мы сразу выберем вариант отображения в виде списка ребёр (edges), хотя он громоздкий и работать с ним не очень удобно.
Представим куб в виде пар координат. Две крайние точки, соединяются в ребро, которое будет отображено в виде линии на экране. Тогда одно такое ребро будет задано координатами (x1, y1, z1, x2, y2, z2). Логичнее разместить их попарно (, , но, для упрощения алгоритма, они будут размещены в линию. У куба 8 вершин. А рёбер уже 12. Поэтому и получаем такую картину:

Если 2D прямоугольник в виде точек описывался массивом [4][2], то куб в виде рёбер уже [12][6].
Также, к центру объекта добавляется третье измерение: float center[3] = < 0.f, 0.f, 0.f >;
Задача 2. Организуем проекцию трёхмерного пространства на двумерное
Как я уже упоминал в первом посте, который некоторые незаслуженно назвали «бредом», модель находится где-то там, в невидимом пространстве, а вот практическую реализацию мы можем увидеть. Проекция двумерных координат очень проста по своей сути: x точки отображаем как x на экране, а y, как y. Здесь же мы имеем дело так же с x и y, но при этом куда-то нужно ещё вставить зависимость от координаты z. Можно сделать это также несколькими способами:
1. Построить ортогональную проекцию, например, представив x как x+ k*cos(a)*z, но этот вариант рассматривать мы сейчас не будем.
2. Непосредственно разделить на z: x’=kx/z и y’=ky/z
Второй вариант, на самом деле, очень прост. формулы расскажут его лучше, чем я:

Для получения двумерной координаты достаточно лишь сделать вот так. WW/2 — половина ширины экрана (x), FOV — коэффициент поля видимости, x — координата из массива (value[0]), z — то же самое (value[2]).
Здесь вводится коэффициент FOV, характеризующий глубину поля зрения. Если кто-то когда-либо интересовался анаглифными 3D-очками, то он мог заметить, что при изменении FOV на рендерере изменяется нагрузка на глаза. При сильных значениях изображение вообще перестаёт сходится. Проще показать наглядно, что я и сделаю в примере. В нашем случае примем его как константу 200-300 единиц, и более не будем трогать. Получив все координаты x’ и y’, осуществляем рендеринг, аналогично 2D из первого поста.
Так как я демонстрирую алгоритм, а не работу с библиотекой, я не использую вспомогательные средства SFML, а только лишь отображение объектов по явным координатам, подобно тому, как это делалось на древних компьютерах типа ZX Spectrum.
Ии.. мы получили вот такую картину:

Четыре точки на поле. А чем она отличается от той, что была в первом посте, спросите вы? Не торопитесь. Давайте теперь включим отображение линий:

Что-то начинает проясняться.. Мы находимся в центре куба, и смотрим прямо. Потому и видим четыре точки одной из его граней. Так и должно быть. При таком способе проекции наш экран соответствует координате z = 0. Таким образом, остальные 4 точки остались за нашей спиной, и к ним уходят соответствующие рёбра. Так же, чтобы избежать вылета приложения при делении на z = 0, нужно перед операцией деления явно указать: if (z=0) z=(0.01f). На этом задача по рендерингу окончена.
Задача 3. Расширяем функции поворота для 3D пространства
Вот здесь мы снова сталкиваемся с матрицами. Только для двумерного прямоугольника они были второго порядка, а сейчас нас ожидают матрицы 3 порядка (иллюстрация с сайта Wikipedia):

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

(цифры выше для примера). В нашем столбце будут координаты трёх вершин 1-й точки. Для линии, соответственно, делается два умножения. Более подробно для людей, мало знакомых с матрицами:
1. Сначала составляется матрица Mx, My, Mz в зависимости от того, вращение вокруг какой оси вам потребовалось: x,y,z.
2. Затем полученная матрица векторно перемножается на данные координаты
3. Полученные координаты записываются в новый объект, который будет являться уже повёрнутой копией старого объекта (в ООП), либо просто в поля (область памяти) старого объекта, как делалось на старинных компьютерах.

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

newValue[i] — новое значение x[0], y[1], z[2] для первой точки линии. Для второй точки — аналогично.
oldData[i] — старое значение x[0], y[1], z[2] для первой точки линии. Аналогично newValue
center[i] — аналогично заданные координаты центра объекта.
Именно здесь нам и пригодился центр объекта. При повороте относительно произвольной оси, если объект уже был сдвинут, при неверно заданном центре объект начнёт вращаться либо с большим радиусом, либо вообще непредсказуемо. Поэтому при операции сдвига, например, +100 по z, в center так же записывается z: 0,0,100.
Грубо говоря, в процессе перемножения матрицы, координата центра вычитается, и объект как бы «возвращается» в центр поля, вычисляется, и затем сдвигается обратно. Скорее всего, есть и более эффективные алгоритмы для решения данной задачи.
Давайте теперь испытаем полученные функции, и попробуем повернуть куб (как я и говорил, поворот осуществляется по прерыванию, т.к. игровой таймер всё ещё отсутствует):
Я кружусь в фантастическом танце. Я почти итальянец!
Также, демонстрация изменения константы FOV из задачи 2 на развёрнутом кубе:
Т.е. изменение FOV как бы искажает отклонение осью z оси x. Проявляется это увеличением количества видимых на экране объектов.
Задача 4. Элементы «гейм-дизайна»
Давайте теперь попробуем реализовать элемент игры, а именно, космический корабль, которым мы управляем. Нам понадобится модель корабля. Так как пишем мы примитивную игру, так сказать, «следуя по стопам» за гигантами прошлого века, модель тоже должна быть несложной в исполнении. Я смоделировал её в 3D редакторе:

Теперь нужно получить её рёбра. Сделать это явно мы не сможем, поэтому получим хотя бы вершины. Экспортируем модель в формате .obj. На примере куба:

Как мы можем видеть, содержимое файла, если открыть его в редакторе, похоже на упомянутое в первой задаче. Но есть небольшие отличия:
v — непосредственно вершины, в виде нормализованного к единице вектора
vn — нормали (нам не нужны)
f — грани (faces). Они нам не помогут. На первый взгляд сложно. Рассмотрим ещё более простую модель: грань.

4 вершины, 1 «face»: задан как 1//1 2//1 4//1 3//1. Но наша программа устроена по другому принципу. Возьмём отсюда только лишь вершины. Так как писать автоматизированное ПО для одной модели нецелесообразно, порядок соединения выставим вручную. Воссоздаём модель корабля:

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

Но, тем не менее, каркасная модель выполнена и состоит из 36 рёбер:

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

В движении смотрится более эффектно:
К полёту готов! Также сразу же можно выполнить некоторую «постановку» сцены: корабль сдвигается вместо центра примерно на 0,7 экрана по y. Однако, конечно, пока это ещё не игра, а всего лишь просмотрщик 3D модели. Но, всё это, как и прежде, поправимо. Вопрос только, когда..
Всем спасибо, с вами был Kekovsky, надеюсь, пост был для вас информативным, комментируйте, поддерживайте автора и т.д.
Показать полностью 19 3
1 год назад
Ответ на пост «Бесплатно помогаю пикабушникам учить программирование, часть 30: «О уместности и правилах хорошего тона.»»
Начну с того, что в 2020 году я закончил медицинский университет на специальность врача-стоматолога без особого желания работать стоматологом и с огромным желанием работать в IT.
Мои познания программирования начались с того, что на 3 курсе мне предложили обучать детей HTML/CSS, Scratch и Python, хотя на тот момент я вообще не имел никакого понятия о том, что это и как с этим работать. Все знания приходилось получать от того же материала, который я приподносил детям. То есть за день, а иногда даже за пару часов до урока я открывал материал, учил его и объяснял уже на уроке.
Всё пришло к тому, что в 2020 я знал базовый HTML/CSS, Python. Ну и на этом всё. В том же году наткнулся на пост про обучение Ruby, загорелся и начал изучать план обучения в телеграм канале. Тогда же у меня родилась дочь, и всё свелось к тому, что мне предстоял выбор между неизвестной мне профессией и работой, которой я посвятил 6 лет обучения в университете. Учитывая давление родственников и родителей выбрал я второе и на программирование на некоторое время полностью забил.
Как начинающему специалисту денег мне не хватало, работы было много, работать приходилось через немогу. Через полгода мне предложили преподавать программирование онлайн, и точно так же я учил материал за пару часов до урока и объяснял его. Время от времени просматривал вакансии на hh и отправлял свои резюме.
К июню 2022, 2 месяца назад я для себя окончательно решил, что хочу работать в IT. Обратно вернулся к плану обучения.
Весь июнь и июль я работал в стоматологии с 9:00 до 18:00, с 19:00 до 00:00 преподавал программирование, а с 00:00 до 3:00 учил Ruby и Ruby on Rails.
Для кого интересно, я постараюсь расписать что я учил до того как получить оффер.
1. Были базовые знания HTML/CSS, JavaScript.
2. Прошёл первые 10 уроков на https://rubyrush.ru/
3. Прочёл Руби для Романтиков и выполнил все задания с этой книги
4. Прочёл где то до 11 главы Майкл Хартл, Ruby on Rails. Дальше всё пошло плохо, так как в книге использовалась старая версия Ruby и Ruby on Rails, до этого как то получалось справляться с ошибками, а потом всё начало полностью ломаться и я забил.
5. Куча видео с youtube, где создавались приложения, делал я их с учётом того, что половину я понимал, а половину не понимал.
В начале августа я активно начал искать работу, составил резюме и отправлял во все появляющиеся вакансии.
Учитывая, что я знал HTML/CSS, JavaScript, Ruby on Rails на базовом уровне, на youtube просмотрел несколько видео по React, я отправлял вакансии и на Frontend, и на Backend. Так же увидев кучу вакансий по PHP решил подучить и его, просмотрел несколько видео и добавил соответствующую графу в резюме. В какой то момент, после первой сотни отправленных резюме мне стало всё равно, в какой стек, на какую должность я попаду, хотелось только сделать первые шаги в IT, а дальше набраться опыта и подыскать работу на Ruby on Rails.
За 2 года я отправил 424 резюме, получил кучу отказов, сделал кучу тестовых заданий, и на днях получил оффер на позицию Ruby on Rails/React разработчик с зп 50 000 рублей удалённо.
Сейчас в голове только страх перед началом работы, так как тестовое задание и техническое интервью были для меня очень сложными, ну и огромное предвкушение перед началом.
Спасибо @prodigal.son, если бы не ваш пост и ваша группа, я так и не сделал бы первые шаги в IT.
Показать полностью
1 год назад

Пишем примитивную 3D игру. Как программировать, чтобы работало. Зачем программисту математика. ч.1
Всем привет! В предыдущих постах я программировал посудомоечную машину. Теперь решил сделать мини-серию постов о программировании, а точнее, о том, как надо мыслить для того, чтобы программа получилась. Я не буду вдаваться в нюансы написания самого кода. Здесь, скорее, будут размышления на тему, так что быть программистом для чтения не обязательно (и не желательно). Поехали.

Для начала, небольшой ракурс в историю. Первая игровая приставка (в привычном нам понимании) появилась в 1972 году. Разработала её группа военных инженеров во главе с Ральфом Бером -«Отцом видеоигр». Эта самая приставка была впоследствии названа Magnavox Odyssey. Выполнена она была полностью на транзисторах. Можете себе представить? (фото взято с сайта Wikimedia).

Такая приставка умела немного. Однако, важнее всего была суть — возможность генерировать свой управляемый телевизионный канал. До этого подобное не приходило никому в голову. Так вот, примерно спустя 10 лет после этих событий, появились в продажу компактные ЭВМ для массового потребления — ZX Spectrum, IBM PC и другие. Каждый получил возможность изучить компьютеры и обрёл пространство для своих творческих экспериментов. А пятьдесят лет спустя, мы привыкли видеть компьютерные игры уже так:

Конечно, у нас есть новейшие производительные процессоры. Практически каждый школьник теперь может «собрать» свой компьютер (вставив для этого комплектующие в соответствующие для них слоты). Но, когда у кого-либо возникает желание разобраться поподробнее, как это всё устроено, и, может быть, написать свою игру (где будут девушки с большими прелестями, можно грабить корованы и т.д.), тут уже возникают нюансы и, оказывается, не всё так просто в этом мире. Но на помощь нам приходят учебники по программированию. Однако, как известно, учат в школе многих, да немногие в итоге выучиваются.
Кстати говоря, скоро наступает 1 сентября. Вспоминаются мои институтские годы — как мы пришли, и первой парой стояла высшая математика. До сих пор во многих провинциальных вузах программа первого семестра начинается с матриц. Я помню ребят, которые невесть как попали в институт, смотрели с задних парт круглыми глазами и только повторяли «- И чё мы тут вообще забыли?». Кстати говоря, ребята эти в итоге семестр не пережили и были отчислены. Но это уже их проблемы. Однако, даже у тех, кто остался, часто возникали вопросы: зачем? что это мы такое учим?
Понимание приходит не сразу, однако, когда ты сталкиваешься с какой-то задачей, то со временем уже начинает что-то доходить. Так же и здесь. Оказывается, с помощью тригонометрических функций, производных и интегралов, матриц очень удобно осуществлять операции над множествами точек и фигур, что является незаменимым при создании графики в компьютерных играх. Собственно, возможность быстро производить подобные операции и характеризует «крутизну и навороченность» компьютера.
Но давайте приступать. Для начала, чтобы программа получилась, необходимо мышление программиста. Я его понимаю, как две составляющие: некая абстрактная модель и её реализация. Короче говоря, теория и практика. Чтобы было более понятно, о чём я говорю, давайте представим, что мы программируем обычную столовую ложку, как в фильме «Матрица»

Так вот, если думать, как программист, то эту ложку можно разделить на две составляющие.
1. Теория ложек
Какие параметры существуют у ложки? Такие, какие вы ей сами зададите. Вы можете взять тарелку, зачерпывать ей суп, и тем самым объявить тарелку ложкой. Но немногие с вами согласятся, ведь, во-первых, это неудобно. Во-вторых, все привыкли видеть ложку привычной ложкой. Поэтому рассмотрим более детальные требования:
— Ложка имеет хлебало и держало черенок и чашечку. Чашечка, в свою очередь, имеет форму полусферы. Мы можем задать чашечку, например как функцию z^2 = x^2 + y^2

Это, конечно же, весьма грубое сравнение. Но оно позволяет представить, каким образом будет реализована дальнейшая ложка. Аналогично можно математически описать и остальные части. Это — будет наша модель ложки. Опять же, одна из возможных.
2. Практика ложек
Как мы знаем, обычно ложки вырезаются из дерева, либо изготавливаются штамповкой из металла. В нашем случае давайте представим, что мы решили запрограммировать ложку в компьютер. Полученная двумерная проекция на экран при этом вполне может выглядеть так:


И то и это — нормально. Всё дело в представлении ложки. В том, как мы её видим и в том, как мы её задали. Последнее компьютер выполняет, в отличие от людей, весьма чётко. Мы задали какую-то задачу, и получили конкретный результат.
Именно поэтому важна проработка обоих частей программы. Понятное дело, новички, когда только врубаются в основы программирования, изучают язык подобно маленьким детям. Т.е., пытаются повторять и копировать какие-то фрагменты кода, составляя из них программу. Однако, каждый чужой кусочек кода является именно практикой, следствием где-то существующей модели. Именно поэтому программа новичка, порою, выглядит, как на известных мемах:

Но, если не сдаваться и упорно двигаться вперёд, можно прийти к какому-то конкретному результату. Для этого требуется выполнять различные «задачки»: на сортировку, на реализацию функций, на описание структур и т.д., пробовать выполнять какие-то кусочки больших и сложных моделей: хвост оленя, чашку ложки и т.п. Собственно, подобным образом осваиваются и другие человеческие науки: для рисования нужно отрабатывать тени и завитки, для игры на музыкальных инструментах — гаммы и аккорды, ну и так далее.
Давайте рассмотрим одну из таких задачек. Вспомним ракурс в историю, который был в начале поста. Первые 3D игры для первых ЭВМ были примерно такие: Battle Tank

Star Wars: Empire strikes back.

Всё это было до Джона Кармака, появления Direct3D, OpenGL и т.д. Примерно в таком ракурсе мы и будем с вами сегодня сегодня работать. Писать будем пока на плюсах, хотя для новичков проще это будет сделать, наверное, на питоне. В качестве подспорья возьмём графическую библиотеку SFML. С 2018 года она уже не поддерживается, но, для иллюстрации сути поста её будет достаточно. Мы хотим получить что-то вроде этого:

Для того, чтобы обдумать реализацию, и, в дальнейшем, это получить, мы должны разобрать «по косточкам» происходящее на экране. Мы видим:
1. GUI (интерфейс с очками и временем). Пока отбросим его.
2. Пространство с астероидами
3. Космический корабль
Также мы знаем, что происходит при нажатии клавиши:
1. Космический корабль наклоняется в сторону
2. Пространство поворачивается вокруг корабля, создавая иллюзию того, что корабль куда-то летит (да, когда вы играете в компьютерную игру, это не игрок поворачивается в пространстве, а пространство вокруг игрока).
Теперь рассмотрим более подробно первую задачу: по нажатии клавиши космический корабль поворачивается. Давайте разберём корабль, по аналогии с ложкой. Он представляет собой проекцию трёхмерного объекта на двумерное, группу точек, соединённых рёбрами (в случае Wireframe моделей — линиями, в остальных — треугольниками, полигонами). Значит, теория корабля следующая:
1. Где-то есть группа точек. Её мы просто задаём, как набор переменных. Произвольно. Но хотя бы один экземпляр её должен существовать.
2. Есть способ изменения этой группы точек по внешним параметрам. Для этого есть матрицы сдвига, масштабирования и поворота в трёхмерном пространстве (материал взят с сайта Wikipedia):

3. Есть способ отображения их на мониторе (хотя это уже больше относится к реализации)
Прежде, чем приступать к реализации 3D варианта (он будет во второй части поста), давайте упростим наш корабль до прямоугольника, и одно измерение уберём. Таким образом, будет проще разобрать задачи на начальном этапе. Да и матрица поворота сильно упрощается и теряет один порядок:

С теорией понятно, давайте перейдём к практике.
1. Развернём IDE, подключим библиотеки, подготовим точку входа.

2. Создадим класс Box2D. Зададим группу точек нашего прямоугольника как двумерный массив — массив пар координат. Пока что с конечным размером.:
float vertices[4][2] = < , , , >;

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

Обратите внимание.
В generateMatrix переменная newMatrix — та самая матрица поворота. Вспоминаем нашу математику. Функция подготовит матрицу поворота на конкретно заданный градус. Именно на неё мы будем перемножать имеющиеся ранее заданные значения точек, чтобы получить новые координаты.
calculatePerVertex — перемножает координаты на матрицу.
recalculateRotation — проходит по всем координатам объекта и выполняет их обработку.
Таким образом, используя матрицу newMatrix, мы можем повернуть любое множество точек, перебрав их каждую по очереди. В двумерном пространстве это будет выглядеть, как движение точки по касательной окружности (в случае положительного угла — по часовой стрелке, и наоборот).
Ну и осталось спроецировать эти точки на экран. Тут есть множество вариантов — можно также создать копию массива точек, соответствующих полученным значениям, если в дальнейшем нам нужно применять к точкам ещё какие-либо операции. В нашем случае это не нужно, поэтому все точки можно просто забить поочерёдно в дисплейную область оперативной памяти посредством средств библиотеки SFML — объекта «Circle» со смещением на координаты, задаваемые вершинами.

Готовый результат при таком способе проекции будет выглядеть так:

При нажатии клавиши фигура поворачивается на заданный угол. На данный момент фигура поворачивается по прерыванию от клавиши, так как игровой таймер ещё не реализован, что будет поправлено в следующих постах, если эта тема понравится.
Обобщим вышесказанное:
Мы имеем некоторую воображаемую модель множества точек. Над которой осуществляем операции, и затем практически отображаем результат этих операций. Именно за это и отвечает математика. Хотя, конечно, это чуть ли не школьный уровень.
Давайте изменим практическую реализацию и соединим наши точки линиями. Для этого мы сообщаем в модель ещё один внешний параметр — порядок соединения этих линий. Он может быть произвольным. Но, как мы знаем, у прямоугольника каждая вершина соединяется лишь с двумя другими под углом 90 градусов. Поэтому порядок соединения будет 1-2-3-4-1.

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

Трёхмерный куб вращается относительно произвольной оси, центр куба не равен линии вращаемой оси (обратите внимание на красную точку). Вершины куба также обозначены окружностями для наглядности. Реализовать такой куб, и, затем, космический корабль мы попробуем во второй части поста.
А для тех, кто дочитал и кто любит компьютерные игры, забежим ещё очень сильно вперёд. В 2009 году Маркус Перссон, изобретатель игры «Minecraft» написал маленький апплет, под названием Minecraft 4k. Вот, как он выглядел:

Переписывание этого апплета также является хорошим упражнением для развития навыков по программированию. Обратите внимание: разворачивается трёхмерный массив кубов, который вращается вокруг игрока, создавая иллюзию того, что игрок осматривается. Оптимизация отсутствует, отрисовываются все грани, однако, также выполняется рейкастинг по 3 осям, и ближайшие грани кубов перекрывают более дальние, создавая такую картинку.
С вами был Kekovsky, на сегодня всё, приятных выходных. Если какие-то из моих постов понравились или были для вас информативными, можете поддержать меня, благодаря стараниям администрации Пикабу у меня теперь так же есть донатная кнопка.
Всем успехов в освоении новых наук!
Показать полностью 21
1 год назад

Интересный Python #14: визуализируем данные
Сегодня напишем вот такое приложение всего в 53 строки кода. Да, всего 53, без шуток. И всё благодаря Streamlit.

Что такое Streamlit?
Streamlit — это библиотека, которая позволяет создавать веб-интерфейс для приложений, работающих с данными. Так что если вы работаете с Pandas, используете Matplotlib, Bokeh, Altair и подобные инструменты, то с помощью Streamlit сможете оборачивать свои скрипты в интерактивные веб-интерфейсы и делиться ими с другими пользователями.
Для кого этот пост?
Для тех, кто уже неплохо знаком с Python и часто работает с данными. Для начинающих многие вещи будут сложноватыми.
Как установить Streamlit
1. Откройте терминал (командную строку);
2. Если умеете пользоваться виртуальным окружением, то создайте и активируйте его. Если нет, то пропустите этот шаг;
3. Выполните команду pip install streamlit.
Документация
Документация просто великолепная, очень удобная и красивая. Ознакомиться с ней можно здесь.
Дальше в посте будет много кода в виде скриншотов. Забрать его в виде текста можно здесь.
Для начала соберём интерфейс приложения. Он будет состоять из:
— формы (контейнера для других элементов);
— пары полей для ввода чисел;
Для каждого из этих элементов в Streamlit есть отдельный класс.

Форма нужна для того, чтобы скрипт не перезапускался полностью при каждом изменении слайдера или поля для ввода (по умолчанию в Streamlit реализовано именно такое поведение).
Задаём настройки страницы, добавляем заголовок «Симулятор ставок» и создаём форму с элементами.

Запускаем сервер
Чтобы запустить сервер Streamlit, выполните в терминале следующую команду:
python -m streamlit run main.py
Вместо main.py , конечно же, укажите имя вашего основного модуля.
Если всё сделано правильно, то спустя несколько секунд откроется окно браузера, в котором отрисуется интерфейс.

Важно: запускать программу с помощью интерпретатора Python не нужно, сервер Streamlit сам это сделает. Если вы внесёте изменения в исходный код, то достаточно будет просто обновить страницу, чтобы изменения вступили в силу.
Теперь реализуем внутреннюю логику. Нужно будет при каждом нажатии на кнопку в форме генерировать датафрейм с результатами ставок.
Начнём с импорта нужных модулей.

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

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

Приложение готово. Обновите страницу в браузере, и Streamlit автоматически подтянет все изменения.
Streamlit щедро раздаёт свои облачные ресурсы, так что развернуть приложение можно буквально в пару команд. Пушим код на Github, даём Streamlit ссылку на репозиторий, немного ждём и получаем ссылку на наше приложение в облаке. Подробности — в документации.
Показать полностью 6
1 год назад

Интересный Python #13: играем «Имперский марш»
В прошлых постах было слишком много слов «работаем». Поэтому сегодняшний пост будет про то, как играть. Причём играть не что-то там, а музыку. Причём музыку не какую-то там, а «Имперский марш» из «Звёздных войн». Так что, товарищи Дарты Вейдеры, отвлекитесь на пару минут от удушения очередного офицера и насладитесь чудесными звуками.

Для воспроизведения звуков будем использовать модуль winsound из стандартной библиотеки Python. Устанавливать ничего не нужно, он уже идёт в комплекте с интерпретатором.
В нём есть функция Beep(), с помощью которой можно воспроизвести звук заданной частоты и заданной длительности.
Важно: модуль работает только под Windows. Господа линуксоиды, сегодня вы в пролёте 🙁
Дальше в посте будет много кода в виде скриншотов. Забрать его в виде текста можно здесь.

Также понадобится модуль time, с помощью которого будем создавать паузы.

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

Ключ — это название ноты, а значение — это частота.
Запишем мелодию в виде набора нот.

1/4, 3/16 и прочее — это длительность каждой ноты.

Нам понадобятся 2 функции: одна для воспроизведения конкретной ноты, а вторая — для воспроизведения всей мелодии.
Воспроизводим ноту

Воспроизводим мелодию

Наслаждаемся результатами
Оригинальный темп мелодии — 103 BPM.

Забрать полный код в текстовом виде можно здесь.
Показать полностью 8
1 год назад

GameMaker Studio 2. Урок 8. Звук
Это воскресенье, мои чуваки.
Всем привет. Сегодня поговорим с вами о том, как подключить звук к своей игре. Как обычно: немного теории, немного практики.
Покажу вам сайт, где большинство звуков можно взять бесплатно и как его настраивать.
Ну и небольшие бонусы, само собой.
Ссылки на предыдущие гайды:
Второй гайд — События отрисовки, коллизия, скрипты.
Третий гайд — Камера и разрешение экрана.
Четвертый гайд — Иерархия объектов. Глобальные переменные.
Пятый гайд — Структуры данных. Сетка комнаты и размещение объектов по сетке.
Шестой гайд — Алгоритмы поиска путей.
Седьмой гайд — Сохранения и их загрузка. Бонус в конце!
План:
— Создание звука
— Подключим звук к кнопкам
— Глобальная настройка звука
— Заставим человечков издавать звук
— Музыка
— Тест звука
— Немного бонусов
Тема сегодня небольшая, поэтому сразу перейдём к делу.
Создание звука.
Прежде, чем приступать к работе, нам нужно подготовить материал. Вы можете записать звук самостоятельно, либо использовать свой: найденный в интернете или записанный самостоятельно.
Я предлагаю скачать уже готовый набор звуков для меню по ссылке:
Ссылка
Как видно, там несколько форматов: OGG, WAV и MP3. Использовать мы можем все из них.
Сохраните архив, распакуйте в удобное место. Можете сразу скопировать путь до него. Дальше нам нужно создать звук и загрузить его. Для этого найдём папочку Sound и там создадим, собственно, Sound.

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

Там всё просто: указываем путь до нужного нам файла. У меня это «modern2.wav«. Можете выбирать любой формат, пока это не столь важно. Стоит лишь отметить, что форматы отличаются не только весом, но и качеством звука и возможностью на него влиять внутри движка.
Нажав пробел, когда меню выделено, либо нажав на стрелочку, вы можете прослушать звук, как просто для того, чтобы убедиться, что вы загрузили то, что нужно, так и для настроек.
Обратите внимание: внизу указанного меню у вас есть вкладка Audio Group. Это ничто иное, как группа звуков. Её можно (и нужно) использовать в тех случаях, когда нам нужно уменьшить или увеличить только определённые звуки. Например, сделать три настройки звука: общий, музыка, игра. С-но, здесь будет три аудио группы: стандарт, музыка, игра.
Чтобы добавить свою аудио группу нужно в верхней части экрана найти вкладку Tools, там — Audio Groups.

Подключим звук к кнопкам.
Для удобства используем объект oMMenu, который сделаем родительским объектом для всех наших кнопок в меню, в т.ч. в настройках.
Не забудьте прописать event_inherited(); у дочерних объектов в тех событиях, которые нужно будет унаследовать.
Для начала, в Create объявим одну переменную:
collided = false;
Она позволит нам проигрывать звук при наведении только один раз.
Теперь в Step’e сделаем простую конструкцию: если наведены на текущий объект и раньше его не касались, то проигрываем звук и говорим, что мы его коснулись.
var dmxg = device_mouse_x_to_gui(0);
var dmyg = device_mouse_y_to_gui(0);
if instance_position(dmxg, dmyg, self) != noone
if !collided
audio_play_sound(sndButton, 10, false);
collided = true;
>
>
else
collided = false;
>
Вот так просто мы сделали следующее:
Соответственно, чтобы проиграть звук нам была нужна команда:
audio_play_sound(Звук, Приоритет, Зациклить)
Приоритет — то, в каком порядке звук обрабатывается. Значения либо от 0 до 100, либо от 0 до 1. Чем больше число — тем выше приоритет.
Глобальная настройка звука.
Раз есть звук — значит его можно настроить.
Для этого в комнате настроек создадим ещё одну кнопку: oSSound.
Как ни странно, но с помощью этой кнопки мы будем устанавливать значение громкости для всех звуков, в т.ч. музыки.
Для этого нужно в событии step будет прописать следующий код:
var dmxg = device_mouse_x_to_gui(0)
var dmyg = device_mouse_y_to_gui(0)
var sound = audio_get_master_gain(0);
if instance_position(dmxg, dmyg, self) != noone
if mouse_check_button_pressed(mb_left)
or mouse_check_button(mb_right)
if dmxg > x + 260
if sound < 1
sound += 0.01;
>
>
else if dmxg < x + 60
if sound > 0
sound -= 0.01;
>
>
audio_master_gain(sound) // Устанавливаем глобальный звук.
>
>
audio_master_gain(x) принимает на вход значение от 0 до 1 (0% — 100%) и устанавливает громкость звуков.
Есть похожая функция, но которая называется audio_set_master_gain(ID слушателя, Громкость). Соответственно, она необходима нам в том случае, если у нас несколько «слушателей«. Полезно при создании мультиплеера, либо для того, чтобы позволить нашему ИИ ориентироваться на звук.
Результат:
Заставим человечков издавать звук.
Прежде, чем приступим, немного теории.
Мы ведь не хотим, чтобы те звуки, которые есть в комнате, были слышны ото всюду, верно? Следовательно, нам нужно использовать немного другой подход к созданию звуков.
Чтобы в дальнейшем иметь возможность на эти звуки влиять, предлагаю воспользоваться эмиттером — или излучателем.
Делать он будет ровно то, о чем говорится в его названии: излучать звук.
А ещё мы сможем его настроить таким образом, чтобы звук был слышен только на определённом расстоянии от излучателя, а также начинал затухать спустя определённое расстояние.
Здесь всё предельно просто.
Перейдём к oCharPlayer и создадим у него событие — alarm 0. Это событие — «будильник», или, если по нормальному, таймер, в котором будет работать код раз в определённое количество кадров. А теперь пошагово:
s_emit = audio_emitter_create(); // Создаём эмиттер.
audio_max_dist = CellWidth * 6; // Макс. расстояние, на котором будет слышно звук
audio_fall_at = CellWidth * 2; // Расстояние, на котором звук начнёт «падать»
audio_falloff_set_model(audio_falloff_linear_distance); // Указываем модель, по которой будет происходить затухание звука
audio_emitter_position(s_emit, x, y, 0); // Устанавливаем стартовую позицию эмиттера.
audio_emitter_falloff(s_emit, audio_fall_at, audio_max_dist, 1); // Устанавливаем затухание.
var len = audio_sound_length(sndButton); // Узнаём длину звука в секундах.
audio_play_sound_on(s_emit, sndButton, false, 1); // Говорим звуку играть.
alarm[0] = room_speed * len; // Запускаем бесконечный таймер, где будет проигрываться звук.
var len = audio_sound_length(sndButton);
audio_play_sound_on(s_emit, sndButton, false, 1);
alarm[0] = room_speed * len;
audio_emitter_position(s_emit, x, y, 0);
Весь код нужно добавлять в конец существующих событий соответственно.
Итак, что у нас получилось? Сейчас увидим.
Как видно, звук перемещается вместе с нашими человечками. Накладывается друг на друга, но играет строго раз в определённый отрезок времени, успевая полностью «проиграться».
По аналогии с oSSound, создадим oSMusic. Разница в том, что в oSMusic мы в Create сразу пропишем переменную music, равную 0.1. Она и будет отвечать у нас за громкость музыки. Соответственно, нужно sound заменить на music в коде и всё.
Создаём файл для музыки: sndMusic
Заходим в Tools -> Audio Groups. Создаём аудио группу под названием audio_music.
Жмакаем «add asset» и выбираем sndMusic.
Теперь перейдём к oGameManager.
В Create допишем:
if !audio_group_is_loaded(audio_music)
audio_group_load(audio_music);
>
audio_group_set_gain(audio_music, 0.1, 0)
Следом в Step:
if !audio_is_playing(sndMusic)
audio_play_sound(sndMusic, 10, true)
>
По сути, обозначенный выше код будет загружать нашу аудио группу музыки в память и начнёт её проигрывать.
Немного бонусов.
В ходе этой недели провёл небольшой рефакторинг кода.
Во-первых, простенькая отрисовка путей для наших человечков, что можно было видеть по видео.
Во-вторых, теперь нам не нужны объекты под каждый вид тайла.
Вместо этого теперь используем структуру и конструктор прямо в объектах меню приказов.
Там всё просто: в массив передаём информацию для конструктора о том, какой тайл нам нужен. Затем, обращаясь к массиву по индексу, вычленяем нужные нам данные по ключевым словам.
Для примера — в проекте советую открыть код для oGBuilding и oCell.
Также дополнил код для игрока, чтобы данную систему можно было спокойно тестировать и работать с ней.
Там же в проекте встроен сделан скрипт по созданию прямоугольных областей с заполнением или без.
Соответственно, где заполнения нет — обводка только по периметру.
Там так-то всё просто. Общая логика работы следующая:
Когда нажали (разово) левую кнопку мыши, мы запоминаем координаты клетки, на которую были наведены.
Далее, отдельно проверяем: если кнопка мыши зажата и координаты первой клетки запомнены, на постоянной основе обновляем координаты второй. Заодно создаём прямоугольник с заполнением или без.
Ну и при отпускании ЛКМ — проверяем, что первая координата у нас задана, обновляем картинку и обнуляем все использованные переменные.
Соответственно, как это выглядит при работе:
Сайт, откуда можно брать бесплатные ассеты: звуки, музыку, спрайты и прочее.
Либо из маркета YoYo. На ваше усмотрение.
Серия гайдов подошла к концу. У нас есть заготовка, которую остаётся дорабатывать, рефакторить и всячески дополнять, чтобы создать полноценную игру. Впрочем, это не значит, что я перестану писать всякие статейки, нет.
Буду и дальше разбирать интересные темы, делиться скриптами и заодно рассказывать о ходе работы над проектом.
На повестке: моддинг без кода ( для игроков:D )
Спасибо всем за тёплые слова и поддержку.
До скорых встреч.
Показать полностью 3 5
1 год назад

Путь дурака. Игра на Unity с нуля до выпуска. Часть 2 — Как сделать главное меню игры (пост #1)
В прошлой части мы скачали Unity, подготовились к работе, создали проект, первую сцену и на ней объекты Canvas и Background. В этот раз мы создадим главное меню нашей игры.
Для начало запомните важную вещь — в 2D игре интерфейс построен на слоях. Одно поверх другого. А также связь Parent — Child (Children) — когда один объект находится в другом и свойства родительского объекта влияют на свойства потомка. Именно по этим принципам мы и будем строить наше меню. Давайте попробуем визуально представить как его можно расположить на экране.

Левый блок — это будет объект mainMenu. Он будет родителем для объектов «название игры», «пункты меню» и «ваше лого». Они тоже будут содержать в себе объекты. Начнем.
Создайте пустой объект (Empty Object) и сделайте его дочерним объекта Canvas (в иерархии перетяните один объект на другой). Другой вариант — клик ПКМ по объекту Canvas и затем выбор создания пустого объекта. Так он автоматически станет дочерним. Переименуйте его в mainMenu.

Чтобы mainMenu стало видно на экране, ему нужно добавить компонент Image через меню Add Component в инспекторе (как добавлять компонент я рассказывал в прошлом посте). Так, ну и где же он?

Чтобы придать нашему меню нужное положение, воспользуемся его компонентом Rect Transform. В разделе Anchors устанавливаем:
Затем Left, Right, Top, Bottom ставим по нулям.
Что это всё означает? Anchors (якоря) устанавливают границы нашего объекта относительно размера экрана. Min X: 0.03 прикрепляет левый край (ставит якорь) объекта на уровне 0.03 ширины экрана. Max X: 0.3 прикрепляет правый край на уровне 0.3 — что в итоге дает нам ширину объекта не в пикселях, а в процентах. Он будет занимать в ширину 27% экрана с небольшим отступом слева в 3%. Аналогично с Y — только это уже про высоту объекта.

Теперь установим нашему меню вместо белого пятна приятный глазу цвет. В компоненте Image нажмите на поле Color и установите:
— R — 0.012 (красный)
— G — 0.012 (зеленый)
— A — 0.7 (альфа-канал или прозрачность)

Для закрепления материала по размещению объекта на экране создадим два декоративных элемента — две полоски по бокам от плашки меню.
1. Создаём два пустых объекта как Child объекта mainMenu — line1 и line2
2. Назначаем Anchors для line1
3. Назначаем Anchors для line2
4. Устанавливаем обоим объектам цвет как у mainMenu
• Если устанавливать Anchor больше или меньше единицы, то границы объекта будут выходить за пределы родительского объекта.
• Можно копировать компонент (например Image) через функцию Copy Component и вставлять его другому объекту или вставлять его значения такому же компоненту другого объекта через функцию Paste Component. Пример того и другого на видео.
Теперь можно похвалить себя и подышать свежим воздухом. Далее нам нужно создать объекты для названия игры, пунктов меню и логотипа. Делаем мы это по тому же принципу — создаем дочерние объекты в mainMenu — top, middle и bottom. Для наглядности сразу добавляем им компонент Image с белым цветом (потом изменим).
• Можно выделить в иерархии сразу несколько объектов про помощи Ctrl+левый клик и добавить компонент всем сразу
Аккуратно распределяем объекты на объекте mainMenu, расставляя значения Anchors.
Top —> X: (0, 1) Y: (0.8, 0.96)
Middle —> X: (0, 1) Y: (0.15, 0.8)
Bottom —> X: (0, 1) Y: (0.04, 0.15)
Для создания небольших отступов или точного корректирования положения объекта можно задавать значения Left, Right, Top, Bottom в компоненте Rect Transform. Попробуйте поставить всем трем объектам эти значения по 2 пикселя. Главное не злоупотреблять этим, а то может верстка перестать быть стабильной.
Итак, мы нашли хорошее расположение для блоков и теперь можем удалить у них компоненты Image и Canvas Renderer, так как больше они нам не понадобятся.
• Вообще старайтесь не оставлять ничего лишнего. Это очень полезная привычка, которая поможет в оптимизации игры
Давайте сделаем для игры графическое название. Можно, конечно, и текстом, но графикой будет симпатичнее. Мы же делаем игру для людей, а не черновик для себя — так ведь?
Откроем Фотошоп и создадим в нём новый файл размером 1000х1000 пикселей.

Затем добавим название игры.

Нарисуем декоративную линию.

Добавьте слоган внизу, затем подрежьте инструментом «Рамка» лист по самые края логотипа.

Далее удаляем слой фона, чтобы сделать логотип прозрачным. Затем устанавливаем размер изображения с шириной в 1000 пикселей. После этого нам очень важно сделать так, чтобы каждая сторона изображения была кратна 4 — это необходимо для применения алгоритмов сжатия, чтобы уменьшить вес игры. Так как обрезать уже нечего, то через «Размер холста» делаем высоту 212 пикселей. Итоговый размер 1000х212.

Когда всё готово, вы должны поменять цвет всех объетов на белый. Зачем? На белое изображение в Unity мы легко сможем наложить любой цвет, если нам приспичит. На чёрный — не получится.
Сохраните полученое изображение в формате PNG и положите его в папку проекта Assets/Resources/Images (создайте её). Сам исходный файл PSD сохраните в отдельной папке вне проекта, где вы будете хранить исходники графики.
Самое время добавить созданное название в игру! Создаем для top дочерний объект gameName, добавляем ему компонент Image и выравниваем на экране. Для того, чтобы изображение не искажалось, в компоненте Image активируем Preserve Aspect — эта функция позволит сохранить пропорции.

Отлично! Гейб Ньюэлл вами был бы доволен.
Теперь займёмся объектом middle и сделаем в нем несколько кнопок, которые будут вести в нужное место — «Новая игра», «Загрузить игру», «Настройки», «Об игре», «Выход».
Нам пригодится полезный компонент под названием Vertical Layout Group. Он выравнивает дочерние объекты по вертикали, регулирует их высоту, ширину и отступы. Это означает, что нам не придётся вручную устанавливать размеры и положение кнопок. Его можно сразу добавить к объекту middle.

• Я предпочитаю пользоваться старой версией компонента Text в Unity. Новая версия называется TextMeshPro. Если в вашей версии Unity нет Button, а есть только Button — TextMeshPro, то посмотрите в подпункте Legacy. Или почитайте как работает TextMeshPro
Таким же образом добавьте еще 4 кнопки и настройте Vertical Layout Group у объекта middle.
Padding — это отступы всего списка кнопок относительно объекта.
Spacing — это отступы между кнопок.
Потыкайте настройки, чтобы понять, как это работает, а потом сделайте как на скриншоте. Как видите, теперь у нас есть аккуратный список кнопок.

Обратите внимание на иерархию — у каждой кнопки есть дочерний объект Text. Это такой же объект, как и все, только с прикрепленным компонентом Text. В нем можно вписать текст объекта, указать шрифт, размер, начертание, цвет, выравнивание. Этих настроек вполне достаточно для нашей игры. Установите размер шрифта 24 пикселя.
• Если вы хотите другой шрифт, то создайте папку Assets/Resources/Fonts и добавьте его туда. Он будет доступен в поле выбора шрифта.

Теперь можно нажать кнопку Play вверху рабочего стола и потыкать по кнопкам.
У каждой кнопки есть компонент Button, у которого есть целый ворох настроек как кнопка будет меняться при нажатии, наведении курсора итд. Однако, для мобильных платформ, где не мышка, а тачскрин, это не нужно. Поэтому отключите эту функцию, поставив Trasition —> none.
Далее можно удалить у кнопок компонент Image — для такого минималистичного дизайна он не нужен. Цвет текста сделайте белым.
В объекте bottom осталось место для вашего логотипа. Подыщите что-нибудь прикольное, нарисуйте в фотошопе и сделайте так же, как мы делали с названием игры.
• В любом случае вы вольны экспериментировать в оформлении и делать как вам нравится. Мы не упустим этот момент — не переживайте. Далее по туториалу нам придётся делать столько кнопок, что они вам в кошмарах сниться будут.
Пришло время для правой части меню — не забыли про неё? Для начала определимся, что будет делать каждая кнопка.
Новая игра — выдает список слотов сохранений, при нажатии на который запустится новая игра и автоматически сохранится в этот слот.
Загрузка игры — аналогично, только загружает сохранение.
Настройки — выбор языка игры, громкости звука и музыки.
Дополнительно — авторы, ссылки, благодарности и прочая второстепенная информация.
Выход — закрывает приложение.
Сделаем на каждую кнопку кроме «Выход» своё поле с информацией, которое будет активироваться при нажатии. Создайте в Canvas дочерний объект newGameList, скопируйте для него компонент Image из объекта mainMenu и установите Anchors —> X: (0.31, 0.9) Y: (0, 1).
В нем создайте 6 кнопок для слотов сохранений и назовите их game_0, game_1, game_2, game_3, game_4, game_5. В этот раз мы не будем распределять их с помощью Vertical Layout Group, но сделаем их с небольшой графикой.
Для начала распределите их равномерно по объекту newGameList, используя Anchors.

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

А затем нарисуйте прямоугольник поменьше, но с заливкой.

Удалите слой фона, сделайте прямоугольники белого цвета и сохраните как PNG в папку Assets/Resources/Images под именем frame_1.
В Unity нам потребуется отредактировать границы спрайта изображения, для того чтобы мы могли его без искажений растягивать в разные стороны. В дереве проекта выберите наше новое изображение frame_1, нажмите в инспекторе Sprite Editor и в открывшемся окне установите Border (границы) на 12 пикселей. Именно в этих местах спрайти будет растягиваться, подгоняясь под размеры кнопки. Закройте это окно и нажмите Save.

Примените новый спрайт ко всем кнопкам и в компоненте Image установите Image Type на Sliced. Надо поправить цвет кнопок.
— R — 0.012 (красный)
— G — 0.012 (зеленый)
— A — 0.7 (альфа-канал или прозрачность)
Если вам хочется немного свободно места между слотами, то добавьте на кнопки отступы по 5 пикселей с каждой стороны.
Цвет текста кнопок так же меняем на белый.

• Не забывайте, что однотипные объекты можно выделять вместе CTRL+клик и массово менять настройки компонентов. Это очень экономит время.
Так как нам нужно точно такое же поле для раздела «Загрузка игры», мы можем просто скопировать это. Деактивируйте объект newGameList, нажав на галочку возле его имени в инспекторе, затем в иерархии щёлкните по нему ПКМ и нажмите Duplicate. Теперь нужно только его переименовать в loadGameList, чтобы не путаться. Деактивируйте его, чтобы не мешался.
Раздел «Настройки». Создайте такое же поле, но только без кнопок. Этот раздел посложнее предыдущего, поэтому сделаем для начала небольшие намётки.

Первым сделаем блок выбора языка с выпадающим списком. Нужно создать объект language внутри optionsList и установить ему Anchors X: (0.03, 0.97) Y (0.85, 0.95). Внутри language создаем объект header, устанавливаем Anchors X: (0, 0.48) Y (0, 1), добавляем ему компонент Text с размером шрифта 24 и выравниванием по правому краю и по центру.

Теперь нам нужно сделать выпадающий список. В Unity уже есть готовое решение — нам нужно только правильно его настроить. Добавьте в language объект UI -> Dropdown и устанвите ему Anchors X: (0.5, 1) Y (0, 1). Сразу же можно установить ему почти чёрный цвет с прозрачностью 0.78.

В нём достаточно много дочерних объектов. Разверните его полностью и внимательно поменяйте настройки. По шаблону «Объект: Компонент —> настройки».
— Dropdown: Dropdown —> Transition «none», список Options — поменять Option A итд на названия языков игры.
— Label: Text —> Русский, размер шрифта 24, цвет белый.
— Arrow: Rect Transform —> PosX -20, Wigth 40, Height 40.
— Template: Rect Transform —> Height 450; цвет сделать как у объекта Dropdown
— Content: Rect Transform —> Height 80.
— Item: Rect Transform —> Height 80; Toggle —> Transition «none»
— Item Background: цвет сделать как у объекта Dropdown
— Item Checkmark: Rect Transform —> PosY -5
— Item Label: Rect Transform —> Left 25, Top 15; Text —> размер шрифта 24, цвет белый.
Если ничего не пропустить, то результат будет как на видео ниже.
По аналогии с языковой панелью делаем объект music, размещаем в нём объект header с компонентом Text (пишем «Громкость музыки»). Нам понадобится слайдер — добавляем его UI/Slider с Anchors X: (0.5, 1) Y (0.3, 0.7).
В компоненте Slider отключаем Transition.
В дочернем объекте Background делаем черный цвет.
Таким же образом создаём слайдер для «Громкости звуков».
Готово! Вы — великолепны!
Лимит картинок опять исчерпан, поэтому продолжим в следующий раз.
Экспериментируйте с оформлением и не бойтесь трогать все настройки подряд. В этот раз я приложу ссылку на архив с проектом из поста.
Показать полностью 22 3
1 год назад

GameMaker Studio 2. Урок 2. События отрисовки. Коллизия. Как работают скрипты. Как подключить русский шрифт. Переходы между комнатами
Сегодня у нас много матчасти и не очень много кода.
Конкретизация разработки.
Как было сказано в прошлом посте, игра, которая будет сделана в ходе данных гайдов, будет относиться к жанру стратегий, а в качестве источников для «вдохновения» у нас — «Oxygen Not Included» и «Rimworld«. Теперь же конкретно поговорим с вами о том, что в игре должно быть реализовано, по пунктам.
—) Персонаж — игровой «объект», способный перемещаться по игровому миру и взаимодействовать с ним. Делятся на подконтрольных и неподконтрольных игроку. Игрок НЕ имеет прямого управления над своими подопечными, кроме специального «боевого» режима.
—) Каждый персонаж должен иметь или уметь следующее:
—) Взаимодействовать с предметами, расположенными на карте: подбирать их в свой инвентарь или выкладывать; взаимодействовать с рабочими станциями; сражаться (наносить, получать урон).
—) Каждый персонаж смертен. Каждый персонаж должен иметь ряд характеристик, которые он будет пытаться «поддерживать» на должном уровне, чтобы не умереть и продолжать быть эффективным. Эти характеристики:
—) Каждый персонаж уникален и должен отличаться от других. Черты, навыки с различными уровнями прокачки. Этот пункт может игнорироваться в случае, если это не подходит под конкретного персонажа.
—) Объекты, с которыми можно взаимодействовать через интерфейс игры и с которыми могут взаимодействовать персонажи или иные игровые сущности с какой-то целью.
—) Пример: верстак для создания вещей; исследовательский стол для открытия новых вещей и пр.
-) Интерфейс.
—) Простой, понятный. Желательно, на русском языке.
—) Хотя бы минимально настраиваемый.
Скажу сразу, что персонажей мы делать будем позже. Начнём мы с интерактивных объектов и размещения их в мире. Но, это будет потом.
Сейчас — продолжаем изучать матчасть.
События отрисовки.
Событие отрисовки добавляется на объект как и любое другое, через кнопку «Добавить событие (Add Event)».
Имеется два события:
—) Событие, которое отрисовывает объект в комнате.
—) Событие, которое отрисовывает объект на экране.

Иными словами, событие отрисовки GUI позволяет нам закрепить на экране наш интерфейс и то, что мы хотим игроку показывать всегда. Draw — стандартная отрисовка.
Возникает вопрос: зачем нам отдельно добавлять событие отрисовки, если объект спокойно отрисовывается и без него?
Ответ: в событии отрисовки можно написать код, который позволит нам менять параметры отрисовки, будь то: изменение размера (соотношения сторон); изменение цвета; создание дополнительных графических эффектов; etc.
Если вы добавили событие отрисовки, то вам необходимо написать в коде следующую строку:
В противном случае, объект не будет отрисован.
Проделайте это сами. 🙂
Итак. Что мы можем делать в событиях отрисовки:
-) Отрисовка спрайтов:
—) draw_self() — отрисовывает спрайт текущего объекта с настройками по умолчанию.
—) draw_sprite_ext( название спрайта в обозревателе, номер изображения, x, y, x-масштабирование, y-масштабирование, поворот, цвет, прозрачность) — отрисовка спрайта «расширенная» — т.е. с настройками. Можно отрисовать любой спрайт, как и везде, где его нужно отдельно указывать.
—) draw_sprite_part(название спрайта в обозревателе, номер изображения, x координата левой верхней точки спрайта, y координата левой верхней точки спрайта, ширина, высота, x, y) — отрисовка части спрайта
—) draw_sprite_stretched(название спрайта в обозревателе, номер изображения, x, y, ширина, высота) — отрисовка спрайта с его «растяжением». Если у спрайта включить и настроить функцию Nine Slice (девять «срезов»), то можно создавать масштабируемые (с одинаковым разрешением, пропорциями и качеством по итогу) элементы интерфейса: окошки, кнопки, etc.
-) Отрисовка фигур:
—) draw_circle(x, y, радиус, заполнение (True/False)) — рисуем круг. Аналогичное есть для прямоугольника (rectangle), стрелки (arrow), эллипса (ellipse), линии (line).
—) draw_button(x, y, x2, y2, up) — Отрисовка кнопки. Где up — True или False, нажата кнопка или нет.
-) Настройки отрисовки.
—) ВАЖНО. Любые настройки отрисовки нужно проводить ПЕРЕД отрисовкой. Можно делать в любом событии, но для удобства лучше тут же.
—) draw_set_color(col) — устанавливаем цвет отрисовки. Базовые значения цветов начинаются с приписки «c_». Пример: c_black, c_red.
—) Для продвинутых — можно использовать HEX, как в CSS: #11CCFF как пример. Используется стандартная RGB система. Если заменить решётку на $, то система сменится на BBGGRR, т.е. наоборот.
—) draw_set_alpha(alpha) — прозрачность отрисовки
—) draw_set_font(font) — устанавливаем шрифт
—) draw_set_halign(halign) — Расположение текста по горизонтальной оси (fa_ left/center/right)
—) draw_set_valign(valign) — Расположение текста по горизонтальной оси (fa_ top/middle/bottom)
Это та часть, что касается кода и с которой мы с вами будем непосредственно работать. Можете держать как памятку у себя перед глазами, когда начнём писать код.
Коллизия — иначе говоря, столкновение объектов, происходит благодаря «маске» спрайта. Если спрайт — это картинка объекта, то маска — это «твёрдое тело», отвечающее за считывание столкновений.
Чтобы её посмотреть, откройте настройки спрайта и разверните пункт «Collision Mask«

Хотя маска может иметь несколько форм, о которых будет написано ниже, устанавливается она исходя из четырёх позиций — левого верхнего и правого нижнего углов. Именно эта маска считывает столкновения, а не сам спрайт.
Mode — режим маски. Варианты:
— Автоматическая
— На всё изображение
— Ручная
Type — тип маски. Варианты:
— Прямоугольник
— Прямоугольник с поворотом
— Эллипс (работает медленнее)
— «Алмаз» (ромб) (работает медленнее)
— Предрасчет / Точный (медленнее) — ГМС постарается сам подстроить маску под форму объекта.
— Предрасчет / Точный по кадрам.
Мой совет:
Если игра небольшая, ставьте ручной режим и настраивайте сами те объекты, для которых это важно. Особенно те объекты, столкновение с которыми должно происходить только в определённой позиции (скажем, как в Стардью, сталкиваешься с основанием дерева, но не с его кроной).
В нашей игре данная настройка нам практически не пригодится, так как будет использоваться система поиска путей. Тем не менее, с помощью маски можно будет реализовать «выталкивание» застрявших персонажей, а также настроить косметическое «расталкивание» персонажей, если те проходят через занятую другим персонажем клетку.
Как работают скрипты.
Скрипт (функция) — именованный блок кода, выполняющий определённую задачу.
Все команды, которые мы использовали до этого, являются такими «скриптами». Иногда я буду называть их функциями, оба варианта правильные.
Создать скрипт просто. Для этого нажмите ПКМ по папке «Scripts» и выберете там «Script«. Назовите его scrMove
У вас откроется следующее окно:

scrMove — название нашей функции. В круглых скобках мы в неё можем передавать аргументы. Аргумент — это переменная, которая используется только в функции, передаётся в неё при вызове и может иметь любое название. В скобках напишите spd — это будет как раз наша переменная скорости.
Вырежьте код движения из Step-события у игрока и вставьте в скрипт, а в Step-событии игрока напишите следующий код:
scrMove(player_speed);
Получится следующая картина:


Как вы заметили, «x» и «y» у нас по прежнему работают. Это потому, что мы выполняем скрипт в объекте oPlayer: данные переменные подтягиваются из объекта автоматически.
Когда нужно применять скрипты?
-) Когда код, который мы пишем, нужно использовать несколько раз.
-) Когда код, который мы пишем, занимает много места, чтобы его было проще отладить.
-) Когда нам нужно получить какое-либо значение на основе каких-либо данных, так как скрипт может не просто выполнять код, но и возвращать значения.
Для того, чтобы скрипт вернул какое-то значение, нужно написать:
return название_переменной/значение.
Переменную можно написать только одну. Команда return прерывает выполнение скрипта. Переменную / возвращаемое значение можно не писать, тогда скрипт просто прервётся.
Конструкцию с прерыванием следует добавлять везде, где это может повлиять на игру, особенно её работоспособность.
Раньше скрипты выполнялись медленнее, чем код, написанный просто в объекте. Сейчас это не так (или, по крайней мере, не так критично).
Также стоит понимать, что создав объект «Скрипт«, вы, по сути, создали просто файл для хранения кода. В одном скрипте может быть собрано сразу множество разных функций, отвечающих за выполнение одной задачи. При вызове обращаться нужно именно к имени функции.
Скрипт для отрисовки текста с обводкой.
Создаём новый скрипт и называем его scrOutlinedText
Нам понадобятся следующие аргументы:
-) xPos — позиция текста по X
-) yPos — позиция текста по y
-) col — цвет текста
-) outlineCol — цвет обводки
-) text — текст
-) curdepth — текущая «глубина» объекта.
-) Шрифт, которым будем рисовать.
-) Позиционирование по X
-) Позиционирование по Y
Общая схема работы скрипта:
1) Назначаем максимальную «глубину».
2) Рисуем текст цветом обводки.
3) Поверх рисуем текст нужным нам цветом, но с небольшим смещением.
4) Возвращаем глубину к первоначальным показателям.
Код:
https://pastebin.com/TahYZKc8
P.S.
Изначально код не мой, а честно стырен подсмотрен с интернета, но я в него добавил больше переменных для лучшей настройки и отрисовки. Фактически, значение глубины тоже можно передавать как аргумент, чтобы разный текст выводился по разному.

Создание русского шрифта
Проверим, как работает скрипт.
Сначала мы создадим русский шрифт. Для этого найдём папку «Fonts«, нажмём ПКМ и создадим «Font«.
Выбирайте любой шрифт и размер, который хотите. Нам с вами нужна кнопка «Add«, чтобы добавить диапазон с русским шрифтом.


Здесь напишем 1040 to 1105, чтобы захватить весь русский алфавит, затем нажмём «Add Range».
Шрифт называйте на Ваше усмотрение. Я назову его: fontArialRusSmall
Теперь перейдём к объекту игрока. Создадим событие Draw GUI и напишем следующий код:
scrOutlinedText(10, 10, c_yellow, c_red, «Скорость: » + string(player_speed), depth, fontArialRusSmall, fa_left, fa_top)
Результат:

Можете заменить c_yellow и c_red на любые цвета. Помните, что можно передавать цвета в HEX с помощью # и $ (в обратном порядке).
Скрипт для поиска значения в массиве:
Так как эту тему мы не проходили, заострять внимание не буду, но вдруг кому пригодится:
https://pastebin.com/1VTNg4Bp
Работает просто, но встроенной функции подобного толка в GMS нет:
— Проверяет, что аргумент является массивом.
— Обходим массив и, если какое-то значение является его элементом, возвращаем индекс. Если же совпадений не было или аргумент не массив — возвращаем «noone» — в GML это значение равняется -4.
По сути, код можно и упростить, убрав проверку на то, что переданное значение является массивом, но с ней меньше шансов накосячить. 🙂
Переходы между комнатами. Создаём меню.
Наконец, с матчастью мы закончили. Приступим к тому, что создадим меню нашей игры.
Создаём новую комнату и обзываем её rmMainMenu. Затем нажмите на значок «домика», чтобы поменять стартовую комнату (ту, что отображается при запуске игры).

Сделайте так, чтобы домик был напротив созданной комнаты.
Заметьте, что при создании новой комнаты у неё изменяются все настройки! Это важно.
Создадим в «Objects» папку «Меню«, где создадим следующие объекты: «oMStart«, «oMSettings«, «oMExit«, «oMMenu«.
OMStart — кнопка для старта нашей игры.
OMSettings — кнопка для перехода в настройки игры.
oMExit — кнопка для выхода из игры
oMLoad — кнопка для загрузки игры
oMMenu — управляющий объект, который будет располагать наши кнопки.
Зайдём в комнату MainMenu и поставим там объект oMMenu.
Затем перейдём в код oMMenu и выберем событие создания (Create)
Там напишем следующий код:
buttons = [oMStart, oMLoad, oMSettings, oMExit]
Здесь мы поместили все наши кнопки в массив. Таким образом, мы можем обратиться к объекту-кнопке, используя название массива и квадратные скобки:
buttons[0] — будет обращаться к первому элементу массива (oMStart)
Теперь нужно каждую из этих кнопок разместить. Но, чтобы правильно их разместить, сделаем на все кнопки один спрайт. Размер подбирайте под себя.
У каждого объекта создадим два события: Draw, которое оставим пустым, и Draw GUI, где напишем draw_self();
Там же напишем:
scrOutlinedText(x, y, c_white, c_black, «Здесь текст кнопки», depth, fontArialRusSmall, fa_center, fa_middle)
Где «Здесь текст кнопки» — заменить на нужное: «Начать игру«, «Загрузить игру«, «Настройки«, «Выход«
Вернёмся в OMMenu.
Нажмите правой кнопкой мыши. Наведитесь на пункт «Code Snippets» и выберите пункт 5.
В данном меню расположены готовые шаблоны кода, а мы создали шаблон цикла.
Напишите следующий код:
for (var i = 0; i < array_length(buttons); ++i)
instance_create_layer(room_width / 2, room_height / 2 — 50 + sprite_get_height(sButton) / 2 + 100 * i, «Instances», buttons[i])
>
Этот код создаёт инстансы объектов, которые перечислены в нашем массиве, по центру комнаты, плавно опускаясь вниз. Не забудьте поставить Origin у спрайта кнопки по центру.
Координаты настраивайте под себя. Для этого изменяйте первые два аргумента (идут до «Instances«.)
Скриншот:

Теперь нужно сделать отслеживание нашей мышки. Кнопку «Настройки» мы сделаем чуть позже. Пока перейдём к кнопке для выхода из игры.
До текста отрисовки нашего текста, нам нужно сделать небольшую подсветку нашей кнопки, чтобы мы знали, что она работает. Для этого нам нужно отслеживать позицию нашей мыши относительно интерфейса, для чего есть специальная команда.
dmxg = device_mouse_x_to_gui(0)
dmyg = device_mouse_y_to_gui(0)
ОК. Теперь мы отслеживаем нашу мышку относительно GUI. Нужно проверить, наведены ли мы на конкретный объект и если да — нарисовать поверх него полупрозрачный белый прямоугольник. Для этого есть команда:
instance_position(x, y, obj)
В x и y мы передадим переменные выше, а в качестве объекта напишем «self«, чтобы проверять координаты нашего объекта.
if instance_position(dmxg, dmyg, self)
draw_set_color(c_white)
draw_set_alpha(0.15)
draw_rectangle(bbox_left, bbox_top, bbox_right, bbox_bottom, false)
draw_set_alpha(1)
>
Уже после этого пишем наш текст.
Теперь при наведении на кнопку «выхода» мы увидим, что она «активна».
Вырежем наши две переменные, добавим их в событие create. Теперь мы можем использовать эти переменные в любом коде этого объекта.
Теперь перейдём в событие step. Сюда также добавим наши две переменные. Теперь они будут постоянно обновляться.
Начнём настраивать логику работы.
Можете скопировать тот код, что мы писали выше, но удалить всё изнутри.
Нам нужно сделать проверку:
Если мы наведены на объект И нажата левая кнопка мыши — выйти из игры. То есть, немного её дополнить.
Для проверки нажатия кнопки левой мыши будем использовать команду:
mouse_check_button_pressed(mb_left)
Для выхода из игры:
game_end()
В итоге, в коде step будет следующий код:
dmxg = device_mouse_x_to_gui(0)
dmyg = device_mouse_y_to_gui(0)
if instance_position(dmxg, dmyg, self) and mouse_check_button_pressed(mb_left)
game_end()
>
Итого, если мы наведёмся на кнопку выхода — она подсветится. Нажмём — игра закроется.
Аналогичный код с подсветкой и проверкой просто скопируем в oMStart. В других кнопках логику работы в step пока не настраиваем на них, работаем с кнопкой начала игры.
Нужно внести одно изменение.
Вместо game_end() нам нужно написать код для смены уровня.
Для этого напишем:
room_goto(Room1)
Где Room1 — название комнаты, в которую вы хотите переместиться. Оно должно совпадать с названием комнаты в браузере ассетов.
Итого, мы можем выйти из игры или перейти в комнату, где у нас есть игрок, которым мы можем управлять. Осталось дополнить написать код, чтобы мы могли вернуться обратно в меню, при этом так, чтобы положение игрока на экране сохранялось.
Для этого нам придётся создать новый объект и назвать его oGameManager.
Разместим его в самой первой комнате и отметим у него чекбокс «Persistent«
Затем напишем у него в Step код:
Если индекс текущей комнаты != 0 и нажата ESC — деактивируем все инстансы, кроме текущего и переходим в меню.
if room != 0 and keyboard_check_pressed(vk_escape)
instance_deactivate_all(true)
room_goto(rmMainMenu)
>
Также отметим комнату с игроком как «Persistent«.
Чтобы объекты в комнате, при возвращении, были активированы, немного дополним наш oGameManager. Для этого создадим у него событие «Room Start».

При старте комнаты, если эта комната не является меню, мы будем запускать все инстансы.
if room != 0
instance_activate_all()
>
Всё. Теперь мы можем спокойно выходить в меню из игры и возвращаться обратно при необходимости.
Реализация сложнее. О том, как работает Nine Slice.
Ниже будет приведена реализация меню с использованием функции Nine Slice, которая позволит нам делать кнопки других размеров.
Используя Nine Slice мы не сможем проверять, наведены ли мы сейчас на нужный нам объект. Также эта функция годится только для отрисовки типовых форм с типовым шрифтом. Если у вас спрайт кнопки идёт с текстом, то данная функция не подойдёт.
Чтобы отслеживать нажатия и наведение, нам необходимо проверять, находятся ли координаты мыши в нужной области. Для этого существует команда
point_in_rectangle(px, py, x1, y1, x2, y2)
Итак. Первый шаг — это убрать спрайт у каждой нашей кнопки.
Для этого откроем объект каждой кнопки, нажмём на выбор спрайта и выберем «None«.
Шаг второй.
У каждой кнопки в событии Create мы пропишем:
width = 0;
height = 0;
При создании кнопок мы и так будем знать их x и y координаты, а ширину и высоту будем передавать.
Шаг третий
Нарисуем спрайт в виде одноцветного квадрата размером 9х9 пикселей.
Затем добавим ему две обводки разных цветов, каждая шириной в 1 пиксель.
Получим следующую картину:

Теперь перейдём в настройки спрайта.
Назовём его sButtonSlice.
Слева выберем и откроем пункт Nine Slice.
После чего поставим галочку «Activate Nine Slice«.
Далее — смотрим на наш спрайт и настраиваем его, как показано на скриншоте ниже:

Чтобы двигать фиолетовые полоски — достаточно на них навестись и зажать ЛКМ.
В правом окошке вы можете порастягивать данный спрайт, как хотите и посмотреть на итоговый результат.
Растягивающаяся рамка для кнопок готова!
Шаг четвертый.
Пропишем в Draw GUI отрисовку кнопки вместо draw_self():
draw_sprite_stretched(sButtonSlice, 0, x — width / 2, y — height / 2, width, height)
Таким образом, мы отрисовываем кнопку.
Вернёмся в oMMenu, в Create, где немного допишем наш код.
Мы создадим переменную, которой будем присваивать значение ID только что созданного объекта. Затем, обращаясь к этой переменной, мы будем менять у данного объекта параметры: ширину и высоту.
buttons = [oMStart, oMLoad, oMSettings, oMExit]
var bwidth = 400
var bheight = 96
for (var i = 0; i < array_length(buttons); ++i)
inst = instance_create_layer(room_width / 2, room_height / 2 — 50 + 100 * i, «Instances», buttons[i])
inst.width = bwidth
inst.height = bheight
>
Меняя значения bwidth и bheight вы сможете сами настроить нужные размеры кнопок.
Обратите внимание, что создавая инстанс мы можем передать struct — то есть структуру данных.
Тогда не нужно будет обращаться к инстансу через переменную, но придется удалить код
width = 0;
height = 0;
При создании наших объектов, а сам код для создания инстанса преобразится следующим образом:
Какой из вариантов удобнее — решать вам.
Вариант с переменными, которые объявляются при создании объекта, я лично считаю более надёжным, а функциональных отличий между данными командами не так много. С другой стороны, такая возможность появилось недавно и её нужно тестировать.
Теперь задача — сделать так, чтобы мы могли считывать, наведены ли мы сейчас на кнопку.
Для этого мы берём координаты x и y, после чего проверяем, находится ли наша мышь в области:
x — width / 2, y — height / 2, x + width / 2, y + height / 2.
Остальное — копируем из прошлого кода. Получается следующий блок:
if point_in_rectangle(dmxg, dmyg, x — width / 2, y — height / 2, x + width / 2, y + height / 2)
draw_set_color(c_white)
draw_set_alpha(0.15)
draw_rectangle( x — width / 2, y — height / 2, x + width / 2, y + height / 2, false)
draw_set_alpha(1)
>
Шаг седьмой.
Проделать тоже самое в событии Step. (По сути, просто заменяем instance_position)
if point_in_rectangle(dmxg, dmyg, x — width / 2, y — height / 2, x + width / 2, y + height / 2)
and mouse_check_button_pressed(mb_left)
//Тут код нужной кнопки
>
Шаг восьмой.
Донастраиваем все кнопки.
Какой из вариантов использовать — решать Вам.
Можно вообще использовать следующий вариант:
Создать спрайт нужных размеров и залить его белым цветом с «прозрачностью» в 1 единицу. Совершенно незаметно. Используя bbox_left / top / right / bottom нарисовать поверх прозрачной основы кнопку и проверять уже не через «point_in_rectangle», а как в первом способе.
На этом сегодняшний гайд подходит к концу.
Вкратце мы разобрали события отрисовки; как с ними работать; как работать со скриптами; как работать со шрифтами; а также вы можете смело скопировать себе и использовать несколько других скриптов в своих проектах.
Что нужно подготовить к следующему гайду:
— Минимум два спрайта «породы» — на фон (темнее) и для объекта (светлее). Размер одного спрайта — 32х32.
— Желательно спрайты нескольких объектов, вроде стен, дверей. Исходите из того, что размер одной клетки будет равен 32х32 пикселей.
— Спрайт для неба/космоса/прочего на фон на ваше усмотрение.
Я подготовлю свои варианты и, используя их, на следующем занятии мы с вами разберёмся, как работает камера.
Сделаем меню настроек с возможностью переключения в полноэкранный режим.
И небольшой спойлер на будущее.
Взаимодействовать с миром можно будет двумя способами: через ЛКМ и наведением «тела» игрока на тот или иной объект.
Загрузить файл проекта и всё пощупать самому можно по ссылке:
Яндекс диск
— Камера и её настройка. Разные способы реализации: от простого к сложному.
— Иерархия объектов. «Объекты-родители» и их «дети». Решение часто встречающихся проблем и немного про то, как удобно выстраивать взаимодействие с объектами. Глобальные переменные.
— Массивы и с чем их едят, а также grid (сетка комнаты), размещение объектов по сетке. Включая объяснение, в каких случаях лучше использовать встроенные функции, в каких – писать свои с нуля.
— Пути. Один большой гайд, включая скрипты поиска путей для различных сеток в т.ч. с примерами из моего личного проекта.
— Иные способы хранения информации в GMS2, когда их стоит или не стоит использовать.
— Сохранение. Встроенное VS самописное.
Есть вопрос — задавай, постараюсь ответить.
Что-то не получилось, вылезла ошибка и так далее — тоже пиши, помогу или поможем.
