Godot Version
v4.6.2.stable.official [71f334935]
Question
“Invalid access to property or key ‘curhp’ on a base object of type ‘Nil’.”
I am trying to add hp to my game but when I run it it returns the above error.
Here is the code:
hit_box.gd:
extends Area2D
signal take_damage
@export var health : healthnode
func _process(delta: float) -> void:
if area_entered:
emit_signal("take_damage")
health.curhp -= health.maxhp * 0.1
healthnode.gd
class_name healthnode
extends Node2D
var maxhp := 100
var minhp := 0
var curhp : int:
set(value):
curhp = clamp(curhp, minhp, maxhp)
print(value)
func _ready() -> void:
curhp = maxhp
Sorry if I did not provide sufficient information, I’m (relatively) new to godot.
Where do you assign healtha value?
A few issues here:
- You named your class in
flatcase whichisalllowercasenospacesorunderscores and is very hard to read. Class names should be PascalCase according to the GDScript Style Guide.
- You have not provided the full error. It tells you what line of code it affects. Please right click errors and paste the full error here.
- It is possible that this particular error is related to your scene tree. Please take a screenshot and post it.
- Abbreviated variable increase the cognitive complexity of your code, making it harder for future you to read, as well as anyone else who is trying to help you. They have no upside.
A few suggestions on improving your code:
class_name Health extends Node
const MAX_HEALTH = 100
const MIN_HEALTH = 0
var health : int = MAX_HEALTH:
set(value):
health = clamp(health, MIN_HEALTH, MAX_HEALTH)
print(value)
func damage(amount: int) -> void:
health -= amount
func heal(amount: int) -> void:
health += amount
- You don’t need to call it a node.
- PascalCase
- It doesn’t need to extend Node2D because it doesn’t exist in space. Anything hanging off it should probably not be.
- Creating helper functions makes the code easier to read. (See below.)
extends Area2D
signal take_damage
@export var health: Health #This should be an @onready variable
func _ready() -> void:
area_entered.connect(_on_area_entered)
func _on_area_entered(area: Area2D) -> void:
take_damage.emit()
health.damage(1)
A few more things:
- You are using the Godot 3 way of emitting a signal.
- You don’t want process monitoring signals. You want to connect to them. This could be the cause of your problem, because
_process() starts running as soon as the node is _ready() and if something else isn’t (like your Health node) you’ll get the error you were seeing.
- The code for taking damage is now clearer, and you only have to do the math once - inside the Health component.
- This should make your code work, but you probably want to be calling this from the thing causing the damage, so you can pass damage to Health based on the enemy.
Anyway, try that code out. It may solve your problem.
I also recommend you read this I reply I made to someone else with a mini Health component tutorial: Am I doing Components/composition right? - #3 by dragonforge-dev
it keeps on printing 99 instead of lessening the health, and when I have the health value be printed it stayed at 100
also it works if i comment out the
set() part but then it doesnt keep it between 0 - 100
Yeah, I just copied your code for the clamp(). I see the issue. You are assigning the value back to itself, not changing it. Also, you were printing the value instead of health when you changed it.
class_name Health extends Node
const MAX_HEALTH = 100
const MIN_HEALTH = 0
var health : int = MAX_HEALTH:
set(value):
health = clamp(value, MIN_HEALTH, MAX_HEALTH)
print(health)
func damage(amount: int) -> void:
health -= amount
func heal(amount: int) -> void:
health += amount
Try that. It should work now.