Virtual Theater-style AI

Started by Ytterbium, Sat 31/01/2004 16:42:27

Previous topic - Next topic

Ytterbium

I'm interested in using Virtual Theater (Lure of the Temptress, Beneath a Steel Sky) style AI for some characters in my game. However, I'm not quite sure how to script it. Here's an example: the main character has a dog, and I'd like it to behave like one. So, it should do the following:

Choose one of these actions at random:
Go to a region and lie down in it
Follow a character
Pick up a toy, run around with it, and put it down
Walk around aimlessly

I'd like the dog to do these actions for a random amount of time before switching to a different one. I know how I'd script some of the actions (in theory):

Lie down in region: Select one of many regions, walk to it, perform the lie down animation and stay in the final frame.
Follow a character: Self explanatory
Pick up a toy: Go to the location of one of several toy objects, remove it, change the view to the dog with the toy, and then go to one of many regions and put it down there.

However, I don't know how I'd program the dog to walk around aimlessly (it might be told to go to a non-walkable area) or how to make it do actions for a random amount of time. I'd also like it to stop what it's doing if the player tries to interact with it.

Another related question: how do I make some walkable areas and walk-behinds only availible to the dog character?

Currently in production: Septigon

deltamatrix

#1
you could try the CCS plugin.

its good for controlling who aren't in the same room as the player character.
BAD WOLF - TORCHWOOD - MR SAXON - THE BEES ARE DISAPPEARING - PANDORICA - RIVER SONG

Ytterbium


Currently in production: Septigon

Rui 'Trovatore' Pires

Reach for the moon. Even if you miss, you'll land among the stars.

Kneel. Now.

Never throw chicken at a Leprechaun.

Pumaman

To walk aroudn randomly, you can just call MoveCharacter with randomly generated X and Y co-ordinates - if the co-ordinates aren't a walkable area, it will just go as close as possible, same as if you clicked to walk there.

To do an action for a random amount of time, you could simply start the animation, then do SetTimer with a random time value, then have an IsTimerExpired check to stop the animation.

JoeDiPnotathome

Shouldn't be too hard. You can call a bunch of random statements and let the dog perform one of those actions for a limited amount of time using a timer, then it would reset and pick another random event.

If I get a chance, I will try and post some code for you.

JD

Ytterbium

#6
I've got the walking aimlessly command working, but Xyzzy (the dog) tends to clip through the player. Is there a way to set him to walk around the player character?

I tried this:

if ((character[XYZZY].x == character[KEITH].x - 45) && (character[XYZZY].y == character[KEITH].y - 45)) {
 MoveCharacter(XYZZY, character[XYZZY].x, character[XYZZY].y - 50);
 }
if ((character[XYZZY].x == character[KEITH].x + 45) && (character[XYZZY].y == character[KEITH].y + 45)) {
 MoveCharacter(XYZZY, character[XYZZY].x, character[XYZZY].y + 50);
 }

But it doesn't seem to work very well.

Currently in production: Septigon

a-v-o

Look at the character page: checkbox "Can be walke through"

Pumaman

Also, comparing X and Y co-ordinates with == as you are doing there will not tend to work well, because there's the characters may never be that exact distance apart. For example, if it's moving quickly, it might skip from 44 to 46 in one frame. It's better to use a small range of values than one fixed one.

Ytterbium

#9
Quote from: a-v-o on Sun 01/02/2004 14:16:49
Look at the character page: checkbox "Can be walke through"
Unchecked.

And how would I script it so that it uses a range of values?

Currently in production: Septigon

a-v-o

You can find a script here:
http://a-v-o.selfhost.de/ags/scripts/dog.zip

The dog will try to keep moving inside a ring around the player character. Only when the player character walks towards the dog they can come close to each other.

I hope this is what you want.

Ytterbium

THANK YOU AVO! I will remember to include you in the credits.

However, I have a few more questions...

Since they still clip if the player walks towards Xyzzy, how can I set Xyzzy to always appear behind the player (my game is from a top-down perspective)?

