What's new
Runion

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Написание AIM нейросети

OverlordGameDev

Light Weight
Депозит
$0

Предисловие:​

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

Что такое Yolo:​

YOLO — это метод из Python-библиотеки, который позволяет распознавать объекты на изображениях и видео. Проще говоря, это нейронная сеть, которая занимается распознаванием объектов. Код, написанный в статье, будет работать с распознанными объектами и информацией о них. В данной статье будет использоваться последняя стабильная версия YOLOv8.

Создание обученной модели для определения объектов:​

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

Скриншоты желательно должны быть размером 640 на 640 пикселей. Около 10% скриншотов должны быть пустыми, то есть без игроков, но в естественной среде игры, например, на карте. Это нужно для того, чтобы нейросеть не определяла случайные объекты как игроков, а имела примеры как игрока, так и всего остального. После подготовки скриншотов необходимо создать разметку на этих изображениях, выделяя объекты игроков. Для этого есть несколько программ и сервисов, таких как: Label Studio, LabelImg и Roboflow. В данной статье будет использован сервис Roboflow.

Чтобы начать разметку, нужно перейти на сайт https://roboflow.com и создать новый проект.
1724033924143.png


1724033944445.png


После создания проекта с подготовленными ранее скриншотами нужно начать делать разметку. Это довольно простое, но времязатратное занятие.
1724033992384.png


1724034010557.png


Вот и всё. Нужно просто выбрать инструмент выделения и начать обводить объекты игроков. После того как все скриншоты будут обработаны таким способом, нужно получить датасет с этими скриншотами и файлами разметки.
1724034050816.png


1724034083807.png


1724034103834.png


1724034128834.png


1724034146403.png


В итоге скачанный датасет с сайта должен выглядеть так же, как и на скриншоте:
1724034179249.png



Теперь нужно запустить обучение на данном датасете. Для этого потребуется достаточно мощный компьютер. Например, на компьютере с 12-ядерным Xeon и 32 GB ОЗУ, система просто зависала намертво при обучении на датасете с 16K скриншотов. Обучение удалось завершить лишь на компьютере с Ryzen 7, 64 GB ОЗУ и файлом подкачки в 100 GB. Если у вас есть компьютер подобной мощности, для обучения нужно лишь установить библиотеку Ultralytics, затем открыть консоль и ввести следующую команду:
Код: Скопировать в буфер обмена
yolo task=detect mode=train imgsz=640 data=Полный путь до файла data.yaml epochs=45 batch=8 name=Имя для готового файла
Единственное, что вы можете изменить для себя, это количество эпох.

Что такое эпохи при обучении модели:​

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

После того как обучение закончится, в папке с датасетом появится файл best.pt. Это и есть готовый файл модели. После его получения можно приступать к написанию логики на Python.

Написание логики на Python:​

Инструкции по созданию проекта на Python и открытию его в PyCharm не будет, поэтому сразу можно приступить к написанию кода. Первым делом нужно добавить необходимые библиотеки:
Python: Скопировать в буфер обмена
Code:
import os
import torch
from ultralytics import YOLO
import dxcam

Что за библиотека torch и cuda:​

Torch — это библиотека, работающая с моделями машинного обучения. В коде она будет использована для определения устройства (CPU или GPU), а также YOLO будет использовать Torch для загрузки обученной модели.

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

С установкой библиотеки Torch совместно с CUDA могут возникнуть некоторые сложности, особенно при установке по отдельности или через PyCharm. Для упрощения процесса можно создать BAT-файл. Но для начала необходимо создать в папке с проектом папку requirements. В этой папке создайте текстовый файл с названием requirements_gpu.txt. Внутри этого файла укажите следующие библиотеки:
Код: Скопировать в буфер обмена
Code:
torch==2.4.0+cu124
torchvision==0.19.0+cu124
--extra-index-url https://download.pytorch.org/whl/cu124

После сохранения файла нужно перейти в корневую папку проекта и создать BAT-файл, внутри которого будет следующая команда:
Код: Скопировать в буфер обмена
Code:
python -m venv venv && call .\venv\Scripts\activate.bat && pip install -r requirements\requirements_gpu.txt

pause

После этого необходимо запустить BAT-файл и дождаться полной установки. Остальные библиотеки можно установить обычным способом. Теперь можно продолжить написание кода. Первое, что нужно сделать, — это создать переменные, в которых будут храниться размеры экрана и размеры FOV (зона, в которой будут делаться скриншоты и детектить объекты).
Python: Скопировать в буфер обмена
Code:
screen_width = 1366
screen_height = 768
fov_width = 500
fov_height = 500

Далее нужно инициализировать CUDA.
Python: Скопировать в буфер обмена
device = 'cuda' if torch.cuda.is_available() else 'cpu'
P.S. device = 'cuda' — если найдена библиотека torch, то функция is_available() проверяет наличие CUDA. Если CUDA доступна, функция возвращает значение больше нуля, и device устанавливается как 'cuda'. Если CUDA недоступна, функция возвращает False, и device устанавливается как 'cpu'.

