Вторник, 19 Марта 2024, 11:53

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 2
  • 1
  • 2
  • »
Форум игроделов » Движки для разработки игр и сложные системы разработки » Unity » Actors (Мой framework для Unity3d на котором я пишу игры.)
Actors
pixeyeДата: Воскресенье, 27 Мая 2018, 23:34 | Сообщение # 1
Red Winter Software
Сейчас нет на сайте








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

Документация

Пример кода:

Код
  public class ActorPlayer : Actor, ITick
{

    [FoldoutGroup("Setup")] public DataMove dataMove;
  
          protected override void Setup()
  {
   Add(dataMove);
   Add<BehaviorInput>();
  }

}


Код

    [System.Serializable]

    public class DataMove : IData
    {
  public float x;
  public float y;

  public void Dispose()
  {
  }
    }


Код
   
          public class BehaviorInput : Behavior, ITick
    {

  [Bind] private DataMove dataMove;

  public override void OnTick()
  {
   dataMove.x = Input.GetAxis("Horizontal");
   dataMove.y = Input.GetAxis("Vertical");
  }
    }


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Понедельник, 27 Мая 2019, 19:51
pixeyeДата: Вторник, 29 Мая 2018, 09:15 | Сообщение # 2
Red Winter Software
Сейчас нет на сайте
Добавил описания для processing скриптов.
Processings - это аналог "менеджеров" , "контроллеров"

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

Код
public class MyCustomClass : ITick{

public MyCustomClass(){
   ProcessingUpdate.Default.Add(this);
}

public void Tick(){
    
}

}


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Вторник, 29 Мая 2018, 09:26
pixeyeДата: Среда, 30 Мая 2018, 11:54 | Сообщение # 3
Red Winter Software
Сейчас нет на сайте
Добавил описание для паттерна пулов объектов и описание таймеров.

Таймеры - это отложенные действия : )
Код
Timer.Add(0.1f, actor.HandleDestroyGO);

Можно сохранить таймер и использовать его потом.
Код
  private Timer timerBlink;
timerBlink = new Timer(BlinkFinish, 0.15f);
void Blink(){
timerBlink.Restart();
}

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

Timer.Add(0.1f, actor.HandleDestroyGO).AddID(actor);
var timers = Timer.FindAllTimers(actor);
  
    if (timers != null)
  for (var i = 0; i < timers.Count; i++)
     {
  timers[i].timeScale = 0.5f;
     }


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Среда, 30 Мая 2018, 12:01
pixeyeДата: Четверг, 31 Мая 2018, 10:28 | Сообщение # 4
Red Winter Software
Сейчас нет на сайте
Добавил описание для cхем ( Blueprints )

Схемы - это scriptable object для удобного хранения информации у одинаковых объектов.

Пример вытаскивания даты из схемы
Код
var weaponData = Get<DataBlueprint>().Get<DataWeapon>();


Настройка самой схемы идентична настройке акторов

Код
[CreateAssetMenu(fileName = "BlueprintCreature", menuName = "Blueprints/BlueprintCreature")]
   public class BlueprintCreature : Blueprint
   {
    [FoldoutGroup("Setup")]
    public DataCreature dataCreature;
    
    [FoldoutGroup("Setup")]
    public DataDeathAnimations dataDeathAnimations;

    public override void Setup()
    {
     Add(dataCreature);
     Add(dataDeathAnimations);
    }
   }


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю

pixeyeДата: Пятница, 01 Июня 2018, 11:23 | Сообщение # 5
Red Winter Software
Сейчас нет на сайте
Добавил описание для ECS. ECS у меня простенький и только для Actor классов. Я его не использую повсеместно. В будущем улучшу.
Вкратце позволяет фильтровать сущности в группы по определенным критериям ( по наличию компонентов или тэгу ) и обрабатывать их в апдейте.

Код