How can I make some walkable areas and walkbehinds only available to Xyzzy (i.e. going under tables)?

How would I set Xyzzy to only run around aimlessly for a random amount of time before randomizing his action again?

Currently in production: Septigon

Kairus

#12
There's a function called SetCharacterBaseline that can help you make the dog invisible under some walk-behinds while the player character is visible when standing over them. Just set the dog's baseline, for example, at 50, the character's baseline at 70 and the table, or whatever walk-behind baseline at 60. That would make the dog always look like it's behind the player as well.

To make Xyzzy walk randomly for some amount of time you could use something like this:

SetTimer (1, 80 + Random (200));
 // Aimless walking state will last at least for two seconds and at most for seven.
MoveCharacter (XYZZY, Random (319), Random (199));
 // Start moving XYZZY

Then, in the repeatedly execute area, you should write:

if (IsTimerExpired (1)) change_dog_action ();
else if (character[XYZZY].walking == 0) { // If XYZZY has stopped moving
 MoveCharacter (XYZZY, Random (319), Random (199));
   // Move it somewhere else
}

Something like this works, I have used it in my game.
I hope it helps. :)

EDIT: Corrected a mistake in the script
Download Garfield today!

DOWNLOADINFOWEBSITE

Ytterbium

#13
Thanks for the scripts and baseline help, but I'd like some walkable areas and walk-behinds to only be availible to Xyzzy. Is that possible?

And AVO, your script seems to make Xyzzy stay close to the player. How should I change it to give him more freedom to move around?

Also, I tested another action...choosing a random location and lying down. However, it seems to slow down my game a lot and Xyzzy doesn't actually move from his starting location. Can someone please fix this for me?

function xyzzy_lie_down(){
 if (IsTimerExpired(1)) {
       xyzzyrerandomize = 1; //used by the function which will randomize Xyzzy's action, haven't coded yet
       xyzzydown = 0;
       SetCharacterView(XYZZY, 22); //normal view
       //xyzzy_randomize() [leaving this in comments until I code it]
       }
 else {
 MoveCharacter(XYZZY, Random(1280), Random(960));
 if (xyzzydown == 0) { //checks if Xyzzy is not lying down
   if (character[XYZZY].walking == 0) {
     SetTimer(1, 1200 + Random(3600));
     FaceLocation(XYZZY, character[XYZZY].x, 0);
     xyzzydown = 1;
     SetCharacterView(XYZZY, 28); //lying down view
     }
   }
 }
}

Currently in production: Septigon

a-v-o

As you have quite a bunch of questions and want to make a game which isn't like the standard adventures, I suggest that you can send the game to me and I try to make it like what you have in mind.

Then you can learn from the result how to script something similar.

Ytterbium

Thanks for the offer, avo, but I'd rather do it myself. I'd just like to know what aspect of your script controls how far away Xyzzy can be from the player.

Currently in production: Septigon

a-v-o

The 2 define:

#define MIN_DISTANCE 30
#define MAX_DISTANCE 100


remember that MoveCharacter isn't blocking:
if (character[XYZZY].walking == 0)
will be true because the character hasn't yet started walking.

You probably call xyzzy_lie_down from repeatedly_execute. Then usually the else-part is executed, so always a MoveCharacter command with new coordinates. Pathfinder has too calculate a new path in each game cycle (-> slowdown, no movement).

#define LS_NORMAL  -1
#define LS_START      0
#define LS_WALKING  1
#define LS_DOWN      2

int l_status = LS_OFF;

