Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (499)
Games in Android Showcase (118)
games submitted by our members
Games in WIP (567)
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  
  On designing data and control aspects of a CCG, need some help  (Read 1383 times)
0 Members and 1 Guest are viewing this topic.
Offline lulero

Junior Newbie





« Posted 2011-10-11 21:37:21 »

Hello,

First post here so I guess a little introduction is in order. I'm french so I hope it won't be too hard of a read. I use mainly java and some odd languages for research purposes (related to my job). Over the years I started a lot of game oriented projects but could never finish one. However I always felt like I learnt a lot doing so, and that's good because that was always the main reason behind them.

This time I'd like to go all the way and picked a theme I already experienced with a couple of times: a collectible card game. I have a background, most of the rules (with a twist I like a lot) and some genetic based algoritms for a deck building AI I already tried with great success (but I'm biased of course).

But I'm no good at picking a good design. I could make it work, and I actualy did it to a certain extend, but I'm not happy with the result. Some pointers about what I have:
  • A GameObject like class (wrongly named Entity) that other classes like Card and Player inherit from.
  • Modifiable values are stored via maps (enums as keys) so I have one setter to rule them all.
  • Previous point was done so mostly to allow an easy use of a recorder to store the changes and replay the games.
  • To add content to the game (aka more cards), I make a new class that inherits Card and override methods like play() to specify how it interacts with the game elements.
  • A factory to instantiate those.
And that covers almost everything data and control related, I'm not worried about the GUI part right now.

My bigger concern is that data and control are heavily mixed together. In a way tho, cards do define part of the control and it can be seen as data. I don't want to limit myself to a set of parameterizable effects/actions. Another part I find ugly are those maps to store the values.

What I like is how easy it was to add the game recorder and to be able to specify almost any effect I can think of. However about that last point I still can't redefine easily player specific rules, like "skip draw phase" and stuff like that.

I looked into entity systems (namely Artemis, hence why I registered here) but even though I like it a lot I'm pretty sure it's not what I'm looking for ... Again because my cards do have a control aspect that components aren't really fitted to handle (as far as I understand). Again I have about as many Card.play() methods as I have different cards. Maybe I could still use such a system (one of my own though, learning purpose) for the model part (up to the recorder) and have the control within another layer. But I'd need help to figure things out.

How would you do (or did)?

- Lulero

ps: As it often does, writting this already helped a bit. But this post is probably full of missunderstandings, feel free to correct me. If I didn't provide enough informations, by all mean ask.
Offline sproingie

JGO Kernel


Medals: 202



« Reply #1 - Posted 2011-10-11 23:04:23 »

In a CCG, you have a base set of immutable rules, then the rules on each card are effectively parameters to those rules, including the various exceptions and special cases.  You say you don't just want parameterizable effects/actions, but that's basically the way it works.  Parameters might be more complex than "ATK=5, DEF=2": they might be "preAttack=opponent.addEffect(PARALYZE).withElement(ELECTRICITY)".  Your rules then consist of applying all these parameters and resolving them with their proper priority -- essentially interpreting the language expressed by those parameters.

Basically, I wouldn't even think of doing a CCG with all its complex rules without using some kind of scripting language.
Offline lulero

Junior Newbie





« Reply #2 - Posted 2011-10-12 18:26:30 »

Hi sproingie, thank you for answering.

