“Hey, you! Let’s battle!” Adding a Line of Sight mechanic to your NPCs

13

Index

Attached Files

The following files have been attached to this tutorial:

.c3p

npc-los-example.c3p

Download now 255.98 KB

Stats

5,200 visits, 8,851 views

Tools

License

This tutorial is licensed under CC BY 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

Published on 15 Oct, 2019.

Raycasting & Player Detection

As always, we’re going to start by adding this mechanic to the simplest object type – in this case, the static NPC. This NPC does not move unless interacted with by the player so there should be almost no competing mechanics when we add this new one.

Initialisation

Before we start that however, we need to add a few initialisation events. These will run on the start of the layout and set a few things up.

Condition

System ▶︎ On Start of Layout

Action

System ▶︎ Set CurrentLayout to LayoutName

Sub-event Action

Player ▶︎ Set solid collision filter to “NPC” Exclusive

We can also add the event for our ‘Go Back’ button into this group as it doesn’t quite fit anywhere else:

Condition

Button ▶︎ On Clicked

Action

System ▶︎ Go to layout “GameLayout”

Adding obstacles to Line of Sight

Next, we need to assign some obstacles to our ‘Custom’ line of sight system. This can actually be done in the overarching NPC group as we can use one of the families to apply this logic to all of the NPC types.

Condition

System ▶︎ On Start of Layout

Action

NPC_Sprite ▶︎ Line of Sight ▶︎ Add obstacle Player

NPC_Sprite ▶︎ Line of Sight ▶︎ Add obstacle Wall

NPC_Sprite ▶︎ Line of Sight ▶︎ Add obstacle NPC_Base

New states for the NPC

In order to make this system work, the NPC needs a few more states adding to it – Wait, Challenge and StartFight. Some of these will be quite similar to existing states, but it’s easier to have these to allow us to better differentiate what the NPC is doing.

The Challenge state is essentially the same as the pre-existing Walk state:

Condition

System ▶︎ For each Static_NPC_Base

Static_NPC_Sprite ▶︎ State = “Challenge”

Action

Static_NPC_Sprite ▶︎ Set animation to “Walk” & Self.Direction (play from beginning)

Static_NPC_Base ▶︎ Set TileMovement Enabled

Sub-event Condition

Static_NPC_Sprite ▶︎ Direction = “Down”

Sub-event Action

Static_NPC_Base ▶︎ Simulate TileMovement pressing Down

As with previous states, add in the same sub-events for Up, Right and Left.

The two states StartFight and Wait are the same in all but name, a quality they also share with the existing Idle state:

Condition

System ▶︎ For each Static_NPC_Base

Static_NPC_Sprite ▶︎ State = “Wait”

Action

Static_NPC_Sprite ▶︎ Set animation to “Idle” (play from beginning)

Static_NPC_Sprite ▶︎ Set animation speed to 0

Sub-event Condition

Static_NPC_Sprite ▶︎ Direction = “Down”

Sub-event Action

Static_NPC_Sprite ▶︎ Set animation frame to 0

Again, add in the sub-events for the remaining directions, making sure that they match up with the correct frames. If you didn’t want to use these similar states in your project, you could probably achieve a similar outcome using instance variables.

One more thing to do, to make sure we don’t lose any functionality of our NPCs is to tweak the existing Talking mechanic – where the player interacts with an NPC to trigger an action (like a dialogue) but they don’t want to battle. The only thing you need to do here is adding an extra condition to the event block:

Condition

Static_NPC_Sprite ▶︎ Is NOT CanBattle

And this will then allow us to trigger the opacity functions that are acting as a dialogue placeholder. It also means that the ‘battleable’ NPCs can have something to say after you’ve fought them.

Now. Onto the ‘Hey you!’ part of this tutorial.

Casting rays

As you’ve already seen in setting up their states, the NPCs are designed to have an instance variable containing their direction so they’d always record which way they were moving or facing. This variable is now going to be used again so that the NPC can cast a Line of Sight ray in the direction that they’re currently facing. We are also going to use the new CanBattle Boolean to define which NPCs will be using this logic.

Condition

Static_NPC_Sprite ▶︎ Is CanBattle

Sub-event Condition

Static_NPC_Sprite ▶︎ Direction = “Right”

Sub-event Action

Static_NPC_Base ▶︎ Cast ray from (Self.X+8, Self.Y+8) to (Self.X+88, Self.Y+8) (use collision cells)

You can then repeat this for the remaining three directions. Given that this project is laid out in a grid, with the origin in the top left corner (0,0) we have to add the 8 to the X and Y coordinates to have the NPC cast their ray from the centre of the object. In the second coordinate, (where we’re casting the ray to) adding 80 to get +88 is just an arbitrary number, equivalent to 5 squares on the grid. You can play around with this number to achieve the effect you want.

Issuing the Challenge

