個人的には、描画関数をObjectクラス自体に含めないことをお勧めします。私は、オブジェクトの位置/座標をオブジェクト自体の外に置くことをお勧めします。
そのdraw()メソッドは、OpenGL、OpenGL ES、Direct3D、これらのAPIのラッピングレイヤー、またはエンジンAPIの低レベルレンダリングAPIを処理します。その間にスワップする必要があるかもしれません(たとえば、OpenGL + OpenGL ES + Direct3Dをサポートしたい場合。
そのGameObjectには、Meshや、シェーダー入力、アニメーション状態などを含むより大きなバンドルなど、視覚的な外観に関する基本情報が含まれている必要があります。
また、柔軟なグラフィックパイプラインが必要になります。カメラまでの距離に基づいてオブジェクトを並べたい場合はどうなりますか。またはその材料タイプ。「選択した」オブジェクトを別の色で描きたい場合はどうなりますか。オブジェクトで描画関数を呼び出すときに実際にsooとしてレンダリングするのではなく、代わりにレンダーが実行するアクションのコマンドリストに入れた場合はどうでしょう(スレッド化に必要な場合があります)。他のシステムでもそのようなことができますが、それはPITAです。
直接描画する代わりに、必要なすべてのオブジェクトを別のデータ構造にバインドすることをお勧めします。このバインディングには、オブジェクトの場所とレンダリング情報への参照が必要なだけです。
レベル/チャンク/エリア/マップ/ハブ/ホールワールド/空間インデックスが与えられるものは何でも、これにはオブジェクトが含まれ、座標クエリに基づいてそれらを返します。単純なリストまたはOctreeのようなものです。また、物理シーンとしてサードパーティの物理エンジンによって実装されたもののラッパーにもなります。「カメラのビューにあるすべてのオブジェクトにいくつかの余分な領域を追加してクエリを実行する」などの操作や、リスト全体を取得してすべてをレンダリングできる単純なゲームを実行できます。
Spacial Indexには、実際の位置情報を含める必要はありません。それらは、オブジェクトを他のオブジェクトの場所に関連してツリー構造で保存することにより機能します。それらは、その位置に基づいてオブジェクトをすばやく検索できる一種の不可逆キャッシュとしても使用できます。実際の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)(おそらく)カメラから見えるオブジェクトを照会します。
5A)視覚情報を処理する必要があります。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で実装されるでしょう。メインオブジェクトにコールバックを提供する必要があります。このようなものは、Bulletなどの適切な物理エンジンによって最適に処理されます。その場合、OctreeをPhysicsSceneに、PositionをCollisionMesh.getPosition()などのリンクに置き換えてください。