Фото

Unity 6. Камера от третьего лица.


Камера от третьего лица в Unity 6 — это система, которая позволяет управлять камерой так, чтобы она следовала за персонажем, а также могла вращаться и изменять положение относительно него. Здесь рассмотрим как это реализовать с помощью скриптов C#.

Сразу условимся, что камера будет дочерним элементом самого игрока, а также добавим условие, что она не должна выходить за стены, при приближении к ним. Чтобы камера от третьего лица в Unity 6 не проходила сквозь стены, лучше всего использовать Raycast: он «прощупывает» пространство между игроком и желаемой позицией камеры и при обнаружении препятствия подтягивает камеру ближе.

1. В редакторе Unity создадим некое подобие дома, состоящего из пола и стен, и поместим в него нашего игрока (Player).

2. В разделе Hierarhy, необходимо мышкой перетащить Main Camera на нашего персонажа Player, таким образом мы камеру сделаем дочерним элементом нашего персонажа.

3. Создадим слой "Stena". Для этого переходим в меню Edit - Project Settings. Там находим Tags and Layers и в свободном поле пишем "Stena".

4. В Hiearhy необходимо выбрать все стены и в Inspector в поле Layer выбрать из списка слой "Stena"

5. Теперь напишем скрипт (Camera) и перетащим его мышкой на Main Camera. Раз камера обязательно дочерняя к игроку, решением будет динамически менять её localPosition.z через Raycast — «подтягивать» камеру ближе к игроку, когда позади стена.

 

using UnityEngine;

public class Camera : MonoBehaviour
{
    [Header("Ссылки")]
    public Transform player;

    [Header("Настройки дистанции и высоты")]
    public float defaultDistance = 5f;          // Дистанция от игрока (Z)
    public float defaultHeight = 2f;             // Высота камеры над игроком (Y)
    public float minDistance = 1f;              // Минимальная дистанция до игрока
    public float smoothSpeed = 12f;         // Скорость сглаживания

    [Header("Слои и геометрия")]
    public LayerMask collisionMask;         // Слои стен/препятствий
    public float sphereRadius = 0.3f;         // Радиус проверки (чтобы камера не прилипала к стене)

    private float _currentDistance;

    void Start()
    {
        _currentDistance = defaultDistance;
    }

    void LateUpdate()
    {
        if (player == null) return;

        // Желаемая позиция камеры в мировых координатах
        Vector3 origin = player.position;
        
        // Локальное смещение: (0, высота, -дистанция)
        Vector3 desiredLocalPos = new Vector3(0f, defaultHeight, -defaultDistance);
        
        // Переводим в мировые координаты с учётом поворота игрока
        Vector3 desiredWorldDir = player.rotation * desiredLocalPos;
        Vector3 desiredWorldPos = origin + desiredWorldDir;

        Vector3 dirToCamera = desiredWorldPos - origin;
        float maxRayLength = dirToCamera.magnitude;

        float targetDistance = defaultDistance;

        // SphereCast от игрока к желаемой позиции камеры
        if (Physics.SphereCast(
            origin,
            sphereRadius,
            dirToCamera.normalized,
            out RaycastHit hit,
            maxRayLength,
            collisionMask))
        {
            // Нашли стену — уменьшаем дистанцию, чтобы не было пересечения
            float safeDist = hit.distance - sphereRadius;
            targetDistance = Mathf.Max(safeDist, minDistance);
        }

        // Плавно меняем дистанцию
        _currentDistance = Mathf.Lerp(_currentDistance, targetDistance, Time.deltaTime * smoothSpeed);

        // Формируем итоговую локальную позицию: высота не меняется, дистанция меняется
        Vector3 localOffset = new Vector3(0f, defaultHeight, -_currentDistance);

        // Плавно двигаем камеру по localPosition
        transform.localPosition = Vector3.Lerp(
            transform.localPosition,
            localOffset,
            Time.deltaTime * smoothSpeed
        );

        // Камера смотрит на игрока
        transform.rotation = Quaternion.LookRotation(player.position - transform.position, Vector3.up);
    }
}

 

Настройка в редакторе:


- Камера (Main Camera) уже дочерняя к игроку — оставляем как есть.
- Повесьте скрипт на саму камеру.
- В поле Player перетащите игрока.
- Collision Mask — выберите слои, на которых у вас стены, пол, потолок (создайте слой Walls, если его нет, и назначьте стенам).
- Default Distance — то расстояние, на котором камера должна быть в норме (например, 5).
- Sphere Radius — «толщина» камеры, чтобы она не прилипала вплотную к стене (0.2–0.5 обычно достаточно).

 

Как это работает:


Каждый кадр скрипт пускает SphereCast от игрока назад — туда, где камера «хотела бы» быть.
Если луч натыкается на стену, камера подтягивается к игроку ровно настолько, чтобы не пересечь стену.
Если стены нет — камера плавно возвращается на defaultDistance.
Всё это делается через localPosition.z, так что камера остаётся полноценным дочерним объектом игрока и двигается вместе с ним.
Подводные камни
Игрок не должен быть в Collision Mask, иначе Raycast упрётся в коллайдер игрока и камера «схлопнется». Вынесите игрока на отдельный слой и исключите его из маски.
Поворот камеры — в скрипте камера поворачивается, чтобы смотреть на игрока через Quaternion.LookAt. Если вы уже управляете вращением камеры отдельно (например, через Cinemachine или мышкой), уберите последнюю строку с transform.rotation и оставьте только корректировку позиции.
LateUpdate — обязателен, иначе камера будет дёргаться, так как обновляется до перемещения игрока.

Вот и всё, теперь наша камера настроена для игры от третьего лица, при этом избегает выхода за стены.



Фото

Добавить программу в автозапуск с помощью Python


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

В данной статье мы рассмотрим, как использовать Python для добавления программ в автозапуск на различных операционных системах. Вы узнаете, как можно реализовать автозапуск на Windows и Linux, используя Python-библиотеки и стандартные подходы.

Что такое автозапуск программ?

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

Подходы к добавлению программ в автозапуск

Существует несколько различных методов добавления приложений в автозапуск в операционных системах Windows и Linux. Способ выбора зависит от специфики ОС и возможностей разработчика. Мы сфокусируемся на подходах, где задействован язык Python, благодаря которому весь процесс станет интуитивно понятным и универсальным.

Установка необходимых библиотек

Python и pip

Если у вас еще не установлен Python или менеджер пакетов pip, начните с их установки. На сегодняшний день большинство операционных систем позволяют установить Python из стандартного репозитория, или же можно скачать с официального сайта python.org.

Библиотеки для работы с автозапуском

Мы будем использовать стандартные Python модули os и platform, чтобы обеспечить взаимодействие с файловой системой и определить текущую ОС. Эти модули идут в комплекте с Python, поэтому требуются лишь базовые установки языка.

Код для добавления программы в автозапуск

Добавление в автозапуск на Windows

Код для добавления программы в автозапуск на Windows создаёт символьную ссылку в специальной папке:

import os
import sys