Теперь нужно открыть модель обучения, используя устройство, которое было определено, то есть CPU или GPU.
Python: Скопировать в буфер обмена
Code:
model = YOLO(os.path.abspath("models\\best.pt"))
model.to(device)

Далее нужно вычислить центр экрана и левый верхний угол FOV, чтобы центрировать его.

Для вычисления центра экрана по координате X берётся ширина экрана, указанная в переменной screen_width, и делится на 2. Полученное значение будет центром по ширине. Например, если ширина экрана 1366 пикселей, то деление на 2 даёт 683 пикселя, и это будет центр по ширине.
Python: Скопировать в буфер обмена
center_x = screen_width // 2

Аналогично ширине, для вычисления центра экрана по координате Y берётся значение из переменной screen_height и делится на 2. Полученное значение будет центром по высоте экрана.
Python: Скопировать в буфер обмена
center_y = screen_height // 2

Теперь переходим к вычислению левого верхнего угла FOV. Ширина прямоугольника FOV (x) делится на 2, затем из центра ширины экрана вычитается разделённая ширина прямоугольника. В итоге получается, что половина прямоугольника находится левее центра экрана.
Python: Скопировать в буфер обмена
fov_x = center_x - fov_width // 2

Также по аналогии нужно делать и с высотой FOV. Высота прямоугольника FOV делится на 2, затем из центра высоты экрана вычитается разделённая высота прямоугольника.
Python: Скопировать в буфер обмена
fov_y = center_y - fov_height // 2
В итоге получается, что половина прямоугольника находится выше центра экрана. Ниже представлен график для большей ясности (надеюсь, он понятен):
1724034714663.png



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

Функция скриншота:​

Для этого первым делом нужно создать функцию screenshot. Внутри функции необходимо инициализировать объект камеры из библиотеки. Для скриншотов в данном случае будет использоваться библиотека dxcam.
P.S. Данная библиотека показала себя намного лучше, чем популярная mss, и скорость создания скриншота была в 2 раза выше.
Python: Скопировать в буфер обмена
Code:
import dxcam
def screenshot():
   camera = dxcam.create()

Затем нужно создать цикл while True и в нем создать переменную, которая будет захватывать скриншот, начиная с точки (x, y) FOV.
Python: Скопировать в буфер обмена
frame = camera.grab(region=(fov_x, fov_y, fov_x + fov_width, fov_y + fov_height))

Также нужно добавить проверку, чтобы если скриншот пуст, цикл начинал выполнение заново.
Python: Скопировать в буфер обмена
Code:
if frame is None:
   continue

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

С функцией создания скриншота закончено. Теперь нужно создать функцию для обработки скриншота и определения на нем объектов.

Функция обработки и детекции объектов на скриншотах:​

Функция будет называться detection. Внутри созданной функции первым делом нужно инициализировать объект с информацией о первом обработанном скриншоте.
Python: Скопировать в буфер обмена
results = model.predict(frame, verbose=True)[0]
model.predict — это функция, которая возвращает список с найденными объектами на обработанных скриншотах. verbose=True — это флаг, который означает, что будет выводиться информация о результате обнаружения. [0] означает, что будет извлечен объект с информацией о первом скриншоте. В данном коде в любом случае будет только один объект, так как в обработку всегда идет только один скриншот, но все же это нужно уточнять.

Далее нужно написать условие if, в котором будет проверяться, чтобы в объекте скриншота были объекты игроков.
Python: Скопировать в буфер обмена
if len(results.boxes) > 0:
Работа продолжается, если в объекте boxes (который находится внутри объекта первого скриншота) есть хотя бы один объект с информацией о найденных объектах игрока..

Далее, если объекты игроков найдены, необходимо инициализировать первый объект игрока.
Python: Скопировать в буфер обмена
box = results.boxes[0]

Затем нужно инициализировать координаты первого найденного игрока.
Python: Скопировать в буфер обмена
box_cord = box.xyxy[0].tolist()

Далее нужно вычислить центр объекта игрока по координатам x и y.
Python: Скопировать в буфер обмена
Code:
center_x_obj = (box_cord[0] + box_cord[2]) / 2
center_y_obj = (box_cord[1] + box_cord[3]) / 2

Теперь нужно вычислить центр объекта по координатам x и y относительно экрана.
Python: Скопировать в буфер обмена
Code:
center_x_obj += fov_x
center_y_obj += fov_y
Знак += означает, что переменной присваивается её текущее значение, к которому прибавляется второе значение. Это сокращает запись кода и делает его более читаемым.
Пример:
Код: Скопировать в буфер обмена
Code:
center_x_obj += fov_x эквивалентно center_x_obj = center_x_obj + fov_x.
center_y_obj += fov_y эквивалентно center_y_obj = center_y_obj + fov_y.

Далее можно добавить настройку высоты по y, чтобы была возможность стрелять не в центр объекта, а в голову, если есть желание, но это не обязательно.
Python: Скопировать в буфер обмена
center_y_obj -= (box_cord[3] - box_cord[1]) * aim_target
В выражении box_cord[3] - box_cord[1] вычисляется высота объекта по y, после чего это значение умножается на aim_target.

