HomeVideos

Create a Reusable Relationship System in Godot 4.5 - Under 15 Minutes!

Now Playing

Create a Reusable Relationship System in Godot 4.5 - Under 15 Minutes!

Transcript

345 segments

0:01

Have you ever wanted your NPCs to

0:03

actually remember how you treat them?

0:05

Today, we're going to build a clean,

0:07

reusable relationship system in GDO 4.5,

0:10

and you'll be able to drop into any game

0:11

in no time. Welcome back to GDAU dev

0:14

checkpoint. I ran a poll recently asking

0:16

what systems you wanted to see most, and

0:19

the relationship system won. But don't

0:21

worry, we're going to do all of them.

0:23

We're going to cover all these topics in

0:24

separate videos in the order they were

0:26

voted for. Now, creating social systems

0:28

can get messy if you hardcode them into

0:31

every single enemy or shopkeeper. So,

0:34

today we're going to build a

0:35

relationship component, a single brain

0:37

that manages friendship, hatred, and

0:40

everything in between. First, we'll

0:42

build our component. Instead of just a

0:44

script, we'll make it a scene. This lets

0:46

us drag it and drop it into any

0:48

character in our game instantly. All

0:50

right. I'm here in a brand new project.

0:51

I'm going to create a new scene. I'm

0:53

going to use other node. Just use

0:54

regular node. And I'm going to rename

0:57

this node to relationship

1:00

component.

1:02

Then I'm attach a script to this. The

1:04

green icon there. I'm going to make a

1:06

new folder. I'm going right click new

1:08

folder. Call it scripts.

1:10

And I'm going to save relationship

1:12

component GD in that and create.

1:15

Then I'm going S and save the scene in a

1:18

new folder called scenes.

1:22

Then save it. Now, let's define what a

1:24

relationship actually is. At its core,

1:26

it's just a number. We need a starting

1:28

value, a maximum value, and a way to

1:30

tell the rest of the game when that

1:32

number changes. Now, back in our

1:34

relationship component script, I'm going

1:35

to get rid of the boilerplate code.

1:37

Going to add in some signals. So, the

1:40

first signal we're going to use is going

1:42

to be called affinity changed, and it's

1:45

going to have a new amount passed into

1:47

it. The second signal we're going to

1:49

want to use is a relationship

1:52

status changed. This how we're going to

1:55

track our friend and foe status or

1:58

neutral. We're going to pass status into

2:01

that. The new amount into the affinity

2:03

change. And we need some export

2:05

variables. First one we're going to use

2:06

is current affinity.

2:10

And we're going to have that to be int.

2:12

We're going to start at 50. So neutral.

2:15

The next export variable we're going to

2:17

use is max affinity.

2:20

We're going to set that in to be 100.

2:23

So if we're at 100 or friends, then we

2:26

need a minimum affinity. So if this is

2:29

zero, we're going to be enemies. And we

2:32

also need to set the

2:36

friend and foe thresholds. So when to

2:39

become friends, when to become enemies.

2:42

So we're going to set that at 70 to be a

2:44

friend.

2:45

And for enemies, we're just going to go

2:48

to 30. So foe or enemy threshold,

2:53

set that in to 30. So it goes below 30,

2:56

we're going to be enemies. Then we need

2:58

a a function here. And we're going to

3:02

call change affinity and we're going to

3:05

change it by an amount. And int is going

3:07

to be that amount. So say if we give a

3:09

gift or if we insult them, we're going

3:11

to change that current affinity by that

3:15

amount we're passing into this function.

3:20

Well, then we also need to emit this

3:22

signal to say this happened. So affinity

3:25

changed. This is a signal. We're going

3:27

to emit it. And what we're going to emit

3:30

is the current affinity

3:33

right there. So then we need to check

3:36

for status changes. Let's check for

3:38

status change. So if current affinity

3:43

right there is greater than or equal to

3:46

our friend threshold, well we need to

3:49

change our relationship status.eit

3:53

to friend

3:55

just like that.

3:57

But if it's not if the current affinity

4:00

is

4:01

less than or equal to the enemy

4:04

threshold, well say the relationship

4:08

status has changed again to enemy.

4:13

But if it's neither of these things,

4:15

well, it's going to be right in the

4:16

middle. So the relationship status there

4:19

is going to be emitted as neutral.

4:24

Just like that. Go and save the script.

4:27

Now you've reached your first

4:28

checkpoint. Stop and look at the code

4:30

what we just wrote. Current affinity

4:32

plus equals amount. What happens if we

4:34

add a thousand to it or subtract a

4:36

thousand? We don't fix this. Your player

4:38

could have 5,000% love with a goblin or

4:41

negative infinity with the merchant

4:43

breaking your game logic. We need to

4:46

clamp it. So this variant here, clamp

