Ghost chases the player

Started by Stranga, Wed 10/06/2020 15:29:47

Previous topic - Next topic

Stranga

Hello everyone.

I'm trying to experiment with my latest game. I want it to play out as if it were a normal adventure game but with a ghost that randomly appears every now and then and chases you if you get too close.

What I'm trying to achieve is changing the state of the ghost (or even create a state machine if I can). So far I have it set down to say if the player is 30 pixels away from the ghost, the ghost chases the player. If the player is >30 pixels, it stops chasing you and returns to it's random movement.

Code: ags

function repeatedly_execute() 
{

 //CHOST CHASE PLAYER 
 if (player.x - cGhost.x < 30 && player.y - cGhost.y < 30)
  {
    EnemyChase = true;//Set state
    cGhost.SetWalkSpeed(-1, -1);
    cGhost.FollowCharacter(player, 0, 0);
  }
  else  //GHOST RANDOM MOVEMENT
  {
    EnemyChase = false;//Set state
    cGhost.SetWalkSpeed(-3, -3);
    int walkx = Random(System.ViewportWidth + cGhost.x);
    int walky = Random(System.ViewportHeight + cGhost.y);
    if (GetWalkableAreaAt (walkx, walky) == 1) 
      {
        cGhost.Walk(walkx + 73, walky - 73, eNoBlock, eWalkableAreas);
      } 
  }


I'm not exactly sure how this sorta thing works with AGS, but any help would be greatly appreciated! :)

Crimson Wizard

IMHO the simplest way to implement states is this:

1. Make "state" variable which defines the state the actor currently is in.
2. Write "UpdateState" function (accepts actor ID or pointer, anything that lets you read the state and control it), and run UpdateState for each actor from rep-exec (room's or global).
3. In UpdateState make a switch based on state (or a big if/else if list).
4. Under every case first of all test for conditions of changing state; if change condition does not trigger, run state actions.

Arbitrary example:
Code: ags

void UpdateState(Character* actor)
{
     switch (CharStates[actor.ID])
     {
      case IDLE_STATE:
          // first test state-change condition:
          if ( CalcDistance(actor, player) < THRESHOLD )
          {
                CharStates[actor.ID] = CHASE_STATE;
                break;
          }
          // do idle animation, etc
          break;
     case CHASE_STATE:
          // first test state-change condition:
          if ( CalcDistance(actor, player) >= TOO_FAR_AWAY)
          {
               CharStates[actor.ID] = IDLE_STATE;
               break;
          }
          // do moves, animation, play spooky sounds etc
          break;
     }
}

Stranga

Thanks for your reply CW. As all the logic makes sense to me the implementation does not. Not too familiar with AGS scripting, still getting the hang of it compared to GMS.

I'm getting an error stating:
Code: ags
EnemyScript.asc(8): Error (line 8): CharStates is not an array


Not sure what this is referring to be honest.

Khris

You need a suitable global array (and enum):

Code: ags
// global script header

enum eCharState {
  IDLE_STATE, CHASE_STATE
};

// top of global script
eCharState CharStates[MAX_CHARACTERS];

Crimson Wizard

#4
My code was an example pseudo-code, it was not complete or usable as-is. I invented some constants, variables and functions. You may replace these with your own. For example, if you have only 1 ghost, then you do not need array and may make 1 regular int variable.

Spoiler

Code: ags

int GhostState;

void UpdateGhostState()
{
     switch (GhostState)
     {
      case IDLE_STATE:
          // first test state-change condition:
          if ( CalcDistance(cGhost, player) < THRESHOLD )
          {
                GhostState = CHASE_STATE;
                break;
          }
          // do idle animation, etc
          break;
     case CHASE_STATE:
          // first test state-change condition:
          if ( CalcDistance(cGhost, player) >= TOO_FAR_AWAY)
          {
               GhostState = IDLE_STATE;
               break;
          }
          // do moves, animation, play spooky sounds etc
          break;
     }
}

[close]

Stranga

Oh I understood that. Sorry I didn't reply sooner, I got it working with some tinkering. I did notice that the state changes are a little slow with the ghost character, this possibly could be caused by the animations if they are blocking the code somewhere(I think?). I'm using the built in FollowCharacter code so that may be the case? It also seems to travel to the players last known co-ordinates if the player stops moving rather than a continuous chase or to the players updated position. Is this something to do with AGS's path finding or something else? I did however find a few modules about following characters so I may have to use one of those.   

Mandle

I've found it is better to just send the chasing character to the player's current x,y location on every loop in re-exec than to use the built-in "follow" function if your goal is an aggressive chasing enemy.

Stranga

It most probably is better. This may be the case where I may just have to do that.

SMF spam blocked by CleanTalk