Need help refactoring my code into RESOURCES rather than a variety of nodes, mainly how to use Skills/Abilities

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By loreplays

I’ve been watching youtube videos about Resources for most of the morning and I tried implementing them myself, but I just can’t wrap my head around it. Basically I want to replace my current skill system with resources to clean things up.

My current structure uses way too many nodes. Skills can currently be KinematicBodies, RigidBodies, or Areas. I have a corresponding script for each of these called “Kinematic_Initialize” or “RigidBody_Intitalize” etc. that extend from their respective node. And then I have actual packed scene (with the sprite, animation, etc.) that extends from each of those scripts. The problem is that these 3 “initialize” scripts are all basically exactly the same. They’re importing data from my skillsheet json file (which I want to replace with resources eventually but one step at a time). Additionally, the packed scenes that extend from the initialize scripts are also all almost the same (they all have sprite, animationplayer, etc.)

Here’s an example of an initialize:

class_name SkillInit_Area2D extends Area2D

onready var signalBus = SignalBus

onready var skillSprite = $Sprite
onready var animationPlayer = $AnimationPlayer
onready var hitbox = $AbilityHitbox

var skillName: String
var skillElement: String
var skillType: String
var cooldownDuration: float
var skillDamage: int

	skillElement = DataImport.skill_data[skillName].Element
	skillType = DataImport.skill_data[skillName].SkillType
	cooldownDuration = DataImport.skill_data[skillName].CooldownDuration
	skillDamage = DataImport.skill_data[skillName].SkillDamage

+ a variety of functions like scaling the sprite, checking aoe timers and starting those, etc.

And then the Area2D that extends that, “extends SkillInit_Area2D”, has the specific behaviors for Area2D skills. The actual skills inherit from this where I can change the sprite, etc. Same with Kinematic and Rigid.

This is all fine except that, again, all my packed scenes are the same, and the data import itself is all the same, so when I want to edit/add/remove one thing, I have to do it in 3 different places.

I feel like using resources would clean this up tremendously but I’m having issues implementing.

When the player uses an ability, it begins with an “AttackManager” node I have attached to the Player. It catches the Skill Name, adds the right skill as a child (which is only using KinematicBody script, Area script, etc.), and then sends the Skill Name to the Child which then checks the “Initialize” script to see how much damage it does, its cooldown, etc.

So what I did was make a resource called “player_ability_data” that includes all of the variables and import data of the skills. I then have a KinematicBody2D node with a script that starts with:

extends KinematicBody2D
onready var skillData = load("res://resources/abilities/")

func _ready():
	skillData.skillName = skillName

But then when I try to use this ability in game, I get the error: “Invalid set index ‘skillName’ (on base ‘GDScript’) with value type of ‘String’” which makes it sound like I can’t send the skill’s name into the Player Ability Data so it can get the right cooldown, damage, etc.

When I instead use

export(Resource) var skillData

And then load the script through the editor, I don’t seem to need to use “skillData.skillName = skillName”, but I get error that it can’t reach the “knockback_vector” variable.

func projectile():
	var projectileRotation = Vector2.RIGHT.rotated(rotation)
	skillData.knockback_vector = projectileRotation

Overall I am just super confused and can’t figure out how to do this right. Sorry if my explanation is confusing, I just wanted to be thorough. Any help would be super appreciated. I am also willing to completely redo things from scratch. Thanks so much!

tldr: How do you use Resources for different types of Skills?

:bust_in_silhouette: Reply From: zhyrin

When you write

onready var skillData = load("res://resources/abilities/")

skillData will be the GDScript resource you loaded. if you want an instance of the Resource your script implements, you need to write

onready var skillData = load("res://resources/abilities/").new()

As for your knockback_vector issue, I can’t really help as I don’t know how it’s declared, assigned it’s default value, at what stage projectile() is called, and also, I don’t think gdscripts throws an error that it “can’t reach” a variable. An exact error message would have been a lot more helpful (like with the previous, it pointed out exactly that the type of skillData was GDScript).

Putting all the data they share in the same place is a step in the right direction. You might also want to think about whether they need to be 3 separate classes (kinematic, rigid and area), maybe you could just use one type.
Without having any more insight, I would try creating a class that is responsible for creating and initializing these abilities, that way all the logic is in one place, not repeated three times.
Another idea is that maybe they can share everything except their base type? In that case you could make your ability a scene that would be a child of a specific kinematic / rigid body or area, and the child scene could simply reference it’s parent (note: all physics objects inherit from the same parent: CollisionObject/2D)