Some cool tricks & gems about Effects, Cinematics and RPG

So I got quite questions about how to Effects, Cinematics and Introductions in JME3 games. Some from my corporated partners and some from students who saw my examples online.

There are many ways to do it. I have my in-house CinematicEditor… We going to have an official CinematicEditor … We have Tonego Emittor Editor. For generic case, there is Animation4J, Java Animation framework. So i will not stop you to write up your own or use thoose. What I want to do it’s to help you with some concepts & classes that may help you to write one:

Concepts

  • Effect … What is it? An activities that not affect the gameplay but visually attractive :slight_smile: Yes. And we may talking about “Real-time effect”. Which involve time, and a timescale. Effect may be played by someone at the righttime. A bunch of effect may create various of beautiful things for your games. We will go for detail of Effect later.
  • Effector … It’s a thing that affect other thing. Change it, but not create or destroy it. The operation of Effector does can be seen as a Function from A->A, which still keep a Person is a Person.
  • Timing … It’s the way to schedule something ehead as a series of time and duration.

Implementation

  • JME3’s Cinematic, CinematicEvent are concepts will be use the help Timing. As You see, it’s pretty obviously designed for the task but not abstract enough. Beside of that, you can also use other scheduler, Guava’s ScheduledExecutor for example.
  • Effect can be parametezied as much as you can, you can also save or load it as a config, a JME object, as ParticleEmitter … The EffectPreset is the original of Effect before it’s staged. EffectPreset can be load and use to create other Effect base on one existed.
  • Effector should be pretty dynamic. Common implementations use Interpolator and Reflection and the alike to make dynamic procedural value.
    tween = vector1.intepolate(vector2,amount)
    Reflection.setProperty(“position”,tween);
    amount usually controlled by a curve call Easing ; Easing can be LINEAR, EASE_IN, EASE_OUT for example

You can see the same concepts in Animation4J which are generic to use in different framework. Also remember that not every value can be interpolated. Spatial.CullHint is one example. So aware of making options for not interpolatable value.

Futher more,

  • You can have a collections of known (Default) interpolator for specific Class, you can have a Map of this known types.
  • You can add dynamic constraints (one more layer of abstractions). For example: Positions can be composed by some vectors called forces. (This forces are actually live Vector3f references). So even if you loaded a effect of a ball moving around a point, but if the point move, the ball will still folow and move around them. This kind of things look a lot like ParticleEffect, which also are the concepts of Influencers based Particle effects which Tonegod write up.

Some applications
<span style=“text-decoration:underline;”>ParticleEffect and beyonds:</span>

Once you’ve learnt how to make Cinematic based ParticleEffect you will want to explore more possibilities as I did. So what i do is to write a EffectBuilder class which make it easy to write effect from Java code, also make it more paramaterized to be saved, also integrate with my codegen and editor to procedure code-generated version of the effects.

<span style=“text-decoration:underline;”>Cinematic</span>
You may want to extends the EffectEvent to which can be intepolated by its self and can be a timing target. Later, it can corporate and trigger Animation, Camera and what is already work in your Cinematic system. Also remember what the Forces static methods give you (something like simple accumulations of vectors like steering behaviors). You now can make better and smooth camera moves. The complete Camera system which driven by HFSM (or other reactive AI) are already in Atom framework that i wrote… But talk about that later.

<span style=“text-decoration:underline;”>Introduction</span>
You may see a lot of game has introductions and help system, tutorials or campaign level. Some of them has introductions which point you to object A, let you do one Action and greeting if you success. Such system can also be create with just JME3 Cinematic system and something like Guava’s Predicate (you can actually write one if you like).

cinematic.add(textEvent(“Pick the apple”));
cinematic.add(sequence(“InToTheGarden”));
cinematic.add(highlight(TheApple));
cinematic.add(waitEvent(tillPick(TheApple),“Greet, you are a quick learner”));

So the event will pause the cinematic system until the predicate is sastified.

Just above things can pretty much let you do kind a bunch of jobs that may be other pros don’t tell you and you have to figure out by your self. Or not? :)) Anyway, hope this actually helps. If you have more question, I hope me and other guys can spend some time to answer.

BUT Make the question short and precise, may be try to do your research time first!

1 Like

[From Guava to Game]
More cool tricks about: Events, ScheduledExecutor ,Futures , Callback.

Projectiles
The projectile is a very common object in games. It’s can be consider a kind of motion envolve gravity, forces and some checks. Naturally you may create a Control with Forces to compute the translations. Now the difficult part, how can the projectile callback as it hit or missed the target.

You can:

  • has a link of the projectile to its shooter, two methods onHit(Target), onMiss() and it’s good enough for almost case.
  • you can let the Projectile take two Predicate<Spatial> isHit and isMiss to check, let EventBus take care of the events propagations. That’s a big deal of loouse coupling. And may let you save some performances (!!!) by open a batch operation on some projectiles at onces.
  • you can make projectile shooting an listenable futures and use builtin callback as the computing complete.

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<Explosion> explosion = service.submit(new Callable<Explosion>() {
public Explosion call() {
return pushBigRedButton();
}
});
Futures.addCallback(explosion, new FutureCallback<Explosion>() {
// we want this handler to run immediately after we push the big red button!
public void onSuccess(Explosion explosion) {
walkAwayFrom(explosion);
}
public void onFailure(Throwable thrown) {
battleArchNemesis(); // escaped the explosion!
}
});

Now use your imagination!

As I not only talk about projectiles but a lot of ad-hoc operations in your game. Asset loading is one examples.

AssetLoading with another Cache and progress management.
Asset loading for simple game is one progress only and can be fast. Asset loading over network are difficult to complete without errors and some retries.

ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
ListenableFuture<Object> assetLoad= service.submit(new Callable<Spatial>() {
public Spatial call() {
return loadData(assetKey);
}
});

Futures.addCallback(assetLoad, new FutureCallback<Spatial>() {

public void onSuccess(Spatial spatial) {
attach(spatial)
}
public void onFailure(Throwable thrown) {
return retry(assetKey);
}
});
Note that you can also use Cache to try minimize your your loading cost in term of memory and performance cost.
LoadingCache<AssetKey, Spatial> models = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<AssetKey, Spatial>() {
public Graph load(AssetKey key) throws AssetNotFoundException {
return loadAsset(key);
}
});

TriggerSystem
You can also create a simple TriggerSystem with only Predicate, Runnable, Callable …

TriggerSystem.$(Predicates… conditions).do(new Runnable(){}).call(new Callable(){}).future(new ListenableFuture(){});

TriggerSystem extends AppState{
// You can also write an executor instead of update loop.
Service service;
// A Map or a Class your choice!
Bimap<Spatial,Trigger > spatialTriggers;
Multimap<Trigger,Predicate> predicates;
ArrayList<Trigger> trigger;
Multimap
update(float tpf){
for (trigger:trigger){
if (Predicates.from(predicates.get(trigger)).apply(spatialTriggers.get(trigger)){
trigger.active();
}
}
}
}

Above are some snippets ripped from Atom framework which i think worth to share. But as long as you know how to expand with the HELP of libraries (Guava) for example, it should not be as difficult as before.