Issue with changing my character's "Job"

Hello there,
I am fairly new to C# and after watching a ton of raw theory tutorials, I tried to make my own Player script, which includes some statistics and a bunch of other experimental stuff
I also tried to give it a Job (didn’t want to call it a class so I don’t get confused with the c# “class”), like we find in tons of games. So I created a Job script

Right now the way it’s working is i’m putting the job script as a component in an empty GameObject, fill in the stats, make it a prefab and drag/drop it into my player Script
While this is really handy for prototyping/playtesting purpose, it think (correct me if im wrong) that it would be very clumsy for an finished game.

So I tried to use Constructors, and the concept of classes and objects to create Jobs but I can’t see how to approach the problem…
How do I create Jobs from my Job script, store them and reference them, to use them in a still-to-be-built ChangeJob() function for example ?
This is a big question for me since it would also solve the inventory problem at the same time :slight_smile:

Sorry if the post sounds messy, i’m still having trouble linking theory, proper vocabulary and practice !

Thanks in advance

Here 's the code :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Job : Player {

    public string job_Name;
    public int job_iD;
    int tier;
    public string job_Description;
    public Skill[] job_Skills;

    public int healthmodifier;
    int moralemodifier;
    int energymodifier;
    int painmodifier;
    Player player;

    public Job (){
       
    }

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {

    string name;
    int iD;

    public int health = 100;
    public int pain;
    public int moral;
    public int energy;
    public int Armor;
    public int Resistance;

    public Job job;
    public Item[] Inventory;
    public Item[] Stock;
    public Item[] Equipment;
    public Skill[] Skills;


    Player.job

    void Start () {
    //    Debug.Log ("Health is" + health);
    //    health = health + job.healthmodifier;
    //    Debug.Log ("Health is" + health);
    //    Debug.Log (job.name);
    }

}

If the job is just ‘stats’ you could create the job class as: regular class, or ScriptableObject.
In your example, ‘Job’ inherits from ‘Player’ – not sure if that’s what you want.
If ‘Job’ is going to have behaviour, then you would maybe want it to be a class derive from MonoBehaviour (unless, just checking, you want it to derive from Player, and I just don’t understand that, yet).
Then, you could add ‘job’ components to game objects and set them up and reference them, etc…

If Job was a regular class (not derived from MonoBehaviour or Player), you could then create a job (somewhere in the player script or elsewhere), like:

Job j = new Job(/*optional constructor stuff*/);
// you could then assign that to the player?

ScriptableObjects are great if you want to work with them - they’re assets, and you can create as many Job assets as you want, and drag / drop them in the inspector where you need them.

Before I explain more on any 1 topic, let me know if any of this sounds remotely useful :slight_smile:

If you think you could explain a little more what you’re intentions are for some more clarified responses, please do so :slight_smile:

2 Likes

Thanks for your reply !
First of all, yes, this sounds more than remotely useful !
The fact that my Job script derives from my Player script is mainly due to me experimenting with Inheritance, I thought it would be easier to make both scrips communicate with each other but i’m actually not sure about the exact implications ! By the way, if Jobd erives from Player which derives from MonoBehaviour, wouldn’t Job have access to the MonoBehavior functions ?

I tried to include a function like the one you wrote but I couldn’t make it work : what I did was

Job job1 = new Job
healthmodifier = 50

Job job2 = new Job
anyparamateter = …

And so on

But everytime i tried to access it through something like player.job,jobname, there just wouldn’t be any completion avaliable, just as if it didn’t exist… I assumed that I didnt use the correct syntax to assign it to the player !
Does it have to do with the fact that my Job is deriving from another class (not a “regular” class) ?
And if so, what is the syntax to do it ?
I think that I might have missed one of the theory concepts in here !

As far as Scriptable Objects are concerned, it sounded promising to me when I read about these but then I noticed a that the unity tutorial about scriptable objects mentions that they are "not meant to be- and shouldn’t be attached to game objects"…
If I do use scriptable objects, is it possible to manipulate them through code to change a player’s job or is it limited to editor drag n drop ?

My objective, if that helps, is to create a Player system that uses jobs, skills, items… as “cartridges”, that could be plugged in and out to create a modulable system, that can be intuitively manipulated through code (and if possible through editor) !

Sorry again for the approximative nomenclature and the long-ish post !

Ideally, you should decide whether or not to make a certain class to inherit from another one based on their semantics, rather than on any conveniency reasons.

There’s a well known ‘test’ to see if it’d make sense to use inheritance in such a case. In summary, you can check if there’s an ‘is-a’ relationship between the two concepts, and use inheritance only when there is.

In your case, “a job is a player” doesn’t make much sense, so you better not make the former to extend the latter.

If you can’t get auto-completion working with “player.Job”, then ensure the actual type of the ‘player’ variable is ‘Player’, and if it is you might suspect there could be some error in your IDE settings.

1 Like

@mysticfall made some good points… :slight_smile: I won’t repeat what he said.

If you only wrote: Job job1 = new Job that’s an invalid syntax.
if Job is still deriving from Player (refer to the poster above – probably not what you want).
Then, as a regular class you can create new ones like that…

However, since you said you read about scriptable objects and you want to use them as “cartridges”, I really do think they might be a good fit for you.
Though this is not the only way you can use them – scriptable objects are great (imho) for storing “data stuff” :slight_smile: for lack of better terminology. If you create, say x number of jobs… each with their own stats. You can make your Job class (inherit scriptable object), and then create 10 instances (10 jobs).
You can then make variables on your monobehaviours to assign these.

When the manual says you don’t add them (and can’t add them) directly to game objects, that’s true… but you can reference them in scripts.

Again, just to be clear and not miss anything – they could be regular C# classes, too. it’s an option. :slight_smile:

You can manipulate either in code for assigning. Scriptable would be read-only (for sake of argument/not extending this topic right now) - but you could have a wrapper around it… feel like i’m getting side tracked.

Inheritance is good to learn/practice/know but that doesn’t mean you want to use it for no reason :slight_smile:

Oh, back to your question about “Would it have access to monobehaviour stuff” (deriving from player) – yes, it would… but (again) not what you want here…

I’m tired here, and fear I’m rambling… I will wait to write more when I’m rested and see what you have to say after you write back :slight_smile:

2 Likes

Thanks a ton to the both of you !
@mysticfall Thanks, I think I visualize what Inheritance is about much better now ! I’ll definitely remove the Player inheritance from the job. If I understood well, i could use Inheritance when I get to my items , to create stuff like sub-item classes ? (weapons potions or anything like that)

@methos5k Okay ! So what I should do (if I understood you correctly) is make the Job a class of its own, not inheriting from anything in particular, and make it a ScriptableObject of which I would create one instance per actual Job, and these instances could be passed to the player script ?

If so, you are perfectly right that it sounds exactly like what I need !
Going back to the items I mentioned in my answer to @mysticfall ,
Following this ScriptableObject pattern, it sounds like I could also build some sort of “item database” with this, right ? By that, I mean set up several explicitly-coded instances of a Item scriptableObject and gather them in an Itemarray like the ones I have in my Player code up there (to access them via code through their index in array). I know XML or .json are the go-to ways to build databases, just wondering if it would be doable like this, with C# all the way !

And, one last question about these scriptable objects; while this is not particularily important for jobs since they are just “data” (by which I mean mostly variables like floats, ints…), if I wanted to replicate such a pattern for, let’s say Items or Skills, i would probably need some custom functions for the effects they have (inflict damage, or whatever it is). I understood ScriptableObjects as being “data containers”, but earlier you mentioned
:

If I understand you well there, it means ScriptableObjects can also carry functions, right ? Besides the usual variables?

(I know this “item” thing is not exacly the topic, but I though i’d ask since the same mecanism is involved)

Sorry if my thought process is messy, I guess my post is sort of unclear but I feel like I’m starting to understand the global idea !

[EDIT : The reason I’m giving pretty specific examples is not because I want to follow them, but because it helps me understand the concepts to make the link between a coding concept and a concrete example of usage within a game :)]

