plik


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 That’s 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 we’re modeling. For example, let’s 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 12–1: 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 12–5: 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 sprite’s 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, we’ll evade the player} if Distance < 100 then State := ssEvading else {if the player is too far away, we’ll 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 {we’re chasing the player, so move the sprite closer to the player sprite’s 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 {we’re evading the player, so move the sprite farther away from the player sprite’s 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 {we’re moving randomly, but we don’t want to move each frame of animation or the sprite will look like it’s 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 timeGetTime–CurPatternTimer > PatternIdxChange then begin {increment to the next step} CurPatternIdx := CurPatternIdx + 1; {if we’ve 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 sprite’s position according to the direction at this pattern’s 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; {we’ll 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łockie
Kobieta nie ma prawa bez zgody męża wyjść z domu (12 04 2009)
12 04
TI 01 12 04 T B pl
Zajecia 12 04 10
Wielkanoc w Bagdadzie (12 04 2009)
12 04 Roboty dekarskie i izolacyjne
surowce II st 2011 12 04 ST i NST metody badania surowców
surowce II st 2011 12 04 ST i NST metody badania surowców
Dz U 12 04 2013r
WM Cw9 Spraw v13 12 04 15
tokarka 24,04,12

więcej podobnych podstron