⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ch29.htm

📁 好书《C++ Builder高级编程技术》
💻 HTM
📖 第 1 页 / 共 4 页
字号:
logic. Game-based problems are put in the game engine, and graphics-based
problems are put in the graphics engine. If you want to have a maintainable code
base, then setting up clearly defined problem domains is important.

<DL>
	<DT></DT>
</DL>




<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>Once again, I have to ask myself
	how completely I have managed to achieve my goals. Can you really afford to forget
	about what goes on in <TT>Mercury1.pas</TT> when you're 
working with the game objects?
	Well, in all truthfulness, you probably can't completely ignore the graphics engine
	or its implementation. However, it is hidden well enough that you can forget about
	it at times, and a clearly defined partition 
exists between the game objects and
	the graphics objects. <BR>
	<BR>
	The only time you might have to bridge the gap between the game engine and graphics
	engine would be if something went wrong, that is, when you find a bug. At such times,
	you have 
to decide whether the bug is in the game engine or in the graphics engine,
	and then you have to implement the fix in the right place. Though it might not seem
	likely from this perspective, fixing the graphics engine by putting some kind of
	patch in 
the game engine, or vice versa, can be very tempting. You should avoid this
	temptation whenever possible. 
<HR>


</BLOCKQUOTE>

<H3><A NAME="Heading22"></A><FONT COLOR="#000077">Understanding the TGame Object</FONT></H3>
<P>The game object, 
implemented in <TT>GameEngine1.cpp</TT>, has five key properties:</P>
<PRE><FONT COLOR="#0066FF">__property TCharacter *Hero;

__property TCharacter *BadGuy;

__property TGameForm *CurrentGameForm;

__property int CurrentScene;

__property 
TCreatureList *CreatureList;

</FONT></PRE>
<P>In Byzantium, <TT>CurrentScene</TT> can be set to one of the following values:</P>
<PRE><FONT COLOR="#0066FF">#define mrWorldMap    0x6001

#define mrFightMap    0x6002

#define mrIntroMap    0x6003


</FONT></PRE>
<P>Each of these values represents one of the possible scenes that can be displayed
by the Byzantium game. Notice that these values are defined as part of Byzantium
itself and are not declared inside the game engine. You therefore can 
make up as
many of these constants as you need to implement your game. In short, the <TT>TGame</TT>
object knows that you will need to define constants specifying the name and type
of the current scene. It does not know or care, however, about the 
specific value
or meaning of these constants.</P>
<P>In almost all cases, the game will have only one main form on which a series of
different scenes will be drawn. But the fact that a programmer would want to have
more than one form is conceivable, 
so I provide for that possibility.</P>
<P>The <TT>CreatureList</TT> is implemented in <TT>Creatures1.pas</TT>. It is needed
internally by the <TT>TGame</TT> object and is made available to the user in case
it might come in handy. Allowing the user to 
access the <TT>CreatureList</TT> directly
in this manner is not very wise from a design point of view, but I found it the most
practical solution to a series of potential problems. The <TT>CreatureList</TT> is
made available in the <TT>TGame</TT> 
object not through multiple inheritance, but
through aggregation.</P>
<P>The hero is probably the most important feature of the <TT>TGame</TT> object.
From both the user's and game programmer's point of view, the hero is the center
of the game. One of 
the primary goals of the game engine is to allow the user and
programmer to access the hero freely and to treat him as a stand-alone entity with
his own autonomous existence. The hero is really stored on the <TT>CreatureList</TT>.
One of the goals of 
the <TT>TGame</TT> object is to allow the programmer to access
the hero without having to think about the <TT>CreatureList</TT> or the hero's position
in it.</P>
<P>The fact that the <TT>CreatureList</TT> is a public property of <TT>TGame</TT>
shows 
that I am not sure the game object automatically provides all necessary access
to the creatures on the <TT>CreatureList</TT>. As a result, I hedge my bets by giving
the user direct access to the <TT>CreatureList</TT>, just in case it is needed.
<H3><A 
NAME="Heading23"></A><FONT COLOR="#000077">Understanding the TCharacter Object</FONT></H3>
<P>The <TT>THermes</TT>, <TT>TScene</TT>, and <TT>THermesChart</TT> objects give
you access to characters that can be moved on a tiled surface. However, these 
characters
have no separate existence apart from the technology that implements them, and in
particular, they are hung on the <TT>CreatureList</TT> object, which is a bit unwieldy
to use.</P>
<P>The <TT>TCharacter</TT> object is designed to give you 
some meaningful way to
access the characters that live on a tiled grid. In particular, notice that you can
use the Entities program to define characters, to give them names, and to give them
traits such as <TT>Hit Points</TT>, <TT>Hunger</TT>, 
<TT>Speed</TT>, <TT>Weapons</TT>,
and so on. You can use the Entities program to add as many characters and traits
as you want to the tiled world implemented by <TT>THermesChart</TT>.</P>
<P><TT>TCharacter</TT> exists in order to lift the characters 
out of their tiled
world and give them a specific, easy-to-recognize identity. In particular, note the
following traits of the <TT>TCharacter</TT> object:</P>
<PRE><FONT COLOR="#0066FF">__property int Row={read=GetRow, nodefault};

