แยกข้อมูลเกม / ตรรกะจากการเรนเดอร์


21

ฉันกำลังเขียนเกมโดยใช้ C ++ และ OpenGL 2.1 ฉันคิดว่าฉันจะแยกข้อมูล / ตรรกะออกจากการแสดงผลได้อย่างไร ในขณะนี้ฉันใช้คลาสพื้นฐาน 'Renderable' ที่ให้วิธีเสมือนจริงเพื่อนำมาใช้วาดภาพ แต่วัตถุทุกชิ้นมีรหัสพิเศษเฉพาะวัตถุเท่านั้นที่รู้วิธีการตั้งค่าเครื่องแบบ shader อย่างถูกต้องและจัดระเบียบข้อมูลบัฟเฟอร์อาร์เรย์จุดสุดยอด ฉันลงเอยด้วยฟังก์ชัน gl * จำนวนมากโทรไปทั่วรหัสของฉัน มีวิธีการทั่วไปในการวาดวัตถุหรือไม่?


4
ใช้การจัดองค์ประกอบเพื่อแนบวัตถุที่สามารถแสดงผลได้กับวัตถุของคุณและให้วัตถุของคุณโต้ตอบกับm_renderableสมาชิกนั้น ด้วยวิธีนี้คุณสามารถแยกตรรกะของคุณได้ดีขึ้น อย่าบังคับใช้ "อินเทอร์เฟซ" ที่สามารถเรนเดอร์ได้บนวัตถุทั่วไปที่มีฟิสิกส์, ไอและอะไรก็ได้ .. หลังจากนั้นคุณสามารถจัดการเรนเดอร์แยกได้ คุณต้องการเลเยอร์ของการทำให้เป็นนามธรรมผ่านการเรียกใช้ฟังก์ชัน OpenGL เพื่อแยกสิ่งต่าง ๆ มากขึ้น ดังนั้นอย่าคาดหวังว่าเอ็นจิ้นที่ดีจะมีการเรียก GL API ภายในการปรับใช้ที่สามารถแสดงผลได้หลากหลาย แค่นั้นแหละ
teodron

1
@teodron: ทำไมคุณไม่ใส่คำตอบนั้น
Tapio

1
@Tapio: เพราะมันไม่ใช่คำตอบที่มากนัก มันเป็นคำแนะนำมากกว่า
teodron

คำตอบ:


20

แนวคิดหนึ่งคือใช้รูปแบบการออกแบบของผู้เข้าชม คุณต้องมีการนำ Renderer มาใช้ซึ่งจะรู้วิธีการแสดงผล props วัตถุทุกชิ้นสามารถเรียกใช้อินสแตนซ์ของตัวแสดงผลเพื่อจัดการงานการแสดงผล

ใน pseudocode ไม่กี่บรรทัด:

class Renderer {
public:
    void render( const ObjectA & obj );
    void render( const ObjectB & obj );
};


class ObjectA{
public:
    void draw( Renderer & r ){ r.render( *this ) };
}

class ObjectB{
public:
    void draw( Renderer & r ){ r.render( *this ) };
}

สิ่ง gl * ถูกใช้งานโดยวิธีการของ renderer และวัตถุจะเก็บข้อมูลที่จำเป็นในการเรนเดอร์ตำแหน่งประเภทพื้นผิวขนาด ... ฯลฯ เท่านั้น

นอกจากนี้คุณสามารถตั้งค่าตัวแสดงผลที่แตกต่างกัน (debugRenderer, hqRenderer, ... ฯลฯ ) และใช้สิ่งเหล่านี้แบบไดนามิกโดยไม่ต้องเปลี่ยนวัตถุ

สามารถใช้ร่วมกับระบบ Entity / Component ได้อย่างง่ายดาย


