Tutorials · Navigation and AI · Chapter 7
Eyes
By the end
guard.rgswith sight: as far as112,0.6radians to either side of the way he faces, and no wall on layer1between them; seen, he goes to where she stood- Sees, a field you can watch in Play, ticked while she is in his cone
cone, a picture of his cone under him, turned bycone.rgsto point the way he walks
On this page
What he sees
The corridor guard hears a noise, and is blind. His script now asks three questions on every step: where is the thief, is she ahead of him and near enough, and is there a wall between them. When all three answers are yes, he has seen her, and he goes to where she stood, the way he goes to a noise.
- Click the Code tab, then
guard.rgsin its Scripts list. Replace everything in it with the script below, and press Ctrl+S.
/// The corridor guard walks his round from end to end. A noise near enough
/// by road sends him to it the shortest way round. He sees ahead of him, in
/// a cone, as far as his sight and no further than a wall: when the thief is
/// in it, he goes to where he saw her. Either way, he looks about a moment
/// and goes back to his round.
enum Errand { Round, Noise, Look }
component Guard {
/// One end of his round.
post: Vec2,
/// The other end.
far: Vec2,
/// How far by road a noise can be and still be heard.
#[suffix("u")]
hearing: f32 = 320.0,
/// How far he sees.
#[suffix("u")]
sight: f32 = 112.0,
/// How far to either side of straight ahead he sees, in radians.
cone: f32 = 0.6,
/// The collision layers that hide what is behind them.
walls: i32 = 1,
/// What he is about.
#[readonly]
errand: Errand = .Round,
/// Whether the thief is in his cone, as this step began.
#[readonly]
sees: bool = false,
/// Which way he faces: the way he last walked.
#[hidden]
facing: Vec2,
/// Where he stood the step before.
#[hidden]
was: Vec2,
/// Whether he is walking to the far end.
#[hidden]
outward: bool = true,
fn on_create() {
was = position();
facing = vec2(0.0, 1.0);
walk_to(far);
}
fn loop() {
let moved = position() - was;
if moved.length() > 0.1 {
facing = moved;
}
was = position();
let thief = nearest("thief");
let at = position_of(thief);
let toward = at - position();
sees = thief != node(0)
&& toward.length() < sight
&& angle_between(facing, toward) < cone
&& can_see(at, walls);
if sees && errand != .Look {
errand = .Look;
walk_to(at);
}
}
fn on_signal() {
if heard("noise") && errand == .Round {
let noise = kept_point("noise", position());
let way = road_distance(noise);
if way >= 0.0 && way < hearing {
errand = .Noise;
walk_to(noise);
}
}
if heard("nav:ended") {
if errand == .Round {
outward = !outward;
} else {
if arrived() {
await wait(2.0);
}
errand = .Round;
}
if outward {
walk_to(far);
} else {
walk_to(post);
}
}
}
}
Select guard_corridor. His Guard section has gained four rows, each at the value the script gives it: Sight 112.00 u, Cone 0.60 and Walls 1, and Sees, greyed. Leave them so: there is nothing to type.
- Sight
- The Guard’s Sight: how far he sees, in world units, straight from his feet to hers.
112is seven tiles, about a third of the view’s width: he sees her before she reaches him, and not across a room. - Cone
- The Guard’s Cone: how far to either side of straight ahead he sees, in radians, not degrees.
0.6is about 34° each way, so the whole cone is about 69° wide. Over1.57, a quarter turn, he would see beside him. - Walls
- The Guard’s Walls: the collision layers that hide what is behind them, as a mask.
1is the walls’ layer, the one the keep’s walls are on. - Sees
- The Guard’s Sees: whether the thief was in his cone as this step began.
#[readonly], so it is greyed and you cannot tick it; in Play you watch it tick and untick. .Look- A third errand: going to where he saw her. While it lasts, seeing her again does not send him again, so he walks to where she stood, not after her; a chase is chapter 8’s.
facing,was- Two hidden fields.
wasis where he stood the step before; when he has moved more than0.1since,facingbecomes the way he moved. So he faces the way he last walked, and a guard standing still keeps facing where he was going. He starts facing down,vec2(0.0, 1.0). - nearest
- The nearest shown node in a group, other than this one, as the step began, by straight distance:
nearest("thief")is her. When there is none, it is nobody, node(0), which is whatthief != node(0)asks about. - position_of
- Where that node stood in the world as the step began: her feet.
.length()- How long a Vec2 is.
towardruns from his feet to hers, so its length is how far apart they are;moved.length()is how far he walked in a step. - angle_between
- The angle between two directions, in radians:
0when they point the same way, half a turn, about3.14, when they are opposite. Undercone, she is ahead of him. - can_see
- Whether no wall on the layers in its mask stood between this node and a point as the step began. A wall is a solid collider on a shown node that is not a walker, or a solid tile layer, on those layers. Areas never hide anything, and neither does another walker.
&&- And:
seesis true only when every part is: she exists, she is nearer thansight, she is insidecone, and no wall onwallsis between them. fn loop()- Runs on every step. He looks on every step, not only when a signal comes.
A cone you can see
Nothing in the game draws what a guard sees: his sight is three numbers and a line. A cone you can see is a picture under him that points his way. cone.png came with the starter, in art/: a wedge 112 long, his sight, and 154 across at its end, so its sides are his cone’s.
- Select
guard_corridor, press + Add node, choose Sprite, and rename itcone. It lands under him. - Set its Position to
0and-8, clicking each box once, typing and pressing Enter. - Drag
art/cone.pngfrom Assets onto the Sprite’s Texture, set Size to112and154, the picture’s own size, Anchor to0and0.5, and Sort Layer to1. - In Assets, press Create, choose New script, type
cone, and press Enter. In the Code tab, replace everything incone.rgswith the script below, and press Ctrl+S.
/// The guard's cone of sight, drawn: it turns to point the way he walks.
component Cone {
/// Where it stood the step before.
#[hidden]
was: Vec2,
fn on_create() {
was = position();
}
fn loop() {
let moved = position() - was;
was = position();
if moved.length() > 0.1 {
anneal_rotation(angle_of(moved), 0.15, .OutQuad);
}
}
}
- Select
cone, click Add component and choose Cone. Its one field,was, is hidden. - Press Ctrl+S.
- Sprite Texture
- The picture the Sprite draws:
cone.png. - Sprite Size
- How large it is drawn, in world units:
112by154, the picture’s own pixels, one unit each. - Sprite Anchor
- Which point of the picture sits on the node, as a fraction of its width and its height.
0,0.5is the middle of its left edge: the wedge’s tip. So the tip sits on the node, and the picture turns about it. A new Sprite’s is0.5,0.5, its middle. - Sprite Sort Layer
- Which pictures draw over which.
1is the layer the tables and the pots are on: the wedge lies on the floor, under the guards and the thief. - angle_of
- The angle a direction points at, in radians, as a rotation:
0to the right, a quarter turn, about1.57, straight down. The way the cone moved is the way he walked. - anneal_rotation
- Turns this node to an angle, the short way round, over a time:
0.15seconds here, with the curve.OutQuad, fast at first and slowing as it arrives. So the cone swings round with him rather than snapping.
What hides her
- Press F5. Walk the thief out of the courtyard and west along the corridor’s carpet, towards the corridor guard as he walks east. When he is near enough, he sees her, and walks to where she stood.
Behind him, or further off than 112, she is not seen, though she stands on the same carpet. Now put a wall between them.
- Walk the thief up into the hall and stand her against its bottom wall, just above the corridor, west of the ladder. Select
guard_corridorand watch Sees as he walks east below her. Press F5 again to stop.
What the chapter set
| Node | Component | Field | Value |
|---|---|---|---|
guard_corridor | Guard | Sight | 112, as the script gives it |
| Cone | 0.6, as the script gives it | ||
| Walls | 1, as the script gives it | ||
cone, under guard_corridor | node | Position | 0, -8 |
| Sprite | Texture | cone.png | |
| Size | 112, 154 | ||
| Anchor | 0, 0.5 | ||
| Sort Layer | 1 | ||
| Cone | added |
Every other field is as it comes. The guard’s Post, Far and Hearing are chapter 6’s.