Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (498)
Games in Android Showcase (115)
games submitted by our members
Games in WIP (563)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Diving into Scripting....  (Read 1843 times)
0 Members and 1 Guest are viewing this topic.
Offline coltonoscopy

Junior Member


Medals: 2



« Posted 2013-09-30 23:35:52 »

Hey guys! Long time no chat.

I'm building up my game engine and beginning to design my scripting system. This is more a theory question than anything else, as this will be the first major scripting system I've ever written (using Lua with libgdx, for the curious). I basically am going to (tentatively, pending suggestions) have a system that operates as follows for character class behavior:

1. Classes are defined in individual .lua files loaded at runtime (i.e., bandit.lua, warrior.lua, necromancer.lua, etc.). These define things like what weapons are compatible, the class description, what abilities it learns, etc.
2. Abilities are defined in their own individual .lua files also loaded at runtime and referenced from the aforementioned class files (i.e., steal.lua will define what actually takes place when a character utilizing a thief class steals, and so forth).

I look at that model and think hm, it seems like it would be a good idea to blend the two together and instead just have one .lua file for each class that encompasses the abilities as well. Though clean, there's also the part of me that wouldn't know how to encapsulate those moves properly, as the end goal is to have Lua call something like new Class() with the class script file and new Ability() with each ability so that the classes are generic enough to fully encapsulate themselves and their behavior, and having the class file also contain the logic for each ability would presumably make this difficult unless I incorporate some meta-based object-oriented programming to hold the information for each ability... this comes with the trade-off of readability and simplicity for someone like my partner to be helping with the scripting, who is more design-based than code-based. Also, having the moves separate allows other classes to reuse those same moves.

Typically, I wouldn't necessarily approach scripting for something like this were my game to have on the order of 10-15 classes, but there are going to be (and don't laugh) roughly 100 classes at this point, all of which my partner and I have outlined already. Being able to script these rather than hardcode them just makes sense, especially because each class will also have 5-10 abilities that are unique to it (though some may have shared moves as mentioned before, lending utility to the scripting approach).

There are other areas of the engine I intend to script (entity generation, behaviors, questing, weapon and spell effects), but I figured this would be a good place to begin in terms of learning how to do things. I would love hearing suggestions as to how to fine-tune or change this approach to be more optimized or cleaner, and if anyone has insight on how to factor or reapproach the aforementioned issue with separate script types, that would be great as well. Scripting is looking like a very exciting frontier for a dynamic engine, but there are many things to consider, it would seem. Thanks in advance!

Colton

Straight flippin.
Offline davedes
« Reply #1 - Posted 2013-10-01 00:54:30 »

I hate to say it, but scripting a Java game is usually overkill. Unless maybe you are developing the next World of Warcraft and you need scriptable plugins for your user-base that is in the hundreds. Java code compiles pretty darn fast. If you need data, e.g. for a game's item database, it would be better to "keep it simple, stupid" and use JSON.

But maybe it would be better to invest your time in a GUI tool that allows you to write new classes. It would export to JSON or another format. Does each ability really need to be uniquely programmed? (Impressive that you even came up with that many unique abilities...)

You can use Java and reflection if you have hundreds of unique abilities. If you have a fixed number of moves (that have different properties, visuals, etc) then you could use another pattern, like Enums, to avoid the need for reflection.
1  
2  
3  
4  
5  
6  
7  
8  
9  
public enum AttackCommands implements Command {
    SwordStab {
        CommandResult performAction(CommandEvent ev) {
            ... do some crazy logic that can't be expressed through JSON ...
        }
    },

   etc...
}


With that out of the way, scripting in Lua can still be pretty cool and fun to work with, even though it will just end up slowing your development down, introducing more bugs, worse performance, and making your game engine less attractive to other devs. I can't really comment on best practices in Lua -- just do whatever feels right for your team and leads to acceptable performance.

Offline coltonoscopy

Junior Member


Medals: 2



« Reply #2 - Posted 2013-10-01 01:52:51 »

Thanks for the reply, davedes!