def add_to_startup(program_name: str) -> None:
    """
    Добавляет программу в автозапуск Windows.
    :param program_name: Имя программы для автозапуска
    """
    startup_path = os.path.join(os.getenv('APPDATA'), 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup')
    target_link = os.path.join(startup_path, f'{program_name}.lnk')
    os.symlink(sys.argv[0], target_link)

 

Код выше создает символическую ссылку в каталоге автозапуска Windows, что позволяет программе запускаться автоматически.

Добавление в автозапуск на Linux

Для Linux мы создаем .desktop файл, который будет выполняться при старте:

 

import os

def add_to_startup(command: str) -> None:
    """
    Добавляет команду в автозапуск на Linux.
    :param command: Команда для автозапуска
    """
    autostart_file = f'~/.config/autostart/{command}.desktop'
    with open(os.path.expanduser(autostart_file), 'w') as file:
        file.write(f'[Desktop Entry]\nType=Application\nExec={command}\nHidden=false\nNoDisplay=false\nX-GNOME-Autostart-enabled=true\nName[en_US]={command}\nName={command}\n')

 

Этот фрагмент создает файл конфигурации автозапуска подходящего формата для среды рабочего стола GNOME.

Проверка, добавлена ли программа в автозапуск

 

Проверка на Windows

Для проверки на Windows вы можете убедиться, что созданный файл .lnk действительно находится в папке автозапуска. Это позволит удостовериться, что файл был корректно создан и программа добавлена в автозапуск.

Проверка на Linux

На Linux правильность работы можно проверить, исследовав .desktop файл на наличие ошибок в синтаксисе и правильную настройку прав доступа.

 

Типичные ошибки и их исправление

 

Общие ошибки при добавлении в автозапуск

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

Отладка с помощью логов

Использование модулей логирования поможет вам отследить ошибки на этапе разработки и быстро выявить проблемные места. Консольный вывод и логи могут быть полезны при попытке воспроизвести ошибку.

 

Заключение

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

 
 
Источник:  external.software


Фото

Парсинг сайтов - Regex в Beautiful Soup


В мире веб-скрапинга и анализа данных, эффективный поиск и извлечение информации из HTML/XML документов имеют решающее значение. Beautiful Soup — это мощная библиотека Python, которая значительно упрощает этот процесс.

Однако стандартные методы поиска Beautiful Soup иногда могут быть недостаточными для сложных или изменчивых структур веб-страниц. Именно здесь на помощь приходят регулярные выражения (Regex).

В этом руководстве мы подробно рассмотрим, как интегрировать Regex с Beautiful Soup, чтобы значительно расширить ваши возможности поиска. Мы начнем с основ и перейдем к продвинутым техникам, предоставляя практические примеры для решения реальных задач веб-скрапинга.

Основы Beautiful Soup и регулярных выражений

Beautiful Soup — это библиотека Python для парсинга HTML и XML документов. Она преобразует сложные веб-страницы в легкодоступную структуру данных, позволяя извлекать информацию с помощью интуитивно понятных методов поиска, навигации и модификации.

Регулярные выражения (Regex) — это мощный инструмент для работы со строками, позволяющий описывать и находить текстовые шаблоны. Они становятся незаменимыми, когда стандартные методы поиска Beautiful Soup оказываются недостаточными для обнаружения элементов со сложными или меняющимися характеристиками.

Что такое Beautiful Soup и зачем он нужен

Beautiful Soup – это мощная библиотека Python, разработанная для парсинга HTML и XML документов. Она преобразует сложные веб-страницы в удобные для навигации Python-объекты, формируя синтаксическое дерево. Это позволяет разработчикам легко извлекать данные, находить элементы и манипулировать их содержимым. Beautiful Soup значительно упрощает процесс веб-скрапинга, предоставляя интуитивно понятные методы для навигации по структуре страницы, поиска тегов, атрибутов и текста, делая ее незаменимым инструментом для автоматизированного сбора информации из веба.

Введение в регулярные выражения (Regex) для поиска

Регулярные выражения (Regex) – это мощный инструмент для поиска и манипулирования текстом, основанный на использовании шаблонов. В контексте Beautiful Soup, Regex позволяет находить элементы, соответствующие определенным текстовым паттернам, а не только точным совпадениям.

  • Определение шаблонов: Regex определяет шаблоны, описывающие искомые строки. Например, шаблон r'^[A-Za-z]+$' ищет строки, состоящие только из букв.

  • Модуль re в Python: Для работы с Regex в Python используется встроенный модуль re. Beautiful Soup интегрируется с этим модулем, позволяя применять Regex для поиска по тегам, атрибутам и тексту.

  • Преимущества Regex: Regex обеспечивает гибкий и точный поиск, позволяя игнорировать регистр, находить элементы с определенными префиксами или суффиксами и многое другое. Это особенно полезно при работе с неструктурированными или динамически генерируемыми веб-страницами.

  • Примеры использования: Regex можно использовать для поиска всех ссылок, начинающихся с определенного домена, или для извлечения всех тегов <div> с атрибутом class, содержащим определенное слово.

Поиск элементов с помощью Regex в Beautiful Soup

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

Использование Regex в методах find() и find_all()

Методы find() и find_all() принимают объект Regex в качестве аргумента для поиска по тегам, атрибутам и тексту. Для этого необходимо передать скомпилированный объект регулярного выражения, созданный с помощью re.compile(), или строку, представляющую шаблон регулярного выражения, непосредственно в метод.

  • find(regex): Возвращает первый элемент, соответствующий заданному регулярному выражению.

  • find_all(regex): Возвращает список всех элементов, соответствующих заданному регулярному выражению.

Поиск по имени тега и тексту с использованием Regex

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

Пример:

import re
from bs4 import BeautifulSoup

html_doc = """<a>Link 1</a><b>Bold text</b>
<a>Link 2</a>"""
soup = BeautifulSoup(html_doc, 'html.parser')

links = soup.find_all(re.compile('^a')) # Найти все теги, начинающиеся с 'a'
print(links)

text = soup.find_all(text=re.compile('Link')) # Найти текст, содержащий 'Link'
print(text)

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

Использование Regex в методах find() и find_all()

Методы find() и find_all() в Beautiful Soup становятся мощнее с использованием регулярных выражений. Вместо точного совпадения строки, вы можете искать элементы, соответствующие определенному шаблону.

  • find(regex) возвращает первый элемент, соответствующий шаблону.

  • find_all(regex) возвращает все элементы, соответствующие шаблону, в виде списка.

Regex можно использовать для поиска:

  1. По содержимому тега (тексту).

  2. По имени тега.

  3. По атрибутам тега (будет рассмотрено далее).

Пример:

from bs4 import BeautifulSoup
import re

html = '<p class="body">Текст 1</p><p class="body">Текст 2</p>'
soup = BeautifulSoup(html, 'html.parser')

# Найти все элементы <p> с классом, содержащим "body"
body_elements = soup.find_all('p', class_=re.compile('body'))

for element in body_elements:
    print(element.text)

 

В этом примере re.compile('body') создает regex-объект, который ищет в атрибуте class значения, содержащие слово "body".

Поиск по имени тега и тексту с использованием Regex

Beautiful Soup позволяет использовать регулярные выражения не только для поиска по атрибутам, но и непосредственно для фильтрации тегов и их содержимого.

  • Поиск по имени тега: Вместо точного имени тега можно передать regex. Beautiful Soup найдет все теги, имена которых соответствуют этому регулярному выражению. Например, <soup.find_all(re.compile('^b'))> найдет все теги, начинающиеся с буквы ‘b’ (body, b и т.д.).

  • Поиск по тексту: Аргумент string в методах find() и find_all() принимает регулярные выражения. Это позволяет находить элементы, содержащие текст, соответствующий заданному шаблону. Например, <soup.find_all(string=re.compile('hello'))> найдет все элементы, содержащие текст "hello" (регистрозависимо).

При использовании regex для поиска текста важно помнить о следующем:

  • Beautiful Soup вернет объект NavigableString, представляющий найденный текст, а не сам тег.

  • Чтобы получить родительский тег, можно использовать свойство parent объекта NavigableString.

Использование regex в сочетании с именами тегов и текстовым поиском предоставляет мощные инструменты для точного и гибкого извлечения информации из HTML/XML документов.

Продвинутые техники поиска

Теперь, когда мы освоили основы, давайте перейдем к продвинутым техникам. В этом разделе мы рассмотрим, как использовать регулярные выражения для поиска элементов по атрибутам, а также как применять re.compile() для создания сложных и эффективных запросов. Это расширит ваши возможности по извлечению данных из HTML и XML документов, предоставляя большую гибкость и контроль над процессом поиска.

Поиск по атрибутам с использованием Regex

Beautiful Soup позволяет искать элементы HTML не только по тегам и тексту, но и по значениям атрибутов, используя регулярные выражения.

Например, чтобы найти все теги <a> с атрибутом href, содержащим слово "example", можно использовать следующий код:

import re
from bs4 import BeautifulSoup

html = '''
<a href="https://www.example.com">Example Link</a>
<a href="https://www.google.com">Google Link</a>
<a href="https://example.org">Another Example</a>
'''

soup = BeautifulSoup(html, 'html.parser')

links = soup.find_all('a', href=re.compile(r'example'))

for link in links:
    print(link['href'])

 

В этом примере re.compile(r'example') создает объект регулярного выражения, который используется для поиска соответствий в атрибуте href. Этот метод особенно полезен, когда необходимо найти атрибуты, соответствующие определенному шаблону, а не конкретному значению.

Использование функций и re.compile() для сложных запросов

Для повышения эффективности при частом использовании одного и того же сложного шаблона рекомендуется применять re.compile(). Это предварительно компилирует регулярное выражение, оптимизируя процесс поиска. Beautiful Soup также позволяет передавать пользовательские функции в методы find() и find_all() для реализации кастомной логики фильтрации элементов, предоставляя максимальную гибкость.

Практические примеры и лучшие практики

Regex в Beautiful Soup открывает широкие возможности для веб-скрапинга. Рассмотрим несколько примеров:

  1. Извлечение данных по шаблону: Например, извлечение всех ссылок с определенным словом в URL.

  2. Работа с динамическим контентом: Парсинг атрибутов, значения которых генерируются динамически.

  3. Очистка данных: Удаление нежелательных символов или форматирование текста.

Лучшие практики:

  • Компилируйте regex с помощью re.compile() для повышения производительности.

  • Используйте try-except блоки для обработки возможных ошибок, связанных с некорректным HTML.

  • Будьте внимательны к edge cases и тщательно тестируйте свои regex.

Решение типовых задач веб-скрапинга с Regex

Рассмотрим несколько типичных задач веб-скрапинга, где Regex демонстрирует свою мощь в Beautiful Soup:

  1. Извлечение цен с веб-сайта: Если цены представлены в разных форматах (например, $100, 100 USD, 100.00), Regex поможет извлечь их все, приводя к единому формату.

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

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

  4. Извлечение данных из атрибутов тегов: Regex позволяет находить элементы, у которых значения атрибутов соответствуют заданному шаблону, например, id="item-[0-9]+".

Использование Regex в Beautiful Soup позволяет решать широкий спектр задач, связанных с извлечением и обработкой данных из HTML и XML документов, значительно расширяя возможности стандартных методов поиска.

Оптимизация и распространенные ошибки

Для оптимизации поиска с Regex в Beautiful Soup всегда старайтесь сужать область поиска, применяя его к поддеревьям HTML, а не ко всему документу. Это значительно ускоряет обработку. Распространенные ошибки включают использование слишком широких регулярных выражений, что приводит к ложным срабатываниям, и забывание обработки случая, когда find() возвращает None.

Заключение

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

 
 
 
 


Фото

Установка Unity3d в Ubuntu 25.10


Unity3D уже давно занимает лидирующие позиции в индустрии разработки игр, позволяя разработчикам создавать захватывающие 2D- и 3D-игры для широкого спектра платформ. Хотя Windows и macOS традиционно являются основными платформами для разработки на Unity, Linux постепенно набирает популярность как жизнеспособная платформа для разработки игр. В этой статье мы подробно рассмотрим как установить Unity3D в Ubuntu 25.10.

Для начала необходимо убедиться, что ваша система соответствует минимальным требованиям:

- 64-разрядная версия дистрибутива Linux.

- Не менее 4 ГБ оперативной памяти.

- Видеокарта с поддержкой OpenGL 3.2 или более поздней версии.

- Mono (кроссплатформенная реализация.NET Framework), установленная в вашей системе.

Первым шагом установим Mono, если он еще не установленю.

 

              sudo apt update

              sudo apt -y install mono-complete

 

Теперь необходимо установить Unity Hub.

Unity Hub — это отдельное приложение, которое упрощает навигацию, загрузку и управление проектами и установками Unity.

С помощью Hub вы можете:

- Управлять, загружать и устанавливать модули и версии Unity Editor.

- Создавать проекты Unity и управлять ими.

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

- Управлять профилем, настройками и лицензиями Unity.

- Отправлять отзывы и получать помощь от Unity.

 

1. Чтобы установить Unity Hub в дистрибутив Linux Debian или Ubuntu, вам нужно добавить репозиторий Unity Hub Debian вместе с открытым ключом для проверки целостности пакетов.

 

Чтобы добавить открытый ключ подписи, выполните следующую команду:

 

wget -qO - https://hub.unity3d.com/linux/keys/public | gpg --dearmor | sudo tee /usr/share/keyrings/Unity_Technologies_ApS.gpg > /dev/null

 

2. Чтобы добавить репозиторий Unity Hub, вам нужно внести изменения в /etc/apt/sources.list.d. Выполните следующую команду, чтобы добавить репозиторий Unity Hub:

 

sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/Unity_Technologies_ApS.gpg] https://hub.unity3d.com/linux/repos/deb stable main" > /etc/apt/sources.list.d/unityhub.list'