I understand why you wrote all that. It made perfect sense to me, and was easy to read.

I’m exhausted atm… :slight_smile: But the short answer(s) are… yes, I think they can be a good fit for what you talked about.

Yes, you can add methods to them. However, sometimes (depending on what you’re doing), you have to pass them data (like a game object parameter) because they can’t access it the same way you would do with a monobehaviour.

If the script you conceive of (at any time - for anything) – is truly about behaviour, then I would think you’d prefer to make a (Monobehaviour) :)… eg: (might be) grenade flying/explosion. anyways… lol

For me, I use regular classes for simple things I want to group together (or maybe a struct). ScriptableObjects for game data (when appropriate - like items ;)). and monobehaviour elsewhere.
As with all things in life, you don’t have to live & die by those (my) examples…

hopefully that makes sense…

Yes, you can use xml/json or whatever suits you depending on your needs.

note: a job (scriptableobject) by definition, would be inheriting from ScriptableObject :slight_smile:

Try this:

// using statements you need here, not commented out, of course.
[CreateAssetMenu("menuName="Scriptable/Job")]
public class Job : ScriptableObject {
  public string jobName = "";
  public int tier = 0;
  }

then , right-click ->create (you should see a new menu) or in the top menu of Unity it’s there , too.

Sorry, my writing is getting worse. I’m off to sleep. Good luck :slight_smile: Maybe I’ll see an update later.
sigh – the code tags failed me a little bit there, but it just looks ugly, but I think it’s still written correctly :slight_smile:

1 Like

You are right. Making each concrete item classes to extend a common abstract item type would be a legitimate application of inheritance (though it might not be an ideal approach in Unity, depending on circumstances).

As to the other points, I think @methos5k has already explained in great details so I can hardly find anything more to add :slight_smile:

1 Like

First of all thanks again for you answers,

Thanks to your instructions and with the help of a ScriptableObjects tutorial from Unity/learn, i’ve been able to create an Occupation (aka Job 2.0) script, and its only child yet (I think inheritance is relevant here ? Not sure though) which is an actual occupation, BruteOccupation.

By R-clicking either of the scripts in the inspector, I could create (Create>Occupation>BruteOccupation) a new of asset, which is the actual “component” which I can drag and drop into my Player script.

If I have understood the architecture of the concept correctly, I can now create more Occupations by creating a child script which inherits from Occupation.

However I feel like there’s something wrong in the way that I approach scripable objects; I think that I should be able to directly create new Occupations with a right click, because what I did here seems fitted for a system where I would have multiple sub-types of Brutes, which i’m not looking for yet :frowning: I just want to be able to create Occupations (and not different BruteOccupations or whatever child script I add)
I tried that :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu (menuName = "Scriptable/Occupation")]
public abstract class Occupation : ScriptableObject {
    public string occ_name;
    public string occ_id;
    public Skill[] occupation_skills;
    public int occ_healthmodifier;

    public abstract void Initialize (GameObject obj);

}

But unity gives me a “NullReferenceException: Object reference not set to an instance of an object” and I’m not sure what causes it despite quite a few tries to tweak it… so for now, I went back to the two scripts from before to try and figure out what is missing

Besides that I’m really amazed by how powerful ScriptableObjects seem to be, I can envision a ton of ways to use these !

I have a last few questions about these though :

-How can I access the parameters stored in the “equipped” Scripable Object from the player script ? And how can I change it to another ScriptableObject ? (another Occupation here)
-In the occupations script, it bothers me a little that I copied this line from a tutorial without understanding it ;

There is the same line with “override” in the Child script, but I can’t figure out what the GameObject obj is refering to :frowning: What does it point at ?

Thanks again for your time and advice !

Here are my 2 prototype codes by the way

//Occupations.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Occupation : ScriptableObject {
    public string occ_name;
    public string occ_id;
    public Skill[] occupation_skills;
    public int occ_healthmodifier;

    public abstract void Initialize (GameObject obj);//=> What does this point at ?
    public abstract void DoSomething();
}
//BruteOccupation.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu (menuName = "Occupation/BruteOccupation")]
public class BruteOccupation : Occupation {
      