public class ProcessingCameraFollow : ProcessingBase, ITick, IMustBeWipedOut{
[GroupBy(Tag.GroupPlayer)]
[GroupExclude(Tag.StateDead)]
private Group groupPlayers;

   public ProcessingCameraFollow()
   {
      groupPlayers.OnGroupChanged += OnGroupPlayersChanged;
   }

   void OnGroupPlayersChanged()
    {  
      for(var i=0;i<groupPlayers.length;i++){
         Debug.Log("Actor: " + groupPlayers.actors[i]);
      }
    }
    
   public void Tick()
   {
         for(var i=0;i<groupPlayers.length;i++){
         DoSomething(groupPlayers.actors[i]);
            }
    }
    
    void DoSomething(Actor a){
    }
    
}


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Пятница, 01 Июня 2018, 12:23
pixeyeДата: Суббота, 02 Июня 2018, 10:31 | Сообщение # 6
Red Winter Software
Сейчас нет на сайте
Добавил описание для тэгов. Тэги это клей игры в моем понимании. Я их использую для валидации или как идентификаторы.

Код

// Оглушен от удара молотом
tags.Add(Tag.Stunned);
// Оглушен от падения с дерева
tags.Add(Tag.Stunned);
// Эффект оглушения от удара молотом закончился
tags.Remove(Tag.Stunned);
// проверка на оглушение. Герой до сих пор оглушен потому что вышел только один тэг оглушения.
bool condition_stunned = tags.Contain(Tag.Stunned);


Тэги можно удобно редактировать через инспектор :
- Добавляем класс тэгов с атрибутом TagField
Код
public static partial class Tag
    {
  [TagField(categoryName = "Weapons")] public const int WeaponGun = 9000;
  [TagField(categoryName = "Weapons/BigGuns")] public const int WeaponLaser = 9001;
    }


- Добавляем переменную где нам надо типа int с атрибутом [TagFilter(typeof(ТИП_КЛАССА_ГДЕ_НАШИ_ТЭГИ))]

Код
[TagFilter(typeof(Tag))] public int tag;




Базовую документацию я закончил. Дальше улучшалки и технодемку к которой скоро приступлю


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Суббота, 02 Июня 2018, 10:32
pixeyeДата: Вторник, 19 Июня 2018, 23:18 | Сообщение # 7
Red Winter Software
Сейчас нет на сайте
В этом видео мы продолжим создавать свою игру на движке Unity3d, познакомимся с концепциями фреймворка на основе небольшой игры.
Игру и фремймворк можно скачать с гитхаба!
=======================================================
Скачать фреймворк
Скачать игру
Поиграть в игру
Вступайте в нашу Discord группу
========================================================



ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Вторник, 19 Июня 2018, 23:18
pixeyeДата: Понедельник, 25 Июня 2018, 07:17 | Сообщение # 8
Red Winter Software
Сейчас нет на сайте
Маленькое обновление.

теперь сцены добавляются при создании в билд лист автоматически. Генерируются объекты сцен которые можно выставлять в стартерах - так же добавлен enum Scenes так что теперь перемещаться между сценами можно через него - не прописывая текстом название сцены - этот энум так же автогенерируется



ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Понедельник, 25 Июня 2018, 07:20
pixeyeДата: Вторник, 11 Сентября 2018, 11:29 | Сообщение # 9
Red Winter Software
Сейчас нет на сайте
Огромное обновление c новым примером и фишками.
Добавлен полноценный ECS паттерн, можно делать как абстрактные сущности так и сущности с GO телом ( Actor )
Документация обновляется.

Пример кода обрабатывающий тени у сущностей
Код
public class ProcessingShadows : ProcessingBase, ITick
{
    [GroupExclude(Tag.StateInAir)] public Group<DataShadow> groupShadows;
    [GroupBy(Tag.StateInAir)] public Group<DataShadow, DataMove> groupShadowsInAir;

