Войти
WarZesФорум

Система сцены для движка (комментарии) (2 стр)

Страницы: 1 2 3 4 5 Следующая »
#15
11:28, 20 ноя. 2013

Что-то подобное в Havok Vision реализовано правда у них оно не Script а Component называется
И в JMonkey Engine - Control.

Кстати зачем коллайдер в GameObject или в Node хранить?


#16
14:10, 20 ноя. 2013

Terkin
> или в Node хранить?
Node, это узел, там только ссылки на родителя и детей и вообще для построения иерархии

Terkin
> Что-то подобное в Havok Vision реализовано правда у них оно не Script, а
> Component называется
Об этом я писал в самой статье - по сути это мое развитие идеи компонентной системы. То есть Script, MeshRenderer, Collider - это компоненты. Но идея компонентной системы - в том что компоненты не знают, чем они являются. В идеальном мире это правильно - абстракция. В реальности приходится кастовать к дочернему классу:
Collider col = go.GetComponent<Collider>();
Такой подход не красив, и в С++ очень слабая RTTI, чтобы такое делать. Но поработав в unity я заметил, что есть единичные компоненты (MeshRenderer, Collider), их не может быть чисто технически больше одного. Так зачем их нужно приводить к родителю - Component, а потом обратно к дочернему типу, если сразу можно сохранить дочерний? С другой стороны есть скрипты, которых очень много, и которые редко взаимодействуют друг с другом.

Вот так и родилась моя идея, сделать не полную компонентую систему, а некий ее упрощенный аналог. И при этом я ничего не потерял:)

Так, а почему Collider в GameObject? Потому что это объект физики (термин из unity), то есть столкновения, и другие физические особенности типа падения. Где еще ему быть?

#17
16:49, 13 янв. 2014

war_zes
Привет.
А как игровые объекты взаимодействуют друг с другом?

#18
10:54, 15 янв. 2014

SunnyBunny
Через скрипты. Каждый скрипт содержит указатель на игровой объект и его компоненты. Поэтому скрипт может обращаться к другим объектам

#19
12:10, 15 янв. 2014

war_zes
Это не нарушение инкапсуляции?
Получается, что если один объект хочет повзаимодействовать с другим объектом, то он залазит ему в компоненты и делает с ними что хочет?

#20
7:33, 16 янв. 2014

В задницу инкапсуляцию! Объект "трава" при выкуривании ее объектом "Игрок" должен уметь превращать его в объект "Жывотнае" :)
Тема интересная, подпишусь. Интересует все-таки исполнение данных скриптов. Допустим скрипт "коллайдер" должен по идее апдейтится до скрипта "FPSCamera". Как это будет регулироваться? При добавлении скриптов они сами себя прописывают в глобальных обработчиках? Или у них будет доп свойство по типу приорити-груп в соответствии с которой они должны апдейтиться at run-time?
Непонятки так-же по полям скрипта:
1) *gameObject - это ссылка на обьект чей скрипт или на субъект действия скрипта?
2) *transform - что есть Transform класс? Для чего он нужен, допустим скрипту "Выпить Яду"?
3) *meshRenderer - опять таки для чего каждому скрипту это надо? Большинство скриптов к мешу и рендеру имеют косвенное отношение.
Просто боюсь что общий класс Script будет по мере прогресса разработки системы будет обрастать хреновой тьмой ненужных ему полей, на всяк случай присутствующих.
Кстати, забыл, а как-же дети? Куда они добавляются для объекта? И ссылка на родителя?
Когда вызывается скрипт апдейтор для объекта?
Хотелось бы все-таки более конкретный пример на основе чего-то реального, например "Яблошное древо" :)

GameObject AppleTree;

AppleTree.AddMesh("tree.mesh"); // Я так понял эт для GameObject->meshRenderer, хммм...
AppleTree.AddCollider("tree_collider.mesh"); // Что за метод? Куда добаляет коллайдер? В скрипты или в поля класса?
AppleTree.Pos(100,0,100); // Я так понял эт для GameObject->transform
AppleTree.AddScript(Grow);
AppleTree.AddScript(WindDeformer); // Допустим, качается от ветра.
AppleTree.AddScript(ProduceObjects); // Растут яблоки.

GameObject Apple0,...,AppleN;

Что тута?...

#21
14:11, 16 янв. 2014

SunnyBunny
> Это не нарушение инкапсуляции?
Обмен данными, можно даже какой умный паттерн применить:) Здесь важно то - как будет удобней конечному пользователю писать скрипт, а ему будет удобней писать так:

this->mesh->"что-то зовем"
Чем так:
Engine::GetRenderResourceMgr()->GetMesh("name")->"что-то зовем"
Инкапсуляция нужна для скрытия данных, а никак не для заумного доступа к этим данным.

#22
14:27, 16 янв. 2014

Zloten
> Допустим скрипт "коллайдер" должен по идее апдейтится до скрипта "FPSCamera".
> Как это будет регулироваться? При добавлении скриптов они сами себя прописывают
> в глобальных обработчиках? Или у них будет доп свойство по типу приорити-груп в
> соответствии с которой они должны апдейтиться at run-time?
Здесь нет особой проблемы. Когда создаются некие компоненты (Mesh, Collider и т.д.), они автоматически (без участия создающего) движком регистрируются в менеджере. Например менеджер мешей (условно конечно зову, у меня нет такого менеджера:) ) внутри себя содержит массив указателей на Mesh:

std::vector<Mesh*> meshs;
То есть когда я добавляю в GameObject компонент - Mesh. Он автоматически помещается в этот массив. А когда приходит очередь рисовать - менеджер просто проходит по этому массиву и дает команду всем мешам на отрисовку. При этом, благодаря тому что внутри Mesh есть указатель на Transform, менеджер сразу же получает нужную позицию.

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

Zloten
> 1) *gameObject - это ссылка на обьект чей скрипт или на субъект действия
> скрипта?
Это ссылка на объект которому принадлежит данный компонент/скрипт.

Zloten
> 2) *transform - что есть Transform класс? Для чего он нужен, допустим скрипту
> "Выпить Яду"?
Это уникальный компонент (у GameObject он может быть только один). Отвечает за позицию, масштаб и вращение всего объекта. Ну да, скрипту "Выпить Яду" он не нужен, а вот скрипту "бьюсь об дверь" он будет нужен, чтобы хотя бы узнать, находится ли GameObject напроив двери:)

Zloten
> 3) *meshRenderer - опять таки для чего каждому скрипту это надо? Большинство
> скриптов к мешу и рендеру имеют косвенное отношение.
Также, кому-то надо. Унификация кода - пользователь всегда знает что то что ему надо, всегда есть

Zloten
> Просто боюсь что общий класс Script будет по мере прогресса разработки системы
> будет обрастать хреновой тьмой ненужных ему полей, на всяк случай
> присутствующих.
Ну во первых не Script, а внутренний класс Component, от которого все и наследуются, получая эти поля. А во-вторых, жалко чтоли? Указатель занимает в памяти всего 4 байта, и при этом память дешевле работы процессора, поэтому пусть лучше 4 байта будут потрачены, чем каждый кадр выискивать компоненты по сто раз. В третьих - в юнити точно также и оно не стало громоздким

#23
14:35, 16 янв. 2014

Ну и на последок, уже более менее реальный код (я пишу эту систему отдельно от движка, поэтому нет кодестайла):

#pragma once

class Transform;
class GameObject;
class MeshRenderer;
class Collider;

class Component
{
public:
  GameObject *gameObject;
  Transform *transform;
  MeshRenderer *meshRenderer;
  Collider *collider;

  bool enable;

};
#pragma once

#include "Component.h"

class MeshRenderer : public Component
{
public:
  void Update();
};

#pragma once

#include "Component.h"

class Transform : public Component
{
public:
};

#pragma once

#include "Component.h"

class Script : public Component
{
public:
  virtual ~Script(){}

  virtual void Start() = 0;

  virtual void Update() = 0;

  virtual void Finalize() = 0;

  virtual void Contacts(const std::vector<Collider*> &conts) = 0;
  
};
#pragma once

#include "Node.h"
#include "Transform.h"
#include "Script.h"
#include "MeshRenderer.h"

class GameObject : public Node<GameObject>
{
public:
  void Update();

  Transform transform;
  MeshRenderer *meshRenderer;
  std::vector<Script *> scripts;
};

Напомню, пользователь из всех этих классов влияет только на Script.
Также, как я и говорил, моя система основана на идее компонентной, но из нее я вырезал всю абстрактность (из-за того что тормозит). Поэтому несмотря на то что есть Component, пользователь с ним работать не будет. Все что есть внутри движка, будет иметь наследника (MeshRenderer, Transform или там Terrain). Все что может писать пользователь, будет писаться через Script (я также думаю добавить компонент Data - мне такого сильно в юнити не хватает, компонент для хранения пользовательских данных в удобном для редактирования способе)

#24
14:44, 16 янв. 2014

Zloten
> Кстати, забыл, а как-же дети? Куда они добавляются для объекта? И ссылка на
> родителя?
Я пока не решил. Либо через Transform, как в юнити, будет строится иерархия. Либо нодовая система для GameObject

Zloten
> Когда вызывается скрипт апдейтор для объекта?
Разные компоненты апдейтятся в разное время. Порядок определен движком.

Zloten
> Что тута?...
ничего. Все GameObject и компоненты апдейтятся автоматически движком. Пользователь может писать свою логику выполняющуюся каждый кадр, только в Script::Update(). То есть через этот метод он сможет двигать объекты и т.д.

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

#25
14:50, 16 янв. 2014

war_zes
Сколько всего методов планируется у Скрипта. Если посмотреть в юнити то там у MonoBehavior их около 60. Это же жесть))
Но в С# они вполне могут через рефлекшн проверять, какие из методов реализованы, и если не нужно то не добавлять Behavior в соответствующую подсистему. А в языке без рефлекшена производительность может просесть серьезно на вызовах ненужных методов.
У тебя как будет это решаться? Проблема уже видна с методом
virtual void Contacts(const std::vector<Collider*> &conts)
Получается, что даже если Скрипт никак не реагирует на столкновение, то все равно собирается список контактов и дергается виртуальный метод.

#26
15:04, 16 янв. 2014

По идее реальные данные должны храниться в системах, в объектах должно быть только указание "компонент такой-то в такой-то системе". Системы уже знают как работать с данными оптимально. Типа MeshRenderer указывает на реальные данные в RenderSystem. RenderSystem уже где то у себя хранит все данные в своих внутренних структурах. По идее даже Transform не нужен - система координат графики, звука и физики может быть разной.
Плюс туда можно добавить сообщения. Например система физики берёт и посылает объектам в которых есть физический компонент сообщения "ты столкнулся с тем то" и если такое сообщение кто то слушает - то он и реагирует.

#27
16:52, 16 янв. 2014

SunnyBunny
> производительность может просесть серьезно на вызовах ненужных методов.
На вызове пустых методов (все методы будут иметь пустое тело по умолчанию, просто для теста я их сделал чисто виртуальными) производительность не должна серьезно падать. Если будут проблемы, вставлю теги...

Кроме того, это еще одна причина, почему у всех есть указатели на всех - Я выше писал, компоненты обновляются почти независимо друг от друга. И Вот как получается:

Движком обновляется система физики
--- в цикле обновляются все компоненты Collider (в менеджере физики есть массив указателей на них, вот по нему и проходим)
--- при событии столкновения, нужный Collider связываются со списком Script своего GameObject и вызывают метод Contacts() (передавая ему столкнувшихся)
А вот смотри. Если у GameObject нет Collider, то соответственно метод Contacts() никогда и никем не будет вызван. Также будет и с другими методами - они будут привязаны к компонентам, и если этих компонентов не будет, то и они выполняться не будут

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

zlos
> По идее реальные данные должны храниться в системах, в объектах должно быть
> только указание "компонент такой-то в такой-то системе". Системы уже знают как
> работать с данными оптимально. Типа MeshRenderer указывает на реальные данные в
> RenderSystem.
Ну вообще да. Я это в другой теме писал. Все компоненты содержат только ссылки на реальные данные.

zlos
> Плюс туда можно добавить сообщения. Например система физики берёт и посылает
> объектам в которых есть физический компонент сообщения "ты столкнулся с тем то"
> и если такое сообщение кто то слушает - то он и реагирует.
Ага, как я выше написал - так оно и есть.

#28
17:01, 16 янв. 2014

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

#29
17:10, 16 янв. 2014

war_zes
> На вызове пустых методов (все методы будут иметь пустое тело по умолчанию, просто для теста я их сделал чисто виртуальными) производительность не должна серьезно падать.
Даже если метод пустой, то входные аргументы для него все равно нужно сформировать.
Для метода

virtual void Contacts(const std::vector<Collider*> &conts)
например нужно создать вектор и заполнить его указателями на коллайдеры (ведь вряд ли физ-движок хранит контакты в удобном для нас виде)

war_zes
> Если у GameObject нет Collider, то соответственно метод Contacts() никогда и никем не будет вызван.
>Поэтому:
> Нет, если скрипт не реагирует на столкновение, виртуальный метод вызван не будет.
Я говорил о тех случаях, когда у объекта есть коллайдер, но на столкновения он никак реагировать не должен. Таких объектов может быть очень много: всякие камни валяющиеся по уровню, стены домов.

Страницы: 1 2 3 4 5 Следующая »
WarZesФорум

Тема в архиве.