So, I love the idea of being able to use something like a GUI or whatnot to create any game asset really and have that exported as a script; I've run this through my head a few times, but JSON (or any pure-data file format) just is limited by the lack of logic, I feel. I love JSON as a tool for writing things that are simple, like items (though even items with functionality, like spawning things or turning you into a different creature, can get funky), and I use it all the time in my day-to-day non-game programming because of its utility and ubiquitousness, but things like class abilities that perform something that interacts with the game world and NPC scripting and whatnot feel impossible with it. My ideal goal would be to have all of this contained within one nice little pretty script file that, though not necessarily short for some cases, would at least be self-contained, dynamically loaded, and editable by others, not just me, so my engine doesn't have to be recompiled every time I'd like to add some new functionality or the community wants to create their own complete spin-off using my engine. Does this seem like a plausible goal to you? Not only are these ~100 classes desirable, but also community-driven ones down the road, and not just classes but many things would be great, like user scripted random world and level generation, weapon generation, etc (all things I've worked on already and am incorporating into the game engine), though we can save that for another time; for now, just focused on classes and abilities! Thank you very much for your time; sorry for the verbosity!

Colton

Straight flippin.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Jeremy
« Reply #3 - Posted 2013-10-01 04:52:23 »

I agree with @davedes

It sounds like you're trying to program the engine in Java, and then the entire game in some scripting interface. You should delegate only as much as you need (for flexibility in terms of your game's construct) to scripting. Going data-driven is the much better idea for game character classes. Maintaining 100 different class files (in scripts or in Java) will be a pain to manage and I can't imagine a scenario where the code won't be very redundant. So I would say it would be safe to do both, but with a much greater tendency to data-driven then script driven (avoid scripts all together if you can.)

Use scripts for things like NPC AI, Maps, Triggers etc (again, as much of the code inside of the engine exposing only what you need to keep it flexible in your games construct.) So for example, you might have npc1.moveTo(x,y); npc2.initiateDialogue(...); npc3.attack(...) etc, they should work at a fairly abstract level. Your game shouldn't be written in scripts, however guiding the game with scripts is a good idea.

The Oracle JRE (6+) distributes with a jsr-223 JavaScript ScriptEngine, and you can plugin luaJ if you're looking to use one under the same interface. OpenJRE doesn't distribute with a JavaScript ScriptEngine, but can be compiled with one with extra configuration.

JevaEngine allows scripts to optionally be associated with entities to drive Entity logic, dialogue, and construct the UI on the server & client side.
jMonkeyEngine uses a scripting and XML data format for their UI and throughout their engine. So maybe take a look there for some ideas.

JevaEngine, Latest Playthrough (This demo is networked with a centralized server model)

http://www.youtube.com/watch?v=rWA8bajpVXg
Offline coltonoscopy

Junior Member


Medals: 2



« Reply #4 - Posted 2013-10-01 16:41:52 »