3. Обновите кэш пакета и установите пакет:

 

sudo apt update

sudo apt -y install unityhub

 

Всё теперь запускаем UnityHub командой:      unityhub

и выбираем пункт installs, для установки нужного нам Unity Editor.

 

Выбираем нужную нам версию и нажимаем кнопку install.

 

 

Далее выбираем необходимые компоненты и нажимаем install:

 

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

 

После окончания установки попробуем создать проект, для этого переходим в пункт меню Proects.

 

Далее нажимаем New project.

 

Здесь выбираем нужный нам шаблон, заполняем все поля и нажимаем кнопку Create project.

 

 

Если всё установилось корректно, то запуститься Unity Editor и можно приступать к написанию своего проекта.

Но когда я все установил, то мой проект ни как не мог запуститься, вернее не запускался сам Unity Editor, а кликнув по созданному объекту появлялось сообщение, что проект уже якобы запущен в Unity Editor, хотя сам редактор запущен не был.

 

 

Попробуем решить эту проблему.

Сам Unity Editor в Ubuntu 25.10 по умолчанию устанавливается в домашнюю папку примерно по такому пути:

 ~/Unity/Hub/Editor/2022.3.62f3/Editor/

Теперь отроем терминал и перейдем в этот каталог:

cd /home/maxim/Unity/Hub/Editor/2022.3.62f3/Editor/

Запускаем Unity Editor:

 ./Unity

И получаем вот такую ошибку:

./Unity: error while loading shared libraries: libxml2.so.2: cannot open shared object file: No such file or directory

Здесь мы видим, что у нас якобы нет библиотеки libxml2.so.2.

Проверим так ли это на самом деле. Попробуем её найти у нас в системе:

sudo ldconfig -p | grep libxml2

В итоге видим, что она у нас установлена, но Unity Editor упорно не хочет её находить.

 

Unity Editor требует libxml2.so.2, но у нас в системе установлена libxml2.so.16, которая находиться по пути: /lib/x86_64-linux-gnu/libxml2.so.16

Сделаем символическую ссылку на этот файл:

 

 sudo ln -s /lib/x86_64-linux-gnu/libxml2.so.16 /lib/x86_64-linux-gnu/libxml2.so.2

 

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

Набираем в терминале:

 

sudo ldd ./Unity

В появившемся списке смотрим каких зависимостей не хватает (=> not found). Если всё установлено, то пытаемся еще раз запустить unityhub (если мы не закрыли его после первого запуска, то необходимо его закрыть и только потом перезапустить). Снова создаем проект, запускаем его и вуаля Unity Editor запустился.

 

 

Вот и всё, можем работать в Unity3D в Ubuntu 25.10

 

 

 

 



Фото

Qt: клик мышью по объекту.


Нередко возникают ситуации, когда нам необходимо узнать по какому объекту на экране кликнули мышью и после ожидать от этого объекта определенные действия. Но оригинальные объекты, созданные, например, с помощью класса QLabel, не имеют возможности реагировать на нажатия кнопок мышки. Как быть в такой ситуации и как определить по какому объекту кликнули? Это мы рассмотрим в данной статье.