    public ProcessingShadows()
    {
        groupShadows.OnAdded += OnAddedShadow;
    }

    void OnAddedShadow(int index)
    {
        var data = groupShadows.component[index];

        foreach (var node in data.nodes)
        {
            node.tr_shadow = node.sr_shadow.transform;
            node.sr_shadow.color = new Color(0, 0, 0, 0.5f);
            node.tr_shadow.localEulerAngles = new Vector3(node.angle, 0, 0);
            node.tr_shadow.localPosition = new Vector3(0, node.posY, 0.2f);
            node.tr_shadow.localScale = new Vector3(1.1f, -1, 1);
        }
    }

    public void Tick()
    {
        for (int i = 0; i < groupShadows.length; i++)
        {
            var data = groupShadows.component[i];
            foreach (var node in data.nodes)
            {
                node.sr_shadow.sprite = node.sr_view.sprite;
            }
        }

        for (int i = 0; i < groupShadowsInAir.length; i++)
        {
            var data = groupShadowsInAir.component[i];
            var dataMove = groupShadowsInAir.component2[i];

            var y = groupShadowsInAir.GetActor(i).selfTransform.position.y;
            var depth = dataMove.jumpDepth - 0.3f;

            foreach (var node in data.nodes)
            {
                node.sr_shadow.sprite = node.sr_view.sprite;

                var posX = node.tr_shadow.position.x;
                node.tr_shadow.position = new Vector3(posX, depth, 0.2f);
                var magic = Math.Abs(0.7f - y - depth);
                var scale = new Vector3(0.5f, -0.5f, 0.5f);
                scale.x *= magic;
                scale.y *= magic;
                node.tr_shadow.localScale = scale;
            }
        }
    }
}


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю

pixeyeДата: Пятница, 19 Октября 2018, 09:32 | Сообщение # 10
Red Winter Software
Сейчас нет на сайте
Обновление 2018.10.17

- Атрибут [RequireTag] для компонентов
- Actor перестали принимать апдейты и сигналы ( в виду бесполезности при ECS подходе ) , если это все же вам нужно наследуете от MonoActor
- Добавлена структура EntityComposer позволяющая собирать или добавлять к имеющимся сущностям новые компоненты в безопасной манере ( инициализировать все компоненты и только потом запустить в системы )
- Чистка API, убирание лишнего функционала.
- Samples переименованы в Template
- IData интерфейс убран, остался IComponent, ВНИМАНИЕ, сохраните на всякий случай интерфейс IData, он наследовался от IComponent так что на худой конец обратно закинете после апдейта и аккуратно компоненты поправите.
- Официально DataComponent теперь просто Component, но можете использовать старые названия если нравится
- Рефакторинг
- Оптимизация
- Поправил работу с акторами при загрузке аддитивных сцен, теперь они должны подгружаться корректно.

Начал работу над wiki - ( установка фреймворка, философия, книга рецептов, основные штуки )


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю

pixeyeДата: Понедельник, 22 Октября 2018, 12:09 | Сообщение # 11
Red Winter Software
Сейчас нет на сайте
Добавил описание работы шаблонов

Шаблоны - это SO ( Scriptable Object ) отвечающие за настройки игровых систем, базовые параметры для компонентов и объектов. Шаблоны так же полезно использовать как скрипты благодаря возможности писать логику.


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Понедельник, 22 Октября 2018, 12:13
pixeyeДата: Суббота, 08 Декабря 2018, 14:41 | Сообщение # 12
Red Winter Software
Сейчас нет на сайте
Большое обновление фреймворка
О фреймворке, как его устанавливать и пользоваться можно почитать на русскоязычной вики

- Добавлена возможность вытаскивать из сущности одновременно несколько компонентов с проверками.
Код
  ComponentMotion cMotion;
            ComponentPlayer cPlayer;
            ComponentView   cView;
        
            if (entity.Get(out cMotion, out cPlayer, out cView))
            {
                
            }