    public int id;
    public int damage;
    public float whatever;

    public override void Initialize    (GameObject obj ){//=> What does this point at ?
        //do stuff with  other monobehaviors?
    }
      
    public override void DoSomething(){
        //execute the stuff we prepared in Initialize ?
    }
}

Null reference I’m not quite sure… you made a BruteOccupation asset then drag it to your Player script?

Okay – so abstract means it’s virtual and undefined. It’s only declared that your deriving class(es) will implement functionality. That’s what the override does. You cannot have an instance of Occupation, the way you’ve written it. You can have BruteOccupation, though.

Abstract methods can only exist in abstract classes and must be defined in subclasses.
virtual methods can be overriden in subclasses, but don’t have to be.

I saw a good piece of advice the other day, on the forums: first try to create 1 class and if/when you feel it would naturally benefit from subclassing, do that. :slight_smile:
I think this can be helpful – for you, for instance, you want to get it working to begin with…:slight_smile:

Let me know if I missed something.

I think I’m still confused with Override and Abstract ! To try and understand, I created an Item scriptable object, plus its Consumable and Weapon subclasses. This way I can create these (Rclick>Items> Consumable/Weapon). It works really well, since it is easy to create items, and for instance drag them into an Item[ ] array in my player script that would serve as an inventory.