Объект созданный на основе класса QLabel не испускает сигнала clicked(), соответственно не имеет возможности реагировать на нажатия кнопки мышки. Можно, конечно сделать объект на основе класса QPushButton, но тогда придется изрядно повозиться, чтобы сделать этот объект похожим на QLabel со всеми его методами и свойтвами.

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

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

Итак, создадим файл main.cpp.

 

main.cpp:

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])

{

QApplication a(argc, argv);

MainWindow w;

w.show();

return a.exec();

}

 

Теперь мы напишем свой класс-наследник QLabel, реагирующий на клики мышки.

 Пишем заголовочный файл:

mylabel.h:

#ifndef MYLABEL_H

#define MYLABEL_H

#include <QObject>

#include <QWidget>

#include <QLabel>

#include <QMouseEvent>

class MyLabel : public QLabel // Наследуемся от класса QLabel

{

Q_OBJECT

public:

explicit MyLabel(QWidget *parent = nullptr);

signals:

void clicked(QMouseEvent *); // Предусматриваем обработку сигнала clicked()

private:

protected:

void mousePressEvent (QMouseEvent *event);

};

endif // MYLABEL_H

 

Назовем наш класс — MyLabel и унаследуем его от класса QLabel. В файле mylabel.h предусматриваем обработку сигнала clicked(), а также обработку событий мыши mousePressEvent(QMouseEvent *event). Теперь напишем файл mylabel.cpp.

 

mylabel.cpp:

#include "mylabel.h"

MyLabel::MyLabel(QWidget *parent)

: QLabel{parent}

{}

void MyLabel::mousePressEvent(QMouseEvent *event)

{

emit clicked(event);

}

 

В файле mylabel.cpp мы написали обработчик событий мыши при нажатии клавиши mousePressEvent(QMouseEvent *event), который искусственно посылает сигнал clicked().

Вот, в принципе, и вся хитрость. Теперь попробуем всё это применить в нашей программе с кликом по меткам разных цветов. Напишем заголовочный файл mainwindow.h.

 

mainwindow.h:

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include <QMainWindow>

#include "mylabel.h"

class MainWindow : public QMainWindow

{

Q_OBJECT

public:

MainWindow(QWidget *parent = nullptr);

~MainWindow();

private:

MyLabel *redLabel = nullptr; // Красная метка

MyLabel *greenLabel = nullptr; // Зеленая метка

MyLabel *blueLabel = nullptr; // Синяя метка

MyLabel *textLabel = nullptr; // Поле где будут выводится сообщения

};

#endif // MAINWINDOW_H

 

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

 

mainwindow.cpp:

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)

: QMainWindow(parent)

{

this->resize(640,480); // Изменяем размеры окна

// Создаем красную метку

redLabel = new MyLabel(this);

redLabel->resize(100,100);

redLabel->move(50,300);

redLabel->setStyleSheet("background-color: red;");

redLabel->show();

// Создаем зеленую метку

greenLabel = new MyLabel(this);

greenLabel->resize(100,100);

greenLabel->move(250,300);

greenLabel->setStyleSheet("background-color: green;");

greenLabel->show();

// Создаем синюю метку

blueLabel = new MyLabel(this);

blueLabel->resize(100,100);

blueLabel->move(450,300);

blueLabel->setStyleSheet("background-color: blue;");

blueLabel->show();

// Создаем текстовое поле для вывода информации

textLabel = new MyLabel(this);

textLabel->resize(400,60);

textLabel->move(120, 100);

textLabel->setStyleSheet("background-color: lightyellow; font-size: 16px;");

textLabel->show();

 // Обрабатываем сигналы clicked() испускаемые метками в лямбда функции

connect(redLabel, &MyLabel::clicked, this, [this]()

{

delete redLabel;

redLabel = nullptr;

textLabel->setText("Уничтожена красная метка !!!");

});

connect(greenLabel, &MyLabel::clicked, this, [this]()

{

delete greenLabel;

greenLabel = nullptr;

textLabel->setText("Уничтожена зеленая метка !!!");

});

connect(blueLabel, &MyLabel::clicked, this, [this]()

{

delete blueLabel;

blueLabel = nullptr;

textLabel->setText("Уничтожена синяя метка !!!");

});

connect(textLabel, &MyLabel::clicked, this, [this]()

{

textLabel->setText("Вы кликнули по текстовому полю !!!");

});

}

MainWindow::~MainWindow()

{

delete textLabel;

textLabel = nullptr;

delete blueLabel;

blueLabel = nullptr;

delete greenLabel;

greenLabel = nullptr;

delete redLabel;

redLabel = nullptr;

}

 

Вот и всё, ниже приводятся скрины результата работы программы:

Поочередно кликаем мышкой по зеленой метке, текстовому полю, красной метке, синей метке:

 

Окно программы:

Кликаем по зеленой метке:

Кликаем по текстовому полю:

Кликаем по красной метке:

Кликаем по синей метке:

 

 



Фото

Управление памятью в C++


Управление памятью в C++ невероятно эффективно для оптимизации производительности, особенно когда речь идёт о крупных приложениях. Сегодня мы поговорим о преимуществах управления памятью в C++ и познакомим вас с базовыми принципами управления памятью в C++.

Краткое введение в управление памятью

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