4:49

value variant minimum to maximum. That's

4:52

what we want to use to clamp our

4:54

affinity. So, back in our project, right

4:57

below our current affinity here, we want

4:59

to set our current affinity

5:02

to equal a clamp. We want to set it

5:05

between a variant, which is what we want

5:07

to set to a minimum and a maximum. So,

5:10

go ahead and give that a try. Clamp our

5:12

current affinity between the max and the

5:15

min affinity. Give that a go and pause

5:18

the video now.

5:22

How'd that go? Did you get it? Let's go

5:24

and do it together. If you didn't, that

5:26

is just fine. So, all we need to put

5:28

here is our current affinity. We're

5:30

going to clamp it between our min

5:31

affinity

5:33

and our max affinity.

5:37

Just like that. Make this bigger. So, we

5:41

should also have something that says,

5:43

hey, what is our current and old

5:46

affinities?

5:48

So, our new affinity, we're just going

5:50

to print it to a debug console here.

5:52

We're going to point out our current

5:53

affinity

5:55

and that's going to be a debug check. So

5:58

you make sure this is going up and down

6:01

as intended. The key takeaway here is

6:03

always sanitize your data. Using clamp

6:06

ensures your values never escape the

6:08

boundaries you set, preventing

6:10

gamebreaking bugs later. Now for a

6:12

little bit of magic. Because we saved

6:14

our component as a scene, adding it to

6:16

an NPC is effortless. And a number in

6:18

the console is boring. So let's make

6:20

that visual. We'll add a progress bar

6:22

that updates automatically whenever our

6:24

relationship changes. Going to make this

6:26

smaller here. I'm going to create a new

6:27

scene. Scene. New scene.

6:30

It's going to be a 2D scene. I'll rename

6:32

the root node to NPC.

6:36

Go back to my 2D scene here. I'm going

6:38

to add a child node of sprite 2D.

6:41

I'm going use the GDO icon that comes

6:43

with every project as my character. We

6:47

also want our new relationship component

6:49

in here. the relationship component

6:51

scene. We're going to drag and drop that

6:53

onto NPC. Now we have it on our NPC.

6:57

What we're also going to do is add a

6:58

progress bar to see the results. So add

7:01

child node NPC, which means the progress

7:04

bar so we can see how friendly or faux

7:06

we are. Now with the progress bar

7:09

selected, I'm going to go over to the

7:11

inspector here. I'm going turn off the

7:13

percentage. I'm going to set the current

7:15

value to 50. So neutral and go down to

7:18

layout. set the custom minimum size to

7:22

100 on the X so we can see it. I'm going

7:24

change the anchor presets to center.

7:29

Now I'm going to go into my window here

7:30

and zoom in a bit. I'm going use my move

7:32

tool. I'm going to drag this straight

7:36

up.

7:37

There we go. Right above our character's

7:39

head. And we're click on our root node

7:41

at PC. We're going to add a new script.

7:44

We're going to put it in our scripts

7:45

folder. Make sure scripts open scripts

7:49

create. I'll make this bigger. I'll go

7:52

ahead and delete this boiler plate code.

7:54

Once that's gone, I'll make this smaller

7:56

again so I add some onesies. I need a

7:59

relationship component reference. I'm

8:02

going to click and drag and hold down

8:03

control and drop that. Now I have that

8:05

in my script. And I also need the

8:07

progress bar. Click, drag, controll,

8:10

drop. All right, now I have those. I'm

8:12

going make it bigger. Enter down a

8:14

couple times. I'm going to create the

8:16

ready function

8:18

right there. And then I may click pass

8:21

real quick as a placeholder. Enter down

8:24

a couple times and create a function.

8:26

It's going to be called on affinity

8:29

changed.

8:31

And then we're going to take in a new

8:32

value into this function. New val.

8:36

And then what this is going to do is

8:38

change the progress bar

8:41

value

8:43

with a new value.

8:47

So when this function is called, it's

8:48

going to change our progress bar. Then

8:51

back in our ready function, we need to

8:52

commit to this signal from the ready

8:55

function

8:57

in the relationship component. The

8:59

signal we want to commit to is the

9:01

affinity changed. and we want to

9:04

connect. And we want to connect with our

9:08

function we just wrote on affinity

9:10

changed. Now we have the brain and the

9:12

visuals. Let's create a fake interaction

9:14

to test it. We'll bind the space bar to

9:16

give a gift and escape to insult them.

9:19

Our component also shouts out friend or

9:21

enemy whenever the value crosses those

9:23

lines. Let's make our NPC listen to it.

9:26

Now we're going to go back to our NPC

9:28

script and save it. and the NPC scene.

9:33

We also need to save that. And back in

9:35

our NPC script and create a new function

9:37

underneath on affinity changed. That's

9:40

going to be on status changed.

9:43

Our new status for our friend or foe.

9:46

Now we're going to use we'll just print

9:48

out to the console as well. So our

9:50

current status

9:53

is going to be

9:56

our new status.

9:59

It helps if you do a underscore in

10:02

between. There we go. And then after

10:05

that, well, we want a simple visual

10:07

feedback so we can see on the screen

10:11

what's happening as well. So if new

10:13

status

10:15

is ren,

10:18

well, we're going to change the progress

10:20

bar to green. We just do progress bar

10:23

modulate

10:24

and we're going to change the color to

10:26

green.

10:28

Just like that. And then well for enemy

10:31

of course I'm going to use red. So enemy

10:36

progress bar

10:39

modulate

10:40

to red.

10:42

There we go. And also if for neutral

10:47

neutral usually runs the gray gambit. So

10:50

we'll do the progress part. Modulate

10:54

colors gray.

10:56

There we go. Notice going to connect

10:58

this function to our relationship

11:00

component. So back in our ready

11:02

function, I'm going to connect the

11:04

relationship component, the relationship

11:07

status

11:09

relationship status changed function.

11:14

Connect that to our new function of pod

11:17

status changed

11:20

and save that. Now we add some input

11:23

function here. So underneath the on

11:25

status change, we're going to do an

11:26

input event function to listen for our

11:32

space bars and our escapes. So if event

11:35

is action pressed, there we go. We want

11:39

to look for UI except that's going to be

11:42

our space bar. By default, GDO has a UI

11:46

accept in it already, so we don't have

11:48

to set that up. So space bar. Now if

11:51

that's pressed well this is going to be

11:52

the give gift.

11:56

There we go. Then we need to change our

11:58

relationship component change affinity

12:03

change affinity

12:06

to 10. And then well if we press escape

12:10

we want to do the opposite. So the vent

12:13

is action pressed. The cancel is the

12:16

escape one. Here I cancel. There we go.

12:20

And then we want to set that to be the

12:22

it's going to usually the escape button

12:24

escape button. And if we do that, well,

12:27

we want to print insulted,

12:31

right? But we also want to change our

12:33

relationship component. Change affinity.

12:37

And we want to do that to minus 10.

12:40

We're going to go and save that. Make

12:42

this smaller. Go back to our 2D scene.

12:45

I'm going to zoom out here. I'm going

12:47

grab the NPC. Use the move tool. And

12:50

this is our viewport window. I'm going

12:51

to drag our NPC to the middle so we can

12:53

see them. Save. We're in the current

12:56

scene.

12:58

All right. Now, I'm going to drag up so

12:59

we can see the console. I'll press

13:01

spacebar. And we go up up. Now we're a

13:04

friend. We go up up to 100. Then we stay

13:08

at 100. Then escape. Back down. Down.

13:12

Down. Now we're neutral. We're back to

13:13

gray. Now we're red. Enemies. Down.

13:17

Down. tab. Now we stay at zero. And just

13:20

like that, you have a fully functional

13:22

relationship component. The beauty of

13:24

this is that the NPC script doesn't need

13:26

to know how the math works. It just

13:28

tells the component add 10, remove 10,

13:31

and the component handles the signals,

13:32

the clamping, and the logic.

13:35

You can drag this specific node onto a

13:37

shopkeeper to determine prices or a

13:39

quest giver to unlock secrets. It's

13:41

completely reusable.

13:43

expanding this, we can take this a step

13:45

further and use resources to give our

13:46

NPC specific likes or dislikes. So, you

13:49

can't just spam the same gift over and

13:51

over. Let me know in the comments if

13:53

that's something you'd be interested in.

13:55

Now, if you found this helpful at all or

13:56

fun, leave a comment what your GDO game

13:59

dev goals are. Please like, subscribe,

14:01

and hit that notification bell. It

14:03

really helps out the channel. And thank

14:04

you to all our current and past Patreon

14:06

and coffee members. Your generous

14:08

support really keeps the channel moving.

14:10

If you want early access to tutorials,

14:12

source code, or suggesting future

14:14

tutorials, please consider becoming a

14:16

member yourself. The links are in the

14:18

description. I am Spaghetti Syntax, and

14:20

remember, always stay curious.

Interactive Summary

This tutorial demonstrates how to build a clean, reusable relationship system in GDO 4.5, allowing Non-Player Characters (NPCs) to remember player interactions. It covers setting up a relationship component as a scene, defining affinity values and thresholds for friend/foe/neutral statuses, and using signals to manage changes. A key aspect is clamping affinity values to prevent game-breaking bugs. The video also shows how to visualize relationship status with a progress bar and integrate simple player inputs, emphasizing a modular design for easy reuse across various NPC types.

Suggested questions

6 ready-made prompts