__property int 
Col={read=GetCol, nodefault};

__property TCreature *Creature={read=FCreature, write=FCreature, nodefault};

__property AnsiString Name={read=GetName, nodefault};

__property TStringList *CustomFeatures={read=GetCustomFeatures, nodefault};


</FONT></PRE>
<P>Each character can have a position, as defined by the <TT>Row</TT> and <TT>Col</TT>
properties. Furthermore, it can have a name and a set of <TT>CustomFeatures</TT>.
The <TT>Creature</TT> property is like the <TT>CreatureList</TT> 
property associated
with the game. In particular, it is implemented by <TT>Creatures1.pas</TT> and should,
from the point of view of an ideal design, be entirely hidden from the programmer.
However, I cover it here in case it is needed by the 
programmer.</P>
<P>The <TT>CustomFeatures</TT> listed in the properties of the <TT>TCharacter</TT>
object can be defined by the Entities program, as shown in Figure 29.5. Notice that
the properties at the top of the form, such as <TT>Name</TT> and 
<TT>Kind</TT>, are
core properties that belong to all characters. The properties in the grid at the
bottom of the form are custom properties that can be created by the user. To edit
one of the custom properties, just double-click the appropriate row 
in the grid.<BR>
<BR>
<A NAME="Heading24"></A><A HREF="29ebu05.jpg" tppabs="http://pbs.mcp.com/ebooks/0672310228/art/29/29ebu05.jpg">FIGURE 29.5.</A><FONT COLOR="#000077">
</FONT><I>Here is a list of the features associated with the hero. <BR>
</I><BR>
All the properties shown in the grid at the bottom of 
the form are custom properties
defined by the user at runtime.
<H3><A NAME="Heading25"></A><FONT COLOR="#000077">Working with the TByzCharacter
Object</FONT></H3>
<P>The <TT>TCharacter</TT> object is an abstraction that can be used in any game.
The 
<TT>TByzCharacter</TT> object is a descendant of the <TT>TCharacter</TT> object
designed for use in Byzantium. <TT>TByzCharacter</TT> is implemented in <TT>ByzEngine1.cpp</TT>.</P>
<P>In addition to the properties it inherits from <TT>TCharacter</TT>, 
<TT>TByzCharacter</TT>
has the following traits:</P>
<PRE><FONT COLOR="#0066FF">__property int Armor={read=GetArmor, write=SetArmor, nodefault};

__property int Hunger={read=GetHunger, write=SetHunger, nodefault};

__property int 
HitPoints={read=GetHitPoints, write=SetHitPoints, nodefault};

__property int Weapon={read=GetWeapon, write=SetWeapon, nodefault};

__property int Strength={read=GetStrength, write=SetStrength, nodefault};

</FONT></PRE>
<P>Each of these properties is 
a custom property surfaced by <TT>TByzCharacter</TT>
so that it can be easily accessed by the programmer. The key point you need to grasp
here is that the <TT>TCreature</TT> object found in <TT>Creatures1.pas</TT> has a
few simple traits such as a 
name, a column, and a row. In addition, it has a series
of custom properties that can be defined by the user via the Entities program. The
type and number of these custom properties can be defined by the user.</P>
<P>In Byzantium, I have decided that 
the hero and each of the bad guys will have
five key traits called <TT>Armor</TT>, <TT>Hunger</TT>, <TT>HitPoints</TT>, <TT>Weapon</TT>,
and <TT>Strength</TT>. These properties are given to the individual creatures in
the tiled map through the good 
graces of the Entities program. The game programmer
can find out about the traits of any one creature at runtime by accessing the <TT>TByzCharacter</TT>
object, which is one of the fields of the game object.</P>
<P>Here is the code that 
<TT>TByzCharacter</TT> uses to define the armor of a character:</P>
<PRE><FONT COLOR="#0066FF">int TByzCharacter::GetArmor(void)

{

  return Creature-&gt;GetCustomInt(&quot;Armor&quot;);

}



void TByzCharacter::SetArmor(int Value)