Теперь необходимо вычислить расстояние объекта от центра экрана.
Python: Скопировать в буфер обмена
Code:
dx = center_x_obj - center_x
dy = center_y_obj - center_y

После этого нужно вызвать функцию наводки (которой пока что нет) и передать в нее переменные dx и dy.
Python: Скопировать в буфер обмена
aim(dx, dy)

Функция наводки на найденный объект:​

Теперь можно приступить к написанию функции наводки на найденный объект игрока. Для этого нужно создать функцию с названием aim, принимающую переменные dx и dy, и внутри этой функции добавить проверку if на нажатие левой кнопки мыши. Для работы с мышью будет использоваться библиотека win32api.
Python: Скопировать в буфер обмена
Code:
import win32api
import win32con
def aim(dx, dy):
   if 0 > win32api.GetKeyState(win32con.VK_LBUTTON):

Внутри проверки if нужно разделить расстояния до объекта на несколько равных частей, указанных в переменной aim_step (которую тоже нужно создать).
Python: Скопировать в буфер обмена
Code:
aim_step = 2
aim_step_x = dx / aim_step
aim_step_y = dy / aim_step

Далее нужно создать цикл, который будет повторяться столько раз, сколько указано в переменной aim_step. Внутри цикла нужно перемещать мышь, используя библиотеку win32api, на координаты, указанные в aim_step_x и aim_step_y.
Python: Скопировать в буфер обмена
Code:
for i in range(aim_step):
   win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(aim_step_x), int(aim_step_y), 0, 0)
time.sleep(aim_time_sleep)
Таким образом, получается, что путь до объекта разделен, например, на 5 частей, и мышь будет передвигаться 5 раз на разделенное число. Таким образом, мышь доходит до объекта не за одно действие, а в несколько шагов, и так как после каждого шага указана пауза, мышь перемещается более плавно, а не телепортируется в конечную точку.

P.S. Для time.sleep нужно импортировать библиотеку time.

На этом вся нейронная сеть готова. Осталось лишь задать запуск функции создания скриншота при запуске программы.
Python: Скопировать в буфер обмена
Code:
if __name__ == "__main__":
   screenshot()

Весь код нейронной сети:​

Python: Скопировать в буфер обмена
Code:
import os
import time
import win32api
import torch
import win32con
from ultralytics import YOLO
import dxcam


screen_width = 1366
screen_height = 768
fov_width = 500
fov_height = 500
aim_step = 2
aim_time_sleep = 0.017
aim_target = 0.25


device = 'cuda' if torch.cuda.is_available() else 'cpu'
if torch.cuda.is_available():
   print("Используется CUDA")
else:
   print("Используется CPU")


model = YOLO(os.path.abspath("models\\best.pt"))
model.to(device)

center_x = screen_width // 2
center_y = screen_height // 2

fov_x = center_x - fov_width // 2
fov_y = center_y - fov_height // 2


def screenshot():
   camera = dxcam.create()


   while True:
       frame = camera.grab(region=(fov_x, fov_y, fov_x + fov_width, fov_y + fov_height))
       if frame is None:
           continue
       detection(frame)


def aim(dx, dy):
   if 0 > win32api.GetKeyState(win32con.VK_LBUTTON):
       aim_step_x = dx / aim_step
       aim_step_y = dy / aim_step

       for i in range(aim_step):
           win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(aim_step_x), int(aim_step_y), 0, 0)
           time.sleep(aim_time_sleep)


def detection(frame):
   results = model.predict(frame, verbose=True)[0]

   if len(results.boxes) > 0:
       box = results.boxes[0]
       box_cord = box.xyxy[0].tolist()
       center_x_obj = (box_cord[0] + box_cord[2]) / 2
       center_y_obj = (box_cord[1] + box_cord[3]) / 2
       center_x_obj += fov_x
       center_y_obj += fov_y
       center_y_obj -= (box_cord[3] - box_cord[1]) * aim_target
       dx = center_x_obj - center_x
       dy = center_y_obj - center_y
       aim(dx, dy)


if __name__ == "__main__":
   screenshot()

Статья в виде документа: https://docs.google.com/document/d/1ny3V6XrqH89oT2YxsPbTk_scHVG0Yqv9PRrklAyFSjk/edit?usp=sharing

Вывод:​

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

Сделано OverlordGameDev специально для форума XSS.IS
 
Я так понимаю это читы нового поколения. Можешь записать видео? Запусти нейронку на видосе и покажи как это работает в игре на практике. И насколько лучше нейросеть справляется обычного аима тоже интересно.
 
Вот что непонятнно:
Геймдев разработчик, занятый разработкой игр, находит как-то ресурс xss, регистрируется на нем и пишет статью.
Зачем это все геймдев разрабу?
Почему он вот этим занялся вместо написать еще одну игру и получать за нее еще больше денег.
Или я че-то не понимаю про геймдев или это все очень странно.
 
Top