Язык программирования может использовать один из двух подходов к управлению памятью:

  • Автоматическое управление памятью (например, в Java, Python, C#)
  • Динамическое управление памятью (например, в C++, C)

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

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

Несмотря на то, что это приводит к увеличению времени разработки, динамическое управление памятью позволяет адаптировать потребление памяти приложением и создавать высокопроизводительные приложения. Динамическое управление памятью — единственный возможный вариант, если вы работаете с устройствами с ограниченными ресурсами, например со встроенными устройствами. Оно также важно для поддержания высокой производительности в системах реального времени, поэтому C++ часто используется в разработке игр. Таким образом, язык C++ становится отличным выбором для ситуаций, когда важны производительность и небольшой объём используемой памяти.

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

Начало работы с управлением памятью в C++

Основы модели памяти C++

Каждое слово (или блок) памяти обычно состоит из двух, четырёх или восьми байт в зависимости от аппаратной архитектуры. В нашей программе на C++ мы можем обратиться к блоку по его числовому адресу. Адрес первого блока равен 0, а адрес последнего блока зависит от объёма памяти вашего компьютера. На рисунке ниже изображён блок памяти.

 

 

В C++ мы можем разделить память программы на три части:

  1. Статическая область, в которой хранятся статические переменные. Статические переменные — это переменные, которые используются на протяжении всего выполнения программы. Размер статической области не меняется во время выполнения программы на C++.

  2. Стек, в котором хранятся фреймы стека. Для каждого вызова функции создаётся новый фрейм стека. Фрейм стека — это блок данных, который содержит локальные переменные соответствующей функции и уничтожается (выталкивается) при возврате из функции.

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

 

 

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

В C++ блок памяти — это непрерывный массив байтов, где каждый байт имеет уникальный адрес.

Мы можем управлять памятью в C++ с помощью двух операторов:

  • new оператор для выделения блока памяти в куче
  • delete оператор для освобождения блока памяти из кучи

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

 

 

Теперь давайте разберёмся, что происходит в предыдущем коде:

  1. new Оператор резервирует место в памяти, где может храниться целое число C++ (то есть 4 байта). Затем он возвращает адрес вновь выделенной памяти.
  1. Мы создаём указатель ptr для хранения адреса памяти, возвращаемого оператором new .

 

 

  1. Мы сохраняем целочисленное значение по вновь выделенному адресу памяти с помощью *ptr=5.

 

 

  1. Мы выводим на экран адрес памяти, в котором хранится целое число, и значение целого числа, хранящееся в этой ячейке памяти.
  1. Наконец, мы освобождаем блок памяти, зарезервированный с помощью new использования delete оператора.

 

 

Безопасное использование управления памятью в C++

Использование операторов new и delete требует некоторой осторожности. Они могут привести к ошибкам в работе с памятью. Однако мы можем использовать умные указатели, которые помогут нам более безопасно управлять памятью.

Распространённые ошибки при управлении памятью

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

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

 

 

В этом примере кода функция memLeak() выделяет память, но эта память не освобождается. После возврата из функции выделенная память продолжает использоваться, даже если к ней нет доступа.

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

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

 

 

Обратите внимание, что ptr++ увеличивает адрес, хранящийся в указателе. Поскольку цикл while выполняется непрерывно, вскоре указатель будет указывать на область за пределами адресного пространства программы, что приведёт к ошибке сегментации. Чтобы избежать ошибок сегментации, мы должны убедиться, что программа не обращается к области памяти, которая ей не выделена.

 

Предотвращение ошибок с помощью умных указателей

 

В C++ предусмотрены различные типы интеллектуальных указателей. Мы называем эти указатели «интеллектуальными», потому что они автоматически освобождаются без явных указаний программиста или сборщика мусора. Хотя интеллектуальные указатели требуют больше ресурсов для работы и занимают больше памяти, чем классические указатели, они помогают уменьшить количество утечек памяти.

Мы немного поговорим об уникальных и общих указателях, а также о некоторых ограничениях интеллектуальных указателей.

Уникальные указатели

Уникальные указатели unique_ptr являются указателями области видимости. Как указатель области видимости, уникальный указатель на определённый объект автоматически освобождается, когда указатель выходит за пределы области видимости.

В качестве примера рассмотрим следующий код, в котором показан уникальный указатель на объект MyClass, созданный в блоке if. Таким образом, областью видимости указателя является блок if. Указатель автоматически освобождается в конце блока if.

 

 

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

На следующем рисунке std::move переключает владение объектом с одного указателя на другой.

 

 

Вместо копирования мы можем использовать функцию std::move для безопасной передачи права владения текущим указателем другому. std::move показано на предыдущем рисунке. Это можно понять из следующего кода, в котором мы передали право владения объектом MyClass от указателя ptr к ptr2. Будьте осторожны: чтобы избежать ошибки сегментации, предыдущий указатель нельзя использовать после передачи права владения.

В следующем коде показано, как безопасно передать право собственности с помощью функции std::move:

 

 

Общие указатели

Общий указатель std::shared_ptr использует подсчёт ссылок для освобождения памяти. В отличие от уникальных указателей, общий указатель позволяет нескольким указателям указывать на один и тот же объект. Общий указатель ведёт подсчёт каждого указателя, который всё ещё находится в области видимости. Когда указатель выходит из области видимости, счётчик уменьшается. Таким образом, объект автоматически удаляется, когда счётчик ссылок достигает нуля.

На следующем рисунке показан общий указатель с количеством ссылок, равным двум:

 

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

Ограничения интеллектуальных указателей

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

Следующий код удаляет смарт-указатель вручную:

 

 

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

В следующем примере кода показано использование циклических указателей, при котором общие указатели не могут автоматически освобождать память:

 

 

Используйте все возможности C++ с помощью управления памятью. Хотя на обучение этому навыку требуется некоторое время, управление памятью в C++ — особенно ценный навык для программиста, особенно в свете того, что мы продолжаем двигаться в сторону распределённых систем.



Фото

Как использовать CSS с PHP


Узнайте о различных способах добавления каскадных таблиц стилей (CSS) на ваш сайт с помощью PHP — с примерами кода.

На веб-сайтах, работающих на PHP, HTML-разметка, таблицы стилей CSS и скрипты JavaScript хранятся в PHP-файлах.

Любой код, не заключённый в тег PHP (то есть <?php ?>), не обязательно должен соответствовать синтаксису PHP и будет выведен как статический код в HTML-документе, который сервер генерирует в ответ на запрос браузера.

С другой стороны, код, заключённый в тег PHP, должен соответствовать синтаксису языка PHP и будет динамически выводиться в HTML-файл, загружаемый браузером пользователя.

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

Статическое добавление CSS с помощью PHP

В PHP-файле вы можете встроить CSS-код в атрибут style="" HTML-элементов, встроить его в тег <style type="text/css"> в заголовке или сослаться на него в теге <link rel="stylesheet">, и он будет выведен без изменений.

Следующий PHP-файл:

<!doctype html>
<html>
    <head>
        <style>
            font-size: 42px;
        </style>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <h1 style="color:blue">Hello, world!</h1>
    </body>
</html>


В результате получится следующая HTML-разметка:

<!doctype html>
<html>
    <head>
        <style>
            font-size: 42px;
        </style>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <h1 style="color:blue">Hello, world!</h1>
    </body>
</html>

Однако это предполагает, что вы пишете только HTML/CSS-код и сохраняете его в PHP-файле. В этом случае вы не используете возможности PHP для динамического изменения вашего сайта.

Динамическое добавление CSS с помощью PHP

Теперь, когда мы рассмотрели статический способ, давайте уделим пару минут обсуждению того, как динамически добавлять CSS-код в HTML-документ.

Одна из многих возможностей PHP — объявление переменных и сохранение в них информации. Затем вы можете ссылаться на эти переменные в echo операторах PHP, чтобы вывести сохранённую в них информацию на экран.

Например, вы можете сохранить значения свойств CSS в переменных PHP, а затем вывести их в HTML-файлах на стороне клиента, чтобы динамически генерировать код CSS при каждом запросе.

 

<?php
$h1_size = "42px";
$h1_color = "blue";
$stylesheet_url = "style.css";
?>
<!doctype html>
<html>
    <head>
    <?php
    echo "<style>
            font-size: {$h1_size}};
        </style>"
        ?>
        <?php
        $url = "style.css";
        echo "<link rel='stylesheet' href='{$stylesheet_url}'>";
        ?>
    </head>
    <body>
        <h1 <?php echo "style='color:blue'" ?>>Hello, world!</h1>
    </body>
</html>
 
 
Результат будет таким же, как в статическом примере, приведённом несколькими абзацами выше. Но разница в том, что вы можете задавать значения в коде CSS и повторно использовать их в правилах CSS.
 

<!doctype html>
<html>
    <head>
        <style>
            font-size: 42px;
        </style>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <h1 style="color:blue">Hello, world!</h1>
    </body>
</html>

 

Пока всё идёт хорошо.

Но настоящая сила PHP,  заключается в его функциях.

Например, вы можете создать функцию для генерации элемента <link> с атрибутами rel=”” и href=””, хранящимися в переменных:

 

<?php
    // Define the linkResource() function
    function linkResource($rel, $href) {
        echo "<link rel='{$rel}' href='{$href}'>";
    }
?>


С помощью этой функции вы можете подключить любую внешнюю таблицу стилей CSS или JS-скрипт.

Обратите внимание на использование одинарных и двойных кавычек. Если в вашем PHP-коде используются двойные кавычки, то для HTML-кода в операторах echo нужно использовать одинарные кавычки, и наоборот.

Если вы вызовете функцию linkResource() в любом месте вашего PHP-файла со следующими параметрами:

// Call the linkResource() function
<?php linkResource("stylesheet", "/css/style.css"); ?>


Он выведет элемент DOM <link> с указанными параметрами в HTML-файл на стороне клиента:

<link rel="stylesheet" href="/css/style.css">


Вот как это выглядит на практике. Ниже приведён PHP-файл на стороне сервера:

 

<?php
    function linkResource($rel, $href) {
        echo "<link rel='{$rel}' href='{$href}'>";
    }
?>
<!doctype html>
<html>
    <head>
        <?php linkResource("stylesheet", "/css/normalize.css"); ?>
        <?php linkResource("stylesheet", "/css/style.css"); ?>
    </head>
    <body>
        <h1>Hello, world!</h1>
    </body>
</html>


Ниже будет выведен HTML-файл на стороне клиента:

 

<!doctype html>
<html>
    <head>
        <link rel='stylesheet' href='/css/normalize.css'>
        <link rel='stylesheet' href='/css/style.css'>
    </head>
    <body>
        <h1>Hello, world!</h1>
    </body>