1
นี่เป็นคำตอบที่ดีมาก! คุณสามารถเน้นEntity/Componentทางเลือกอีกเล็กน้อยเพราะมันสามารถช่วยแยกผู้ให้บริการด้านเรขาคณิตออกจากชิ้นส่วนเครื่องยนต์อื่น ๆ (AI, ฟิสิกส์, ระบบเครือข่ายหรือการเล่นเกมทั่วไป) +1!
teodron

1
@teodron ฉันจะไม่อธิบายทางเลือก E / C เพราะมันจะบ่นสิ่งต่าง ๆ แต่ผมคิดว่าคุณควรเปลี่ยนObjectAและObjectBต่อDrawableComponentAและDrawableComponentBและภายในทำให้วิธีการใช้ส่วนประกอบอื่น ๆ ถ้าคุณต้องการมันชอบposition = component->getComponent("Position");และในวงหลักคุณมีรายชื่อของส่วนประกอบ drawable โทรวาดด้วย
Zhen

ทำไมไม่เพียงมีอินเทอร์เฟซ (เช่นRenderable) ที่มีdraw(Renderer&)ฟังก์ชันและวัตถุทั้งหมดที่สามารถเรนเดอร์ได้ ในกรณีใดRendererเพียงแค่ต้องการฟังก์ชั่นเดียวที่รับวัตถุใด ๆ ที่ใช้อินเทอร์เฟซทั่วไปและการโทรrenderable.draw(*this);?
Vite Falcon

1
@ViteFalcon ขออภัยถ้าฉันไม่ทำให้ชัดเจน แต่สำหรับคำอธิบายโดยละเอียดฉันควรมีที่ว่างและรหัสเพิ่มเติม โดยพื้นฐานแล้วโซลูชันของฉันจะย้ายgl_*ฟังก์ชันไปที่ตัวแสดงผล (แยกตรรกะออกจากการแสดงผล) แต่โซลูชันของคุณจะย้ายการgl_*เรียกไปยังวัตถุ
Zhen

วิธีนี้ฟังก์ชั่น gl * ถูกย้ายออกจากรหัสวัตถุ แต่ฉันยังคงใช้ตัวแปรหมายเลขอ้างอิงที่ใช้ในการแสดงผลเช่นตำแหน่งของบัฟเฟอร์ / พื้นผิวรหัสตำแหน่ง / แอตทริบิวต์
เป้

4

ฉันรู้ว่าคุณยอมรับคำตอบของ Zhen แล้ว แต่ฉันต้องการที่จะพูดออกไปอีกในกรณีที่มันช่วยคนอื่น

ในการย้ำปัญหาอีกครั้ง OP ต้องการความสามารถในการแยกรหัสการแสดงผลออกจากตรรกะและข้อมูล

วิธีแก้ปัญหาของฉันคือการใช้คลาสที่แตกต่างกันทั้งหมดเพื่อแสดงองค์ประกอบซึ่งแยกจากRendererและคลาสตรรกะ ก่อนอื่นจะต้องมีRenderableส่วนต่อประสานที่มีฟังก์ชั่นbool render(Renderer& renderer);และRendererคลาสใช้รูปแบบผู้เยี่ยมชมเพื่อดึงข้อมูลRenderableอินสแตนซ์ทั้งหมดเนื่องจากรายการของGameObjects และแสดงวัตถุเหล่านั้นที่มีRenderableอินสแตนซ์ ด้วยวิธีนี้ Renderer ไม่จำเป็นต้องรู้เกี่ยวกับวัตถุแต่ละประเภททุกประเภทและยังคงเป็นความรับผิดชอบของวัตถุแต่ละประเภทที่จะแจ้งให้ทราบRenderableผ่านgetRenderable()ฟังก์ชั่น หรืออีกวิธีหนึ่งคุณสามารถสร้างRenderableVisitorคลาสที่เยี่ยมชม GameObjects ทั้งหมดและขึ้นอยู่กับGameObjectเงื่อนไขของแต่ละบุคคลที่พวกเขาสามารถเลือกที่จะเพิ่ม / ไม่ให้เพิ่มการแสดงผลให้กับผู้เข้าชม ไม่ว่าจะด้วยวิธีใดส่วนสำคัญคือgl_*การโทรนั้นอยู่นอกวัตถุและอยู่ในคลาสที่รู้รายละเอียดของวัตถุเองอย่างละเอียดแทนที่จะเป็นส่วนหนึ่งของสิ่งRendererนั้น

