Моя новая модель, Ground Breaker из игры Helldivers 2
Представляю к вашему вниманию Первопроходец вооруженный огнеметом, на полях сражения с термидами.
Представляю к вашему вниманию Первопроходец вооруженный огнеметом, на полях сражения с термидами.
Какой вам нравится больше?
Мы постарались сделать каждый город, с которого начинается еженедельный заед в нашей новой игре, по-настоящему уникальным. Оценить можно на странице совместной игры Torero и Пикабу.
Реклама АО «Кордиант», ИНН 7601001509
Красный
Желтый
Хочу сделать футаж и анимацию в стиле ретровейва в блендере 2.76-2.79. Просто у блендера 2.76-2.79 более низкие требования по сравнению с более новыми версиями.
Ноут HP Pavilion Gaming Laptop 15: Windows 10 Home SL 64bit 22h2, Intel Core i5-8300H 2.3 GHz, Intel UHD Graphics 630, NVIDIA GeForce 1060 MaxQ 3 ГБ, 12ГБ ОЗУ.
Покупать ПК или добавлять диск ssd и озу в ноут не вариант, так как это дорого, а размеры стола не позволяют поставить пк (он по габаритам будет более громоздкий, чем ноут)
Это продолжение поста Первый легендарный мобильный GPU: каким был PowerVR MBX Lite? Пишем игру-демку про «жигули» с нуля, ч.1
Дисклеймер: игра была написана как простенькая, но познавательная демка именно для PowerVR MBX и именно для Axim X51v. Именно поэтому здесь нет нормального Update-таймера, расчёта дельты времени, а игра прибита к константным временным отрезкам и величинам скорости!
Итак, как же игры подобного планы работают «под капотом»? По факту, обычно мы с вами никуда не едем: фоновые модели ландшафта и дороги просто скроллятся и телепортируются друг за другом, когда одна из частей уходят за экран, что создаёт эффект бесконечной дороги. И эта техника используется во многих играх! Что же касается машинок, от которых мы должны лавировать, то это не мы едем на них, это они едут на нас! По итогу создаётся эффект будто мы с вами куда-то едем и уворачиваемся от машинок, хотя на деле это не так!
Начинаем с реализации базовой вещи в архитектуре любой современной игры, а именно системы игровых объектов. В нашей игре нет необходимости в реализации сложного графа сцены с комплексной компонентной системой, или, например, ECS. Хватит классического линейного списка игровых объектов (который использовался, например, в Half-Life), по которому объект World проходится каждый кадр, вызывая необходимые функции для обновления состояния объекта и его отрисовки:
public abstract class Entity
{
public Transform Transform;
public abstract void Update();
public abstract void Draw();
}
public void Spawn(Entity ent)
{
if(ent != null)
Entities.Add(ent);
}
public void Remove(Entity ent)
{
entityRemovalList.Add(ent);
}
public void Update()
{
sky.Update();
renderer.Update();
spawner.Update();
foreach (Entity ent in Entities)
ent.Update();
foreach (Entity ent in entityRemovalList)
Entities.Remove(ent);
entityRemovalList.Clear();
}
public void Draw()
{
sky.Draw();
renderer.Draw();
foreach (Entity ent in Entities)
ent.Draw();
}
Самым первым нашим объектом будет машинка игрока, которой можно будет управлять!
Модельки я взял лоуполи со скетчфаба, вот ссылка на ВАЗ 21099 и VW Golf Mk2. Спасибо авторам моделей за их работу!
Наследуемся от Entity и реализуем абстрактные методы с логикой объекта. Здесь мы получаем состояние аппаратных кнопок влево и вправо, в зависимости от них вычисляем направление поворота машинки и, собственно, поворачиваем машинку путём сложения с координатой X вычисленного направления, помноженного на «скорость» поворота машинки. Для лучшего визуального эффекта, мы также плавно поворачиваем машинку эффектом а-ля EaseIn/EaseOut:
float hVel = Engine.Current.Input.GetKeyState(GamepadKey.Left) ? -1 : (Engine.Current.Input.GetKeyState(GamepadKey.Right) ? 1 : 0);
Transform.Position.X += hVel * SteerSpeed;
Transform.Rotation.Y = MathUtils.lerp(Transform.Rotation.Y, 180 + (hVel * 35), 0.1f);
Теперь нам нужно, чтобы машинка где-то «ездила». Для этого мы моделируем в блендере примитивный кусок дороги с элементами ландшафта:
А затем реализуем примитивный рендерер фона, который будет скроллить два одинаковых seamless-куска уровня и как я уже говорил ранее, просто телепортировать их друг за другом, создавая эффект бесконечности.
public SectorRenderer()
{
road = Model.FromFile("road.mdl");
roadMaterial.Diffuse = Texture2D.FromFile("road.tex");
terrain = Model.FromFile("terrain.mdl");
terrainMaterial.Diffuse = Texture2D.FromFile("grass.tex");
sector1.Position.Y = -4;
sector2.Position.Y = -4;
sector2.Position.Z = SectorSize;
}
public void Update()
{
sector1.Position.Z -= ScrollingSpeed;
sector2.Position.Z -= ScrollingSpeed;
if (sector1.Position.Z + SectorSize < 0)
sector1.Position.Z = SectorSize;
if (sector2.Position.Z + SectorSize < 0)
sector2.Position.Z = SectorSize;
}
public void Draw()
{
Engine.Current.Graphics.DrawModel(road, sector1, roadMaterial);
Engine.Current.Graphics.DrawModel(terrain, sector1, terrainMaterial);
Engine.Current.Graphics.DrawModel(road, sector2, roadMaterial);
Engine.Current.Graphics.DrawModel(terrain, sector2, terrainMaterial);
}
Где terrain.mdl — окружающий ландшафт, а road.mdl — собственно, сам меш дороги. Получаем вот такой эффект:
Артефакты на видео — следствие проблем с точностью float у MBX Lite в процессе клиппинга геометрии при ближней плоскости отсечения в 0.1f. Меняем на 1.0f и всё снова работает нормально :)
Чуть изменяем проекцию, переместив камеру выше и наклонив на 45 градусов и игра уже похожа на Traffic Racer!
Переходим к реализации машин трафика. Модельки их машин будут загружаться при старте игры:
private static void LoadTrafficModel(int idx, string name)
{
PreloadedCars[idx] = Model.FromFile(name + ".mdl");
PreloadedMaterials[idx].Diffuse = Texture2D.FromFile(name + ".tex");
}
public static void Preload()
{
PreloadedCars = new Model[1];
PreloadedMaterials = new Material[1];
LoadTrafficModel(0, "traffic1");
}
А сама их логика предельно проста. При спавне, машинка выбирает себе полосу, по которой будет ехать и рандомный множитель скорости, который вносит разнообразие в игру:
Rand rand = new Random();
Transform.Position.X = Game.Current.world.PickLane(rand .Next(0, 4));
Transform.Position.Y = Game.Current.world.Player.Transform.Position.Y;
Transform.Position.Z = rand .Next(ZOffset, ZOffsetMax);
selectedBias = rand.Next(0, SpeedBias.Length - 1);
int carModel = rand .Next(0, PreloadedCars.Length - 1);
model = PreloadedCars[carModel];
material = PreloadedMaterials[carModel];
А при обновлении, машинка просто продолжает ехать вниз! Логика простая до жути, даже без перестроений.
Transform.Position.Z -= BaseSpeed * SpeedBias[selectedBias];
Переходим к обработке столкновений. Помним, что мы на этапе конвертации моделей посчитали Axis Aligned Bounding Box для каждой модели? В качестве алгоритма мы будем использовать классический AABB — или Rect vs rect:
public bool Intersects(BoundingBox box)
{
return (X < box.X + box.X2 && Y < box.Y + box.Y2 && Z < box.Z + box.Z2 && box.X < X + X2 && box.Y < Y + Y2 && box.Z < Z + Z2);
}
Теперь для проверки столкновения между ними, нам надо посчитать абсолютный Bounding Box для каждого игрового объекта:
Bounds = model.Bounds;
Bounds.X += Transform.Position.X;
Bounds.Y += Transform.Position.Y;
Bounds.Z += Transform.Position.Z;
Затем итерируемся по списку всех игровых объектов в сцене, и если у нас есть машинка трафика, то проверяем на столкновение с машинкой игрока. Если столкнулись, то помечаем машинку игрока как разбитую и предлагаем игроку рестартнуть игру.
foreach (Entity ent in Game.Current.World.Entities)
{
if (ent is TrafficCar)
{
if (Player.Bounds.Intersects(((TrafficCar)ent).Bounds))
{
// TODO: Damage logic
Player.IsDestroyed = true;
}
}
}
Уже что-то немного похожее на игру. Добавим конечное препятствие — необходимость рестарта при столкновении с другой машинкой и для демки пока-что хватит.
public void Draw()
{
string scoreFmt = string.Format("Score: {0} x{1}", Game.Current.world.Statistics.Score, 1);
Engine.Current.Graphics.DrawString(scoreFmt, 15, 15, StatsColor);
if (Game.Current.world.Player.IsDestroyed)
{
int measure = Engine.Current.Graphics.MeasureString(RestartString);
Engine.Current.Graphics.DrawString("Press Return to restart", Engine.Current.Graphics.ViewWidth / 2 - (measure / 2), Engine.Current.Graphics.ViewHeight / 2, StatsColor);
}
}
Вот что у нас получилось:
Правда, что на МКАДе каждый вечер такое? Я просто не с МСК :)
Вот такой у нас получился материал про PowerVR MBX! С выходом iPhone, этот GPU дал толчок для появления красивых мобильных игр с уровнем графики, близким к полноценным домашним консолям… жаль, что золотая эра интересных, самодостаточных и бездонатных мобильных игр и закончилась во времена iPhone 5 :(
В остальном же, надеюсь материал был достаточно интересен и познавателен для всех моих читателей, даже тех, кто никогда не программировал игры! Был у вас Dell Axim X51v? Пишите в комментариях!
Исходный код демки и бинарники можно найти на моём гитхабе.
Материал написан при поддержке TimeWeb Cloud. Подписывайтесь на меня и @Timeweb.Cloud , чтобы не пропускать новые статьи каждую неделю! А ещё у меня есть своя телега, куда я публикую бэкстейдж статей и вовремя публикую ссылки на новый материал!
А ещё я собираю деньги на проект с уже настоящим, физическим ТАЗом и его электронным дооснащением бортовым компьютером "по самому дешману" своими руками! Уже собрано 50.000 рублей из планируемых 70.000 на машину, из них 45.000 моих личных сбережений и 5.000 рублей - помощь читателей, за что вам большое спасибо :)
Конкурс мемов объявляется открытым!
Выкручивайте остроумие на максимум и придумайте надпись для стикера из шаблонов ниже. Лучшие идеи войдут в стикерпак, а их авторы получат полугодовую подписку на сервис «Пакет».
Кто сделал и отправил мемас на конкурс — молодец! Результаты конкурса мы объявим уже 3 мая, поделимся лучшими шутками по мнению жюри и ссылкой на стикерпак в телеграме. Полные правила конкурса.
А пока предлагаем посмотреть видео, из которых мы сделали шаблоны для мемов. В главной роли Валентин Выгодный и «Пакет» от Х5 — сервис для выгодных покупок в «Пятёрочке» и «Перекрёстке».
Реклама ООО «Корпоративный центр ИКС 5», ИНН: 7728632689