Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: Stranga on Wed 10/06/2020 15:29:47

Title: Ghost chases the player
Post by: Stranga on Wed 10/06/2020 15:29:47
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) Select

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! :)
Title: Re: Ghost chases the player
Post by: Crimson Wizard on Wed 10/06/2020 15:45:00
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) Select

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;
     }
}
Title: Re: Ghost chases the player
Post by: Stranga on Thu 11/06/2020 03:40:37
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) Select
EnemyScript.asc(8): Error (line 8): CharStates is not an array

Not sure what this is referring to be honest.
Title: Re: Ghost chases the player
Post by: Khris on Thu 11/06/2020 13:19:35
You need a suitable global array (and enum):

Code (ags) Select
// global script header

enum eCharState {
  IDLE_STATE, CHASE_STATE
};

// top of global script
eCharState CharStates[MAX_CHARACTERS];
Title: Re: Ghost chases the player
Post by: Crimson Wizard on Thu 11/06/2020 13:40:45
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) Select

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]
Title: Re: Ghost chases the player
Post by: Stranga on Fri 12/06/2020 01:00:03
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.   
Title: Re: Ghost chases the player
Post by: Mandle on Fri 12/06/2020 09:53:52
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.
Title: Re: Ghost chases the player
Post by: Stranga on Fri 12/06/2020 10:35:44
It most probably is better. This may be the case where I may just have to do that.