1. Trang chủ
  2. » Công Nghệ Thông Tin

Object oriented Game Development -P10 pdf

30 333 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 30
Dung lượng 246,59 KB

Nội dung

What we want to do is move the abstract concept of renderers and sound managers into the PLATFORM namespace without bogging down said namespace with any specifics about hardware or implementation. In other words, we’d like the sort of structure illustrated in component terms here and in classes in Figure 6.7: which decouples the packages. The Renderer and SoundManager types within the PLATFORM package are Strawman classes (again, see Chapter 4) that define no behaviour, only pass type information, e.g. // File: PLATFORM_Renderer.hpp #ifndef PLATFORM_RENDERER_INCLUDED #define PLATFORM_RENDERER_INCLUDED namespace PLATFORM { class Renderer { public: Renderer(); virtual ~Renderer(); }; Object-oriented game development256 SoundManager SOUND Renderer REND SoundManagerPlatform Renderer PLATFORM Figure 6.7 Object diagram for platform-independent renderer and sound manager. PLATFORM REND SOUND 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 256 } // end of PLATFORM namespace #endif The Renderer within the REND package defines the generic renderer behaviour. Though in principle we could have placed that generic behaviour within the PLATFORM component, that would probably result in contamination of the PLATFORM name space with generic renderer specifics, which it really has no business in knowing. This way keeps it sparkling clean. Cast your mind back to Figure 6.3, the outline of a cross-platform renderer. Notice that there is no provision for rendering to off-screen buffers (for exam- ple, to draw shadows or a rear-view mirror in a vehicle-based game). This is because we may not be able to assume that all targets have sufficient video RAM (VRAM) to allow this. It’s often better – and easier – in the long run to not define a piece of non-generic functionality in a generic system than to provide it and disable it in a platform-specific system. It is always possible to create a new component associated with the renderer that provides the functionality in the middle-level functionality band. Now, we have to make the abstract into the concrete. For each platform type we need to support, we define a namespace that implements the Platform class and defines the specific services (see Figure 6.8). In the PLATFORM1 package, we define and implement our Platform class something like this: Cross-platform development 257 SoundManagerPlatform1Platform1 RendererPlatform1 SoundManager SOUND Renderer REND SoundManagerPlatform Renderer PLATFORM PLATFORM1 Figure 6.8 Cross-platform infrastructure. 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 257 // File: PLATFORM1_Platform1.hpp #ifndef PLATFORM1_PLATFORM_INCLUDED #define PLATFORM1_PLATFORM_INCLUDED #ifndef PLATFORM_PLATFORM_INCLUDED #include <PLATFORM\PLATFORM_Platform.h> #endif namespace PLATFORM1 { class Platform1 : public PLATFORM::Platform { public: Platform1(); ~Platform1(); Renderer * CreateRenderer(); // etc. }; } #endif // // File: PLATFORM1_Platform.cpp #include "PLATFORM1_Platform.hpp" #include "PLATFORM1_Renderer.hpp" using namespace PLATFORM1; Platform1::Platform1() { } Platform1::~Platform1() { } /*virtual*/ PLATFORM::Renderer * Platform1::CreateRenderer() { return( new PLATFORM1::Renderer ); } Object-oriented game development258 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 258 Notice that the PLATFORM1 namespace looks a bit ‘flat’. We’ve lost the compo- nent structure we’d introduced to keep independent functionality neatly partitioned. To remedy this, we can subclass the service namespaces just as we did for PLATFORM (see Figure 6.9). The application component So, by using the PLATFORM package we can define a factory for creating plat- form-specific classes and components. The intention is to use these in an application, so the next element in our model is to define an abstract applica- tion class. Platform-specific subclasses of the application class will create an appropriate Platform subclass and use that to create the required service compo- nents for the game, thus: Cross-platform development 259 SoundManagerPlatform1Platform1 RendererPlatform1 SoundManager SOUND Renderer REND SoundManagerPlatform Renderer PLATFORM PLATFORM1 RENDPLATFORM1 SOUNDPLATFORM1 Figure 6.9 Cross-platform infrastructure with partitioned name spaces. Platform1 ApplicationPlatform1 Platform Application PLATFORM PLATFORM1 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 259 Which translates to C++ like this: // File: PLATFORM_Application.hpp #ifndef PLATFORM_APPLICATION_INCLUDED #define PLATFORM_APPLICATION_INCLUDED namespace PLATFORM { class Platform; class Application { public: Application( Platform * pPlatform ) : m_pPlatform( pPlatform ) { } virtual ~Application() { delete m_pPlatform; } // Main loop code for the application. virtual void Run() = 0; Platform * GetPlatform() { return( m_pPlatform ); } private: Platform * m_pPlatform; }; } // end of namespace PLATFORM #endif // // File: PLATFORM1_ApplicationPlatform1.hpp #ifndef PLATFORM1_APPLICATION_INCLUDED #define PLATFORM1_APPLICATION_INCLUDED Object-oriented game development260 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 260 #ifndef PLATFORM_APPLICATION_INCLUDED #include <PLATFORM\PLATFORM_Application.hpp> #endif namespace PLATFORM1 { class ApplicationPlatform1 : public PLATFORM::Application { public: ApplicationPlatform1(); ~ApplicationPlatform1(); // Still abstract because of Run(). private: }; } // end of namespace PLATFORM1 #endif // // File: PLATFORM1_ApplicationPlatform1.cpp #include "PLATFORM1_ApplicationPlatform1.hpp" #include "PLATFORM1_Platform1.hpp" using namespace PLATFORM1; ApplicationPlatform1::ApplicationPlatform1() : PLATFORM::Application( new Platform1 ) { } can now be used as a base from which to derive a game class: // File: GamePlatform1.hpp #ifndef GAME_PLATFORM1_INCLUDED #define GAME_PLATFORM1_INCLUDED #ifndef PLATFORM1_APPLICATIONPLATFORM1_INCLUDED #include <PLATFORM1\PLATFORM1_ApplicationPlatform1.hpp> #endif Cross-platform development 261 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 261 namespace RENDPLATFORM1 { class Renderer; } class GamePlatform1 : public PLATFORM1::ApplicationPlatform1 { public: GamePlatform1(); ~GamePlatform1(); // Accessors for the services required by the game. RENDPLATFORM1::Renderer * GetRenderer(); private: RENDPLATFORM1::Renderer * m_pRenderer; }; #endif // // File: GamePlatform1.cpp #include "GamePlatform1.hpp" #include <RENDPLATFORM1\RENDPLATFORM1_Renderer.hpp> GamePlatform1::GamePlatform1() : PLATFORM1::ApplicationPlatform1() , m_pRenderer( 0 ) { // Note: DON’T try to set up the services here – // virtual functions don’t work in a constructor. } GamePlatform1::~GamePlatform1() { delete m_pRenderer; } REND::Renderer * GamePlatform1::GetRenderer() { if ( m_pRenderer == 0 ) { Object-oriented game development262 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 262 // These casts are safe because we know what // platform we’ve created the object for. PLATFORM1::Platform1 * pPlatform = (PLATFORM1::Platform1 *)GetPlatform(); m_pRenderer = (RENDPLATFORM1::Renderer*) pPlatform->CreateRenderer(); } return( m_pRenderer ); } /*virtual*/ void GamePlatform1::Run() { /* Initialisation… */ bool bGameOver = false; /* Main loop for the game… */ while( !bGameOver ) { /* … */ } /* Termination… */ } Hmmm, now that we can see it in the flesh, that Run method seems to be a bit of a code-sharing obstacle. Suppose that we had identical main loops on our n target platforms – which we can achieve using (say) the State Manager system described in Chapter 4 – then we’d write the same loop code n times. Let’s avoid that by writing a game loop class with which we can initialise our application: // File: PLATFORM_GameLoop.hpp #ifndef PLATFORM_GAMELOOP_INCLUDED #define PLATFORM_GAMELOOP_INCLUDED namespace PLATFORM { class GameLoop { public: GameLoop(); virtual ~GameLoop(); Cross-platform development 263 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 263 virtual void Initialise() = 0; virtual void Run() = 0; virtual void Terminate() = 0; private: }; } // end of namespace PLATFORM #endif The amended application looks like this: namespace PLATFORM { class GameLoop; class Application { public: Application( Platform *pPlatform, GameLoop *pGameLoop ); // etc. private: GameLoop * m_pGameLoop; }; } //… Application:: Application( Platform * pPlatform, GameLoop * pGameLoop ) : m_pPlatform( pPlatform ) , m_pGameLoop( pGameLoop ) { } void Application::Run() { m_pGameLoop->Initialise(); m_pGameLoop->Run(); m_pGameLoop->Terminate(); } Object-oriented game development264 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 264 The only thing left to do now is to choose what sort of application we want to create. Though we could do this using a preprocessor macro, let’s try to avoid using one of those altogether, as the use of it as either a command-line option or a global include file will invite physical dependencies where none is needed. Instead, let’s use a separate file defining main() for each build platform: // File: mainPlatform1.cpp #include <GamePlatform1.hpp> #include <MyGameLoop.hpp> void main() { MyGameLoop aLoop; GamePlatform1 * pGame; pGame = new GamePlatform1( &aLoop ); pGame->Run(); } // // File: mainPlatform2.cpp #include <GamePlatform2.hpp> #include <MyGameLoop.hpp> int main( int argc, char * argv[] ) { MyGameLoop aLoop; GamePlatform2 * pGame = new GamePlatform2( argc, argv, &aLoop ); pGame->Run(); return( pGame->GetExitCode() ); } 6.2 Summary ● Cross-platform development is not easy. There are many ways to screw up, and if you do it can be costly. Object orientation provides natural ways to organise the development of code on multiple target platforms in parallel. By separating and Cross-platform development 265 8985 OOGD_C06.QXD 1/12/03 2:42 pm Page 265 [...]... from GameObject (which is exactly the choice we eventually made during the development of this game) 7.2 Game object management However you choose to structure your object hierarchies, you will still need a way of allocating them Of course, the assumption is that we need to do a bit more than just: GameObject * pObject = new GameObject; First of all, we’ll probably need quite a few of these game objects... us our base game object, shown here: comm_IsReferenceCounted script_IsScriptable coll_Collider part _Object GameObject The GameObject is still an abstract class Concrete objects – real in -game types – are still to be derived from it However, by not putting all the required behaviour into a single object, we can reuse the property classes in other places where appropriate For example, the game s NPC types... example, consider a game where you require a bullet class to represent gunfire Is a Bullet a GameObject? It certainly has a position, and the GameObject has a position It certainly is collidable, otherwise it wouldn’t be much of a bullet It can be drawn, and we can certainly draw GameObjects There seems to be a good case for a bullet being a GameObject Yet – looking at the GameObject outlined above... in itself; it depends on a reference-counting property class: // Object. hpp #include "IsReferenceCounted.hpp" class Object : public IsReferenceCounted { public: Object( ); virtual ~Object( ); Figure 7.1 Abstract vertical hierarchy Object Object1 1 1 Object1 2 2 2 Object1 23 3 3 Object1 234 4 4 8985 OOGD_C07.QXD 2/12/03 1:07 pm Page 271 Game objects virtual void Draw( Renderer * pRenderer ) = 0; virtual void... 274 1:07 pm Page 274 Object- oriented game development Figure 7.2 A near-vertical object hierarchy Object ObjectProxy ObjectNull ObjectActor Asteroid Vehicle GroundVehicle Spaceship have failed to counteract the ‘touch the base class and you rebuild everything’ characteristic Indeed, you could argue that it has got worse, since you can now touch one of several classes and most of the game rebuilds However,... fine, so long as: ● ● the object was allocated via new; you want to delete only unreferenced objects 271 8985 OOGD_C07.QXD 272 2/12/03 1:07 pm Page 272 Object- oriented game development In other words, it’s not so fine Suppose we allocated the object from a pool? Suppose we want a garbage collector to dispose of unreferenced objects to avoid other objects referencing a deleted object? We can fix the problem... 2/12/03 1:07 pm Page 273 Game objects class ObjectNull : public ObjectProxy { public: ObjectNull(); void SetPosition( const Vector3 & ); void SetRotation( const Matrix33 & ); private: Vector3 m_vPosition; Matrix33 m_mRotation; }; At the next level, there was the actor object This added the properties of renderability and collidability: class ObjectActor : public ObjectNull { public: ObjectActor(); void... and be sorted spatially (though their in -game representations, called Avatars, can), so making an NPC a GameObject would not be correct Refer to the figure below: comm_IsReferenceCounted NPC script_IsScriptable Avatar GameObject Avatar 8985 OOGD_C07.QXD 2/12/03 1:07 pm Page 281 Game objects An analysis of dependencies for the mix-in system for creating game objects is both good and bad news In certain... should think carefully before opting for either one 8985 OOGD_C07.QXD 2/12/03 1:07 pm Page 279 Game objects So, back to our game objects, and since we’ve been talking theoretically for a bit, let’s illustrate our discussion with a real object hierarchy from a game The design brief required that all objects in the game have the following attributes: ● ● ● ● They should be dynamic entities: reference counting... pm Page 275 Game objects Component B Component A Component F Component D Component E Component C Object Type 2 Object Type 1 Object Type 3 Object Type 4 In this scheme, all the derived object types inherit from Component A Other object types include only the functionality that they require This means that objects are less likely to include functionality that they do not need – a characteristic of vertical . "IsReferenceCounted.hpp" class Object : public IsReferenceCounted { public: Object( ); virtual ~Object( ); Object- oriented game development2 70 Object1 234 Object1 23 Object1 2 Object1 Object 4 3 2 1 1 2 3 4 Figure. mainPlatform1.cpp #include <GamePlatform1.hpp> #include <MyGameLoop.hpp> void main() { MyGameLoop aLoop; GamePlatform1 * pGame; pGame = new GamePlatform1( &aLoop ); pGame->Run(); } //. running some game function when another object came within its target radius: Object- oriented game development2 72 8985 OOGD_C07.QXD 2/12/03 1:07 pm Page 272 class ObjectNull : public ObjectProxy { public: ObjectNull(); void

Ngày đăng: 01/07/2014, 15:20

TỪ KHÓA LIÊN QUAN