Thanks for the reply, Jeremy! I would love to have a strictly data-driven interface for loading classes, abilities, and so forth, which would make development in that part of the game like butter, but I just have a hard time mainly with the abilities. Here is a sample of the latest design of my class JSON file:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
{
    "className" : "Bandit",
    "classId" : "bandit",
    "classDescription" : "A sly picker of pockets, gifted with the feet of a cat.",
    "classIcon" : "bandit.png",
    "effectiveWeapons" : ["daggers", "shortswords", "bows"],
    "ineffectiveWeapons" : ["axes", "maces", "hammers"],
    "effectiveArmor" : ["light"],
    "ineffectiveArmor" : ["heavy"],
    "abilities" : [
        {
            "name" : "Pickpocket",
            "id" : "pickpocket",
            "description" : "Deftly acquire that which belongs to someone else's pockets!",
            "type" : "move",
            "manaCost" : 0,
            "healthCost" : 0,
            "cooldown" : 3,
            "successChance" : [50, 55, 60]
        },
        {
            "name" : "Sneak",
            "id" : "sneak",
            "description" : "Render those around you oblivious of your subtle presence...",
            "type" : "move",
            "manaCost" : 0,
            "healthCost" : 0,
            "cooldown" : 5,
            "successChance" : 100
        },
        {
            "name" : "Deft Strike",
            "id" : "deftStrike",
            "description" : "Utilize your dexterity and cunning to cleverly and critically strike your opponent!",
            "type" : "move",
            "manaCost" : 10,
            "healthCost" : 0,
            "cooldown" : 5
            "successChance" : 100
        },
        {
            "name" : "Flee",
            "id" : "flee",
            "description" : "Gain an increase in running speed to dodge tricky situations!",
            "type" : "move",
            "manaCost" : 15,
            "healthCost" : 0,
            "cooldown" : 25,
            "successChance" : 100
        },
        {
            "name" : "Steal",
            "id" : "steal",
            "description" : "Strike your enemy with the chance to steal something of theirs!",
            "type" : "move",
            "manaCost" : 10,
            "healthCost" : 0,
            "cooldown" : 15,
            "successChance" : [50, 55, 60]
        },
        {
            "name" : "Dagger Mastery",
            "id" : "daggerMastery",
            "description" : "Improve your overall success with daggers!",
            "type" : "passive",
            "manaCost" : 0,
            "healthCost" : 0,
            "cooldown" : 0,
            "successChance" : 100
        },
        {
            "name" : "Quick Strike",
            "id" : "quickStrike",
            "description" : "Nimbly strike your opponent twice in rapid succession!",
            "type" : "move",
            "manaCost" : 20,
            "healthCost" : 0,
            "cooldown" : 10,
            "successChance" : 100
        },
        {
            "name" : "Leap",
            "id" : "leap",
            "description" : "Add extra juice to your jump to escape, aid dungeon crawling, or show off!",
            "type" : "move",
            "manaCost" : 10,
            "healthCost" : 0,
            "cooldown" : 5,
            "successChance" : 100
        }
    ],
}


It contains most of the foreseeably necessary data, but in something like Pickpocket, for example (were I to want to keep the abilities and the class collected together rather than splitting up the moves into separate files), I find it difficult to describe the flow of how the ability should proceed. I would want it to go something like:

1  
2  
3  
if player.distanceToTarget(3):
   player.playAnimation("touch")
   player.stealItem("opponent", successChance)


It's simplified in this example but still a reasonable estimate of what an ability may require. Were I to want a strictly data-driven approach (which again, I would love), how could this be accomplished utilizing such, ideally incorporating into the JSON file shown above? Thanks for your time!

Straight flippin.
Offline xsvenson
« Reply #5 - Posted 2013-10-01 18:28:34 »

In what exactly lies the problem ? I think Your json looks okay.

Only things I see are "distanceToTarget" is missing from the json. Animation can be described in a data-driven format also or already included in the package. Success chances are already described in the json and the opponent comes from the engine.

“The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet.” - Michael A. Jackson
Offline Danny02
« Reply #6 - Posted 2013-10-01 19:04:01 »

When you go totally data driven, you have one generic type which gets configured with data. This doesn't work for your abilities, because they have different logic at the highest level.

My tip for you would be to implement all the different abilities on their own, but let them share some common interface. Each ability only has the parameter it needs, so all abilities are unique. JSON for those abilities could look like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
"abilities" : [
        {
            "name" : "Pickpocket",
            "cooldown" : 3,
            "successChance" : [50, 55, 60]
        },
        {
            "name" : "Flee",
            "manaCost" : 15,
            "cooldown" : 25,
        },
}


Now the only thing you would need is some parsing utility. I would do it like this, extract all key/value pairs of the JSON and get some Builder by name. Give that builder the key/values and ask if he can build an ability from that data.

something like:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
interface Ability{
  String getDescription();
}
class Pickpocket implements Ability{
  int cooldown;
  int[] successChance;
}
interface Builder{
  Ability build(Map<String, JsonValue> values) throws DataException;
}
class PickpocketBuilder implements Builder
{
    Ability build(Map<String, JsonValue> values) {
       Integer cd = values.get("cooldown").toInt();
       int[] ch = values.get("successChance").toArray();
       
       if(cd == null || ch == null)
         throw new DataException();
       else
         return new Pickpocket(cd, ch);
   }
}
Offline coltonoscopy

Junior Member


Medals: 2



« Reply #7 - Posted 2013-10-01 19:28:12 »

Hi Danny,

Thanks for the reply. That's not a bad idea, but it introduces the need to create a class still for every single ability I want to make, defeating my goal of being able to completely define these things as data files. The end goal is something a lot more akin to having a generic Ability class that can take in a list of Events that comprise it, which maybe could be tied with a list of Fields that are dynamic and basically object wrappers around names and values. I'm having a hard time actually writing out a concrete implementation base for this at the moment; any thoughts on how this could be accomplished? Thanks!

Colton

Straight flippin.
Offline davedes
« Reply #8 - Posted 2013-10-01 22:02:14 »

You need to think a little more abstractly. For example, Pickpocket is really just a "Steal Something From Target" action. Flee, Dagger Mastery and Leap are all "Change Player/Target Stats" action.


I would break it down like so. You have the following types, triggers and actions with the logic defined in Java:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
Types
    Melee    //close-combat attack, needs a melee weapon in hand
   Range    //range attack, needs bow + arrows
   Spell    //a magical spell of some kind
   Generic  //could be used for Pickpocket, a generic Taunt, or a fast movement
   Passive  //does not need to be activated

Triggers
    Use     //on action use/click, for spells like Spawn, Pickpocket, etc.
   Attack  //on successful melee/range strike
   Block   //on successful block
   Evade   //on successful evade
   Death   //on player death
   etc...

Actions
    Effect   //generic effect to increase/decrease stats/health/etc
   Steal    //steals an item or attribute from the enemy target
   Hide     //invisibility effect
   Spawn    //spawns some minions
   etc...


Now you can describe almost all of your skills/spells/etc in a pure data format. Here are two examples to give you an idea:
http://pastie.org/8370747

The above uses YAML, but it could just as easily be JSON exported from a visual tool.

Online Roquen
« Reply #9 - Posted 2013-10-02 08:29:21 »

I vaguely started to babble about these kinds of systems here: http://www.java-gaming.org/topics/archtypes-composition-and-code-is-data/26554/view.html
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline coltonoscopy

Junior Member


Medals: 2



« Reply #10 - Posted 2013-10-02 19:23:56 »

davedes, that was a great example and fits the model I was trying to capture in my mind fantastically. Thank you very much! I will eagerly pursue fleshing it out.

Roquen, I will take a look at your article as well; from the first few paragraphs, it looks interesting! Thank you!

Colton

Straight flippin.
Offline coltonoscopy

Junior Member


Medals: 2



« Reply #11 - Posted 2013-10-02 21:21:45 »

@davedes,

I went ahead and modified my entire list of abilities from before with this new model; does this look like it's following yours well enough? Thank you very much for all your time and help!

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
"abilities" : {
        "pickpocket" : {
            "name" : "Pickpocket",
            "desc" : "Deftly acquire that which belongs to someone else's pockets!",
            "type" : "generic",
            "cooldown" : 3,
            "OnUse" : [
                {
                    "Steal" : {
                        "chance" : [25, 45],
                        "steals" : "randomItem"
                    }
                }
            ]
        },
        "sneak" : {
            "name" : "Sneak",
            "desc" : "Render those around you oblivious of your subtle presence...",
            "type" : "generic",
            "mana" : 5,
            "cooldown" : 5,
            "duration" : 30,
            "OnUse" : [
                {
                    "Effect" : {
                        "statIncrease" : {
                            "stat" : "sneak",
                            "amount" : ["25%", "30%", "35%"]
                        },
                        "target" : "self",
                        "duration" : "parent",
                    }
                }
            ]
        },
        "deftStrike" : {
            "name" : "Deft Strike",
            "desc" : "Utilize your dexterity and cunning to cleverly and critically strike your opponent!",
            "type" : "melee",
            "mana" : 10,
            "cooldown" : 5,
            "OnUse" : [
                {
                    "MeleeAttack" : {
                        "animation" : "fastSingleAttack",
                        "vfx" : [],
                        "bonusDamage" : ["20%", "25%", "30%"]
                    }
                }
            ]
        },
        "flee" : {
            "name" : "Flee",
            "desc" : "Gain an increase in running speed to dodge tricky situations!",
            "type" : "generic",
            "mana" : 15,
            "cooldown" : 25,
            "duration" : 15,
            "OnUse" : [
                {
                    "Effect" : {
                        "StatIncrease" : {
                            "stat" : "speed",
                            "amount" : ["30%", "35%", "40%"]
                        }
                    }
                }
            ]
        },
        "mug" : {
            "name" : "Mug",
            "desc" : "Strike your enemy with the chance to steal something of theirs!",
            "type" : "generic",
            "mana" : 10,
            "duration" : 60,
            "cooldown" : 15,
            "OnAttack" : [
                {
                    "Steal" : {
                        "steals" : "random_item",
                        "chance" : ["40%", "45%", "50%"]
                    }
                }
            ]
        },
        "daggerMastery" : {
            "name" : "Dagger Mastery",
            "desc" : "Improve your overall success with daggers!",
            "type" : "passive",
            "OnUnlock" : [
                {
                    "SkillIncrease" : {
                        "skill" : "daggers",
                        "amount" : [5, 10, 15]
                    }
                }
            ]
        },
        "quickStrike" : {
            "name" : "Quick Strike",
            "desc" : "Nimbly strike your opponent twice in rapid succession!",
            "type" : "melee",
            "mana" : 20,
            "cooldown" : 10,
            "OnUse" : [
                {
                    "MeleeAttack" : {
                        "animation" : "fastDoubleAttack",
                        "vfx" : ["blurPlayer"],
                        "hits" : {
                            "frames" : [2, 4]
                        }
                    }
                }
            ]
        },
        "leap" : {
            "name" : "Leap",
            "desc" : "Add extra juice to your jump to escape, aid dungeon crawling, or show off!",
            "type" : "generic",
            "mana" : 10,
            "cooldown" : 5,
            "duration" : 15,
            "OnUse" : [
                {
                    "Effect" : {
                        "statIncrease" : "jumpHeight",
                        "amount" : [3, 4, 5],
                    }
                }
            ]
        }
    }

Straight flippin.
Offline davedes
« Reply #12 - Posted 2013-10-02 21:40:14 »

Are you hand-writing the JSON? JSON is not really that easy to hand-write, and doesn't include comments. I think YAML is a better fit if you plan to write it all by hand; it leads to better readability and ultimately less human error.

I think your new JSON looks more reasonable, and doesn't need any scripting languages. Wink

Keep in mind you will probably want to use something very similar for items and weapons, to define its attributes and events (like 5% mana steal on hit, or 10% chance of going invisible on block).

Offline coltonoscopy

Junior Member


Medals: 2



« Reply #13 - Posted 2013-10-02 22:31:51 »

davedes,

Yes, I do plan on hand-writing the JSON, and I also really love the look of YAML (I program in Python often for my actual programming job). Do you have a preferred YAML parser? Also, thanks again for your help in figuring out a viable data-based alternative to scripting; this is what I've truly wanted from the get go rather than having any scripting, but it didn't seem viable until put in perspective.

Also, yes, I would love to keep the format as uniform as possible amongst the different scripts throughout my engine, as I intend to use this as a means of user-generated content in any domain I can find good reason for.

Straight flippin.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

radar3301 (12 views)
2014-09-21 23:33:17

BurntPizza (29 views)
2014-09-21 02:42:18

BurntPizza (19 views)
2014-09-21 01:30:30

moogie (20 views)
2014-09-21 00:26:15

UprightPath (27 views)
2014-09-20 20:14:06

BurntPizza (31 views)
2014-09-19 03:14:18

Dwinin (48 views)
2014-09-12 09:08:26

Norakomi (74 views)
2014-09-10 13:57:51

TehJavaDev (102 views)
2014-09-10 06:39:09

Tekkerue (50 views)
2014-09-09 02:24:56
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!