天行健 君子当自强而不息

Controlling Players and Characters(10)

 

Controlling Non-Player Characters
 

As you’ve been able to surmise from the past few sections, controlling the player is
relatively simple. Now comes the tough part—controlling the game’s NPCs. This
section shows you the various methods of navigating your game’s NPCs.
 

Although games might trick you into thinking some elaborate scheme is moving
the NPCs around the world, that just isn’t the case.

Do you remember the five general types of NPC movements that I mentioned earlier—
standing still, wandering around an area, walking along a route, following a character,
and evading a character? With these in mind, you might want to take a closer look at
your favorite role-playing games to find out which control schemes they use.

As for your role-playing game, take a moment to examine the following controls
and how to implement them.

 

Standing Still
 

There’s not much to think about here—just place a character and he stands still
facing a specific direction. That direction is an angular rotation.

 

Wandering an Area
 

Games such as Ultima Online allow NPCs to wander around a set area, whether it is
the entire level or a section that you define. To keep things simple, you can specify
the range in which you want a character to wander, within a specific range of coordinates
(as illustrated in Figure 16.3).

These coordinates can be stored in variables such as these:

float WanderMinX, WanderMinY, WanderMinZ;
float WanderMaxX, WanderMaxY, WanderMaxZ;

Now, assuming that you are tracking a character’s coordinates in the level in a trio of
variables, you can move them around randomly and check whether a move is valid:

float CharXPos, CharYPos, CharZPos; // Character coordinates
float XMove, ZMove; // Movement amounts - skip YMove movements

// Distance to move character
float Distance;

// Determine a random direction to move - loop until found
while(1) {
  float Direction = 6.28f / 360.0f * (float)(rand() % 360);

  XMove = cos(Direction) * Distance;
  ZMove = sin(Direction) * Distance;

  // Check if move is valid - ignore height for now
  if(CharXPos+XMove >= WanderMinX && \
     CharXPos+XMove <= WanderMaxX && \
     CharZPos+ZMove >= WanderMinZ && \
     CharZPos+ZMove <= WanderMaxZ) {
       // Movement allowed, update coordinates
       CharXPos+=XMove;
       CharZPos+=ZMove;
       break; // break out of loop
  }
}

CAUTION
Don’t randomly move a character around at every frame, or you’ll find yourself with characters that look
like they’re having a conniption fit. Instead, update a character’s direction only every few seconds or so.

 

Walking a Route
 

Although NPCs aren’t intelligent enough to know their way around the level, you
can assign them routes to travel. These routes include coordinates that must be
reached in order to proceed to the next coordinates. Once the last set of coordinates
is reached, the character returns to the first set of coordinates and starts the
path all over again.

 

Using Route Points
 

Route points are defined as a set of coordinates, and keeping with the 3-D concept that
you’re accustomed to, you can use the following structure to store those coordinates:


typedef struct sRoutePoint {
   float XPos, ZPos;
} sRoutePoint;

NOTE
Note that there’s no need for a Y-coordinate when using a 3-D engine because the height is
determined by the height of the ground below the character.

In order to construct a route, you pick the points you want a character to walk and
construct an array of sRoutePoint structures to store the coordinates. Figure 16.4, for
example, shows a simple map, with five points marked.

Because each point in the route is marked with coordinates, you can see how to
construct the sRoutePoint structures array:

sRoutePoint Route[5] = {
  { -200.0f, -100.0f },
  { 100.0f, -300.0f },
  { 300.0f, -200.0f },
  { 200.0f, 100.0f },
  { 0.0f, 400.0f }
};

long NumRoutePoints = 5; // To make it easier to know # points

 

Walking from Point to Point
 

In order to proceed from point to point, a character walking a route needs to compare
its current coordinates to the point where it’s headed. You use this, combined
with the character’s walking speed, to compute a pair of movement variables that
update the character’s position.

Start by assuming that the character’s coordinates are kept in the following variables
(along with the character’s walking speed):


float CharXPos, CharZPos; // No Y-coordinate needed
float WalkSpeed; // Walking speed per frame
 

At this point, assume that you’ve already retrieved the coordinates you want the
character to walk to and placed them into another pair of variables:
 

float RouteXPos, RouteZPos; // Again, no Y-coordinate

Now, to start the character moving, you calculate the movement variables:

// Calculate distance from character to route point
float XDiff = (float)fabs(RouteXPos - CharXPos);
float ZDiff = (float)fabs(RouteZPos - CharZPos);
float Length = sqrt(XDiff*XDiff + ZDiff*ZDiff);

// Calculate movement towards point
float MoveX = (RouteXPos - CharXPos) / Length * WalkSpeed;
float MoveZ = (RouteZPos - CharZPos) / Length * WalkSpeed;

Whenever you update the character per frame from now on, you’ll need to add
MoveX and MoveZ to the character’s coordinates, as in the following:

CharXPos += MoveX;
CharZPos += MoveZ;

With that aside, go back and see just how to track which route point a character is
walking toward. When one route point is reached, the character must walk toward
the next. To determine when a route point is reached, you check the distance from
the character to the route point; if the distance is within a certain limit, the character
has reached the point and is allowed to continue on to the next route point.


posted on 2007-11-14 15:05 lovedday 阅读(176) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论