Ursina Engine. Игры на Python. pinned a photo
Ursina Engine. Игры на Python.
@ursinaengine
Добро пожаловать в наш канал, посвященный Ursina Engine — мощному и простому в использовании игровому движку на Python!Курс на Stepik - https://stepik.org/course/250738!!!Поддержать канал !!!! https://www.donationalerts.com/r/ursinaenginepy
Похожие каналы
Все →Последние посты
Ещё у меня есть идея редактора уровней написать, уже есть кое какие наброски.Я когда-то давно публиковал пост на эту тему с примером, но сейчас я уже придумал интерфейс и удобное управление.Как только доработаю, обязательно поделюсь 😊
А вот это уже хорошо 😁
Мини карту тоже можно так будет сделать👍
Нашел способ сделать «зеркало», пока есть нюансы, но я добью до приемлемого результата и всё покажу😉

Поддержите моё творчество ✨ Нравится канал? Поддержите меня, и я смогу создать ещё больше интересного контента. Ваш вклад — это моя мотивация!

Делюсь скринами из примера новой игры😉На данный момент есть карта маленького городка и персонаж игрока, добавленного с помощью Actor. Есть пока что только одна механика стрельбы. Управление реализовано через FSM(Finite State Machine)Делюсь скринами из примера новой игры😉На данный момент есть карта маленького городка и персонаж игрока, добавленного с помощью Actor. Есть пока что только одна механика стрельбы. Управление реализовано через FSM(Finite State Machine)
🤖Пример применения AIBehaviorsЧтобы использовать поведенческие алгоритмы, необходимо сделать два обязательных импорта, кроме основного:from ursina import *from panda3d.ai import AIWorldfrom panda3d.ai import AICharacter app = Ursina()Теперь добавим в сцену поверхность и два куба, которые будут в качестве персонажей:plane = Entity(model="plane", scale=100, texture="brick", collider="box")plane.texture_scale = (10, 10)# Игрокplayer = Entity(model="cube", color=color.azure, position=(0, 0, 0), collider='box')# Врагenemy = Entity(model="cube", color=color.red, position=(15, 0, 15), collider='box')Создаём AIWorld:ai_world = AIWorld(app.render)render — это корневой узел всей 3D-сцены Создаём AICharacterenemy_char = AICharacter("red_cube", enemy, 50, 1, 3)Остановимся подробнее:AICharacter(name, entity, mass, movement_force, max_force)name - задаём имя, можно любоеentity - передаём объект, который будет ИИ-персонажемИзменяя следующие параметры, мы можем тонко отрегулировать динамику и характер движения:mass — массаmovement_force — сила движенияmax_force — ограничение ускорения Добавляем нашего персонажа в AIWorldai_world.addAiChar(enemy_char)Получаем алгоритмы поведения для персонажаenemy_ai = enemy_char.getAiBehaviors()Задаем поведение:enemy_ai.pursue(player) В функции update обязательно вызываем обновление AIWorlddef update(): ai_world.update() EditorCamera()app.run()В этом примере враг будет постоянно преследовать игрока👾
🎮 AIBehaviors в Ursina EngineРазберёмся, что это такое и как это реально работает 👇--——————————————————————————————-🧠 Что такое AIBehaviors?Это система “поведенческого ИИ” .Не путай с полноценным AI:🔻 Это НЕ:— сложная логика— не поиск пути👉 Это:— набор сил, которые “толкают” объект в нужную сторону--————————————————————————————————-⚙️ Основные понятия🔹 AICharacterОбёртка над объектом, которая даёт ему “физику поведения”(масса, ускорение, сила)🔹 AIWorldКонтейнер, где живут все AI-объекты и препятствия👉 именно здесь обновляется весь AI🔹 AIBehaviorsНабор действий, которые можно включать:— движение— избегание— блуждание--——————————————————————————————————-🚶 Базовые алгоритмы поведения🔸 seek (преследование)Двигается прямо к цели👉 самый простой вариант врага——🔸 pursue (умное преследование)Предсказывает, куда движется цель👉 лучше работает против игрока——🔸 wander (блуждание)Случайное движение👉 патрули, “живой” мир——🚧 obstacleAvoidance (обход препятствий)Звучит мощно, но:👉 AI НЕ строит маршрут👉 AI просто “отталкивается” от объектов📌 Как работает:🔻есть направление к цели🔻 если впереди препятствие → добавляется сила в сторону--———————————————————————————————————————AIBehaviors — это:👉 не интеллект, а математика движенияПоэтому:— может застревать— может дёргаться— не умеет обходить сложные уровни---🔥 Когда использовать✅ Быстрое прототипирование✅ Простые враги---🚀 Как делают нормальный AI в UrsinaВместо AIBehaviors:• FSM (машина состояний)• raycast (зрение)• векторная логика движения👉 Это даёт полный контроль
Друзья, всех приветствую 👋 В связи с нестабильной работой телеграм, вести полноценно канал пока не удается. Но по мере возможности посты будут появляться, так же вы можете задавать вопросы в чате и предлагать темы для новых постов.Работа над курсом также продолжается. Новые темы курса: actor, машина состояний, и совсем скоро появиться AIBehavior и урок по сборке в exe.Спасибо ,что остаетесь здесь и спасибо за поддержку!!😉
✅И обязательно нужно применить lit_with_shadows_shader!!! Без этого шейдера теней не будет вообще!!!🔻Чтобы применить его сразу ко всем объектам в начале кода импортируем шейдер и прописываем:Entity.default_shader = lit_with_shadows_shader🔻Теперь тени будут у всех объектов, если не переопределить шейдер для какого либо объекта(ов) явно.
✅Правильный код для теней от DirectionalLight.light = DirectionalLight(color=color.dark_gray, shadows=True, position = (0,10, -50))light.look_at(Vec3(1,-1, 2))shadow_bounds_box = Entity(model='wireframe_cube', scale=1000, visible=0)light.update_bounds(shadow_bounds_box)🔻Разберём этот кусок по слоям1️⃣ Создание направленного светаlight = DirectionalLight( color=color.dark_gray, shadows=True, position=(0, 10, -50))✅Что такое DirectionalLightЭто солнце 🌞➖Свет идёт параллельными лучами➖Не имеет «источника» как лампаИдеален для:➖солнца➖луны➖общего освещения сценыПараметры:color=color.dark_gray тёмно-серый Не слепит сцену, даёт мягкие тениshadows=True включено Обязательно, иначе теней не будетposition=(0,10,-50) точка в пространстве Используется для расчёта теней⚠️ Для DirectionalLight позиция не влияет на яркость,но очень важна для карты теней.2️⃣ Направление светаlight.look_at(Vec3(1, -1, 2))Что происходит➖Свет смотрит в точку (1, -1, 2)➖Лучи идут ОТ света → К этой точке❗️Почему это важно➖Тени считаются в направлении света❗️Если look_at неправильный:➖тени исчезают➖тени обрезаются➖появляются артефакты3️⃣ Ограничительная рамка для тенейshadow_bounds_box = Entity( model='wireframe_cube', scale=1000, visible=0)🔥 Это ключевой момент, который многие не понимают.❓Зачем нужен ограничитель тенейDirectionalLight НЕ ЗНАЕТ,где в мире нужно считать тени.👉 Ему нужно сказать:«Вот область мира, где считаем тени»❓Что это за объект➖wireframe_cube — просто форма➖scale=1000 — огромная коробка(размер выбирайте сами)➖visible=0 — не отображается📦 Это воображаемая коробка, внутри которой:➖объекты отбрасывают тени➖объекты получают тени4️⃣ Привязка границ тенейlight.update_bounds(shadow_bounds_box)❓Что делает эта строка➖Берёт ограничительная рамку объекта➖Использует её как усеченную теневую камеру(👉 объём пространства, который “видит” свет,и только внутри этого объёма
Подробную инфу по работе с Actor и подготовке моделей вы найдёте тут: https://stepik.org/course/250738
🔁 FSM (Finite State Machine)🔄✅FSM — это конечный автомат состояний.Объект всегда находится в одном состоянии, может переходить в другие, и у каждого состояния есть логика входа, обновления и выхода.FSM реализован классом :from direct.fsm.FSM import FSM🧠 Зачем нужен FSMFSM идеально подходит для:🎮 Игры➖Состояния игрока: Idle, Walk, Run, Jump, Attack➖Анимации персонажей➖Управление оружием➖Камеры (follow / free / cinematic)🧩 Логика➖Меню (MainMenu → Settings → Game)➖Режимы игры🧩 Базовая концепция FSMКаждое состояние — это набор методов с именами по шаблону:🔻enterState() — Вызывается при переходе в состояние🔻exitState() — Вызывается при выходе из текущего состояния🔻filterState(request, args) — Разрешить / запретить переход❗️State — имя состояния🎮 Управление переходамиПереходы между состояниями запрашиваются с помощью метода:fsm.request('State')🏗 Минимальный пример использования FSM + Actor# =========================# ИМПОРТЫ# =========================from ursina import * # основной движок Ursinafrom direct.actor.Actor import Actor # Actor из Panda3D (анимации)from direct.fsm.FSM import FSM # FSM (машина состояний)from ursina.shaders import lit_with_shadows_shader# =========================# ИНИЦИАЛИЗАЦИЯ ПРИЛОЖЕНИЯ# =========================app = Ursina()# Делаем шейдер с освещением и тенями шейдером по умолчаниюEntity.default_shader = lit_with_shadows_shader# Источник направленного света (как солнце)DirectionalLight(color=color.dark_gray, shadows=True)# =========================# СЦЕНА / ЗЕМЛЯ# =========================plane = Entity( model="plane", scale=100, texture="brick", collider="box")plane.texture_scale = (10, 10)# =========================# РОДИТЕЛЬ ДЛЯ ИГРОКА И КАМЕРЫ# =========================# Это позволяет вращать персонажа и камеру вместеparent_entity = Entity()camera.parent = parent_entitycamera.position = (0, 2.5, 4.5)camera.rotation_y = 180# =======
# =========================# ФЛАГИ СОСТОЯНИЯ# =========================is_moving = False # игрок движетсяis_running = False # игрок бежитis_aiming = False # прицеливание (пока не используется)has_gun = False # оружие в руках# =================================================# FSM — МАШИНА СОСТОЯНИЙ ИГРОКА# =================================================class PlayerFSM(FSM): def __init__(self, player): super().__init__("PlayerFSM") self.player = player # ------------------------- # IDLE # ------------------------- def enterIdle(self): # метод enter позволяет добавить логику при входе в какое либо состояния self.player.loop("idle") # ------------------------- # WALK # ------------------------- def enterWalk(self): self.player.loop("walk") def exitWalk(self): pass # метод exit позволяет добавить логику при выходе из какого либо состояния # ------------------------- # RUN # ------------------------- def enterRun(self): self.player.loop("run")# Создаём FSMfsm = PlayerFSM(player)# Стартовое состояниеfsm.request("Idle")# =========================# UPDATE — КАЖДЫЙ КАДР# =========================def update(): global is_running # Вращение персонажа мышью parent_entity.rotation_y += mouse.velocity[0] * mouse_sensitivity[1] # Скорость зависит от бега speed = 3 if is_running else 1 # Движение вперёд if is_moving: parent_entity.position += parent_entity.back * speed * time.dt # Фиксированный наклон камеры camera.rotation_x = 10# =========================# INPUT — СОБЫТИЯ КЛАВИАТУРЫ# =========================def input(key): global is_moving, is_running, is_aiming, has_gun if key == "escape": application.quit() # Начало движения if key == "w": is_moving = True fsm.request("Walk") # Остановка if key == "w up" and not has_gun: is_moving = False fsm.request("Idle") # Начало бега