//Items.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Item : ScriptableObject {
  
    public string i_name;
    public int i_id;

    public float i_weight;
    public string rarity;

    public Effects[] effects;

    public abstract void Initialize (GameObject obj);//=> still not sure what it points at
    public abstract void OnUse();
}
//consumable


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu (menuName = "Item/Consumable")]
public class Consumable : Item {

    public bool deleteOnUse;
    public int amount_of_uses;
    public string i_type =("Consumable");


    public override void Initialize    (GameObject obj ){
    }

    public override void OnUse(){
//This a little experimentation, items hold an Effect[]; effects are scriptable objects, this way I can set up items quickly and reuse everything; so far it works, I made a poison that hurts my player to test calling other scripts
        foreach (Effects effect in effects) { 
            effect.OnHappen ();                   
        }
    }
}

After making sure it worked,I tried to tweak the Item script to make it work alone, exactly like I would like the Occupation script to work : which is, in this case, create an Item, without using any subclasses, but I get errors whatever I try to play with (remove/change abstracts…)

What would be a correct way to create a single ScriptableObject with no children, like Item or Occupation (without BruteOccupation in the previous case), and create an asset out of it ?

thank you in advance !

Depending on whether or not you need to initialize the item at all, you could get rid of that function , for instance.

An abstract class cannot be created on its own. Therefore, to create a single item type, you’d make it a public class instead of a public abstract class.

Depending on how your inventory works, you might consider another script as a ‘holder’ (that’s what I used, because it made sense for me).
I have a script, let’s say it’s called “Slot” which can keep track of how many items are there, and react to Unity methods, and so on… my ScriptableObject is the ‘item’ variable on the slot, and so forth.

If your class is not going to be extended, you can write your methods as public or private (depending on what you need them for). The abstract is only for abstract classes, the override is when you implement an abstract class method/property or if you use a virtual function on a non-abstract class that has subclasses. :slight_smile:

Back to the topic at hand. Just want to create an ‘Item’ class? Just remove the ‘abstract’ modifiers on the one you have here :slight_smile: I don’t know if you’ll need ‘initialize’

1 Like