การปฏิเสธความรับผิด : ฉันเขียนคลาสเหล่านี้ในเครื่องมือแก้ไขด้วยตนเองเพื่อให้มีโอกาสดีที่ฉันพลาดอะไรบางอย่างในรหัส แต่หวังว่าคุณจะได้รับความคิด

ในการแสดงตัวอย่าง (บางส่วน):

Renderable อินเตอร์เฟซ

class Renderable {
public:
    Renderable(){}
    virtual ~Renderable(){}
    virtual void render(Renderer& renderer) const = 0;
};

GameObject ระดับ:

class GameObject {
public:
    GameObject()
        : mVisible(true)
        , mMarkedForDelete(false) {}

    virtual ~GameObject(){}

    virtual Renderable* getRenderable() {
        // By default, all GameObjects are missing their Renderable
        return NULL;
    }

    void setVisible(bool visible) {
        mVisible = visible;
    }

    bool isVisible() const {
        return getRenderable() != null && !isMarkedForDeletion() && mVisible;
    }

    void markForDeletion() {
        mMarkedForDelete = true;
    }

    bool isMarkedForDeletion() const {
        return mMarkedForDelete;
    }

    // More GameObject functions

private:
    bool mVisible;
    bool mMarkedForDelete;
};

Rendererคลาส(บางส่วน)

class Renderer {
public:
    void renderObjects(std::vector<GameObject>& gameObjects) {
        // If you want to do something fancy with the renderable GameObjects,
        // create a visitor class to return the list of GameObjects that
        // are visible instead of rendering them straight-away
        std::list<GameObject>::iterator itr = gameObjects.begin(), end = gameObjects.end();
        while (itr != end) {
            GameObject* gameObject = *itr++;
            if (gameObject == null || !gameObject->isVisible()) {
                continue;
            }
            gameObject->getRenderable()->render(*this);
        }
    }

};

RenderableObject ระดับ:

template <typename T>
class RenderableObject : public Renderable {
public:
    RenderableObject(T& object)
        :mObject(object) {}
    virtual ~RenderableObject(){}

    virtual void render(Renderer& renderer) {
        return render(renderer, mObject);
    }

protected:
    virtual void render(Renderer& renderer, T& object) = 0;
};

ObjectA ระดับ:

// Forward delcare ObjectARenderable and make sure the constructor
// definition in the CPP file where ObjectARenderable gets included
class ObjectARenderable;

class ObjectA : public GameObject {
public:
    ObjectA()
        : mRenderable(new ObjectARenderable(*this)) {}

    // All data/logic

    Renderable* getRenderable() {
        return mRenderable.get();
    }

protected:
    // boost or std shared_ptr to make sure that the renderable instance is
    // cleaned up with the destruction of this object.
    shared_ptr<Renderable> mRenderable;
};

ObjectARenderable ระดับ:

#include "ObjectA.h"

