Delphi Graphics and Game Programming Exposed! with DirectX For versions 5.0-7.0:Artificial Intelligence Techniques
Search Tips
Advanced Search
Title
Author
Publisher
ISBN
Please Select
-----------
Artificial Intel
Business & Mgmt
Components
Content Mgmt
Certification
Databases
Enterprise Mgmt
Fun/Games
Groupware
Hardware
IBM Redbooks
Intranet Dev
Middleware
Multimedia
Networks
OS
Productivity Apps
Programming Langs
Security
Soft Engineering
UI
Web Services
Webmaster
Y2K
-----------
New Arrivals
Delphi Graphics and Game Programming Exposed with DirectX 7.0
by John Ayres
Wordware Publishing, Inc.
ISBN: 1556226373 Pub Date: 12/01/99
Search this book:
Previous
Table of Contents
Next
Probability Machines
Thats where probability machines come in. A probability machine is really just an extension of a finite state machine, allowing the application to simulate personalities based on a certain distribution factor. Using this technique, we can make some sprites appear more passive, some more aggressive, some a little bit sneakier, etc.
It works by choosing the next state based on a weighted distribution as identified by the type of personality were modeling. For example, lets say we have five states, and we want to model an aggressive behavior. Whenever it comes time to change states, we want to choose the next state based on a percentage chance as illustrated below.
Table 121: Aggressive personality percentage chance for state selection
State
Percentage
Chase
50%
Evade
10%
Wait
10%
Random Movement
10%
Patterned Movement
20%
So, in this example, an aggressive personality would choose the chase state 50% of the time. A more timid personality would likely choose the evade state more often. Other personality types would result in varying distributions.
One way to implement such a system would be to create an array of 10 elements, where each element holds a state (either as an integer index or an enumerated type). This array would then be populated by each state a number of times equal to its probability of selection divided by 10. Over time, each state would be selected according to its probability. For example, if the chase state had a 50% chance of selection, it would be in the array five times, and over time, it would indeed have a 50% chance of being selected when the state changed. This is the method by which personalities are implemented in the following example.
Listing 125: Personalities through probability machines
type
.
.
.
{this defines the different states}
TSpriteStates = (ssChasing, ssEvading, ssRandomMoves, ssPatternMoves, ssWait,
ssPlayer);
TStateNames = array[ssChasing..ssWait] of string;
{this defines the different personalities}
TPersonalities = (perAggressive, perDefensive, perTactical);
TPersonalityNames = array[perAggressive..perTactical] of string;
{this defines the probabilities tables that implement the personalities}
TProbabilityTable = array[0..9] of TSpriteStates;
TPersonality = array[TPersonalities] of TProbabilityTable;
{our sprite class}
TSprite = class
XPos, YPos: Integer;
CurPattern,
CurPatternIdx: Integer;
PatternIdxChange,
CurPatternTimer,
StateChange,
StateTimer,
PersonalityChange,
PersonalityTimer: Longint;
State: TSpriteStates;
Personality: TPersonalities;
procedure ChangeState;
procedure Move;
procedure Draw(Canvas: TCanvas);
end;
TPatterns = array[0..7, 0..31] of Integer;
const
{these probability tables implement the behavior of each personality.
each state is entered into each table a number of times equal to its
percentage chance of selection divided by 10 (i.e., 50% chance of selection =
5 times in the table)}
AggressivePersonality: TProbabilityTable = (ssChasing, ssChasing, ssChasing,
ssChasing, ssChasing, ssChasing,
ssPatternMoves, ssPatternMoves,
ssRandomMoves, ssWait);
DefensivePersonality: TProbabilityTable = (ssEvading, ssEvading, ssEvading,
ssEvading, ssEvading, ssEvading,
ssRandomMoves, ssRandomMoves,
ssPatternMoves, ssWait);
TacticalPersonality: TProbabilityTable = (ssEvading, ssEvading, ssRandomMoves,
ssRandomMoves, ssWait, ssWait,
ssPatternMoves, ssPatternMoves,
ssChasing, ssChasing);
{this allows us to easily display the name of the current
state and personality}
StateNames: TStateNames = (Chasing, Evading, RandomMoves,
PatternMoves, Wait);
PersonalityNames: TPersonalityNames = (Aggressive, Defensive, Tactical);
{this table implements patterned movement. moves are stored as directions
in this example, with 0 equal to north (or straight up movement) incrementing
in a clockwise direction}
Patterns: TPatterns = (
(0, 0, 0, 1, 1, 1, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7,
1, 1, 1, 0, 0, 0),
(1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 1, 1, 1),
(2, 2, 2, 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1,
3, 3, 3, 2, 2, 2),
(3, 3, 3, 4, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2,
4, 4, 4, 3, 3, 3),
(4, 4, 4, 5, 5, 5, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3,
5, 5, 5, 4, 4, 4),
(5, 5, 5, 6, 6, 6, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 4, 4, 4, 4, 4, 4, 4, 4,
6, 6, 6, 5, 5, 5),
(6, 6, 6, 7, 7, 7, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5,
7, 7, 7, 6, 6, 6),
(7, 7, 7, 0, 0, 0, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6,
0, 0, 0, 7, 7, 7)
);
{these indicate sprite positions in the array of sprites}
ENEMYIDX = 0;
PLAYERIDX = 1;
var
.
.
.
{track the sprites}
Sprites: array[ENEMYIDX..PLAYERIDX] of TSprite;
{our personality table}
Personalities: TPersonality;
implementation
.
.
.
procedure TfrmDXAppMain.FormDestroy(Sender: TObject);
begin
.
.
.
{free the sprite objects}
Sprites[ENEMYIDX].Free;
Sprites[PLAYERIDX].Free
end;
procedure TfrmDXAppMain.FormActivate(Sender: TObject);
begin
.
.
.
{seed the random number generator}
Randomize;
{initialize the enemy sprite}
Sprites[ENEMYIDX] := TSprite.Create;
Sprites[ENEMYIDX].XPos := 320;
Sprites[ENEMYIDX].YPos := 240;
Sprites[ENEMYIDX].State := ssWait;
Sprites[ENEMYIDX].StateChange := 10;
Sprites[ENEMYIDX].StateTimer := timeGetTime;
Sprites[ENEMYIDX].PatternIdxChange := 100;
Sprites[ENEMYIDX].Personality := perAggressive;
Sprites[ENEMYIDX].PersonalityChange := 25000;
Sprites[ENEMYIDX].PersonalityTimer := timeGetTime;
{initialize the player sprite}
Sprites[PLAYERIDX] := TSprite.Create;
Sprites[PLAYERIDX].XPos := 320;
Sprites[PLAYERIDX].YPos := 478;
Sprites[PLAYERIDX].State := ssPlayer;
.
.
.
end;
.
.
.
procedure TfrmDXAppMain.DrawSurfaces;
var
SrfcDc: HDC;
TempCanvas: TCanvas;
iCount: Integer;
begin
{erase the last frame of animation}
ColorFill(FBackBuffer, clBlack, nil);
{move the player sprite based on which cursor keys are depressed}
if (GetAsyncKeyState(VK_LEFT) and $8000) = $8000 then
Sprites[PLAYERIDX].XPos := Sprites[PLAYERIDX].XPos 2;
if (GetAsyncKeyState(VK_RIGHT) and $8000) = $8000 then
Sprites[PLAYERIDX].XPos := Sprites[PLAYERIDX].XPos + 2;
if (GetAsyncKeyState(VK_UP) and $8000) = $8000 then
Sprites[PLAYERIDX].YPos := Sprites[PLAYERIDX].YPos 2;
if (GetAsyncKeyState(VK_DOWN) and $8000) = $8000 then
Sprites[PLAYERIDX].YPos := Sprites[PLAYERIDX].YPos + 2;
{move both sprites (note that this simply clips the player sprite to
the screen boundaries, as the player sprites position has already
been updated)}
for iCount := ENEMYIDX to PLAYERIDX do
Sprites[iCount].Move;
{retrieve a device context and create a canvas}
FBackBuffer.GetDC(SrfcDc);
TempCanvas := TCanvas.Create;
try
{use the canvas for easy drawing functionality}
TempCanvas.Handle := SrfcDc;
TempCanvas.Font.Color := clWhite;
{draw the sprites}
for iCount := ENEMYIDX to PLAYERIDX do
Sprites[iCount].Draw(TempCanvas);
{display the current state and personality of the enemy sprite}
TempCanvas.Brush.Style := bsClear;
TempCanvas.TextOut(0, 0, Personality: +
PersonalityNames[Sprites[ENEMYIDX].Personality]);
TempCanvas.TextOut(0, 15, State: +StateNames[Sprites[ENEMYIDX].State]);
finally
{clean up}
TempCanvas.Handle := 0;
FBackBuffer.ReleaseDC(SrfcDc);
TempCanvas.Free;
end;
end;
.
.
.
procedure TSprite.ChangeState;
var
Distance: Integer;
begin
{set the state randomly based on the personality}
State := Personalities[Personality][Random(10)];
{if this is the tactical state...}
if Personality = perTactical then
begin
{determine the distance to the player}
Distance := Abs(XPos Sprites[PLAYERIDX].XPos)+
Abs(YPos Sprites[PLAYERIDX].YPos);
{if the player is too close, well evade the player}
if Distance < 100 then
State := ssEvading
else
{if the player is too far away, well move toward the player}
if Distance > 300 then
State := ssChasing
end;
{if this is the pattern move state...}
if (State = ssPatternMoves) then
begin
{choose a random pattern}
CurPattern := Random(8);
{initialize to the start of the pattern}
CurPatternIdx := 0;
{retrieve the current time}
CurPatternTimer := timeGetTime;
{32 steps in a pattern multiplied by the time per each step should
give enough time to complete the entire pattern}
StateChange := PatternIdxChange * 32;
end
else
{...otherwise, change state again in 1 to 3 seconds}
StateChange := Random(2000)+1000;
{begin timing the state}
StateTimer := timeGetTime;
end;
procedure TSprite.Draw(Canvas: TCanvas);
begin
{draw enemy sprites in blue, player sprites in red}
if State <> ssPlayer then
Canvas.Brush.Color := clBlue
else
Canvas.Brush.Color := clRed;
{draw a small dot for the sprite}
Canvas.Pen.Style := psClear;
Canvas.Brush.Style := bsSolid;
Canvas.Ellipse(XPos, YPos, XPos + 3, YPos + 3);
end;
procedure TSprite.Move;
const
{initialize a horizontal and vertical velocity}
XVel: Integer = 1;
YVel: Integer = 1;
begin
{if this sprite is an enemy sprite...}
if State <> ssPlayer then
begin
{if it is time to change the personality, then change it}
if timeGetTime PersonalityTimer > PersonalityChange then
begin
{change to the next personality}
if Personality = perTactical then
Personality := perAggressive
else
Personality := Succ(Personality);
{reset the personality change timer}
PersonalityTimer := timeGetTime;
end;
{if it is time to change the state, then change it}
if timeGetTime StateTimer > StateChange then
ChangeState;
{for each state...}
case State of
ssChasing : begin
{were chasing the player, so move the sprite
closer to the player sprites position}
if Sprites[PLAYERIDX].XPos > XPos then
XPos := XPos + 1;
if Sprites[PLAYERIDX].XPos < XPos then
XPos := XPos 1;
if Sprites[PLAYERIDX].YPos > YPos then
YPos := YPos + 1;
if Sprites[PLAYERIDX].YPos < YPos then
YPos := YPos 1;
end;
ssEvading : begin
{were evading the player, so move the sprite
farther away from the player sprites position}
if Sprites[PLAYERIDX].XPos > XPos then
XPos := XPos 1;
if Sprites[PLAYERIDX].XPos < XPos then
XPos := XPos + 1;
if Sprites[PLAYERIDX].YPos > YPos then
YPos := YPos 1;
if Sprites[PLAYERIDX].YPos < YPos then
YPos := YPos + 1;
end;
ssRandomMoves : begin
{were moving randomly, but we dont want to move
each frame of animation or the sprite will look
like its having a seizure}
if timeGetTime CurPatternTimer > PatternIdxChange*3
then
begin
{modify the horizontal and vertical velocity}
XVel := Random(3) 1;
YVel := Random(3) 1;
{reset the timer}
CurPatternTimer := timeGetTime;
end;
{add the current velocity to the horizontal and
vertical positions}
XPos := XPos + XVel;
YPos := YPos + YVel;
end;
ssPatternMoves : begin
{if it is time to change the current pattern step
index...}
if timeGetTimeCurPatternTimer > PatternIdxChange then
begin
{increment to the next step}
CurPatternIdx := CurPatternIdx + 1;
{if weve reached the end of this pattern...}
if CurPatternIdx > 31 then
begin
{choose a new pattern}
CurPattern := Random(8);
{initialize to the first step of the pattern}
CurPatternIdx := 0;
end;
{reset the pattern timer}
CurPatternTimer := timeGetTime;
end;
{as stated above, pattern movement is implemented
as a direction, so modify the sprites position
according to the direction at this patterns
movement step}
case Patterns[CurPattern, CurPatternIdx] of
0 : begin
XPos := XPos + 1;
end;
1 : begin
XPos := XPos + 1;
YPos := YPos + 1;
end;
2 : begin
YPos := YPos +1;
end;
3 : begin
XPos := XPos 1;
YPos := YPos + 1;
end;
4 : begin
XPos := XPos 1;
end;
5 : begin
XPos := XPos 1;
YPos := YPos 1;
end;
6 : begin
YPos := YPos 1;
end;
7 : begin
XPos := XPos + 1;
YPos := YPos 1;
end;
end;
end;
end;
end;
{clip to the boundaries of the screen}
if XPos < 0 then
XPos := 0;
if XPos > DXWIDTH 21 then
XPos := DXWIDTH 21;
{well keep sprites from moving up where the status is displayed}
if YPos < 30 then
YPos := 30;
if YPos > DXHEIGHT 3 then
YPos := DXHEIGHT 3;
end;
initialization
{set up the personality tables}
Personalities[perAggressive] := AggressivePersonality;
Personalities[perDefensive] := DefensivePersonality;
Personalities[perTactical] := TacticalPersonality;
end.
Previous
Table of Contents
Next
Products | Contact Us | About Us | Privacy | Ad Info | Home
Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.
Wyszukiwarka
Podobne podstrony:
12 04 2010 płockieKobieta nie ma prawa bez zgody męża wyjść z domu (12 04 2009)12 04TI 01 12 04 T B plZajecia 12 04 10Wielkanoc w Bagdadzie (12 04 2009)12 04 Roboty dekarskie i izolacyjnesurowce II st 2011 12 04 ST i NST metody badania surowcówsurowce II st 2011 12 04 ST i NST metody badania surowcówDz U 12 04 2013rWM Cw9 Spraw v13 12 04 15tokarka 24,04,12więcej podobnych podstron