- Добавлены методы Plus/Minus/Every - они расположены в Framework Extensions
Код

if ((cReload.timer = cReload.timer.Minus(delta)) == 0)
        cWeapon.clip = cWeapon.weapon.clipSize;


Так же выкладываю немного своего рабочего кода чтобы можно было посмотреть как оно на практике выглядит : )

Processing Motion
- движение/физика/падения
Processing Player - кусочек логики игрока


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Суббота, 08 Декабря 2018, 14:43
drcrackДата: Суббота, 08 Декабря 2018, 15:48 | Сообщение # 13
старожил
Сейчас нет на сайте
по тем кускам кода которые я видел на форуме сложилось впечатление что это просто архитектура ради архитектуры, один из видов перфекционизма
очень сомневаюсь что такое количество абстракций реально нужно в играх проще чем ммо

другими словами, неплохо бы увидеть какие-то примеры решения типовых задач, которые демонстрируют преимущества этого фреймворка в реальной работе


Сообщение отредактировал drcrack - Суббота, 08 Декабря 2018, 15:54
pixeyeДата: Суббота, 08 Декабря 2018, 16:15 | Сообщение # 14
Red Winter Software
Сейчас нет на сайте
Фреймворк рабочий и на нем можно писать игры. Свои я пишу на нем :) И знаю парочку ребят которые тоже используют его при разработке.
Решаю исключительно те задачи которые нужны мне в работе и делюсь наработками.

Приходится много учиться и порой принимать не лучшие решения. Они стоят мне времени и денег. Но я себе это могу позволить :)


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю

pixeyeДата: Суббота, 08 Декабря 2018, 16:51 | Сообщение # 15
Red Winter Software
Сейчас нет на сайте
Фреймворк - это просто реализация ECS для юнити.
Вот тебе ситуация. Пишут тебе игру программисты. Пишут на ооп, в лучшем случае архитектор игры принимает какие-то решения по коду чтобы все это как-то шло в русле. Но правила субъективны. Человек ушел и на его место пришел новый программер который скажет "фу бяка так нельзя" и в любом случае затратит кучу времени на то чтобы разбираться со всеми зависимостями по коду в игре и понять логику.

Я же предалагаю тебе конвеер кода :) Компоненты не зависят друг от друга и обрабатываются в рамках изолированных систем. Игра не сломается от того если какая то система будет отключена. Поддержка кода сильно упрощается. Легко добавлять новые фичи не ломая старые. Это полезно для игр любого масштаба.

Вот ситуация. Решил я в игру добавить мошен блюр. Типа скорость передать сочнее при падении героя.


А потом решил что хочу чтобы босс тоже двигался с блюром.

Я пишу обработчик блюра
Код