Okay so I fially made it work, the reason was because the assetmenu I created was referenced : (menu = “Occupation”)], I replaced it with (menu = “Scriptable/Occupation”) and it worked ! Otherwise I would just get 2 separators with nothing between them in the menu

So now I can create pretty much every modulable system I want with this ! :slight_smile:

So I can finally get precisely to the topic : when I made different Occupations (jobs), how can I reference the… “instances” ? (not sure about the terminology, I mean the assets you create) so that I can change the job through script ?
Right now they work well but I use them by drag-n-dropping in my player script !

Thank you !

Okay, so as I was saying before you might want a ‘holder’ script. :slight_smile:

If you have a set number of ‘job’ that a player can have, you could be okay with an array. In the array you’d have to modify the index to alter the job, or set it null if it’s not there/filled.
If the list can expand and/or shrink, I’d suggest using a List instead of an array (for simplicity).

You can alter the list (or array) from wherever in your code that you can access it.

I’d call them instances (and/or assets) the item/job s you create. :slight_smile:

I see ! This would be a monobehavior, right ? That would initialize an array / list of jobs / items (the assets) on start or awake like a sort of database, and then I can access the jobs/items using the index ?

If so, then my problem is solved :slight_smile:

Since you’ve been kind enough to answer for quite some time now I’ll be rude and abuse your kindness for one additional “conceptual” bonus question:
In the last bit of code I posted I mentioned in a comment a little “experiment”; to make a very modulable system, I tried to make “Effects” scriptableobjects, that would be like “Heal”, “Damage”, “Freeze” or whatever. Items (and possibly skills or things like that) could hold an Effects[ ] array that I could fill in with the previously mentioned Effects to “build” them. It would be very valuable for our gamedesigner which isn’t really willing to dive into c# and could build the content in the Unity editor

As an example, I added a Damage Effect to a Poison. Effects contain a OnHappen(), that defines what they do when the effect happens, and in this case it is triggered by the OnUse, which executes the OnHappen() of each effect it hold.

It works very well so far; I call the OnUse on one instance of this Poison with a button for testing purpose, and my player is damaged.
but to make it really as modulable as I could think of, I encountered a little problem : right now the damage it deals is “hard coded” into the Damage Effect. While it is not really a problem since I can very easily create a few of them for every damage I need (10 Freeze Damage, 20 Thrust Damage or whatever)…

Buuut it tickles my curiosity and I would like to be able to pass the identity of the Item or other instance/asset that triggers the effect to let the Damage script “improvise” on the amount of damage it has to deal (maybe even the target, didn’t really think about it yet)
Let me put it simple if I can : In the case I mentioned up there, (a Damage effect dragged on a Poison item), how do I let the Damage effect deal damage based on a damage variable that is stored in the Item which I put the Damage effect on ?

I’m sorry to bug you with all that !

EDIT here’s my Damage script

//Effect/Damage

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu (menuName = "Effects/Damage")]
public class Damage : Effects {

    public int dmg = 15;
    public string damageType = ("Physical");
    public Player player;
    public ScriptableObject source; //dont pay attention, thats a failed attempt to get the item / skill / weapon that triggers the effect's OnHappen()


    public override void OnHappen(){
        //execute the stuff
        player = GameObject.Find("Cube").GetComponent<Player>();
        InflictDmg (player, dmg);
        Debug.Log("I TOOK" + dmg + "DMG ");


    }
    public void InflictDmg(Player target, int dmg){ //ScriptableObject source,
        target.health = target.health - dmg;
        Debug.Log ("Inflicted " + dmg + " damage on " + target);
    }
}

Can’t be totally certain what you mean by improvise, but a couple of things come to mind:
a) the dmg is a random range (you set the range, and the dmg is dealt within that range).
b) you set the base dmg amount, but other skills/the player’s defenses “filter” the dmg amount and boost/decrease it.
If there’s something else you meant, please let me know…

This is something I’ve thought about off and on, but have never actually made in terms of a large modular system. There are a number of ways I’ve imagined doing it, so I can’t really say which is perfect or even better… some of it depends on what you want to do, too :slight_smile:

