Do characters have something similar to a WalkToPoint?

Started by artium, Fri 15/12/2017 23:06:52

Previous topic - Next topic

artium

If a WalkToPoint of a hot-spot is set, every interaction with that hot-spot will automatically make the player walk to that point.

I do not see similar property for characters. Interacting with characters will make the interaction immidiate, without walking the player first to face the character.

It is of course possible to add some code:

Code: ags

function cNPC_Look()
{
  // Walking and orientation boilerplate
  cPlayer.Walk(cNPC.x, cNPC.y + 15, eBlock, eWalkableAreas);
  cPlayer.FaceCharacter(cNPC);
  cNPC.FaceCharacter(cPlayer);

  // What I actually want to happen
  cPlayer.Say("I have no idea who is this");
}


This approach has problems:

  • Every interaction (look, talk, interact etc) type of every character needs to have this boilerplate code.
  • The "15" magic number needs to be separate for each character and updated after every change to the position of the character.
  • If a mistake is made, there is no way to catch it before testing. Even indirect changes can have effect. For example a walk area can be reduced to exclude this point.
  • If the character can move during the game, it requires "15" to be calculated dynamically (how?)

Is there a better way doing the same thing?

Snarky

Quote from: artium on Fri 15/12/2017 23:06:52
1. Every interaction (look, talk, interact etc) type of every character needs to have this boilerplate code.

You can make it a function and reduce the boilerplate to one line:

Code: ags
void ApproachCharacter(this Character*, Character* npc, int xOffset, int yOffset)
{
  this.Walk(npc.x + xOffset, npc.y + yOffset, eBlock, eWalkableAreas);
  this.FaceCharacter(npc);
  npc.FaceCharacter(this);
}


Now you can call it like so:

Code: ags
function cNPC_Look()
{
  cPlayer.ApproachCharacter(cNPC, 0, 15);
  cPlayer.Say("I have no idea who is this");
}


If that's still too much work, you can write some helper functions, e.g. for Say():

Code: ags
void SayToCharacter(this Character*, Character* npc, int xOffset, int yOffset, String message);
{
  this.ApproachCharacter(npc, xOffset, yOffset);
  this.Say(message);
}


And now the whole function is a one-liner again:

Code: ags
function cNPC_Look()
{
  cPlayer.SayToCharacter(cNPC, 0, 15, "I have no idea who is this");
}


Quote from: artium on Fri 15/12/2017 23:06:522. The "15" magic number needs to be separate for each character and updated after every change to the position of the character.

Does the "magic number" depend on the character (e.g. with cAlice you should always be 10 pixels away), on where they are on the screen (e.g. if you're talking to a character who's behind the desk, you should always stand in a certain position), or both? Does it need to change depending on the character scaling? Do your characters stand in one of a number of pre-defined locations, or do they wander about anywhere on the walkable area?

If it just depends on their position on the screen (from some limited list of possible positions), you should define a lookup table from each position to the corresponding WalkTo point. If characters can be anywhere on the screen, with different WalkTo behavior in different parts of the screen, you might need to define regions, and a lookup table from each region to its WalkTo point/offset. If it just depends on the Character, you should make a custom property. If it depends on both, you need to do both, and define the formula to calculate the combined value. If it depends on scaling, you need to include that in the calculation.

Quote from: artium on Fri 15/12/2017 23:06:523. If a mistake is made, there is no way to catch it before testing. Even indirect changes can have effect. For example a walk area can be reduced to exclude this point.

If a WalkTo point falls outside a walkable area, the character should walk to the closest point it can reach. If that's not acceptable, what is it that you want to happen?

Quote from: artium on Fri 15/12/2017 23:06:524. If the character can move during the game, it requires "15" to be calculated dynamically (how?)

See above.

artium

Thanks, "SayToCharacter" function does makes it more simple.

QuoteDoes the "magic number" depend on the character (e.g. with cAlice you should always be 10 pixels away), on where they are on the screen (e.g. if you're talking to a character who's behind the desk, you should always stand in a certain position), or both? Does it need to change depending on the character scaling? Do your characters stand in one of a number of pre-defined locations, or do they wander about anywhere on the walkable area?

This question is not for a specific game, I just experiment with AGS and try to see what is possbile, so it can depend on any of the things you listed.