{

  
Creature-&gt;SetCustomInt(&quot;Armor&quot;, Value);

}

</FONT></PRE>
<P>As you can see, these methods are just wrappers around the <TT>Creature</TT> object
defined in <TT>Creatures1.pas</TT>. You can retrieve an individual creature by finding
where 
it is stored on the <TT>CreatureList</TT>.</P>
<P><TT>TByzCharacter</TT> hides complexity. For example, if this object did not exist,
then you could find out the hero's current armor value only be iterating through
the <TT>CreatureList</TT> till you 
found the creature called <TT>Hero</TT>. Then
you would have to ask that creature for a custom value called <TT>Armor</TT>. The
game engine objects allow you to avoid all this confusion; instead, you can write
simple code along these lines:</P>

<PRE><FONT COLOR="#0066FF">int Armor = ByzGame-&gt;Hero-&gt;Armor;

ByzGame-&gt;Hero-&gt;Armor = 3;

</FONT></PRE>
<H3><A NAME="Heading26"></A><FONT COLOR="#000077">The Character in Battle Against
the Queen</FONT></H3>
<P>Another key trait of the 
<TT>TByzCharacter</TT> object is that it helps define
how a character performs in battle:</P>
<PRE><FONT COLOR="#0066FF">int GetResistanceChance()

{

  int i = random(49);

  i -= (24);

  return i;

}



int GetWeaponChance()

{

  return 0;

}




void PlaySound(AnsiString S)

{

  sndPlaySound(S.c_str(), SND_ASYNC);

}



bool TByzCharacter::DefendYourself(TByzCharacter *Attacker)

{

  int Resistance = (Strength - Attacker-&gt;Strength) + (Armor - Attacker-&gt;Weapon);



  if (Resistance + 
GetResistanceChance() &lt; 0)

  {

    HitPoints -= (Attacker-&gt;Weapon - GetWeaponChance());

    PlaySound(&quot;..\\media\\bang.wav&quot;);

    return False;

  }

  else

  {

    PlaySound(&quot;..\\media\\rev.wav&quot;);

    return True;

  
}

}

</FONT></PRE>
<P>The <TT>DefendYourself</TT> method is called whenever a character is forced to
defend himself or herself. For example, when the hero is wandering around the world
and encounters a bad guy, the fight scene is launched. Whenever 
you click the bad
queen, she is forced to defend herself. If she survives, then she goes on the attack
and calls on the hero to defend himself.</P>
<P>The math shown in <TT>DefendYourself</TT>, <TT>GetWeaponChance</TT>, and 
<TT>GetResistanceChance</TT>
is designed to give a fair degree of randomness to any particular battle. More sophisticated
simulations take into account a wider number of factors and have more complex forms
of randomness. However, the simple math shown 
here should serve as a starting point
if you want to design your own games.</P>
<P>The actual course of a battle between the hero and the bad guy is dictated by
the <TT>TFightClass</TT> object, found in <TT>FightClass1.cpp</TT>:</P>
<PRE><FONT 
COLOR="#0066FF">class TFightClass

{

private:

  AnsiString FBadGuyName;

  AnsiString FDisplayString;

  HWND FHandle;

  TScene *FScene;

  bool FHitInProcess;

  void Button1Click(void);

  bool BadGuyAttacks(void);

  bool CheckCharacters(void);

  
bool HeroAttacks(void);

  void DisplayData(AnsiString S);

public:

  TFightClass(HWND AHandle, TScene *AScene);

  void PerformHit(TObject *Sender);

  void ShowData();

  __property AnsiString BadGuyName={read=FBadGuyName};

};

</FONT></PRE>
<P>As 
you can see, this object has only a few public methods. The <TT>ShowData</TT>
method is meant to be called whenever a new buffer is being prepared so it can be
flipped to the front. In particular, the object gets a chance to write text to the
screen 
describing how the battle is proceeding.</P>
<P>The <TT>PerformHit</TT> method ends up calling the <TT>DefendYourself</TT> method
described previously:</P>
<PRE><FONT COLOR="#0066FF">bool TFightClass::HeroAttacks(void)

{

  TBadGuy *B = 
dynamic_cast&lt;TBadGuy*&gt;(ByzGame-&gt;BadGuy);

  THero *H = dynamic_cast&lt;THero*&gt;(ByzGame-&gt;Hero);



  FDisplayString = B-&gt;Name + &quot; Under attack!&quot;;

  WaitTime(1);

  if (B-&gt;DefendYourself(H))

    FDisplayString = 
B-&gt;Name + &quot;: No damage!&quot;;

  else

    FDisplayString = B-&gt;Name + &quot; is hit!&quot;;

  WaitTime(1);



  return CheckCharacters();

}