</html>

В заключение

 

Есть два способа добавить CSS-код с помощью PHP. Первый — статический, то есть жёсткое кодирование в PHP-файлах, а второй — динамический, то есть генерация с помощью функций и переменных.

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

 

Источник: https://makersaid.com

 



Фото

21 фича современного C++


В этой статье вашему вниманию будут представлены 21 новая фича современного C++, которые помогут сделать ваш проект лучше, а работу над ним легче.

1. Разделители разрядов чисел

 

int no = 1'000'000;                      // визуальное разделение единиц, тысяч, миллионов и т.д.
long addr = 0xA000'EFFF;                 // визуальное разделение 32-битного адреса на
uint32_t binary = 0b0001'0010'0111'1111; // удобочитаемые сегменты
  • Раньше вам нужно было считать цифры или нули, но, начиная с C++14, вы можете сделать большие числа намного нагляднее.

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

  • Благодаря сгруппированным разрядам, ваш код станет немного выразительнее.

 

2. Псевдонимы типов

 

template <typename T>
using dyn_arr = std::vector<T>;
dyn_arr<int> nums; // эквивалентно std::vector<int>

using func_ptr = int (*)(int);
  • Семантически похоже на использование typedef, однако псевдонимы типов легче читаются и совместимы с шаблонами С++. Поблагодарите С++11.

 

3. Пользовательские литералы

 

using ull = unsigned long long;

constexpr ull operator"" _KB(ull no)
{
return no * 1024;
}

constexpr ull operator"" _MB(ull no)
{
return no * (1024_KB);
}

cout<<1_KB<<endl;
cout<<5_MB<<endl;
  • По большей части это будут какие-нибудь реальные единицы, такие как kb, mb, км, см, рубли, доллары, евро и т.д. Пользовательские литералы позволяют вам не определять функции, для выполнения преобразования единиц измерения во время выполнения, а работать с ним как с другими примитивными типами.

  • Очень удобно для единиц и измерения.

  • Благодаря добавлению constexpr вы можете добиться нулевого влияния на производительность во время выполнения, что мы увидим позже в этой статье, и более подробно вы можете почитать об этом в другой статье, которую я написал, — “Использование const и constexpr в С++”.

 

4. Унифицированная инициализация и инициализация нестатических членов

 

Раньше вам нужно было инициализировать поля их значениями по умолчанию в конструкторе или в списке инициализации. Но начиная с C++11 можно задавать обычным переменным-членам класса (тем, которые не объявлены с ключевым словом static) инициализирующее значение по умолчанию, как показано ниже:

class demo
{
private:
uint32_t m_var_1 = 0;
bool m_var_2 = false;
string m_var_3 = "";
float m_var_4 = 0.0;

public:
demo(uint32_t var_1, bool var_2, string var_3, float var_4)
: m_var_1(var_1),
m_var_2(var_2),
m_var_3(var_3),
m_var_4(var_4) {}
};

demo obj{123, true, "lol", 1.1};
  • Это особенно полезно, когда в качестве полей выступают сразу несколько вложенных объектов, определенных, как показано ниже:

class computer
{
private:
cpu_t m_cpu{2, 3.2_GHz};
ram_t m_ram{4_GB, RAM::TYPE::DDR4};
hard_disk_t m_ssd{1_TB, HDD::TYPE::SSD};

public:
// ...
};
  • В этом случае вам не нужно инициализировать их в списке инициализации. Вместо этого вы можете напрямую указать значение по умолчанию во время объявления.

class X
{
const static int m_var = 0;
};

// int X::m_var = 0; // не требуется для статических константных полей
  • Вы также можете инициализировать во время объявления const static члены класса, как показано выше.

 

5. std::initializer_list

 

std::pair<int, int> p = {1, 2};
std::tuple<int, int> t = {1, 2};
std::vector<int> v = {1, 2, 3, 4, 5};
std::set<int> s = {1, 2, 3, 4, 5};
std::list<int> l = {1, 2, 3, 4, 5};
std::deque<int> d = {1, 2, 3, 4, 5};

std::array<int, 5> a = {1, 2, 3, 4, 5};

// Не работает для адаптеров
// std::stack<int> s = {1, 2, 3, 4, 5};
// std::queue<int> q = {1, 2, 3, 4, 5};
// std::priority_queue<int> pq = {1, 2, 3, 4, 5};
  • Присваивайте значения контейнерам непосредственно с помощью списка инициализаторов, как это можно делать с C-массивами.

  • Это справедливо и для вложенных контейнеров. Скажите спасибо С++11.

 

6. auto & decltype

 

auto a = 3.14; // double
auto b = 1; // int
auto& c = b; // int&
auto g = new auto(123); // int*
auto x; // error -- `x` requires initializer
  • auto-типизированные переменные выводятся компилятором на основе типа их инициализатора.

  • Чрезвычайно полезно с точки зрения удобочитаемости, особенно для сложных типов:

// std::vector<int>::const_iterator cit = v.cbegin();
auto cit = v.cbegin(); // альтернатива

// std::shared_ptr<vector<uint32_t>> demo_ptr(new vector<uint32_t>(0);
auto demo_ptr = make_shared<vector<uint32_t>>(0); // альтернатива
  • Функции также могут выводить тип возвращаемого значения с помощью auto. В C++11 тип возвращаемого значения должен быть указан либо явно, либо с помощью decltype, например:

template <typename X, typename Y>
auto add(X x, Y y) -> decltype(x + y)
{
return x + y;
}
add(1, 2); // == 3
add(1, 2.0); // == 3.0
add(1.5, 1.5); // == 3.0
  • Приведенная выше форма определения возвращаемого типа называется trailing return type, т.е. -> return-type.

 

7. Циклы for по диапазону

 

  • Синтаксический сахар для перебора элементов контейнера.

std::array<int, 5> a {1, 2, 3, 4, 5};
for (int& x : a) x *= 2;
// a == { 2, 4, 6, 8, 10 }
  • Обратите внимание на разницу при использовании int в противовес int&:

std::array<int, 5> a {1, 2, 3, 4, 5};
for (int x : a) x *= 2;
// a == { 1, 2, 3, 4, 5 }

 

8. Умные указатели

 

  • C++11 добавляет в язык новые умные указатели: std::unique_ptr, std::shared_ptr, std::weak_ptr.

  • А std::auto_ptr устарел, и в конечном итоге удален в C++17.

std::unique_ptr<int> i_ptr1{new int{5}}; // Не рекомендуется 
auto i_ptr2 = std::make_unique<int>(5); // Так лучше

template <typename T>
struct demo
{
T m_var;

demo(T var) : m_var(var){};
};

auto i_ptr3 = std::make_shared<demo<uint32_t>>(4);
  • Гайдлайны ISO CPP рекомендуют избегать явных вызовов new и delete, выразив это в правиле “никаких голых new”.

 

9. nullptr

 

  • C++11 добавил новый тип пустого указателя, предназначенный для замены макроса C NULL.

  • nullptr имеет тип std::nullptr_t и может быть неявно преобразован в типы непустых указателей, и в отличие от NULL, не конвертируем в целочисленные типы, за исключением bool.

void foo(int);
void foo(char*);
foo(NULL); // ошибка -- неоднозначность
foo(nullptr); // вызывает foo(char*)

 

10. Строго типизированные перечисления

 

enum class STATUS_t : uint32_t
{
PASS = 0,
FAIL,
HUNG
};

STATUS_t STATUS = STATUS_t::PASS;
STATUS - 1; // больше не валидно, начиная с C++11
  • Типобезопасные перечисления, которые решают множество проблем с C-перечислениями, включая неявные преобразования, арифметические операции, невозможность указать базовый тип, загрязнение области видимости и т.д.

 

11. Приведение типов

 

  • Приведение в стиле C изменяет только тип, не затрагивая сами данные. В то время как старый C++ имел небольшой уклон в типобезопасность, он предоставлял фичу указания оператора/функции преобразования типа. Но это было неявное преобразование типов. Начиная с C++11, функции преобразования типов теперь можно сделать явными с помощью спецификатора explicit следующим образом:

struct demo
{
explicit operator bool() const { return true; }
};

demo d;
if (d); // OK, вызывает demo::operator bool()
bool b_d = d; // ОШИБКА: не может преобразовать 'demo' в 'bool' во время инициализации
bool b_d = static_cast<bool>(d); // OK, явное преобразование, вы знаете, что делаете

 

 

12. Move-семантика

 

  • Когда объект будет уничтожен или не будет более использоваться после выполнения выражения, целесообразнее переместить (move) ресурс, а не копировать его.

  • Копирование включает в себя ненужные накладные расходы, такие как выделение памяти, высвобождение и копирование содержимого памяти и т.д.

  • Рассмотрим следующую функцию, меняющую местами два значения:

template <class T>
swap(T& a, T& b) {
T tmp(a); // теперь у нас есть две копии a
a = b; // теперь у нас есть две копии b (+ отброшена копия a)
b = tmp; // теперь у нас есть две копии tmp (+ отброшена копия b)
}
  • Использование move позволяет вам напрямую обменивать ресурсы вместо их копирования:

template <class T>
swap(T& a, T& b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
  • А теперь представьте, что происходит, когда Т это, скажем, vector<int> размера n. И n достаточно велико.

  • В первой версии вы читаете и записываете 3*n элементов, во второй версии вы в по сути читаете и записываете только 3 указателя на буферы векторов плюс 3 размера буферов.

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

  • Эта фича даст вам значительный прирост в производительности — именно то, поэтому люди используют C++ (т.е., чтобы выжать последние 2-3 капли скорости).

 

13. Универсальные ссылки

 

  • В официальной терминологии известные как forwarding references (передаваемые ссылки). Универсальная ссылка объявляется с помощью синтаксиса Т&&, где Т является шаблонным параметром типа, или с помощью auto&&. Они в свою очередь служат фундаментом для двух других крупных фич:

    • move-семантика

    • И perfect forwarding, возможность передавать аргументы, которые являются либо lvalue, либо rvalue.

Универсальные ссылки позволяют ссылаться на привязку либо к lvalue, либо к rvalue в зависимости от типа. Универсальные ссылки следуют правилам свертывания ссылок:

  1. T& & становится  T&  

  2. T& && становится T&

  3. T&& & становится T&

  4. T&& && становится T&&

Вывод шаблонного параметра типа с lvalue ​​и rvalue:

// Начиная с C++14 и далее:
void f(auto&& t) {
// ...
}

// Начиная с C++11 и далее:
template <typename T>
void f(T&& t) {
// ...
}

int x = 0;
f(0); // выводится как f(int&&)
f(x); // выводится как f(int&)

int& y = x;
f(y); // выводится как f(int& &&) => f(int&)

int&& z = 0; // ПРИМЕЧАНИЕ: z — это lvalue типа int&amp;&amp;.
f(z); // выводится как f(int&& &) => f(int&)
f(std::move(z)); // выводится как f(int&& &&) => f(int&&)

 

 

14. Шаблоны с переменным количеством аргументов

 

void print() {}

template <typename First, typename... Rest>
void print(const First &first, Rest &&... args)
{
std::cout << first << std::endl;
print(args...);
}

print(1, "lol", 1.1);
  • Синтаксис ... создает пакет параметров или расширяет уже существующий. Шаблонный пакет параметров — это шаблонный параметр, который принимает ноль или более аргументов-шаблонов (нетипизированных объектов, типов или шаблонов). Шаблон С++ с хотя бы одним пакетом параметров называется вариативный шаблоном с переменным количеством аргументов (variadic template).

 

15. constexpr

 

constexpr uint32_t fibonacci(uint32_t i)
{
return (i <= 1u) ? i : (fibonacci(i - 1) + fibonacci(i - 2));
}

constexpr auto fib_5th_term = fibonacci(6); // равноценно auto fib_5th_term = 8
  • Константные выражения — это выражения, вычисляемые компилятором во время компиляции. В приведенном выше примере функция fibonacci выполняется/вычисляется компилятором во время компиляции, и будет заменена на результат в вызове места.

 

16. Удаленные и дефолтные функции

 

struct demo
{
demo() = default;
};

demo d;
  • У вас вполне закономерно может возникнуть вопрос, зачем вам писать 8+ букв (т.е. = default;), когда можно просто использовать {}, т.е. пустой конструктор? Никто вас не останавливает. Но подумай о конструкторе копирования, операторе копирования присваиванием, и т.д.

  • Пустой конструктор копирования, например, не то же самое, что конструктор копирования по умолчанию (который будет выполнять почленную копию всех членов).

Вы можете ограничить определенную операцию или способ инстанцирования объекта, просто удалив соответствующий метод, как показано ниже:

class demo
{
int m_x;

public:
demo(int x) : m_x(x){};
demo(const demo &) = delete;
demo &operator=(const demo &) = delete;
};

demo obj1{123};
demo obj2 = obj1; // ОШИБКА -- вызов удаленного конструктора копирования
obj2 = obj1; // ОШИБКА -- оператор = удален

В старом С++ вы должны были сделать его приватным. Но теперь в вашем распоряжении есть директива компилятора delete.

 

 

17. Делегирование конструкторов

 

struct demo
{
int m_var;
demo(int var) : m_var(var) {}
demo() : demo(0) {}
};

demo d;
  • В старом C++ вам нужно создавать функцию-член для  инициализации и вызывать ее из всех конструкторов для достижения универсально инициализации.

  • Но начиная с C++11 конструкторы теперь могут вызывать другие конструкторы из того же класса с помощью списка инициализаторов.

 

18. Лямбда-выражения

 

auto generator = [i = 0]() mutable { return ++i; };
cout << generator() << endl; // 1
cout << generator() << endl; // 2
cout << generator() << endl; // 3
  • Я думаю, что эта фича не нуждается в представлении и является фаворитом среди других фич.

  • Теперь вы можете объявлять функции где угодно. И это не будет стоить вам никаких дополнительных накладных расходов. 

 

19. Операторы ветвления с инициализатором

 

  • В более ранних версиях C++ инициализатор либо объявлялся перед оператором и просачивался во внешнюю область видимости, либо использовалась явная область видимости.

  • В C++17 появилась новая форма if/switch, которую можно записать более компактно, а улучшенный контроль области видимости делает некоторые ранее подверженные ошибкам конструкции немного более надежными:

switch (auto STATUS = window.status()) // Объявляем объект прямо в операторе ветвления
{
case PASS:// делаем что-то
break;
case FAIL:// делаем что-то
break;
}
  • Как это работает

{
auto STATUS = window.status();
switch (STATUS)
{
case PASS: // делаем что-то
break;
case FAIL: // делаем что-то
break;
}
}

 

20. std::tuple

 

auto employee = std::make_tuple(32, " Vishal Chovatiya", "Bangalore");
cout << std::get<0>(employee) << endl; // 32
cout << std::get<1>(employee) << endl; // "Vishal Chovatiya"
cout << std::get<2>(employee) << endl; // "Bangalore"
  • Кортежи представляют собой набор разнородных значений фиксированного размера. Доступ к элементам std::tuple производится с помощью std::tie или std::get.

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

auto get_employee_detail()
{
// делаем что-нибудь . . .
return std::make_tuple(32, " Vishal Chovatiya", "Bangalore");
}

string name;
std::tie(std::ignore, name, std::ignore) = get_employee_detail();
  • Используйте std::ignore в качестве плейсхолдера для игнорируемых значений. В С++ 17, вместо этого следует использовать структурированные привязки.

 

21. Выведение аргумента шаблона класса

 

std::pair<std::string, int> user = {"M", 25}; // раньше
std::pair user = {"M", 25}; // C++17

std::tuple<std::string, std::string, int> user("M", "Chy", 25); // раньше
std::tuple user2("M", "Chy", 25); // выведение в действии!
  • Автоматическое выведение аргументов шаблона очень похоже на то, как это делается для функций, но теперь также включает и конструкторы классов.

 

Пара слов в заключение 

 

Здесь мы только слегка коснулись огромного набора новых фич и возможности их применения. В современном C++ можно найти еще очень много чего, но тем не менее вы можете считать этот набор хорошей отправной точкой. Современный C++ расширяется не только с точки зрения синтаксиса, но также добавляется гораздо больше других функций, таких как неупорядоченные контейнеры, потоки, регулярное выражение, Chrono, генератор/распределитель случайных чисел, обработка исключений и множество новых алгоритмов STL (например, all_of(), any_of(), none_of(), и т.д).

Да пребудет с вами C++!



Фото

Основные классы объектов в Unity


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

Классы объектов в Unity — это специальные скрипты, которые определяют поведение отдельных объектов в сцене. Они содержат всю логику и функциональность, необходимую для работы объекта, включая его движение, анимацию, взаимодействие с другими объектами и многое другое. Классы объектов в Unity могут быть написаны на языке C#, который является основным языком программирования для разработки в Unity.

В Unity существует несколько основных типов классов объектов, которые разработчики могут использовать в своих проектах. Это включает в себя классы для обработки рендеринга и графики, классы для управления физикой, классы для создания пользовательского интерфейса и многое другое. Классы объектов в Unity обеспечивают гибкость и мощность разработки, позволяя разработчикам создавать разнообразные и уникальные игровые сцены и элементы.

Основные типы классов в Unity

GameObject: этот класс представляет игровой объект в сцене. Он содержит информацию о позиции, масштабе и повороте объекта, а также его компоненты.

Transform: этот класс представляет преобразование объекта в трехмерном пространстве. Он содержит информацию о позиции, масштабе и повороте объекта.

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

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

Rigidbody: этот класс представляет физическое тело объекта. Он может использоваться для имитации реалистичной физики объектов в игре.

Camera: этот класс представляет камеру в сцене. Он используется для отображения игровой сцены на экране игрока.

Material: этот класс представляет материал, который определяет внешний вид объекта в сцене. Он содержит информацию о цвете, текстуре и других свойствах материала.

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

Animation: этот класс представляет анимацию, которая может быть присоединена к игровым объектам. Он содержит информацию о ключевых кадрах и способе воспроизведения анимации.

GUIElement: этот класс представляет элемент пользовательского интерфейса (UI) в игре. Он может содержать текст, изображения и другие элементы интерфейса.

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

Автомобили: классы и их особенности

1. Car — основной класс автомобиля. Он содержит основные атрибуты и функции, которые относятся ко всем автомобилям в проекте. В этом классе можно определить скорость автомобиля, его цвет, размер и другие общие характеристики.

2. PlayerCar — класс, наследующийся от Car. Он добавляет функциональность, специфичную для автомобиля, управляемого игроком. Например, в этом классе можно определить, как должен реагировать автомобиль на команды игрока, какие анимации проигрывать при управлении и т.д.

3. AICar — класс, наследующийся от Car, предназначенный для автомобилей, управляемых искусственным интеллектом. Этот класс может содержать алгоритмы, определяющие поведение автомобиля на треке, его стратегию гонки и так далее.

4. PoliceCar — класс, наследующийся от Car, представляющий полицейскую машину. Он может иметь особые возможности, такие как сирена, возможность преследования других автомобилей и т.д.

5. Truck — класс, наследующийся от Car, представляющий грузовик. Он может иметь большие размеры и дополнительные функции, такие как перевозка груза.

Классы автомобилей в Unity могут быть настроены и расширены с помощью множества дополнительных функций и атрибутов. Это позволяет создавать уникальные и интересные автомобили для ваших игр. Удачного создания и настройки автомобилей в Unity!

Здания: типы и использование классов

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

Одним из основных типов зданий в Unity являются статичные здания. Данный тип зданий не движется и является основным элементом окружения. Для создания статичных зданий в Unity можно использовать классы GameObject и Transform. Класс GameObject представляет игровой объект, а класс Transform отвечает за его положение и преобразование в пространстве.

Для создания динамических зданий в Unity можно использовать классы Rigidbody и Collider. Класс Rigidbody позволяет объектам имитировать физическую силу, взаимодействуя с другими объектами в игре. Класс Collider определяет границы объекта и обрабатывает столкновения с другими объектами.

Еще одним типом зданий в Unity являются интерактивные здания. Они обладают различными возможностями взаимодействия с игроком, такими как открытие дверей, взаимодействие с предметами и т.д. Для создания интерактивных зданий в Unity используются классы Animator и Trigger, которые позволяют задавать анимации и управлять их воспроизведением.

Также в Unity можно создавать разрушаемые здания. Для этого используются классы Particle System и Destroyer. Класс Particle System позволяет создавать эффекты разрушения, а класс Destroyer отвечает за удаление разрушенных объектов.

Использование различных типов зданий и соответствующих классов позволяет создавать более реалистичные и интересные игровые миры в Unity. Выбор подходящих классов и настройка их параметров зависит от конкретных потребностей и задач игры.

Персонажи: классы и анимации

Одним из ключевых классов для управления персонажами в Unity является класс CharacterController. Этот класс позволяет осуществлять контроль за передвижением персонажа, его поворотом, а также обнаруживать столкновения с другими объектами.

Для создания анимаций персонажей в Unity используется система Mecanim. Mecanim предоставляет мощные средства для управления анимациями, позволяя задавать последовательность и параметры анимаций, переключать между ними и управлять их проигрыванием.

Для работы с анимациями в Unity необходимо создать анимационный контроллер. Анимационный контроллер объединяет различные анимации вместе и определяет условия перехода между ними. В самом простом случае анимационный контроллер может содержать всего одну анимацию для персонажа.

Кроме того, Unity предоставляет набор готовых анимаций для персонажей, которые могут быть использованы в игре. Например, анимации ходьбы, бега, прыжка и атаки. Эти анимации могут быть применены к персонажу при помощи специального компонента Animator.

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

Оружие: классы и их роли

В Unity оружие реализуется с использованием классов и их ролей. Существуют несколько основных классов оружия:

 

Класс Описание
Огнестрельное Оружие, которое стреляет пулями или снарядами. Включает в себя пистолеты, автоматы, снайперские винтовки и прочее.
Ближнего боя Оружие, предназначенное для атаки вблизи. Включает в себя мечи, кинжалы, топоры и другие режущие и колющие предметы.
Магическое Оружие, основанное на использовании магических способностей. Включает в себя посохи, жезлы, кольца и другие предметы, позволяющие использовать магические заклинания или эффекты.
Гранаты и взрывчатка Оружие, которое позволяет атаковать врагов с помощью гранат, взрывов и других взрывчатых веществ. Обычно используется для нанесения повреждений области.

 

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

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

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

Продвинутые классы объектов Unity

1. NavMeshAgent

Класс NavMeshAgent позволяет объектам перемещаться по навигационной сетке (NavMesh) в Unity. Он автоматически рассчитывает путь и управляет перемещением объекта, учитывая преграды и другие факторы. Этот класс особенно полезен для создания искусственного интеллекта (AI) в играх, когда нужно сделать так, чтобы объекты имели способность самостоятельно перемещаться по сцене.

2. Animator

Класс Animator предназначен для управления анимациями объектов в Unity. С его помощью можно создавать и управлять состояниями и переходами между ними. Аниматор позволяет указывать, какие анимации должны проигрываться в зависимости от различных событий, таких как нажатие кнопки или заданное условие. Это незаменимый класс для создания реалистичных анимаций персонажей и объектов в играх.

3. AudioSource

Класс AudioSource служит для воспроизведения звуковых эффектов в Unity. Он позволяет загружать и проигрывать звуки из различных источников, таких как файлы аудио или потоковые источники. Класс AudioSource управляет громкостью, панорамой и другими параметрами звука, а также позволяет контролировать его воспроизведение — запускать, останавливать и перезапускать звуковые эффекты.

4. ParticleSystem

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

5. LineRenderer

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

 

Источник:    peredelka38.ru