class ObjectARenderable : public RenderableObject<ObjectA> {
public:
    ObjectARenderable(ObjectA& instance) {
        : RenderableObject<ObjectA>(instance) {}

protected:
    virtual void render(Renderer& renderer, T& object) {
        // gl_* class to render ObjectA
    }
};

4

สร้างระบบการแสดงผลคำสั่ง วัตถุระดับสูงซึ่งสามารถเข้าถึงทั้งOpenGLRendererและและฉาก / เกมวัตถุจะทำซ้ำกราฟฉากหรือเกมและสร้างชุดของRenderCmdsจากนั้นจะถูกส่งไปที่OpenGLRendererซึ่งจะดึงแต่ละในทางกลับกันและมี OpenGL ทั้งหมด รหัสที่เกี่ยวข้องนั้น

มีข้อดีมากกว่านี้เพียงแค่นามธรรม ในที่สุดเมื่อความซับซ้อนในการเรนเดอร์ของคุณเพิ่มขึ้นคุณสามารถจัดเรียงและจัดกลุ่มคำสั่ง render แต่ละรายการโดยใช้ texture หรือ shader Render()เพื่อกำจัดคอขวดจำนวนมากในการเรียกใช้ draw ซึ่งสามารถสร้างความแตกต่างอย่างมากในประสิทธิภาพ

class OpenGLRenderer
{
public:
    typedef GLuint GeometryBuffer;
    typedef GLuint TextureID;
    typedef std::vector<RenderCmd> RenderBatch; 

    void Render(const RenderBatch& renderBatch);   // set shaders, set active textures, draw geometry, ...

    MeshID CreateGeometryBuffer(...);
    TextureID CreateTexture(...);

    // ....
}

struct RenderCmd
{
    GeometryBuffer mGeometryBuffer;
    TextureID mTexture;
    Mat4& mWorldMatrix;
    bool mLightingEnabled;
    // .....
}

std::vector<GameObject> gYourGameObjects;
RenderBatch BuildRenderBatch()
{
    RenderBatch ret;

    for (GameObject& object : gYourGameObjects)
    { 
        // ....
    }

    return ret;
}

3

ขึ้นอยู่กับว่าคุณสามารถตั้งสมมติฐานเกี่ยวกับสิ่งที่พบได้ทั่วไปสำหรับเอนทิตีที่สามารถแสดงผลได้ทั้งหมดหรือไม่ ในเครื่องยนต์ของฉันวัตถุทั้งหมดจะแสดงผลในลักษณะเดียวกันดังนั้นเพียงแค่ต้องการให้ vbos พื้นผิวและการเปลี่ยนแปลง จากนั้นโปรแกรมแสดงภาพจะดึงข้อมูลทั้งหมดดังนั้นจึงไม่จำเป็นต้องเรียกใช้ฟังก์ชั่น OpenGL ในวัตถุอื่นเลย


1
สภาพอากาศ = ฝน, แสงแดด, ร้อน, เย็น: P ->
wether

3
@TobiasKienzler หากคุณกำลังแก้ไขตัวสะกดให้ลองสะกดว่าถูกต้อง :-)
TASAGENT

@TASagent อะไรและเบรคกฎหมาย Muphry ของ ? m- /
Tobias Kienzler

1
แก้ไขคำผิดนั้นด้วย
danijar

2

ใส่รหัสการเรนเดอร์และตรรกะเกมในคลาสที่แตกต่าง การจัดองค์ประกอบ (ตามคำแนะนำของ teodron) อาจเป็นวิธีที่ดีที่สุดในการทำเช่นนี้ แต่ละเอนทิตีในโลกของเกมจะมีเรนเดอร์ของตนเอง - หรืออาจเป็นเซตของพวกมัน

คุณอาจยังมีคลาสย่อยอีกหลายชั้นของ Renderable ตัวอย่างเช่นเพื่อจัดการกับภาพเคลื่อนไหวโครงกระดูกตัวปล่อยอนุภาคและเฉดเงาที่ซับซ้อน คลาส Renderable และคลาสย่อยควรมีข้อมูลที่จำเป็นสำหรับการเรนเดอร์เท่านั้น: เรขาคณิตพื้นผิวและเฉดสี