I'm not sure what you meant by "some kind of scripting language". If you meant JavaScript and other languages interpreted at runtime I did try something like that but had performance issues. It's usualy not a problem with a CCG but it is for this particular one (I can explain why if you wish, not sure if it's relevant yet). That's why (for now) every card has its own class inheriting Card and implementing this card' specific mechanics. Indirectly of course, I have card archetypes like Unit and Spell.

But I think you meant something else ... Let me see if I got your example right.

1  
2  
3  
Action a = opponent.buildEffect(PARALYZE); // Builds a parallyzing effect with opponent as target
a.addElement(ELECTRICITY);
this.preAttack = a;


Some kind of Action factory and methods to set the parameters. Can be similar for effects. Pretty sure I'm wrong but that's what I understand so far. Sorry for being slow.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline sproingie

JGO Kernel


Medals: 202



« Reply #3 - Posted 2011-10-12 19:00:58 »

Except for running simulations where you're playing thousands of rounds per second, I can't imagine what kind of performance issues would arise.  Certainly it's easier to write horribly inefficient code in a scripting language, but that's largely because it's easier to write any code at all.  What you're probably going to want is some flexible means of adding new behavior, but without having to scatter it across potentially hundreds of subclasses. 

This is going to mean some amount of "code as data", either with a really rich declarative system and complex rule base (like what Dwarf Fortress uses) or by way of scripting, which is what many commercial games do by way of attaching scripts (typically Lua, but you'd probably use something else in Java) to various in-game objects.

Either way, you're probably going to want some authoritative "resolver" that takes these properties and/or scripts and applies them.  Rather than letting every lightning-immune creature have to code their electricity resistance (or god forbid using a parent class, which will really kill you when you need to combine them), you have a single resolver simply toss the effect (and presumably add a message with accompanying animation that says the effect was negated).  Whether that resolver itself is script-based is probably a lot less important than whether your individual cards are.

BTW, that code example pretty much fits what I was talking about.  And it's valid Java and JavaScript Smiley
Offline Chromanoid

Junior Member


Medals: 3



« Reply #4 - Posted 2011-10-12 19:08:22 »

When using Java I don't really see the need for a scripting language. Class reloading, sandboxing and on demand compiling can be done with java too.

I would try do "keep it simple stupid". Just implement a basic prototype without abstract objects but with Card, CardList, Phase, Action etc. as building blocks. Maybe you can look at LackeyCCG or similar applications to learn what basic actions you might need.
Offline sproingie

JGO Kernel


Medals: 202



« Reply #5 - Posted 2011-10-12 19:48:12 »

I wouldn't call reloading, sandboxing, and on-demand compilation "simple".  But neither do I want to come off as so gung ho as saying "write all your game logic in javascript".  It's probably a good idea to start with a statically compiled model, and if it stays manageable and easy to work with the whole time, then sticking with it might just be the best idea.  There's little difference in theory between interpreting scripts and invoking external "logic objects" other than the language you use, and IDE support for doing those logic objects is a bonus of doing it the latter way.  But I think we can all agree that "public class BlueSlime extends LightningImmune { ... }" is not the way to go. Smiley
Offline Chromanoid

Junior Member


Medals: 3



« Reply #6 - Posted 2011-10-12 20:03:14 »

Yeah Smiley
I always have the feeling that using Clojure for "code as data" might be very elegant. http://clojure.org/runtime_polymorphism looks very interesting for games. This Clojure stuff is on my try-out-list for such a long time now -.-.
Offline lulero

Junior Newbie





« Reply #7 - Posted 2011-10-12 20:29:36 »

Thank you both for those answers, learnt a lot. Here are my thoughts:

About performances (and bits about the game):
This game is different than most CCGs as the focus is on collection management and deck building. The player isn't required to make a choice during the actual match. It might sound a bad idea at first, but does allow some interesting stuff like asynchronous gaming, best of 101 matches, king of the hill and efficient deck building AI. It's not the only game like that (see Warstorm), but IMHO they fail at making full use of this aspect and it gets boring quick. Hell, they even force you to watch every single game from start to end.
I'll need those "thousands of rounds per second" and I'd like to (and will) avoid scripts.

About the lightning-immune issue:
Not all the methods from my base Card class or the major archetypes like Unit and Spell can be overriden, some are final. An example would be applyDamage(...) that implements the immune mechanism. To know if an Unit is lightning immune or not, it looks for an entry in the map of value I described in the OP. It works and is somewhat manageable. Still though, if I was happy with this way of doing things I wouldn't post here.

About on-demand compilation:
If I make an editor and still handle things as I do know, that's what I'll use. Being honest I'm a little affraid to get into it right now.

What now?
I like the idea of a "really rich declarative system" and will try something like that. I'll probably come back with questions tho.
But first I'll have a look at Clojure Wink.

ps: The game is somewhat closer to Netrunner than MtG, another game by WotC. Rather than permanents and spells I have programs and scripts. I'm not 100% set on the naming, but it's impossible to satisfy both geeks others.
pps: <3 Dwarf Fortress.
Offline Chromanoid

Junior Member


Medals: 3



« Reply #8 - Posted 2011-10-12 20:47:09 »

If you want to use Java for "logical" game data, you might want to look into annotation processing and code generation. It is more simple than it seems.
Offline Orangy Tang

JGO Kernel


Medals: 56
Projects: 11


Monkey for a head


« Reply #9 - Posted 2011-10-12 23:56:15 »

CCGs tend to need lots of different cards to play well, so you need to be able to declare simple cards with little/no code with some kind of declarative syntax, but still leave you options for adding arbitrary code for the really special, rule bending cards.

So I'm going to second Chromanoid's recommendation. By creating custom annotations you can create simple cards easily (eg. you could just create something that declares 'type: creature, cost: 2, stats:2/3, has trample'). But then hook in onCast, onCountered, onResolved, onDie, etc. events to implement custom rule bending.

This is kind of a compromise between a strictly data-driven approach (which makes simple cards easy but hard cards hard) and full domain-specific language (which would probably be best, but requires a lot more implementation effort).

[ TriangularPixels.com - Play Growth Spurt, Rescue Squad and Snowman Village ] [ Rebirth - game resource library ]
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline lulero

Junior Newbie





« Reply #10 - Posted 2011-10-13 08:42:01 »

A "really rich declarative system" is a readable API that can be seen as an internal DSL (without the need to create and maintain a compiler). It sounds like a good starting point, no matter if I later decide to go for a real (external) DSL or annotations driven code generation. Correct?

Plus, I don't think there is such thing as a simple card in a CCG. Whether or not they bend the rules, the amount of work needed to get things balanced will make the time spent writting them meaningless. And the way I do things right now isn't that slow anyway, here's the idea illustrated by an example:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
public final class Rhino extends Creature {
    static {
        CardFactory.add(this.getName(), Rhino.class);
    }

    @Override
    public String getName() {
        return "Rhino";
    }

    @Override
    public String getFlavorText() {
        return "They can be heard trampling from many miles away.";
    }

    @Override
    public void init() {
        super.init();
        this.setIntField(Card.Field.Cost, 2);
        this.setIntField(Creature.Field.Power, 2);
        this.setIntField(Creature.Field.Life, 3);
        this.setBoolField(Creature.Field.Trample, true);
    }
}


There's ton of ways to improve on that, and I'd like to hear your thoughts, but point is it's definitly not that time consuming to write. And if I want some on play/death/whatever effects I simply override the right method.

Still, I didn't discard the idea of annotations processing, but I have yet to clearly understand what it means. If you know of some good article/tutorial, let me know.
Offline sproingie

JGO Kernel


Medals: 202



« Reply #11 - Posted 2011-10-13 16:37:45 »

What you have is sort of declarative: rather than a method that defines how trampling works, you declare that it has the "trampling" effect, and presumably something else works it out.  The essence of declarative is "what, not how", i.e. you declare what something does and let some runtime figure it out.  

Thus in Dwarf Fortress, if Urist McFumblefingers casts an "ironskin" spell (magic is coming eventually) then gets hit by an imp's fireball, the game knows from the material properties and layers alone that the fireball will cook Urist internally, then leave a puddle of molten iron that used to be his skin, all without any extra procedural logic other than something like "target.replaceLayerMaterial(getLayer("skin"),getMaterial("iron"))".  Everything else is embodied in the definitions of fire (and imp fire is damn hot) the heat transfer functions, and the melt/boil/burn points of the various material layers that make up poor late Urist.

Heck DF will probably be smart enough soon to figure out that the boiling fat and water within in Urist McPressureCooker's iron skin will turn him into a shrapnel bomb before melting.
 
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.

Pippogeek (39 views)
2014-09-24 16:13:29

Pippogeek (30 views)
2014-09-24 16:12:22

Pippogeek (19 views)
2014-09-24 16:12:06

Grunnt (45 views)
2014-09-23 14:38:19

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

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

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

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

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

BurntPizza (54 views)
2014-09-19 03:14:18
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!