In the initialisation event block, we added several objects to the Obstacles list for the Line of Sight behavior attached to our NPC Bases. This means that more than just the player object is capable of intersecting the ray cast by the NPC and registering as such. So, we need to add a way of defining what intersects the ray, and only triggering the mechanic if that object is the player. Step forward the RayHitID variable we added to the NPC_Base family. Using this variable in the following events, we can trigger the mechanic based on what intersected the NPC’s line of sight:

Condition

Static_NPC_Base ▶︎ Ray intersected

Action

Static_NPC_Base ▶︎ Set RayHitID to Static_NPC_Base.LineOfSight.HitUID

Sub-event Condition

Static_NPC_Base ▶︎ RayHitID = Player.UID

System ▶︎ Trigger once

Sub-event Action

Static_NPC_Sprite ▶︎ Set animation speed to 0

Static_NPC_Sprite ▶︎ Set InterruptedState to Self.State

Static_NPC_Sprite ▶︎ Set InterruptedDirection to Self.Direction

System ▶︎ Create object Exclamation on layer 0 at (Static_NPC_Sprite.X, Static_NPC_Sprite.Y-16)

Functions ▶︎ Call LOSWalk {NPCUid: Static_NPC_Sprite.UID)

Player ▶︎ Set State to “Talking”

This is all well and good, but currently calling LOSWalk won’t actually do anything, seeing as the function doesn’t exist. So, let’s build that and another function we’ll be using shortly. For LOSWalk:

On function LOSWalk – Number parameter NPCUID

Condition

NPC_Sprites ▶︎ Pick instance with UID NPCUid

Action

NPC_Sprites ▶︎ Set State to “Wait”

System ▶︎ Wait 1 seconds

Exclamation ▶︎ Destroy

System ▶︎ Set State to “Challenge”

And for our other function, NPCFight:

On function NPCFight – Number parameter NPCUID

Condition

NPC_Sprites ▶︎ Pick instance with UID NPCUid

Action

System ▶︎ Wait 2 seconds

System ▶︎ Signal “EndInteraction”

System ▶︎ Go to layout “BattleLayout”

Right, now we have those in place, we can go back to the Challenge mechanic. The LOSWalk function eventually sets the NPC’s state to Challenge so we need to define what to do when that happens:

Condition

Static_NPC_Sprite ▶︎ State = “Challenge”

Sub-event Condition

Static_NPC_Sprite ▶︎ Is overlapping Player at offset (-8,0)

OR

Static_NPC_Sprite ▶︎ Is overlapping Player at offset (8,0)

OR

Static_NPC_Sprite ▶︎ Is overlapping Player at offset (0,8)

OR

Static_NPC_Sprite ▶︎ Is overlapping Player at offset (0,-8)

Sub-event Action

Static_NPC_Sprite ▶︎ Set State to “StartFight”

And finally, it’s time to trigger the layout change, which only happens when the NPC has the CanBattle Boolean set to true and is in the “StartFight” state:

Condition

Static_NPC_Sprite ▶︎ State = “StartFight”

Static_NPC_Sprite ▶︎ Is CanBattle

Action

Functions ▶︎ Call NPCFight (NPDUid: Static_NPC_Sprite.UID)

Static_NPC_Sprite ▶︎ Set CanBattle to False

And that’s that – the game should now switch to the second layout and you’ve already added the events which allow the player to return to the first layout. But, something’s not quite right still.

Fixing the Player

As it stands, the NPC can spot the player, and trigger the required action, you can still see and control the player on the Battle layout, which is not what we want! So, we need to do some tweaking to the player events.

The first thing to do is make the player invisible and uncontrollable in the BattleLayout – the last thing you need in the middle of a fight is a little person wandering about, getting in the way! This will use another On start of layout event:

Condition

System ▶︎ On start of layout

Sub-event Condition

System ▶︎ CurrentLayout = “BattleLayout”

Sub-event Action

Player ▶︎ Set visibility Invisble

System ▶︎ Set group “Player States” Deactivated

System ▶︎ Set group “Player Interactions” Deactivated

But when we come back from the BattleLayout, then the player needs to be seen and controllable once again:

Sub-event Condition

System ▶︎ CurrentLayout ≠ “BattleLayout”

Sub-event Action

Player ▶︎ Set visibility Visble

System ▶︎ Set group “Player States” Activated

System ▶︎ Set group “Player Interactions” Activated

And just to make sure your player doesn’t get stuck in its Talking state, add this final sub-event:

Sub-event Condition

System ▶︎Is NOT BattleInProgress

Player ▶︎ State = “Talking”

Sub-event Action

Player ▶︎ Set State to “Normal”

Now your player shouldn’t be visible when you enter the BattleLayout, but should still function as intended when not on that layout!

Nesting all of that under the one Start of Layout event you had in the initialisation group should look something like this:

Now we can add that challenge mechanic to the other NPCs!

  • 2 Comments

  • Order by
Want to leave a comment? Login or Register an account!