public class ProcessingMotionBlur : ProcessingBase, ITick
    {
  public Group<ComponentMotionBlur, ComponentView> groupMotion;

  public ProcessingMotionBlur()
  {
   groupMotion.Remove += entity =>
   {
    var cMotionBlur = entity.ComponentMotionBlur();

    var len = cMotionBlur.length;

    for (int i = 0; i < len; i++)
    {
     cMotionBlur.renderers[i].gameObject.Release();
    }

    cMotionBlur.length = 0;
    cMotionBlur.lengthEmpty = 0;
   };
  }

  public void Tick()
  {
   var delta  = Time.delta;
   var frames = UnityEngine.Time.frameCount;
   foreach (var entity in groupMotion)
   {
    var cMotionBlur = entity.ComponentMotionBlur();
    var cView = entity.ComponentView();

    for (int i = 0; i < cMotionBlur.length; i++)
    {
     var index = cMotionBlur.length - 1 - i;
     var rend  = cMotionBlur.renderers[i];
     var time  = cMotionBlur.fadings[index];
     var color = rend.color;
     if (color.a == 0) continue;
     color.a = color.a.Minus(delta / time);
     rend.color = color;
     if (color.a == 0)
     {
      cMotionBlur.lengthEmpty++;
     }
    }

    if ((cMotionBlur.time = cMotionBlur.time.Minus(delta)) == 0)
    {
     if (cMotionBlur.length <= cMotionBlur.lengthEmpty)
     {
      entity.Remove<ComponentMotionBlur>();
     }
     continue;
    }

    if (frames % cMotionBlur.step == 0)
    {
      
     var renderer = entity.Populate<SpriteRenderer>("Obj Motion Blur", cView.parts[0].renderer.transform.position, cView.parts[0].renderer.transform.rotation);
     var l        = cMotionBlur.length++;
     cMotionBlur.fadings[l] = cMotionBlur.timeFade * (l + 1) * cMotionBlur.timeOffset;
     cMotionBlur.renderers[l] = renderer;
     renderer.color = cMotionBlur.color;
     renderer.sprite = cView.parts[0].renderer.sprite;
     var transform = renderer.transform;
     var scale     = transform.localScale;
     scale.x = cView.facing;
     transform.localScale = scale;

     if (cMotionBlur.length == 128)
     {
      var len = cMotionBlur.length << 1;
      Array.Resize(ref cMotionBlur.renderers, len);
      Array.Resize(ref cMotionBlur.fadings, len);
     }
    }
   }
  }
    }


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

Код

public class StarterLevel1 : Starter
{
    protected override void Setup()
    {
   
  Add<ProcessingInputConnect>();

  Add<ProcessingPlayer>();
  Add<ProcessingMotion>();
  Add<ProcessingRender>();
  Add<ProcessingRenderShoot>();
  // добавляем его в системы
  Add<ProcessingMotionBlur>();
  Add<ProcessingCamera>();


И всякий раз когда мне нужен блюр я добавляю просто к сущности его как компонент

Код

// обработка удара героя в прыжке
foreach (var entity in groupActionJumpAttack)
   {
    var cAnimation        = entity.ComponentAnimation();
    var cMotion           = entity.ComponentMotion();
    var cActionJumpAttack = entity.ComponentActionJumpAttack();
    var cPlayer           = entity.ComponentPlayer();
    var cInAir            = entity.ComponentInAir();

    if (cPlayer.source.GetButtonDown(DataInputActions.Default.Punch))
    {
     cMotion.velocity.y = cActionJumpAttack.addJumpForce;
     var x    = cMotion.direction.x;
     var side = Math.Abs(cMotion.velocity.x) > 0.5f ? 1 : 0;
     cAnimation.Set(Anim.JumpMelee, side);

     cInAir.actionOnFinish = ChangeAnimationToHit;

     Timer.Add(0.1f, () =>
     {
       cMotion.velocity.y = -3;
       cMotion.velocity.x += x * 1.5f;
     });

                    // добавляем сущности мошен блюр
     var composer = new EntityComposer(entity, 1);
   
     var cMotionBlur = composer.Add<ComponentMotionBlur>();
     cMotionBlur.time = 1.2f;
     cMotionBlur.timeFade = 0.2f;
     cMotionBlur.timeOffset = 0.8f;
     cMotionBlur.color = new Color(0.5f, 0.5f, 0.5f, 0.4f);

     composer.Deploy();

     entity.Add(Tag.StateJumpHit);
    }
   }


Я убираю себе всякий головняк с вопросами " а что будет если объект уничтожен, а блюр еще работает ", " будут ли null ref от того что объект уничтожился раньше " - так как группы автоматически обновляются по фильтру компонентов. Если соответствующие компоненты в группе отсутствую то срабатывают события выхода сущности из группы ( если есть ) и все. Мне достаточно указать в группе мошен блюра что если сущность выбывает из группы то следует уничтожить все созданные спрайты в этой системе. При этом мне не надо знать произошло ли это из-за гибели сущности, ее деактивации или просто потому что время пришло :)


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Суббота, 08 Декабря 2018, 17:29
pixeyeДата: Понедельник, 27 Мая 2019, 19:37 | Сообщение # 16
Red Winter Software
Сейчас нет на сайте
2019.05.27 обновление. Добавилась подписка на изменение компонентов ( реактивные компоненты )
https://github.com/dimmpixeye/ecs/wiki/(RU)-Observers -> пример использования

Код

sealed class ProcessorTesting: Processor, ITick
{