Back to one of the original parts of your post (responding to mine)… You can have a database of items/jobs or lists on certain scripts, sure. (local to a script and/or global to the game).
If per chance you were asking about the ‘holder’ script I mentioned- that was something for like say items in an inventory, so it’s easier to perform behaviour on them (like double-click , or fight click or whatever). It’s also the way I went about stacking items… otherwise, how would I keep track of how many are in the “inventory slot”? To me, having the script hold the count + the item seemed like the best way :slight_smile:

1 Like

Almost that :slight_smile: actually by improvise I meant “deal with whatever damage value has been passed to it by an item/skill or whatever”, so there would be no indication of damage in the Damage effect intself but only the function that treats a damage number that comes from the Item itself
(so that the Damage effect would be as generic as possible)

Looking like : Item (holds a damage value) → has a Damage effect : takes the damage value → Inflicts the Item damage value to the target

And, by the way, how could I “change the cartridges” in-game ? I mean doing the equivalent of drag-n-dropping, for example, an occupation into my player script ?
I tried that:

public Occupation occupation;
occupation = …

I tried quite a few things to give occupation a new value, from lots of stuff I found on the Unity scripting references on ScriptableObjects, but nothing seems to work :frowning:

Okay thanks for the tip, i’ll definitely try a holder script for the inventory ! But i will face a problem related to the question above, about "cartridges’ : i can’t find how to reference a scriptableobject-derived asset like an Item or Job in script, so I couldn’t really add it to a holder right now :frowning:

[edit : actually, my 1st question rejoins this cartridge problem as well, i’d just need to get the scriptable object responsible for the effect and get the value from it ! But how do I access it ? thats a mystery to me!]

Well, it all depends…
I mean, say it’s a job… you must have somewhere / somehow in the game to say you’re starting/getting a job…
Whatever bit of code that is, must have a list of jobs (or just 1 job) that it already has a reference to: from there, you can assign that reference to your own job (or job list).

Same goes for inventory. You want to reward the player for a task or mission or loot – somewhere, you must have “what you want to give them”… then, using that you give the player what (“that”) is :slight_smile:

I mean, you could be thinking of anything in your design… I don’t know.
Say you had a list of buttons with jobs on them, and the user picks one. Like, I’ll take that job… ( I dunno)
player.occupation = occupation (<- stored on the script on the button, maybe).

With loot/reward and an item, maybe: player.AddToInventory(item); (where ‘item’ is also in the loot/reward script).

I guess, if what you’re asking is “where do I find this reference in code”? The easiest answer is that it’s referenced somewhere/somehow, and from there you can assign it. :slight_smile:

Maybe that’s getting a bit long-winded for the solution/examples… If it’s still unclear, just tell me how would a player get a (new) job and maybe I can write a more concrete example :wink:

As for improvising, I guess you could just pass in an amount of dmg to whatever method and it uses that value with its calculations…Why not? :slight_smile:

Hello ! Sorry about the late answer
I have to admit that I did a mistake here which was posting again before I looked deep enough by myself ! By reusing all your previous posts and the unity references I was finally able to get this interchangable ScriptableObjects systemto work the way I want it !

Sofar my player has a job, whose modifiers impact its stats, items that he can use, remove or add to his inventory and trigger effects…

The thing I did was add database scripts (itemDB, JobsDB…) that just hold references to Items, which I can drag and drop in the script in the editor to make a database ! Or even let a five-years-old cousin create one, given how simple it makes it :slight_smile:
Just incredible how flexible these scriptableObjects are !
@methos5k I owe you a big thanks for your sustained help despite my approximative c# mastery, terrible terminology and novel-long posts ! I definitely made a lot of progress in a few days
thank you @mysticfall as well !
wish you guys the best :slight_smile:

Great to hear. Glad you got it working nicely and it’s easy for you to use :slight_smile: Take care and good luck with your project.