Особисто я рекомендую зберігати функцію малювання поза самим класом Object. Я навіть рекомендую тримати розташування / координати Об'єктів поза самим Об'єктом.
Цей метод draw () має справу з API рендерингу низького рівня або OpenGL, OpenGL ES, Direct3D, і вашим шаром обгортання для цих API, або API двигунів. Можливо, вам доведеться поміняти місцями між собою (Якщо ви хочете підтримати OpenGL + OpenGL ES + Direct3D, наприклад.
Цей GameObject повинен просто містити основну інформацію про його візуальний зовнішній вигляд, такий як Mesh або, можливо, більший набір, включаючи шейдерні входи, стан анімації тощо.
Крім того, ви хочете отримати гнучкий графічний конвеєр. Що станеться, якщо ви хочете замовити об’єкти залежно від їх відстані до камери. Або їх тип матеріалу. Що станеться, якщо ви хочете намалювати 'вибраний' об'єкт іншого кольору. Що робити, якщо замість того, щоб насправді відтворити так soo, як ви викликаєте функцію малювання на об’єкті, він замість цього вводить його в список команд дій для виконання візуалізації (може знадобитися для нанизування). Ви можете робити таку річ за допомогою іншої системи, але це PITA.
Що я рекомендую, замість того, щоб малювати безпосередньо, ви прив'язуєте всі об'єкти, які ви хочете, до іншої структури даних. Для цього прив'язки дійсно потрібно мати посилання на розташування об'єктів та інформацію про надання.
Ваші рівні / шматки / райони / карти / хаби / весь світ / що завгодно отримують просторовий індекс, він містить об'єкти та повертає їх на основі запитів координат і може бути простим списком чи чимось на зразок Окрем. Це також може бути обгорткою чогось, що реалізується стороннім фізичним двигуном як сцена фізики. Це дозволяє вам робити такі речі, як "Запитати всі об'єкти, які перебувають у зорі камери з деякою додатковою зоною навколо них", або для більш простих ігор, де ви можете просто зробити все, що захоплює весь список.
Просторові покажчики не повинні містити фактичної інформації про позиціонування. Вони працюють, зберігаючи предмети в структурах дерев по відношенню до місця розташування інших об’єктів. Вони можуть бути хоч як своєрідний кеш-програш, що дозволяє швидко шукати об'єкт, виходячи з його положення. Немає реальної необхідності дублювати фактичні координати X, Y, Z. Сказавши, що ви можете, якщо хочете зберегти
Насправді ваші ігрові об’єкти навіть не повинні містити власну інформацію про місцезнаходження. Наприклад, об'єкт, який не був поставлений на рівень, не повинен мати координат x, y, z, що не має сенсу. Ви можете містити це у спеціальному покажчику. Якщо вам потрібно шукати координати об'єкта на основі його фактичного посилання, тоді вам потрібно буде встановити зв'язок між об'єктом та графіком сцени (графіки сцен призначені для повернення об'єктів на основі координат, але повільні при поверненні координат на основі об'єктів) .
Коли ви додаєте об'єкт до рівня. Він зробить наступне:
1) Створіть структуру місцеположення:
class Location {
float x, y, z; // Or a special Coordinates class, or a vec3 or whatever.
SpacialIndex& spacialIndex; // Note this could be the area/level/map/whatever here
};
Це також може бути посиланням на об'єкт у сторонніх фізичних двигунах. Або це можуть бути координати зміщення з посиланням на інше місце (для камери відстеження, прикріпленого об'єкта чи прикладу). З поліморфізмом це може бути або залежно від того, чи є статичним чи динамічним об'єктом. Зберігаючи посилання на просторовий індекс тут, коли координати оновлюються, просторовий індекс також може бути.
Якщо вас турбує динамічне розподіл пам’яті, використовуйте пул пам’яті.
2) Прив'язка / зв'язок між вашим об'єктом, його місцезнаходженням та графіком сцени.
typedef std::pair<Object, Location> SpacialBinding.
3) Прив'язка додається до просторового індексу всередині рівня у відповідній точці.
Коли ви готуєтеся до надання.
1) Дістаньте камеру (це буде просто інший об'єкт, за винятком того, що місце розташування буде відстежувати характер гравців, і ваш рендер матиме спеціальну посилання на нього, адже це все, що йому дійсно потрібно).
2) Отримайте SpacialBinding камери.
3) Отримайте просторовий індекс від зв’язування.
4) Запитайте об’єкти, які (можливо) видимі для камери.
5А) Вам потрібно обробляти візуальну інформацію. Текстури, завантажені в GPU тощо. Це найкраще зробити заздалегідь (наприклад, на рівні навантаження), але, можливо, це можна зробити під час виконання (для відкритого світу ви можете завантажувати речі, коли ви наближаєтесь до шматка, але це все одно слід робити заздалегідь).
5B) Необов’язково побудуйте кешоване дерево візуалізації, якщо ви хочете впорядкувати глибину / матеріал або відслідковувати об'єкти, які знаходяться поблизу, вони можуть бути видно пізніше. Інакше ви можете просто запитувати просторовий індекс кожного разу, коли це залежатиме від ваших вимог до гри / продуктивності.
Ваш візуалізатор, ймовірно, знадобиться об'єкт RenderBinding, який буде зв'язувати між Об'єктом, координатами
class RenderBinding {
Object& object;
RenderInformation& renderInfo;
Location& location // This could just be a coordinates class.
}
Потім, коли ви надаєте, просто запустіть список.
Я використовував посилання вище, але вони можуть бути розумними вказівниками, необробленими вказівниками, ручками об'єктів тощо.
Редагувати:
class Game {
weak_ptr<Camera> camera;
Level level1;
void init() {
Camera camera(75.0_deg, 1.025_ratio, 1000_meters);
auto template_player = loadObject("Player.json")
auto player = level1.addObject(move(player), Position(1.0, 2.0, 3.0));
level1.addObject(move(camera), getRelativePosition(player));
auto template_bad_guy = loadObject("BadGuy.json")
level1.addObject(template_bad_guy, {10, 10, 20});
level1.addObject(template_bad_guy, {10, 30, 20});
level1.addObject(move(template_bad_guy), {50, 30, 20});
}
void render() {
camera->getFrustrum();
auto level = camera->getLocation()->getLevel();
auto object = level.getVisible(camera);
for(object : objects) {
render(objects);
}
}
void render(Object& object) {
auto ri = object.getRenderInfo();
renderVBO(ri.getVBO());
}
Object loadObject(string file) {
Object object;
// Load file from disk and set the properties
// Upload mesh data, textures to GPU. Load shaders whatever.
object.setHitPoints(// values from file);
object.setRenderInfo(// data from 3D api);
}
}
class Level {
Octree octree;
vector<ObjectPtr> objects;
// NOTE: If your level is mesh based there might also be a BSP here. Or a hightmap for an openworld
// There could also be a physics scene here.
ObjectPtr addObject(Object&& object, Position& pos) {
Location location(pos, level, object);
objects.emplace_back(object);
object->setLocation(location)
return octree.addObject(location);
}
vector<Object> getVisible(Camera& camera) {
auto f = camera.getFtrustrum();
return octree.getObjectsInFrustrum(f);
}
void updatePosition(LocationPtr l) {
octree->updatePosition(l);
}
}
class Octree {
OctreeNode root_node;
ObjectPtr add(Location&& object) {
return root_node.add(location);
}
vector<ObjectPtr> getObjectsInRadius(const vec3& position, const float& radius) { // pass to root_node };
vector<ObjectPtr> getObjectsinFrustrum(const FrustrumShape frustrum;) {//...}
void updatePosition(LocationPtr* l) {
// Walk up from l.octree_node until you reach the new place
// Check if objects are colliding
// l.object.CollidedWith(other)
}
}
class Object {
Location location;
RenderInfo render_info;
Properties object_props;
Position getPosition() { return getLocation().position; }
Location getLocation() { return location; }
void collidedWith(ObjectPtr other) {
// if other.isPickup() && object.needs(other.pickupType()) pick it up, play sound whatever
}
}
class Location {
Position position;
LevelPtr level;
ObjectPtr object;
OctreeNote octree_node;
setPosition(Position position) {
position = position;
level.updatePosition(this);
}
}
class Position {
vec3 coordinates;
vec3 rotation;
}
class RenderInfo {
AnimationState anim;
}
class RenderInfo_OpenGL : public RenderInfo {
GLuint vbo_object;
GLuint texture_object;
GLuint shader_object;
}
class Camera: public Object {
Degrees fov;
Ratio aspect;
Meters draw_distance;
Frustrum getFrustrum() {
// Use above to make a skewed frustum box
}
}
Що стосується того, щоб робити речі «обізнаними» один про одного. Це виявлення зіткнення. Це, мабуть, було б реалізовано в Указі. Вам потрібно буде надати деякий зворотний дзвінок у вашому головному об’єкті. З цими речами найкраще обробляти належний фізичний двигун, такий як Куля. У цьому випадку просто замініть Octree на PhysicsScene та Position на посилання на щось на зразок CollisionMesh.getPosition ().