นอกจากนี้คุณควรแยกอินสแตนซ์ของตาข่ายที่ระบุออกจากตาข่ายนั้น สมมติว่าคุณมีต้นไม้นับร้อยต้นบนหน้าจอแต่ละต้นใช้ตาข่ายเดียวกัน คุณต้องการจัดเก็บรูปทรงเรขาคณิตเพียงครั้งเดียว แต่คุณจะต้องใช้เมทริกซ์ตำแหน่งและการหมุนแยกต่างหากสำหรับต้นไม้แต่ละต้น วัตถุที่มีความซับซ้อนมากขึ้นเช่นดาวเคราะห์ที่มีการเคลื่อนไหวจะมีข้อมูลสถานะเพิ่มเติม (เช่นโครงกระดูกชุดของภาพเคลื่อนไหวที่ใช้ในปัจจุบันเป็นต้น)

ในการแสดงผลวิธีการที่ไร้เดียงสาคือการวนซ้ำทุกเอนทิตีของเกมและบอกให้แสดงตัวเอง อีกวิธีหนึ่งเอนทิตีแต่ละอัน (เมื่อวางไข่) สามารถแทรกวัตถุที่สามารถเรนเดอร์เข้าไปในวัตถุฉาก จากนั้นฟังก์ชั่นการเรนเดอร์ของคุณจะบอกฉากให้เรนเดอร์ สิ่งนี้ทำให้ฉากสามารถทำสิ่งที่เกี่ยวข้องกับการเรนเดอร์ที่ซับซ้อนโดยไม่ต้องฝังโค้ดนั้นลงในเอนทิตีของเกมหรือคลาสย่อยที่สามารถเรนเดอร์ได้


2

คำแนะนำนี้ไม่เฉพาะเจาะจงสำหรับการเรนเดอร์ แต่ควรช่วยในการสร้างระบบที่แยกสิ่งต่าง ๆ ออกเป็นส่วนใหญ่ ก่อนอื่นให้พยายามเก็บข้อมูล 'GameObject' แยกจากข้อมูลตำแหน่ง

เป็นที่น่าสังเกตว่าข้อมูลตำแหน่ง XYZ แบบง่าย ๆ อาจไม่ง่ายนัก หากคุณใช้เอ็นจิ้นฟิสิกส์คุณสามารถเก็บข้อมูลตำแหน่งไว้ในเอ็นจิ้นบุคคลที่สามได้ คุณอาจต้องการซิงโครไนซ์ระหว่างพวกเขา (ซึ่งจะเกี่ยวข้องกับการคัดลอกหน่วยความจำที่ไม่มีจุดหมายมากมาย) หรือสอบถามข้อมูลโดยตรงจากเอ็นจิ้น แต่วัตถุบางอย่างไม่จำเป็นต้องใช้ฟิสิกส์บางอย่างจะได้รับการแก้ไขเพื่อให้ชุดของโฟลตที่เรียบง่ายทำงานได้ดีที่นั่น บางคนอาจติดกับวัตถุอื่นดังนั้นตำแหน่งของพวกเขาจึงเป็นจริงของตำแหน่งอื่น ในการตั้งค่าขั้นสูงคุณอาจมีตำแหน่งที่เก็บไว้ใน GPU เพียงครั้งเดียวเท่านั้นที่จะต้องใช้คอมพิวเตอร์ในการเขียนสคริปต์การจัดเก็บและการจำลองเครือข่าย ดังนั้นคุณน่าจะมีตัวเลือกที่เป็นไปได้หลายอย่างสำหรับข้อมูลตำแหน่งของคุณ ที่นี่มันเหมาะสมที่จะใช้มรดก

แทนที่จะเป็นวัตถุที่เป็นเจ้าของตำแหน่งแทนวัตถุนั้นควรเป็นเจ้าของโครงสร้างข้อมูลการทำดัชนี ตัวอย่างเช่น 'ระดับ' อาจมี Octree หรืออาจเป็น 'ฉาก' ของเครื่องมือฟิสิกส์ เมื่อคุณต้องการแสดงผล (หรือตั้งค่าฉากเรนเดอร์) คุณจะค้นหาโครงสร้างพิเศษของคุณสำหรับวัตถุที่มองเห็นได้ด้วยกล้อง