void TFightClass::PerformHit(TObject *Sender)

{

  if (FHitInProcess)

    return;

  
FHitInProcess = True;



  if (HeroAttacks())

    BadGuyAttacks();



  FHitInProcess = False;

  FDisplayString = &quot;Waiting...&quot;;

}

</FONT></PRE>
<P>As you can see, <TT>PerformHit</TT> uses a flag to ensure that the user can perform
only 
one hit at a time. This game is turn-based, so the user must wait for the bad
guy to strike back before attempting a second hit.</P>
<P>The <TT>HeroAttacks</TT> method is really just a wrapper around <TT>DefendYourself</TT>.
It tells the user that an 
attack is beginning and then pauses the game for a moment
so that things don't happen so quickly that the user can't follow the logic of the
events as they unfold. The actual call to <TT>DefendYourself</TT> is over in a flash,
but I again pause the 
game long enough for the user to read a message about what
has happened.</P>
<P>After the call to <TT>HeroAttacks</TT>, a similar method called <TT>BadGuyAttacks</TT>
is called.</P>
<P>The following method checks to see if either the hero or the bad 
guy has been
defeated:</P>
<PRE><FONT COLOR="#0066FF">bool TFightClass::CheckCharacters(void)

{

  TBadGuy *B = dynamic_cast&lt;TBadGuy*&gt;(ByzGame-&gt;BadGuy);



  if (B-&gt;HitPoints &lt;= 0)

  {

    
ByzGame-&gt;CreatureList-&gt;HideCreature(B-&gt;Name, False);

    FDisplayString = &quot;Victory is sweet!&quot;;

    WaitTime(1);

    ByzGame-&gt;CurrentScene = mrWorldMap;

    PostMessage(FHandle, WM_STARTSHOW, 0, 0);

    return False;

  }



  
if (dynamic_cast&lt;THero*&gt;(ByzGame-&gt;Hero)-&gt;HitPoints &lt;= 0)

  {

    FDisplayString = &quot;Defeat is bitter ashes!&quot;;

    WaitTime(1);

    ByzGame-&gt;CurrentScene = mrIntroMap;

    PostMessage(FHandle, WM_STARTSHOW, 0, 0);

    
return False;

  }

  return True;

}

</FONT></PRE>
<P>As you can see, the condition for losing or winning is simply that the hit points
of some character descend below zero. If this happens to a bad guy, then he is erased
from the screen, and the 
game continues on the tiled world map. If the hero runs
out of hit points, then the user is returned to the introductory screen, and the
game is assumed to be over. To restart the game, the user must run the Entities program
and restore the hero's hit 
points to some reasonably healthy value.
<H3><A NAME="Heading27"></A><FONT COLOR="#000077">Managing Game Flow</FONT></H3>
<P>The course of the game is managed by the <TT>GameForm</TT> object. In particular,
it has one method called <TT>Run</TT> that 
sets up each scene:</P>
<PRE><FONT COLOR="#0066FF">void TGameForm::Run(void)

{

  if (FFightClass)

  {

    delete FFightClass;

    FFightClass = NULL;

  }



  switch(ByzGame-&gt;CurrentScene)

  {

    case mrWorldMap:

      Hermes1-&gt;Scene = 
HermesChart1;

      break;

    case mrIntroMap:

      Hermes1-&gt;Scene = Scene1;

      break;

    case mrFightMap:

      Hermes1-&gt;Scene = SpriteScene1;

      FFightClass = new TFightClass(Handle, SpriteScene1);

      break;

  }



  
Hermes1-&gt;InitObjects();

  ByzGame-&gt;Initialize(this, Hermes1-&gt;CreatureList);

  Hermes1-&gt;Flip();

}

</FONT></PRE>
<P>If the <TT>CurrentScene</TT> is set to <TT>mrWorldMap</TT>, the <TT>Scene</TT>
property of the <TT>THermes</TT> object is 
set to <TT>HermesChart1</TT>, which creates
and controls the tiled world map. <TT>InitObjects</TT> is then called. This method
will ensure that the graphics objects associated with the last scene are destroyed
and that the graphics objects for the new 
scene are properly allocated and initialized.
The <TT>ByzGame</TT> object is then given a chance to catch up with what is happening.
In particular, it checks to make sure that the hero and bad guy, if any, are set
up properly. Finally, the 
<TT>DirectDraw</TT> pump is primed through a call to <TT>Flip</TT>.</P>
<P>As you can see, the <TT>GameForm</TT> object calls the graphics engine through
a series of very abstract calls that do not have anything specific to do with DirectDraw.
It 
does, in fact, matter that I call <TT>InitObjects</TT> first, then <TT>ByzGame-&gt;Initialize</TT>,
and finally <TT>Flip</TT>. In an ideal API, the order in which I do things would
not be important, and the act of starting the graphics engine would 
take one call
instead of two. However, the degree of complexity shown here is manageable, and the
possibility that any serious bugs could be introduced while completing these simple
steps is unlikely.</P>
<P>A really ugly architecture would force you 
to get into the specifics of DirectDraw
at such a time. For example, it might ask you to create a surface or adjust the palette.
That kind of detail should never be necessary on the level of the game objects. Game
objects are about writing the game; 
they should not ask the user to also manipulate
the innards of the graphics engine.

<DL>
	<DT></DT>
</DL>



<BLOCKQUOTE>
	<P>
<HR>
<FONT COLOR="#000077"><B>NOTE:</B></FONT><B> </B>I make such extreme statements about
	good and bad versions of a game 
engine and graphics engine only because I personally
	made a host of mistakes while creating previous versions of these objects. As I said
	earlier, seeing an object emerge perfectly the first time it is implemented is rare.
	Most objects mature only 
over time and over a series of revisions. <BR>
	<BR>
	As I gain experience creating objects, I find that I tend to avoid certain egregious
	errors even in my first drafts of an object hierarchy. Creating software is part
	science and part art. Someone 
can teach you how to get a science right the first
	time and every time you implement it. The artistic portion of the equation is a bit
	trickier, and my skill in that part of programming emerges only slowly, and generally
	only through experience. 
<BR>
	<BR>
	Of course, the artistic side of programming is the most interesting. If writing code
	ever really did become a science, then I imagine I would lose interest in the field
	altogether. Designing objects is fun, and perhaps the most 
interesting part of the
	process is the joy found in improving an object through a series of revisions. 
<HR>


</BLOCKQUOTE>

<P>That's all I'm going to say about the Byzantium program. Clearly, there is more
to this game than I have explained in 
these pages. I hope, however, that I have given
you enough insight so that you can productively play with the code on your own.
<H3><A NAME="Heading29"></A><FONT COLOR="#000077">Summary</FONT></H3>
<P>In this chapter, you saw some simple game objects 
and the rudimentary framework
of a game. The main theme of this chapter is the importance of separating the game
objects from the underlying graphics engine. As such, the task of creating a game
becomes manageable, primarily because it supports 
separate problem domains, each
of which has an easily defined scope.</P>
<P>For all but a handful of programmers, the future of programming is likely to center
on content manipulation, education, or entertainment. In short, most programs will
either 
manage content of some kind or another, or else be intended to entertain or
educate the user. Database and Web-based applications will focus on content, whereas
games and various educational tools will round out the picture for most programmers.
Of 
course, other programming jobs will involve the creation of operating systems,
compilers, or hardware management, but they will probably employ only a relatively
small number of workers.</P>
<P>In this book, you learned about creating database 
applications, and about how
to publish over the Web. In these last two chapters, I introduced some rudimentary
tools for creating strategy games. All these fields should be very important during
the next few years of computer development.</P>

<P>Whether your interest lies primarily in games, in content, or in an esoteric field
such as compilers, I hope you have found this text interesting and informative. As
programmers, we are all very fortunate to be involved in such a fascinating 
profession
with so many opportunities.</P>
<P>Twenty or thirty years ago the possibility that a class of workers called programmers
would emerge from nowhere to become one of the major forces shaping our society was
inconceivable. Each of us must 
remember that our primary goal is not to make money,
not to wield power, but to create the kind of world that we and our children will
want to inhabit.</P>
<P ALIGN="CENTER"><A HREF="index-3.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/index.htm"><IMG SRC="toc.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/toc.gif" WIDTH="41" HEIGHT="41"

ALIGN="BOTTOM" ALT="TOC" BORDER="0" NAME="toc8"></A><A HREF="ch28.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/ch28.htm"><IMG SRC="back-1.gif" tppabs="http://pbs.mcp.com/ebooks/0672310228/buttonart/back.gif"
WIDTH="41" HEIGHT="41" ALIGN="BOTTOM" ALT="BACK" BORDER="0" NAME="toc6"></A></P>
<P>
<P ALIGN="CENTER"><FONT COLOR="#000000">&copy;</FONT><A 
HREF="copy.htm" tppabs="http://pbs.mcp.com/ebooks/0672310228/copy.htm">Copyright</A><FONT
COLOR="#000000">, Macmillan Computer Publishing. All rights reserved.</FONT>

</BODY>

</HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -