Godot Version
v4.5.1
Question
Hello, I’m working on a top-down RPG and am trying to make some more NPCs. I know it’s not best practice to make a new scene for every NPC, so I coded the main NPC script so I can edit things like the NPC’s name, sprite and the dialogic timeline they use for dialogue in the inspector once I’ve instanced them into the main scene. Here’s part of that script:
@tool
extends CharacterBody2D
# signals
signal chatting
signal stopchatting
# NPC Elements
@export var npc_name = ""
@export var npc_sprite: Texture
@export var dialogue = ""
var scene_path = "res://Scenes/NPC.tscn"
@export var speed = 30 # sets start state and gives move speed
# states
var current_state = IDLE
var dir = Vector2.DOWN
var is_roaming = true # makes the character move to begin with rather than being in chatting state
var player_in_chat_zone = false # to begin with, player is not in the chatting zone
enum {
IDLE,
NEW_DIR, # Adds states for the NPC to be in
MOVE
}
# connects to dialogic
func _ready():
Dialogic.signal_event.connect(_on_dialogic_signal)
randomize()
# changing states and movement
func _process(delta): # this says what to do in each state
if current_state == 0: # don't do anything in idle
pass
elif current_state == 1: # changes direction and sprite
dir = choose([Vector2.RIGHT, Vector2.UP, Vector2.LEFT, Vector2.DOWN])
elif current_state == 2 and is_roaming == true: # move based on direction and if not chatting
if dir.x == -1:
$AnimatedSprite2D.play("left_walk")
move(delta)
if velocity == Vector2.ZERO:
$AnimatedSprite2D.play("left_idle")
if dir.x == 1:
$AnimatedSprite2D.play("right_walk")
move(delta)
if velocity == Vector2.ZERO:
$AnimatedSprite2D.play("right_idle")
if dir.y == -1:
$AnimatedSprite2D.play("up_walk")
move(delta)
if velocity == Vector2.ZERO:
$AnimatedSprite2D.play("up_idle")
if dir.y == 1:
$AnimatedSprite2D.play("down_Walk")
move(delta)
if velocity == Vector2.ZERO:
$AnimatedSprite2D.play("default")
# talking
if Input.is_action_just_pressed("ui_add") and player_in_chat_zone == true:
current_state = IDLE
chatting.emit()
Dialogic.start(dialogue)
print("interacting")
is_roaming = false
$AnimatedSprite2D.play("default")
# choose
func choose(array): # shuffles what option is chosen and chooses one from an array
array.shuffle()
return array.front()
# move
func move(delta): #makes it move if not having a chat
if is_roaming == true:
velocity = speed * dir
move_and_slide()
# for chat detection
func _on_chat_detection_zone_body_entered(body):
player_in_chat_zone = true
# detection zone
func _on_chat_detection_zone_body_exited(body):
player_in_chat_zone = false
is_roaming = true
# when the timer runs out, change the state
func _on_timer_timeout():
if is_roaming == true:
current_state = choose([IDLE, NEW_DIR, MOVE])
print(current_state)
# stop talking when it's over, give item if it's supposed to
func _on_dialogic_signal(argument: String):
if argument == "stopchatting":
is_roaming = true
stopchatting.emit()
Firstly, I haven’t worked out how to edit the NPC’s sprite using this method, that’s not the problem I’m asking about, but any suggestions there would be helpful too. As you can see in the screenshot below, I’ve got one NPC with the variable “dialogue” set to “timeline1”. I’ve also got another NPC instanced with “timeline2” in the same place.
When I play the game in the debugger, there’s a chance that NPC 2 uses timeline 1 when I speak to it. NPC 1 never seems to use timeline 2 and the chat_detection_zones around the NPCs are not large enough to be overlapping. I’m not sure what’s causing this and any suggestions would be appreciated!
