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

Object oriented Game Development -P10 pdf

30 334 0
Tài liệu đã được kiểm tra trùng lặp

Đ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

Tiêu đề Object-oriented Game Development
Chuyên ngành Object-oriented Game Development
Thể loại Thesis
Năm xuất bản 2003
Định dạng
Số trang 30
Dung lượng 246,59 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The Renderer and SoundManager types withinthe PLATFORM package are Strawman classes again, see Chapter 4 that define no behaviour, only pass type information, e.g.. // File: PLATFORM_Ren

Trang 1

What we want to do is move the abstract concept of renderers and sound managersinto the PLATFORM namespace without bogging down said namespace with anyspecifics about hardware or implementation In other words, we’d like the sort ofstructure illustrated in component terms here and in classes in Figure 6.7:

which decouples the packages The Renderer and SoundManager types withinthe 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{

SoundManagerPlatform

Renderer

PLATFORMFigure 6.7

Object diagram for

platform-independent

renderer and sound manager

PLATFORM

Trang 2

} // 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 platformtype 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 classsomething like this:

SoundManagerPlatform1Platform1

RendererPlatform1

SoundManager

SOUND

RendererREND

SoundManagerPlatform

RendererPLATFORM

PLATFORM1

Figure 6.8Cross-platforminfrastructure

Trang 3

class Platform1 : public PLATFORM::Platform{

#include "PLATFORM1_Platform.hpp"

#include "PLATFORM1_Renderer.hpp"

using namespace PLATFORM1;

Platform1::Platform1(){

}Platform1::~Platform1(){

}/*virtual*/

PLATFORM::Renderer * Platform1::CreateRenderer(){

return( new PLATFORM1::Renderer );

}

Trang 4

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 applicaapplica-tion class will create an

appropriate Platform subclass and use that to create the required service

compo-nents for the game, thus:

SoundManagerPlatform1Platform1

RendererPlatform1

SoundManager

SOUND

RendererREND

SoundManagerPlatform

RendererPLATFORM

PLATFORM1

Figure 6.9Cross-platforminfrastructure withpartitioned namespaces

Platform1ApplicationPlatform1

PlatformApplication

PLATFORM

PLATFORM1

Trang 5

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:

Trang 7

namespace RENDPLATFORM1{

class Renderer;

}

class GamePlatform1 : public PLATFORM1::ApplicationPlatform1{

{// 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 )

Trang 8

// 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();

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:

Trang 9

virtual void Initialise() = 0;

virtual void Run() = 0;

virtual void Terminate() = 0;

private:

};

} // end of namespace PLATFORM

#endifThe amended application looks like this:

namespace PLATFORM{

class GameLoop;

class Application{

Application::

Application( Platform * pPlatform, GameLoop * pGameLoop ): m_pPlatform( pPlatform )

, m_pGameLoop( pGameLoop ){

}void Application::Run(){

m_pGameLoop->Initialise();

m_pGameLoop->Run();

m_pGameLoop->Terminate();

}

Trang 10

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:

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 thedevelopment of code on multiple target platforms in parallel By separating and

Trang 11

subclassing the variant behaviours, we can – with care – create a generic set ofcomponents to be used in all the skus without compromising performance orstructure, in some cases irrespective of the differences between the varioushardware architectures In the cases where we cannot do this, object-orientedanalysis still gives us metrics that we can use to organise our code and data inways that are beneficial to the development process.

● The differences in toolsets between platforms can make life pretty awkward forthe developer Use tools such as PC-Lint to analyse code more thoroughly thancompilers to find the trouble spots

● Use intermediate file formats to simplify, segregate and clarify the import andexport of data

● As well as the capability and methodology of hardware, the distinction betweenmajor and minor platforms can have an impact on the game architectures.Identify these and plan the architecture accordingly

● Components work well in multiplatform systems By introducing platform nents, we can abstract away the differences in underlying hardware and still usegeneric components in the majority of the game

Trang 12

compo-In this chapter, we’ll examine the design and implementation of the central

participants in a game’s architecture, the game object or GOB Getting thecorrect logical and physical structure here is particularly critical, since thegame’s functioning will depend intimately on the operation and cooperation of

these class instances, and we will find that the project’s compile and link times

also depend critically on how your object classes are written

We’ll look at three strategies for implementing game objects and analysethe pros and cons of writing them that way We’ll then go on to discuss man-

agement of the objects, in particular memory-allocation strategies and some

other common implementation issues that you’ll meet along the way

7.1 Open your GOB

The term ‘game object’ is both accurate and misleading After all, every class in

your game will have an instance that is an object And while we’re at it, doesn’t

every application – never mind game – contain objects? Flippancy aside, there’s

an important issue here: every non-trivial application has an object hierarchy

What sort of hierarchies might we see?

7.1.1 Collapsed hierarchy

The collapsed hierarchy is shown below There is no inheritance in this

whatso-ever It’s a throwback to those bad old C programming days, and you’re pretty

unlikely to see it in a medium to large modern C++ project

Trang 13

Notice that there are only ‘has a’ relationships between classes This makes reuse

at least awkward and more likely than not near impossible Nevertheless, thereare benefits to be gained from this structural organisation First, remember thatinheritance is a strong binding between classes: physically, you have to includethe header file of the base class in the derived class’s header file With no inheri-tance, there are fewer compile-time dependencies: things are going to compileand link just about as quickly as they can

Second, one of the classes in the hierarchy will be the ‘ApplicationObject’,the class around which almost all behaviour pivots Since these are all identical

in size and basic functionality, the creation and deletion of these objects can be

made arbitrarily efficient by pool allocation At run time, allocate a block of

these, slap them into a free list and allocate as required When the pool dries up,allocate another bunch, add those to the free list, and so on This kind of alloca-tion strategy is extremely fast compared with the usual new/malloccombination and also helps to reduce fragmentation within the memory man-ager (which can lead to increasingly long allocation times when the applicationhas been running for some time)

So that’s the good news Now, why might we choose not to adopt a lapsed hierarchy? Well, for one thing, we may have made allocation efficientand reduced fragmentation, but this is at the cost of storage overhead Each ofour objects needs to support the functionality of any entity in the system Thatmeans its data fields and member functions are comprised of the union of allthe subclasses it needs to support functionality for

col-That means a lot of wasted space: many of the subclass objects will requireonly a small subset of the data and functionality in the object If there are lots

of objects in the system, then that’s potentially a lot of RAM that you know youwill be grovelling for as you near master Do you really want to do that? Evenmore importantly, you have created a software engineering bottleneck here: theobject grows in complexity, functionality depends on state, and state is some-times far from obvious One piece of legacy code I inherited on a project(admittedly written – badly – in C) a while back had this sort of thing going on:struct OBJECT

{//…

context With hilarious consequences

Trang 14

In a team environment where there is only one object class, there will be abig demand from your colleagues to edit and modify that class Over the course

of time, they’ll add more and more var fields, or find novel and bug-ridden ways

to use the existing ones Welcome to development hell

Having a monolithic, do-everything class is exactly what object orientationtries to avoid Yes, a well-written C++ project will have quite a few more files

kicking around than a C project of equivalent scope might have had, but the

ease of maintenance gained by the divide-and-conquer methodology is not to

be sniffed at

So, in summary, the collapsed hierarchy has little to recommend it to thetwenty-first-century developer

7.1.2 Shallow hierarchy

The temptation for inexperienced developers (or, for that matter, some so-called

experienced ones) is to use this wonderful thing called inheritance to scatter

some basic properties (such as the ability to have a name, or to be serialised)

The defining pattern for the shallow hierarchy is, for most, if not all, of theobjects in your system to inherit from one single base class, as here:

If your object hierarchy looks a bit like this, then you’re in good company:

Microsoft’s MFC is one system that looks quite similar But don’t let that put

you off

The act of factoring common functionality into a base class is able and certainly correct in principle Which is really damning with faint

commend-praise, because although the shallow hierarchy has certain traits that look like

object orientation, it exhibits the hallmarks of a flawed design or a serious lack

of design

Why, for example, do a file and a container share a common base class? Thefact that they can be serialised does not, in itself, justify the cost of inheriting

from the purportedly common class And what does it mean to serialise a

window? In other words, are there classes in the system that have functionality

that it doesn’t even make sense for them to have?

Let’s assume the base object confers useful functionality or passes typeinformation meaningful to some higher-level system There is no pressing need

to slap all those bits of functionality into the space of one class if they can

hap-pily sit in separate classes and be inherited where needed

The shallow hierarchy is definitely an improvement on the collapsed chy; at least we have a bunch of smaller classes that are more easily maintained

Trang 15

and reasonably defined Its flaw is that base class, which has become the place

to dump functionality that looks even slightly useful to its client classes Those of you who have worked on projects with this sort of structure willalso be familiar with the terrifying announcement towards the end of develop-ment that someone has changed (‘fixed’) a problem with the base class, andyou just know that invoking a build is going to take the best part of the morn-ing away

7.1.3 Vertical hierarchy

This structure is usually the result of realising that the collapsed hierarchy isunusable and that the shallow hierarchy gives little gain and a moderate deal ofpain The general idea is to start from an abstract description of the object andincrementally add properties in a series of subclasses Figure 7.1 shows anabstract vertical hierarchy We start with a near-dataless base class and add prop-erties 1, 2, 3 and 4 in a series of subclasses

As a concrete example, I’ll use a design I was playing with for a space game

a while ago First, here’s the base class, which as you can see is not atomic initself; it depends on a reference-counting property class:

432

11

2

3

4

Figure 7.1Abstract vertical

hierarchy

Trang 16

virtual void Draw( Renderer * pRenderer ) = 0;

virtual void Update( float fDeltaT ) = 0;

private:

};

Reference counting is an integral part of almost all non-trivial game systems It

is tempting to make this a common base class for all application classes, but

that really ought to be resisted without further thought Here’s a first take on a

reference counting class:

class IsReferenceCounted

{

public:

IsReferenceCounted(): m_iCount(0)

{}

virtual ~IsReferenceCounted(){

}

void AddRef(){

++m_iCount;

}

void Release(){

m_iCount;

if ( m_iCount == 0 ){

delete this;

}}

private:

int m_iCount;

};

This is fine, so long as:

● the object was allocated via new;

● you want to delete only unreferenced objects

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

TỪ KHÓA LIÊN QUAN