  public float hp;
  public ent observer;
  
public ProcessorTesting()
{
// где source = объект из которого берется переменная
observer = this.ValueChange(source => hp, HandleChangeHP);
}

// метод который должен сработать с изменением. Возвращает изменившиеся значение.
void HandleChangeHP(float val)
{
   Debug.Log($"HP IS {val}");
}
}


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю



Сообщение отредактировал pixeye - Понедельник, 27 Мая 2019, 19:53
k0feДата: Понедельник, 27 Мая 2019, 19:48 | Сообщение # 17
BRONX
Сейчас нет на сайте
pixeye, обнови ссылку на документацию в шапке на
Код
[url=https://github.com/dimmpixeye/ecs/wiki/%28RU%29-Framework-setup]Документация[/url]


мой стрим, который я редко включаю, но зато на нём я делаю игры
pixeyeДата: Понедельник, 27 Мая 2019, 19:54 | Сообщение # 18
Red Winter Software
Сейчас нет на сайте
Цитата k0fe ()
pixeye, обнови ссылку на документацию в шапке на

Там еще надо будет обновлять доку. Настройка давно упростилась : )


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю

XakepДата: Вторник, 28 Мая 2019, 05:32 | Сообщение # 19
めちゃくちゃちゃ
Сейчас нет на сайте
Цитата pixeye ()
Вот тебе ситуация. Пишут тебе игру программисты. Пишут на ооп, в лучшем случае архитектор игры принимает какие-то решения по коду чтобы все это как-то шло в русле. Но правила субъективны. Человек ушел и на его место пришел новый программер который скажет "фу бяка так нельзя" и в любом случае затратит кучу времени на то чтобы разбираться со всеми зависимостями по коду в игре и понять логику.

А чем твое решение не ООП решение? ECS - это просто архитектурный шаблон проектирования, который может быть выполнен как в ООП стиле так и в любом другом (ФП к примеру). Кстати, сейчас Майк Актон разрабатывает ECS с Data Oriented Design для юнити и все официально: DOTS, не пробовал использовать этот подход? и может просто добавить необходимые удобные абстракции поверх DOTS.


Сообщение отредактировал Xakep - Вторник, 28 Мая 2019, 05:36
pixeyeДата: Вторник, 28 Мая 2019, 08:47 | Сообщение # 20
Red Winter Software
Сейчас нет на сайте
Цитата Xakep ()
А чем твое решение не ООП решение? ECS - это просто архитектурный шаблон проектирования, который может быть выполнен как в ООП стиле так и в любом другом (ФП к примеру). Кстати, сейчас Майк Актон разрабатывает ECS с Data Oriented Design для юнити и все официально: DOTS, не пробовал использовать этот подход? и может просто добавить необходимые удобные абстракции поверх DOTS.


Все верно. Под ООП я тут скорее имел ввиду интенсивное использование принципов ООП, что до юнити. Что они делают мне известно, но я пользуюсь юнити исключительно как ванильным рендер движком с возможностью билдить под все платформы. Конкретно ecs юнити мне просто не нравится : )


ACTORS - мой фреймворк на Unity
Until We Die - игра над которой работаю

Форум игроделов » Движки для разработки игр и сложные системы разработки » Unity » Actors (Мой framework для Unity3d на котором я пишу игры.)
  • Страница 1 из 2
  • 1
  • 2
  • »
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг