0
\$\begingroup\$

I have a Json file holding data for my LineData type i created.

[Serializable]
public class LineData
{
public string lineID;
public string lineDialog;
public float lineDuration;
public string condition;
}

Note the last one, 'condition', that is what I hope to use to hold a name of a specific bool in my code, so that the line can only be spoken when that bool is true.

Is this a decent way to achieve this? Or is it even possible? I've read about making 'custom markup' on my Json things like (!!) and (->) built in the text to mean stuff in my code, but I don't know where to start with it and cannot find any tutorials (I've only seen it on some Youtube GDC lecture style videos from professional game developers working for companies)

But failing the custom markup thing, if I can somehow use the string of the name of the bool, and use that to check the state of it's corresponding bool in the code, that would be enough for me so far.

Any ideas or help is fantastic and very welcome. Many thanks!!!

(PS. The json looks like this (Im sure you don't need it, but just in case)):

{ "lineDatas":
[
{
    "lineID": "BEDROOM_DAVE_0001",
    "lineDialog": "Yaawwn, up i get for another fun packed day.",
    "lineDuration": 3,
    "condition": ""
},
{
    "lineID": "BEDROOM_DAVE_0002",
    "lineDialog": "Well I think I need a cigarette before I get to work!",
    "lineDuration": 3,
    "condition1": "notSmokedCig"
}
]
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Put all your bool states into a Dictionary, which will kindly look-up from String to the bool it handles. \$\endgroup\$ – Patrick Hughes Dec 19 '18 at 0:04
  • \$\begingroup\$ Thanks very much! I will look up how to do it now. \$\endgroup\$ – Big T Larrity Dec 19 '18 at 0:24
  • 2
    \$\begingroup\$ Note that "data" is already plural. If you want just one data item, it's a "datum" (Sorry, seeing "datas" just made my inner pedant flinch). \$\endgroup\$ – DMGregory Dec 19 '18 at 3:48
  • 1
    \$\begingroup\$ You can also technically use reflection to look up variables from name strings - I show an example of doing this to call methods by name here. But I wouldn't recommend that for this use if a dedicated table of condition bools like Patrick Hughes suggests would meet your needs. The reflection approach would be slower, and could be unsafe (a hacker who can modify the input string could use this channel to make your game leak data or otherwise behave badly in some circumstances) \$\endgroup\$ – DMGregory Dec 19 '18 at 4:35
  • 1
    \$\begingroup\$ Why don't you just use a Set<String> for your Conditions at runtime? A Set is similar to an Array or a Dictionary, except it only contains unique values. While your Game is running, you obviously fulfill some conditions. Your conditions are represented in the Set and putting them in the Set implicitly means the condition is met. \$\endgroup\$ – Raildex Dec 25 '18 at 17:23
4
\$\begingroup\$

Storing your boolean condition variables in a dictionary keyed by strings as Patrick Hughes recommends will work just fine, but it can lead to your code being "stringly typed"

If we don't know the full set of condition variables until runtime (eg. if we're running mods/scripts that added more content beyond what's in the compiled game), then this might be the best we can do.

But for stuff that's known at compile time, using strings means relying unnecessarily on every bit of code that reads or writes a particular condition flag to spell & capitalize it identically (or responsibly use the right constant instead of bodging in a literal). A single typo in a string literal somewhere can introduce insidious bugs that the compiler won't be able to detect for you. Also, strings are needlessly heavy to store/compare/hash if a simpler type will do.

We can improve on this method by using an enum to define our variable "slots":

public enum Condition : int {
    HasNotSmokedCigarette,
    HasPetParakeet,
    KnowsTheMacarena,
    SpeaksEsperanto, 
    /* ... add more conditions here */
    ConditionsCount
}

By making the enum backed by an integer type like this, we can cheat a little and use the enum directly as an index into an array of values, and skip the hashing / redundancy of a Dictionary entirely. Putting a "count" entry at the end lets us easily keep the array the right size:

bool[] conditionValues = new bool[(int)Condition.ConditionsCount];

Now we can get & set bools in one of these slots using:

void SetCondition(Condition condition, bool value) {
    conditionValues[(int)condition] = value;
}

bool GetCondition(Condition condition) {
    return conditionValues[(int)condition];
}

We can call these using the enum values like so, keeping compile-time checking & error reporting throughout:

SetCondition(Condition.HasPetParakeet, true);

And finally, the part we've all been waiting for: we can still look them up from strings when necessary:

bool GetCondition(string conditionName) {
    if(string.isNullOrEmpty(conditionName))
        return true;

    Condition condition;
    if(System.Enum.TryParse(conditionName, out condition))
        return GetCondition(condition);

    Debug.LogErrorFormat("Tried to check non-existent condition named {0}", conditionName);
}
\$\endgroup\$
  • \$\begingroup\$ dude!!! I have learnt so much from this post. Trying to implement it now! (believe it or not I had come dangerously close to doing it like this from my own trial-n-error but kinda messed it all up and had to delete it lol.) Knew you could make enum with ints but didn't remember how to exactly. Thanks so much for the easy to follow help. \$\endgroup\$ – Big T Larrity Dec 19 '18 at 13:14
  • 1
    \$\begingroup\$ Just be aware that once you come up with a name for the enum value you can’t rename it without updating the json file as well. This would be true in any case, but this exact thing has bitten us once or twice due to insufficient documentation around the enum’s declaration indicating as much. \$\endgroup\$ – Ed Marty Dec 19 '18 at 14:10
  • \$\begingroup\$ thanks Ed. I think ill make a Strings class so I can use constant string variable names instead of typing the string each time. But I am looking into using the enum Conditions as above, any idea where would be appropriate place to put the enum declaration, should it go inside my LineData class? \$\endgroup\$ – Big T Larrity Dec 19 '18 at 16:32
2
\$\begingroup\$

It's not uncommon to store the variables used by the scripting system in a Dictionary with a string key. In my opinion, the "stringly typed" argument does not apply in this case, because you already got stringly typing in your JSON data and you won't be able to verify those at compile-time. If you want to confirm that there are no mistyped variable names in your script files, add a way to declare variables to them and do a validation at load-time (Btw: I made the experience that building a sophisticated validation system into your JSON loaders is usually well worth the time investment).

But if you really want the variables in your script files to map to "real" variables on the C# side, then you can use reflection to get the value of a member-variable by its name stored in a string at runtime.

For example, assume you have an object GameState state with a field public bool notSmokedCig and you have a string condition with the value of "notSmokedCig" at runtime, you can do this:

bool conditionIsTrue = (bool) typeof(GameState).GetField(condition).GetValue(state);

I am looking forward to playing your game.

\$\endgroup\$
  • \$\begingroup\$ Thanks Phillip, this is really super helpful too. Between yourself and DM Gregory I have lots of tools in my bag now to try and solve the problem in a logical way I will understand. I'm getting right into it today and hoping to have a little small version of a game working soon!! :DD I will send you a link to a copy when its done :D (Don't hold your breathe for it though, might be next Christmas before I get there xD) \$\endgroup\$ – Big T Larrity Dec 19 '18 at 13:19
  • 1
    \$\begingroup\$ BTW - i wish I could accept both answers as correct. They both helped me learn loads (and thats what its really about, the game I make will likely be ditched, its all about learning for me) \$\endgroup\$ – Big T Larrity Dec 19 '18 at 13:19
  • \$\begingroup\$ Finally, if you want to get really crazy in the future with the declarations you can consider adding a tools stage to your build that will read all your scripts and generate the relevant source for you, and along the way detect simple errors. I prefer validation at tool+build time over runtime, you can end up with thousands of conditions in any light RPG or puzzle-triggered game. \$\endgroup\$ – Patrick Hughes Dec 19 '18 at 19:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.