นอกจากนี้ยังช่วยให้การจัดการหน่วยความจำที่ดี วิธีนี้วัตถุที่ไม่ได้อยู่ในพื้นที่จริง ๆ ไม่มีตำแหน่งที่เหมาะสมแทนที่จะส่งคืน 0.0 coords หรือ coords ที่มีเมื่ออยู่ในพื้นที่

หากคุณไม่ได้เก็บพิกัดไว้ในวัตถุอีกต่อไปแทนที่จะเป็น object.getX () คุณจะต้องมีระดับ level.getX (วัตถุ) ปัญหาของการค้นหาวัตถุในระดับนั้นน่าจะเป็นการดำเนินการที่ช้าเนื่องจากจะต้องตรวจสอบวัตถุทั้งหมดและจับคู่กับการสอบถามของคุณ

เพื่อหลีกเลี่ยงการที่ฉันอาจจะสร้างคลาส 'ลิงก์' พิเศษ หนึ่งที่ผูกระหว่างระดับและวัตถุ ฉันเรียกมันว่า "ตำแหน่งที่ตั้ง" นี้จะมีพิกัด xyz เช่นเดียวกับที่จับในระดับและการจัดการกับวัตถุ ลิงค์คลาสนี้จะถูกเก็บไว้ในโครงสร้าง / ระดับพิเศษและวัตถุจะมีการอ้างอิงที่อ่อนแอ (ถ้าระดับ / สถานที่ถูกทำลายการอ้างอิงวัตถุจำเป็นต้องได้รับการอัปเดตเป็นโมฆะ 'เจ้าของ' วัตถุด้วยวิธีนี้หากระดับถูกลบไปดังนั้นโครงสร้างดัชนีพิเศษสถานที่ที่มีและวัตถุ

typedef std::tuple<Level, Object, PositionXYZ> Location;

ตอนนี้ข้อมูลตำแหน่งจะถูกเก็บไว้ในที่เดียวเท่านั้น ไม่ซ้ำกันระหว่างวัตถุโครงสร้างการจัดทำดัชนีพิเศษเรนเดอร์และอื่น ๆ

โครงสร้างข้อมูลพิเศษเช่น Octrees มักไม่จำเป็นต้องมีพิกัดของวัตถุที่เก็บไว้ มีตำแหน่งถูกเก็บไว้ในตำแหน่งสัมพัทธ์ของโหนดในโครงสร้างตัวเอง (มันอาจจะคิดว่าเป็นเหมือนการบีบอัดแบบสูญเสียเสียสละความแม่นยำสำหรับการค้นหาครั้งเร็ว) ด้วยวัตถุที่ตั้งใน Octree แล้วจะพบพิกัดจริงภายในเมื่อมีการสอบถาม

หรือถ้าคุณใช้เอ็นจิ้นฟิสิกส์เพื่อจัดการตำแหน่งวัตถุของคุณหรือส่วนผสมระหว่างสองคลาสคลาส Location ควรจัดการสิ่งนั้นอย่างโปร่งใสในขณะที่รักษาโค้ดทั้งหมดของคุณไว้ในที่เดียว

ข้อดีอีกประการคือตอนนี้ตำแหน่งและการอ้างอิงถึงระดับจะถูกเก็บไว้ในตำแหน่งเดียวกัน คุณสามารถใช้ object.TeleportTo (other_object) และทำให้มันทำงานข้ามระดับได้ การค้นหาเส้นทางของ AI ในทำนองเดียวกันสามารถติดตามบางสิ่งในพื้นที่อื่น

เกี่ยวกับการเรนเดอร์ การเรนเดอร์ของคุณสามารถมีผลผูกพันกับตำแหน่งได้ ยกเว้นว่าจะมีสิ่งที่แสดงเฉพาะในนั้น คุณอาจไม่ต้องการ 'วัตถุ' หรือ 'ระดับ' เพื่อเก็บไว้ในโครงสร้างนี้ วัตถุอาจมีประโยชน์หากคุณพยายามทำบางอย่างเช่นการเลือกสีหรือการแสดงผล hitbar ที่ลอยอยู่เหนือวัตถุเป็นต้น แต่มิฉะนั้นโหมดแสดงภาพจะให้ความสำคัญกับตาข่ายและเช่นนั้น RenderableStuff น่าจะเป็น Mesh ก็สามารถมี bounding boxes ได้เช่นกัน

typedef std::pair<RenderableStuff, PositionXYZ> RenderThing;

renderer.render(level, camera);
renderer: object = level.getVisibleObjects(camera);
level: physics.getObjectsInArea(physics.getCameraFrustrum(camera));
for(object in objects) {
    //This could be depth sorted, meshes could be broken up and sorted by material for batch rendering or whatever
    rendering_que.addObjectToRender(object);
}

คุณอาจไม่จำเป็นต้องทำสิ่งนี้ทุกเฟรมคุณสามารถมั่นใจได้ว่าคุณจะได้พื้นที่ที่ใหญ่กว่าที่กล้องแสดงอยู่ แคชติดตามการเคลื่อนไหวของวัตถุเพื่อดูว่ามีกล่องขอบเขตอยู่ในระยะหรือไม่ติดตามการเคลื่อนไหวของกล้องและอื่น ๆ แต่อย่าเริ่มยุ่งกับสิ่งนั้นจนกว่าคุณจะทำการเปรียบเทียบ

เอ็นจิ้นฟิสิกส์ของคุณเองอาจมีนามธรรมที่คล้ายกันเนื่องจากมันไม่ต้องการข้อมูล Object เพียงแค่คุณสมบัติการชนกันของตาข่ายและคุณสมบัติทางฟิสิกส์

ข้อมูลวัตถุหลักทั้งหมดของคุณจะมีชื่อของตาข่ายที่วัตถุใช้ เอ็นจิ้นของเกมสามารถไปข้างหน้าและโหลดสิ่งนี้ในรูปแบบใดก็ได้โดยไม่ต้องสร้างภาระวัตถุของคุณด้วยการเรนเดอร์สิ่งต่าง ๆ ที่เฉพาะเจาะจง (ซึ่งอาจเฉพาะเจาะจงกับ API การเรนเดอร์ของคุณเช่น DirectX vs OpenGL)

นอกจากนี้ยังแยกส่วนประกอบต่าง ๆ สิ่งนี้ทำให้ง่ายต่อการทำสิ่งต่าง ๆ เช่นแทนที่เครื่องยนต์ฟิสิกส์ของคุณเนื่องจากสิ่งเหล่านั้นส่วนใหญ่มีอยู่ในที่เดียว นอกจากนี้ยังทำให้ง่ายขึ้น คุณสามารถทดสอบสิ่งต่าง ๆ เช่นเคียวรีฟิสิกส์โดยไม่จำเป็นต้องตั้งค่าวัตถุปลอมใด ๆ เนื่องจากสิ่งที่คุณต้องการคือคลาสที่ตั้ง นอกจากนี้คุณยังสามารถปรับปรุงสิ่งต่าง ๆ ได้ง่ายขึ้น มันทำให้ชัดเจนมากขึ้นว่าแบบสอบถามที่คุณต้องดำเนินการกับสิ่งที่เรียนและสถานที่เดียวเพื่อเพิ่มประสิทธิภาพพวกเขา (เช่นระดับดังกล่าวข้างต้น getVisibleObject จะเป็นที่ที่คุณสามารถแคชสิ่งถ้ากล้องไม่ได้ย้ายไปมาก)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.