I just thought about additional problem. If the player character is already near the NPC character, but the offset makes it walk to a different position, the player will walk to it instead speaking to the NPC from the current position.

Quote
If characters can be anywhere on the screen, with different WalkTo behavior in different parts of the screen, you might need to define regions, and a lookup table from each region to its WalkTo point/offset.

This part I do not understand.


Quote
If a WalkTo point falls outside a walkable area, the character should walk to the closest point it can reach. If that's not acceptable, what is it that you want to happen?

Great! Did not know about this.

Snarky

Quote from: artium on Sat 16/12/2017 14:05:01
This question is not for a specific game, I just experiment with AGS and try to see what is possbile, so it can depend on any of the things you listed.

And that's why it's not built in to AGS: Unlike hotspots, which are stationary and easy to deal with, there's no one standard behavior that is right for characters. It depends, and you have to decide what you want to happen before we can figure out how to make that happen.

Quote from: artium on Sat 16/12/2017 14:05:01I just thought about additional problem. If the player character is already near the NPC character, but the offset makes it walk to a different position, the player will walk to it instead speaking to the NPC from the current position.

So put a test for that in ApproachCharacter().

Quote from: artium on Sat 16/12/2017 14:05:01
QuoteIf characters can be anywhere on the screen, with different WalkTo behavior in different parts of the screen, you might need to define regions, and a lookup table from each region to its WalkTo point/offset.

This part I do not understand.

OK, imagine that there's a game where you're a prisoner in a prison. There's a room with other characters (prisoners and guards) walking around dynamically. You want to have a rule like:

-If you try to talk to a prisoner or guard who's in the main hall, you just walk up to them.
-If you try to talk to a a guard who's on the catwalk above (where you can't go), you walk to a point beneath them to the side.
-If you try to talk to a prisoner who's in one of the locked cells, you walk to the nearest ventilation duct.

(All of this is in one big, scrolling background.)

One way to do this would be to define different regions on the background. The hall would be one region, the catwalk another region, the cells a third region. When you try to talk to a character, the game checks what region they're in, and looks up the x and y offset for that region in the lookup table. E.g. for the hallway that could just be x+10 (you stand next to them). For the catwalk it might be y+150, x-20 (you stand below them to the side). For the locked cells it might be x-40 (you stand by the ventilation duct outside the cell).

In extreme situations such as this you might want to use different logic/calculations for each case, not just different values, but hopefully you see the point.

artium

Ok, now I understand what you meant regarding the regions.

I have one more question.

The "ApproachCharacter" function now looks like that:

Quote
function ApproachCharacter(this Character*,
                           Character*    npc,
                           int           offsetX,
                           int           offsetY
                           /*BlockingStyle blockingStyle, 
                           WalkWhere     walkWhere*/)
{
  int trueOffsetX = 0;
  int trueOffsetY = 0;

  // Facing npc allows calculating direction of approach
  this.FaceCharacter(npc);
 
  switch (this.Loop) {
   
    case eDirectionUp:
      trueOffsetY = offsetY;
      break;
    case eDirectionDown:
      trueOffsetY = (-1) * offsetY;
      break;
    case eDirectionRight:
      trueOffsetX = (-1) * offsetX;
      break;
    default:
      // fall through to eDirectionLeft
    case eDirectionLeft:
      trueOffsetX = offsetX;
      break;
  }
 
  this.Walk(npc.x + trueOffsetX, npc.y + trueOffsetY, eBlock, eWalkableAreas);
 
  this.FaceCharacter(npc);
  npc.FaceCharacter(this);
}

It is working fine as is, but I want to change it a little. I want to be able to interrupt the walking sequence and move the character to a different location.

For this to be possible, this.Walk should be non-blocking.

My question is, how do I determine that the player finished walking and reached it's destination so that a dialog can start?

I had an idea that it is possible to busy-wait for the "Moving" property of the player to become false, but how do I know that it became false due to the player reaching it's destination and not because the walking was interrupted?

Snarky

I'm not sure how to do those kinds of walks off the top of my head, but I know it's part of the Tumbleweed Verbs template (see "Semi-blocking movement functions").

SMF spam blocked by CleanTalk