function xyzzy_lie_down()
{
 if (l_status == LS_START)
 {
   MoveCharacter(XYZZY, Random(1280), Random(960));
   l_status = LS_WALKING;
 }
 else if (l_status == LS_WALKING)
 {
   if (character[XYZZY].walking == 0)
   {
     FaceLocation(XYZZY, character[XYZZY].x, 0);
     SetCharacterView(XYZZY, 28); //lying down view
     l_status = LS_DOWN;
   }
 }
 else if (l_status == LS_DOWN)
 {
   if (IsTimerExpired(1))
   {
    l_status = LS_NORMAL;
     xyzzyrerandomize = 1; //used by the function which will randomize Xyzzy's action, haven't coded yet
     SetCharacterView(XYZZY, 22); //normal view
     //xyzzy_randomize() [leaving this in comments until I code it]
   }
 }
}

when you want to start the lie down action, then set:
l_status = LS_START;

Ytterbium

Thank you again, Avo!

Except now I'm having problems with the script that chooses one at random...

xyzzy_randomize is called in repeatedly_execute, and xyzzyrerandomize starts at 1. Here's the complete code:

Quotefunction xyzzy_walk_aimlessly(){
if (IsTimerExpired(2)){
  if (character [XYZZY].walking == 0) { //thanks to a-v-o for this script
  r_outer = r_inner;
   while (r_outer == r_inner)
   {
     r_outer = MIN_DISTANCE + Random (MAX_DISTANCE - MIN_DISTANCE);
     r_inner = MIN_DISTANCE + Random (r_outer - MIN_DISTANCE);
   }
   r_middle = (r_outer + r_inner) / 2;
   direction = 0;
 }
 int pc = GetPlayerCharacter ();
 int dx = character [XYZZY].x - character [pc].x;
 int dy = character [XYZZY].y - character [pc].y;
 int dist = (dx * dx) + (dy * dy);
 if (direction == 0)
 {
   if (dist < (r_middle * r_middle))
   {
     direction = 1;
     MoveCharacter (XYZZY, character [pc].x + Random (r_outer * 2) - r_outer, character [pc].y + Random (r_outer * 2) - r_outer);
   }
   else
   {
     direction = -1;
     MoveCharacter (XYZZY, character [pc].x, character [pc].y);
   }
 }
 else if (((direction > 0) && (dist >= (r_outer * r_outer))) || (((distance > dist) && (character [pc].walking == 0)) && (dist <= (r_inner * r_inner))))
 {
   StopMoving (XYZZY);
 }
 distance = dist;
 }
else {
 xyzzyrerandomize = 1;
 }
}
function xyzzy_lie_down() //thanks again a-v-o
{
if (l_status == LS_START)
{
MoveCharacter(XYZZY, Random(1280), Random(960));
l_status = LS_WALKING;
}
else if (l_status == LS_WALKING)
{
if (character[XYZZY].walking == 0)
{
FaceLocation(XYZZY, character[XYZZY].x, 0);
SetCharacterView(XYZZY, 28); //lying down view
l_status = LS_DOWN;
SetTimer(1, 1200 + Random(3600));
}
}
else if (l_status == LS_DOWN)
{
if (IsTimerExpired(1))
{
l_status = LS_NORMAL;
xyzzyrerandomize = 1; //used by the function which will randomize Xyzzy's action, haven't coded yet
SetCharacterView(XYZZY, 22); //normal view

}
}
}
function xyzzy_randomize() {
 if (xyzzyrerandomize == 1) {
 xyzzyrerandomize = 0;
 xyzaction = Random(1);
 }
 if (xyzaction == 0) {
   xyzzy_lie_down();
   }
 if (xyzaction == 1) {
   xyzzy_walk_aimlessly();
   SetTimer(2, 360 + Random(200));
   }
 }

However, it doesn't seem to actually call either function. Xyzzy doesn't move from his starting location.

Currently in production: Septigon

a-v-o


Ytterbium

#19
Thank you so much, avo. You really didn't have to do that, and I appreciate it. This will not go forgotten.

You have made Xyzzy a very happy puppy.



Oh, and I, myself, without any help (Wow! Amazing!) also programmed a little thing where if the player is carrying food, Xyzzy will start following him and occasionally whine or bark, asking for a handout.

Currently in production: Septigon

SMF spam blocked by CleanTalk